diff options
Diffstat (limited to 'cmds/installd/run_dex2oat_test.cpp')
-rw-r--r-- | cmds/installd/run_dex2oat_test.cpp | 561 |
1 files changed, 561 insertions, 0 deletions
diff --git a/cmds/installd/run_dex2oat_test.cpp b/cmds/installd/run_dex2oat_test.cpp new file mode 100644 index 0000000000..0a638cd51b --- /dev/null +++ b/cmds/installd/run_dex2oat_test.cpp @@ -0,0 +1,561 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <map> +#include <memory> +#include <string> + +#include <android-base/logging.h> + +#include <gtest/gtest.h> + +#include "execv_helper.h" +#include "run_dex2oat.h" +#include "unique_file.h" + +namespace android { +namespace installd { + +class RunDex2OatTest : public testing::Test { + public: + static constexpr const char* INPUT_PATH = "/dir/input/basename.apk"; + static constexpr const char* OUTPUT_PATH = "/dir/output/basename.oat"; + static constexpr const char* FLAG_UNUSED = "{{FLAG_UNUSED}}"; + + // UniqueFile closes FD. Avoid using standard I/O since the test is expected to print gtest + // results. Alternatively, mock out UniqueFile to avoid the side effect of close(2). + static constexpr int ZIP_FD = 3; + static constexpr int OAT_FD = 4; + static constexpr int INPUT_VDEX_FD = 5; + static constexpr int OUTPUT_VDEX_FD = 6; + static constexpr int IMAGE_FD = 7; + static constexpr int PROFILE_FD = 8; + static constexpr int DEX_METADATA_FD = 9; + static constexpr int SWAP_FD = 10; + + using FakeSystemProperties = std::map<std::string, std::string>; + + // A fake RunDex2Oat that allows to override (fake) system properties and starts with none. + class FakeRunDex2Oat : public RunDex2Oat { + private: + static constexpr const char* TRUE_STR = "true"; + static constexpr const char* FALSE_STR = "false"; + + public: + FakeRunDex2Oat(ExecVHelper* execv_helper, FakeSystemProperties* properties) + : RunDex2Oat("/dir/bin/dex2oat", execv_helper), properties_(properties) { } + + virtual ~FakeRunDex2Oat() {} + + virtual std::string GetProperty(const std::string& key, + const std::string& default_value) override { + if (!properties_) { + return default_value; + } + auto iter = properties_->find(key); + if (iter == properties_->end()) { + return default_value; + } + return iter->second; + } + + virtual bool GetBoolProperty(const std::string& key, bool default_value) override { + std::string value = GetProperty(key, ""); + if (value == "") { + return default_value; + } + return value == TRUE_STR; + } + + private: + FakeSystemProperties* properties_; + }; + + struct RunDex2OatArgs { + static std::unique_ptr<RunDex2OatArgs> MakeDefaultTestArgs() { + auto args = std::make_unique<RunDex2OatArgs>(); + args->input_dex.reset(ZIP_FD, INPUT_PATH); + args->output_oat.reset(OAT_FD, OUTPUT_PATH); + args->input_vdex.reset(INPUT_VDEX_FD, "UNUSED_PATH"); + args->output_vdex.reset(OUTPUT_VDEX_FD, "UNUSED_PATH"); + args->instruction_set = "arm64"; + args->compilation_reason = "rundex2oattest"; + return args; + } + + UniqueFile output_oat; + UniqueFile output_vdex; + UniqueFile output_image; + UniqueFile input_dex; + UniqueFile input_vdex; + UniqueFile dex_metadata; + UniqueFile profile; + int swap_fd = -1; + const char* instruction_set = nullptr; + const char* compiler_filter = "extract"; + bool debuggable = false; + bool post_bootcomplete = false; + bool for_restore = false; + const char* class_loader_context = nullptr; + std::string class_loader_context_fds; + int target_sdk_version = 0; + bool enable_hidden_api_checks = false; + bool generate_compact_dex = true; + bool use_jitzygote_image = false; + const char* compilation_reason = nullptr; + }; + + class FakeExecVHelper : public ExecVHelper { + public: + bool HasArg(const std::string& arg) const { + auto end = argv_.end() - 1; // To exclude the terminating nullptr + return find(argv_.begin(), end, arg) != end; + } + + bool FlagNotUsed(const std::string& flag) const { + auto has_prefix = [flag](const char* arg) { + return strncmp(arg, flag.c_str(), flag.size()) == 0; + }; + auto end = argv_.end() - 1; // To exclude the terminating nullptr + return find_if(argv_.begin(), end, has_prefix) == end; + } + + virtual void Exec(int exit_code) override { + std::string cmd; + for (auto arg : argv_) { + if (arg == nullptr) { + continue; + } + cmd += arg; + cmd += " "; + } + LOG(DEBUG) << "FakeExecVHelper exit_code: " << exit_code << " cmd: " << cmd << "\n"; + } + }; + + virtual void SetUp() override { + execv_helper_.reset(new FakeExecVHelper()); + system_properties_.clear(); + initializeDefaultExpectedFlags(); + } + + // Initializes the default flags expected to a run. It currently matches to the expected flags + // with RunDex2OatArgs::MakeDefaultTestArgs. + // + // default_expected_flags_ defines a mapping of <flag_name, expected_value>, where flag_name is + // something like "--flag-name", and expected_value can be "=value" or ":value" (depending on + // its delimiter), "" (if no value is needed), or a special value of FLAG_UNUSED to indicates + // that it should not be used. + void initializeDefaultExpectedFlags() { + default_expected_flags_.clear(); + + // Files + default_expected_flags_["--zip-fd"] = "=" + std::to_string(ZIP_FD); + default_expected_flags_["--zip-location"] = "=basename.apk"; + default_expected_flags_["--oat-fd"] = "=" + std::to_string(OAT_FD); + default_expected_flags_["--oat-location"] = "=" + std::string(OUTPUT_PATH); + default_expected_flags_["--input-vdex-fd"] = "=" + std::to_string(INPUT_VDEX_FD); + default_expected_flags_["--output-vdex-fd"] = "=" + std::to_string(OUTPUT_VDEX_FD); + default_expected_flags_["--classpath-dir"] = "=/dir/input"; + default_expected_flags_["--app-image-fd"] = FLAG_UNUSED; + default_expected_flags_["--profile-file-fd"] = FLAG_UNUSED; + default_expected_flags_["--swap-fd"] = FLAG_UNUSED; + default_expected_flags_["--class-loader-context"] = FLAG_UNUSED; + default_expected_flags_["--class-loader-context-fds"] = FLAG_UNUSED; + + // Arch + default_expected_flags_["--instruction-set"] = "=arm64"; + default_expected_flags_["--instruction-set-features"] = FLAG_UNUSED; + default_expected_flags_["--instruction-set-variant"] = FLAG_UNUSED; + default_expected_flags_["--cpu-set"] = FLAG_UNUSED; + + // Misc + default_expected_flags_["--compiler-filter"] = "=extract"; + default_expected_flags_["--compilation-reason"] = "=rundex2oattest"; + default_expected_flags_["--compact-dex-level"] = FLAG_UNUSED; + default_expected_flags_["-j"] = FLAG_UNUSED; + default_expected_flags_["--max-image-block-size"] = FLAG_UNUSED; + default_expected_flags_["--very-large-app-threshold"] = FLAG_UNUSED; + default_expected_flags_["--resolve-startup-const-strings"] = FLAG_UNUSED; + + // Debug + default_expected_flags_["--debuggable"] = FLAG_UNUSED; + default_expected_flags_["--generate-debug-info"] = FLAG_UNUSED; + default_expected_flags_["--generate-mini-debug-info"] = FLAG_UNUSED; + + // Runtime + // TODO(victorhsieh): Check if the previous flag is actually --runtime-arg. + default_expected_flags_["-Xms"] = FLAG_UNUSED; + default_expected_flags_["-Xmx"] = FLAG_UNUSED; + default_expected_flags_["-Xbootclasspath"] = FLAG_UNUSED; + default_expected_flags_["-Xtarget-sdk-version"] = FLAG_UNUSED; + default_expected_flags_["-Xhidden-api-policy"] = FLAG_UNUSED; + default_expected_flags_["-Xnorelocate"] = FLAG_UNUSED; + + // Test only + default_expected_flags_["--foo"] = FLAG_UNUSED; + default_expected_flags_["--bar"] = FLAG_UNUSED; + default_expected_flags_["--baz"] = FLAG_UNUSED; + } + + void SetExpectedFlagUsed(const std::string& flag, const std::string& value) { + auto iter = default_expected_flags_.find(flag); + ASSERT_NE(iter, default_expected_flags_.end()) << "Must define the default value"; + iter->second = value; + } + + void VerifyExpectedFlags() { + for (auto const& [flag, value] : default_expected_flags_) { + if (value == FLAG_UNUSED) { + EXPECT_TRUE(execv_helper_->FlagNotUsed(flag)) + << "Flag " << flag << " should be unused, but got the value " << value; + } else if (value == "") { + EXPECT_TRUE(execv_helper_->HasArg(flag)) + << "Flag " << flag << " should be specified without value, but got " << value; + } else { + EXPECT_TRUE(execv_helper_->HasArg(flag + value)) + << "Flag " << flag << value << " is not specificed"; + } + } + } + + void setSystemProperty(const std::string& key, const std::string& value) { + system_properties_[key] = value; + } + + void CallRunDex2Oat(std::unique_ptr<RunDex2OatArgs> args) { + FakeRunDex2Oat runner(execv_helper_.get(), &system_properties_); + runner.Initialize(args->output_oat, + args->output_vdex, + args->output_image, + args->input_dex, + args->input_vdex, + args->dex_metadata, + args->profile, + args->class_loader_context, + args->class_loader_context_fds, + args->swap_fd, + args->instruction_set, + args->compiler_filter, + args->debuggable, + args->post_bootcomplete, + args->for_restore, + args->target_sdk_version, + args->enable_hidden_api_checks, + args->generate_compact_dex, + args->use_jitzygote_image, + args->compilation_reason); + runner.Exec(/*exit_code=*/ 0); + } + + private: + std::unique_ptr<FakeExecVHelper> execv_helper_; + std::map<std::string, std::string> default_expected_flags_; + FakeSystemProperties system_properties_; +}; + +TEST_F(RunDex2OatTest, BasicInputOutput) { + auto execv_helper = std::make_unique<FakeExecVHelper>(); + CallRunDex2Oat(RunDex2OatArgs::MakeDefaultTestArgs()); + + VerifyExpectedFlags(); +} + +TEST_F(RunDex2OatTest, WithAllOtherInputFds) { + auto args = RunDex2OatArgs::MakeDefaultTestArgs(); + args->output_image.reset(IMAGE_FD, "UNUSED_PATH"); + args->profile.reset(PROFILE_FD, "UNUSED_PATH"); + args->swap_fd = SWAP_FD; + CallRunDex2Oat(std::move(args)); + + SetExpectedFlagUsed("--app-image-fd", "=" + std::to_string(IMAGE_FD)); + SetExpectedFlagUsed("--profile-file-fd", "=" + std::to_string(PROFILE_FD)); + SetExpectedFlagUsed("--swap-fd", "=" + std::to_string(SWAP_FD)); + VerifyExpectedFlags(); +} + +TEST_F(RunDex2OatTest, WithClassLoaderContext) { + auto args = RunDex2OatArgs::MakeDefaultTestArgs(); + args->class_loader_context = "CLASS_LOADER_CONTEXT"; + CallRunDex2Oat(std::move(args)); + + SetExpectedFlagUsed("--class-loader-context", "=CLASS_LOADER_CONTEXT"); + SetExpectedFlagUsed("--class-loader-context-fds", FLAG_UNUSED); + VerifyExpectedFlags(); +} + +TEST_F(RunDex2OatTest, WithClassLoaderContextAndFds) { + auto args = RunDex2OatArgs::MakeDefaultTestArgs(); + args->class_loader_context = "CLASS_LOADER_CONTEXT"; + args->class_loader_context_fds = "CLASS_LOADER_CONTEXT_FDS"; + CallRunDex2Oat(std::move(args)); + + SetExpectedFlagUsed("--class-loader-context", "=CLASS_LOADER_CONTEXT"); + SetExpectedFlagUsed("--class-loader-context-fds", "=CLASS_LOADER_CONTEXT_FDS"); + VerifyExpectedFlags(); +} + +TEST_F(RunDex2OatTest, WithOnlyClassLoaderContextFds) { + auto args = RunDex2OatArgs::MakeDefaultTestArgs(); + args->class_loader_context_fds = "CLASS_LOADER_CONTEXT_FDS"; + CallRunDex2Oat(std::move(args)); + + SetExpectedFlagUsed("--class-loader-context", FLAG_UNUSED); + SetExpectedFlagUsed("--class-loader-context-fds", FLAG_UNUSED); + VerifyExpectedFlags(); +} + +TEST_F(RunDex2OatTest, DoNotGenerateCompactDex) { + auto args = RunDex2OatArgs::MakeDefaultTestArgs(); + args->generate_compact_dex = false; + CallRunDex2Oat(std::move(args)); + + SetExpectedFlagUsed("--compact-dex-level", "=none"); + VerifyExpectedFlags(); +} + +TEST_F(RunDex2OatTest, DoNotGenerateCompactDexWithVdexInPlaceUpdate) { + auto args = RunDex2OatArgs::MakeDefaultTestArgs(); + args->generate_compact_dex = true; + args->input_vdex.reset(INPUT_VDEX_FD, "UNUSED_PATH"); + args->output_vdex.reset(INPUT_VDEX_FD, "UNUSED_PATH"); + CallRunDex2Oat(std::move(args)); + + SetExpectedFlagUsed("--compact-dex-level", "=none"); + SetExpectedFlagUsed("--output-vdex-fd", "=" + std::to_string(INPUT_VDEX_FD)); + VerifyExpectedFlags(); +} + +TEST_F(RunDex2OatTest, ISA) { + setSystemProperty("dalvik.vm.isa.x86.features", "a-x86-feature"); + setSystemProperty("dalvik.vm.isa.x86.variant", "a-x86-variant"); + auto args = RunDex2OatArgs::MakeDefaultTestArgs(); + args->instruction_set = "x86"; + CallRunDex2Oat(std::move(args)); + + SetExpectedFlagUsed("--instruction-set", "=x86"); + SetExpectedFlagUsed("--instruction-set-features", "=a-x86-feature"); + SetExpectedFlagUsed("--instruction-set-variant", "=a-x86-variant"); + VerifyExpectedFlags(); +} + +TEST_F(RunDex2OatTest, CpuSetPreBootComplete) { + setSystemProperty("dalvik.vm.boot-dex2oat-cpu-set", "1,2"); + auto args = RunDex2OatArgs::MakeDefaultTestArgs(); + args->post_bootcomplete = false; + CallRunDex2Oat(std::move(args)); + + SetExpectedFlagUsed("--cpu-set", "=1,2"); + VerifyExpectedFlags(); +} + +TEST_F(RunDex2OatTest, CpuSetPostBootCompleteNotForRestore) { + setSystemProperty("dalvik.vm.dex2oat-cpu-set", "1,2"); + auto args = RunDex2OatArgs::MakeDefaultTestArgs(); + args->post_bootcomplete = true; + args->for_restore = false; + CallRunDex2Oat(std::move(args)); + + SetExpectedFlagUsed("--cpu-set", "=1,2"); + VerifyExpectedFlags(); +} + +TEST_F(RunDex2OatTest, CpuSetPostBootCompleteForRestore) { + setSystemProperty("dalvik.vm.restore-dex2oat-cpu-set", "1,2"); + setSystemProperty("dalvik.vm.dex2oat-cpu-set", "2,3"); + auto args = RunDex2OatArgs::MakeDefaultTestArgs(); + args->post_bootcomplete = true; + args->for_restore = true; + CallRunDex2Oat(std::move(args)); + + SetExpectedFlagUsed("--cpu-set", "=1,2"); + VerifyExpectedFlags(); +} + +TEST_F(RunDex2OatTest, CpuSetPostBootCompleteForRestore_Backup) { + setSystemProperty("dalvik.vm.restore-dex2oat-cpu-set", ""); + setSystemProperty("dalvik.vm.dex2oat-cpu-set", "1,2"); + auto args = RunDex2OatArgs::MakeDefaultTestArgs(); + args->post_bootcomplete = true; + args->for_restore = true; + CallRunDex2Oat(std::move(args)); + + SetExpectedFlagUsed("--cpu-set", "=1,2"); + VerifyExpectedFlags(); +} + +TEST_F(RunDex2OatTest, Runtime) { + setSystemProperty("dalvik.vm.dex2oat-Xms", "1234m"); + setSystemProperty("dalvik.vm.dex2oat-Xmx", "5678m"); + auto args = RunDex2OatArgs::MakeDefaultTestArgs(); + args->target_sdk_version = 30; + args->enable_hidden_api_checks = true; + CallRunDex2Oat(std::move(args)); + + SetExpectedFlagUsed("-Xms", "1234m"); + SetExpectedFlagUsed("-Xmx", "5678m"); + SetExpectedFlagUsed("-Xtarget-sdk-version", ":30"); + SetExpectedFlagUsed("-Xhidden-api-policy", ":enabled"); + SetExpectedFlagUsed("-Xnorelocate", FLAG_UNUSED); + VerifyExpectedFlags(); +} + +TEST_F(RunDex2OatTest, SkipRelocationInMinFramework) { + setSystemProperty("vold.decrypt", "trigger_restart_min_framework"); + CallRunDex2Oat(RunDex2OatArgs::MakeDefaultTestArgs()); + + SetExpectedFlagUsed("--compiler-filter", "=extract"); + SetExpectedFlagUsed("-Xnorelocate", ""); + VerifyExpectedFlags(); +} + +TEST_F(RunDex2OatTest, SkipRelocationIfDecryptedWithFullDiskEncryption) { + setSystemProperty("vold.decrypt", "1"); + CallRunDex2Oat(RunDex2OatArgs::MakeDefaultTestArgs()); + + SetExpectedFlagUsed("--compiler-filter", "=extract"); + SetExpectedFlagUsed("-Xnorelocate", ""); + VerifyExpectedFlags(); +} + +TEST_F(RunDex2OatTest, DalvikVmDex2oatFilter) { + setSystemProperty("dalvik.vm.dex2oat-filter", "speed"); + auto args = RunDex2OatArgs::MakeDefaultTestArgs(); + args->compiler_filter = nullptr; + CallRunDex2Oat(std::move(args)); + + SetExpectedFlagUsed("--compiler-filter", "=speed"); + VerifyExpectedFlags(); +} + +TEST_F(RunDex2OatTest, ResolveStartupStartings) { + setSystemProperty("dalvik.vm.dex2oat-resolve-startup-strings", "false"); + CallRunDex2Oat(RunDex2OatArgs::MakeDefaultTestArgs()); + + SetExpectedFlagUsed("--resolve-startup-const-strings", "=false"); + VerifyExpectedFlags(); +} + +TEST_F(RunDex2OatTest, ResolveStartupStartingsOverride) { + setSystemProperty("dalvik.vm.dex2oat-resolve-startup-strings", "false"); + setSystemProperty("persist.device_config.runtime.dex2oat_resolve_startup_strings", "true"); + CallRunDex2Oat(RunDex2OatArgs::MakeDefaultTestArgs()); + + SetExpectedFlagUsed("--resolve-startup-const-strings", "=true"); + VerifyExpectedFlags(); +} + +TEST_F(RunDex2OatTest, ThreadsPreBootComplete) { + setSystemProperty("dalvik.vm.boot-dex2oat-threads", "2"); + auto args = RunDex2OatArgs::MakeDefaultTestArgs(); + args->post_bootcomplete = false; + CallRunDex2Oat(std::move(args)); + + SetExpectedFlagUsed("-j", "2"); + VerifyExpectedFlags(); +} + +TEST_F(RunDex2OatTest, ThreadsPostBootCompleteNotForRestore) { + setSystemProperty("dalvik.vm.dex2oat-threads", "3"); + auto args = RunDex2OatArgs::MakeDefaultTestArgs(); + args->post_bootcomplete = true; + args->for_restore = false; + CallRunDex2Oat(std::move(args)); + + SetExpectedFlagUsed("-j", "3"); + VerifyExpectedFlags(); +} + +TEST_F(RunDex2OatTest, ThreadsPostBootCompleteForRestore) { + setSystemProperty("dalvik.vm.restore-dex2oat-threads", "4"); + setSystemProperty("dalvik.vm.dex2oat-threads", "5"); + auto args = RunDex2OatArgs::MakeDefaultTestArgs(); + args->post_bootcomplete = true; + args->for_restore = true; + CallRunDex2Oat(std::move(args)); + + SetExpectedFlagUsed("-j", "4"); + VerifyExpectedFlags(); +} + +TEST_F(RunDex2OatTest, ThreadsPostBootCompleteForRestore_Backup) { + setSystemProperty("dalvik.vm.restore-dex2oat-threads", ""); + setSystemProperty("dalvik.vm.dex2oat-threads", "5"); + auto args = RunDex2OatArgs::MakeDefaultTestArgs(); + args->post_bootcomplete = true; + args->for_restore = true; + CallRunDex2Oat(std::move(args)); + + SetExpectedFlagUsed("-j", "5"); + VerifyExpectedFlags(); +} + +TEST_F(RunDex2OatTest, Debuggable) { + auto args = RunDex2OatArgs::MakeDefaultTestArgs(); + args->debuggable = true; + CallRunDex2Oat(std::move(args)); + + SetExpectedFlagUsed("--debuggable", ""); + VerifyExpectedFlags(); +} + +TEST_F(RunDex2OatTest, AlwaysDebuggable) { + setSystemProperty("dalvik.vm.always_debuggable", "1"); + CallRunDex2Oat(RunDex2OatArgs::MakeDefaultTestArgs()); + + SetExpectedFlagUsed("--debuggable", ""); + VerifyExpectedFlags(); +} + +TEST_F(RunDex2OatTest, GenerateDebugInfo) { + setSystemProperty("debug.generate-debug-info", "true"); + CallRunDex2Oat(RunDex2OatArgs::MakeDefaultTestArgs()); + + SetExpectedFlagUsed("--generate-debug-info", ""); + VerifyExpectedFlags(); +} + +TEST_F(RunDex2OatTest, HiddenApiCheck) { + auto args = RunDex2OatArgs::MakeDefaultTestArgs(); + args->enable_hidden_api_checks = true; + CallRunDex2Oat(std::move(args)); + + SetExpectedFlagUsed("-Xhidden-api-policy", ":enabled"); + VerifyExpectedFlags(); +} + +TEST_F(RunDex2OatTest, Misc) { + setSystemProperty("dalvik.vm.dex2oat-max-image-block-size", "524288"); + setSystemProperty("dalvik.vm.dex2oat-very-large", "100000"); + CallRunDex2Oat(RunDex2OatArgs::MakeDefaultTestArgs()); + + SetExpectedFlagUsed("--max-image-block-size", "=524288"); + SetExpectedFlagUsed("--very-large-app-threshold", "=100000"); + VerifyExpectedFlags(); +} + +TEST_F(RunDex2OatTest, ExtraFlags) { + setSystemProperty("dalvik.vm.dex2oat-flags", "--foo=123 --bar:456 --baz"); + CallRunDex2Oat(RunDex2OatArgs::MakeDefaultTestArgs()); + + SetExpectedFlagUsed("--foo", "=123"); + SetExpectedFlagUsed("--bar", ":456"); + SetExpectedFlagUsed("--baz", ""); + VerifyExpectedFlags(); +} + +} // namespace installd +} // namespace android |