diff options
author | 2020-08-13 21:13:31 +0000 | |
---|---|---|
committer | 2020-08-13 21:13:31 +0000 | |
commit | 151a52e1ebe58831df7ec48073c65ffb280916d0 (patch) | |
tree | 4a455ad9b706438ff17c37549251b1d9b2cb4a79 | |
parent | d6c2c257b3b1b232ceb7d98cca582c9b748ec8e4 (diff) | |
parent | 8971b49161314f81ad57ef7f627c16013f7602f0 (diff) |
Merge changes from topic "run_dex2oat_test" am: 8971b49161
Original change: https://android-review.googlesource.com/c/platform/frameworks/native/+/1395635
Change-Id: I62546194171ebc4b3889c05d49d415931d2e2a18
-rw-r--r-- | cmds/installd/Android.bp | 28 | ||||
-rw-r--r-- | cmds/installd/TEST_MAPPING | 3 | ||||
-rw-r--r-- | cmds/installd/dexopt.cpp | 413 | ||||
-rw-r--r-- | cmds/installd/execv_helper.cpp | 67 | ||||
-rw-r--r-- | cmds/installd/execv_helper.h | 55 | ||||
-rw-r--r-- | cmds/installd/run_dex2oat.cpp | 379 | ||||
-rw-r--r-- | cmds/installd/run_dex2oat.h | 82 | ||||
-rw-r--r-- | cmds/installd/run_dex2oat_test.cpp | 588 | ||||
-rw-r--r-- | cmds/installd/run_dex2oat_test.xml | 24 |
9 files changed, 1253 insertions, 386 deletions
diff --git a/cmds/installd/Android.bp b/cmds/installd/Android.bp index 75dec371bc..f7f8a939f9 100644 --- a/cmds/installd/Android.bp +++ b/cmds/installd/Android.bp @@ -16,7 +16,9 @@ cc_defaults { "InstalldNativeService.cpp", "QuotaUtils.cpp", "dexopt.cpp", + "execv_helper.cpp", "globals.cpp", + "run_dex2oat.cpp", "utils.cpp", "utils_default.cpp", "view_compiler.cpp", @@ -102,6 +104,30 @@ cc_library_headers { } // +// Unit tests +// + +cc_test_host { + name: "run_dex2oat_test", + test_suites: ["general-tests"], + clang: true, + srcs: [ + "run_dex2oat_test.cpp", + "run_dex2oat.cpp", + "execv_helper.cpp", + ], + cflags: ["-Wall", "-Werror"], + shared_libs: [ + "libbase", + "server_configurable_flags", + ], + static_libs: [ + //"libinstalld", + ], + test_config: "run_dex2oat_test.xml", +} + +// // Executable // @@ -203,9 +229,11 @@ cc_binary { srcs: [ "dexopt.cpp", + "execv_helper.cpp", "globals.cpp", "otapreopt.cpp", "otapreopt_utils.cpp", + "run_dex2oat.cpp", "utils.cpp", "utils_default.cpp", "view_compiler.cpp", diff --git a/cmds/installd/TEST_MAPPING b/cmds/installd/TEST_MAPPING index c6583a1bf4..3f0fb6d2ba 100644 --- a/cmds/installd/TEST_MAPPING +++ b/cmds/installd/TEST_MAPPING @@ -15,6 +15,9 @@ { "name": "installd_utils_test" }, + { + "name": "run_dex2oat_test" + }, // AdoptableHostTest moves packages, part of which is handled by installd { "name": "AdoptableHostTest" diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp index 82be00747a..2b36067983 100644 --- a/cmds/installd/dexopt.cpp +++ b/cmds/installd/dexopt.cpp @@ -50,11 +50,14 @@ #include "dexopt.h" #include "dexopt_return_codes.h" +#include "execv_helper.h" #include "globals.h" #include "installd_deps.h" #include "otapreopt_utils.h" +#include "run_dex2oat.h" #include "utils.h" +using android::base::Basename; using android::base::EndsWith; using android::base::GetBoolProperty; using android::base::GetProperty; @@ -67,16 +70,6 @@ using android::base::unique_fd; namespace android { namespace installd { -// Should minidebug info be included in compiled artifacts? Even if this value is -// "true," usage might still be conditional to other constraints, e.g., system -// property overrides. -static constexpr bool kEnableMinidebugInfo = true; - -static constexpr const char* kMinidebugInfoSystemProperty = "dalvik.vm.dex2oat-minidebuginfo"; -static constexpr bool kMinidebugInfoSystemPropertyDefault = false; -static constexpr const char* kMinidebugDex2oatFlag = "--generate-mini-debug-info"; -static constexpr const char* kDisableCompactDexFlag = "--compact-dex-level=none"; - // Deleter using free() for use with std::unique_ptr<>. See also UniqueCPtr<> below. struct FreeDelete { @@ -186,93 +179,6 @@ bool clear_primary_current_profile(const std::string& package_name, const std::s return clear_current_profile(package_name, location, user, /*is_secondary_dex*/false); } -static std::vector<std::string> SplitBySpaces(const std::string& str) { - if (str.empty()) { - return {}; - } - return android::base::Split(str, " "); -} - -static const char* get_location_from_path(const char* path) { - static constexpr char kLocationSeparator = '/'; - const char *location = strrchr(path, kLocationSeparator); - if (location == nullptr) { - return path; - } else { - // Skip the separator character. - return location + 1; - } -} - -// ExecVHelper prepares and holds pointers to parsed command line arguments so that no allocations -// need to be performed between the fork and exec. -class ExecVHelper { - public: - // Store a placeholder for the binary name. - ExecVHelper() : args_(1u, std::string()) {} - - void PrepareArgs(const std::string& bin) { - CHECK(!args_.empty()); - CHECK(args_[0].empty()); - args_[0] = bin; - // Write char* into array. - for (const std::string& arg : args_) { - argv_.push_back(arg.c_str()); - } - argv_.push_back(nullptr); // Add null terminator. - } - - [[ noreturn ]] - void Exec(int exit_code) { - execv(argv_[0], (char * const *)&argv_[0]); - PLOG(ERROR) << "execv(" << argv_[0] << ") failed"; - exit(exit_code); - } - - // Add an arg if it's not empty. - void AddArg(const std::string& arg) { - if (!arg.empty()) { - args_.push_back(arg); - } - } - - // Add a runtime arg if it's not empty. - void AddRuntimeArg(const std::string& arg) { - if (!arg.empty()) { - args_.push_back("--runtime-arg"); - args_.push_back(arg); - } - } - - protected: - // Holder arrays for backing arg storage. - std::vector<std::string> args_; - - // Argument poiners. - std::vector<const char*> argv_; -}; - -static std::string MapPropertyToArg(const std::string& property, - const std::string& format, - const std::string& default_value = "") { - std::string prop = GetProperty(property, default_value); - if (!prop.empty()) { - return StringPrintf(format.c_str(), prop.c_str()); - } - return ""; -} - -static std::string MapPropertyToArgWithBackup(const std::string& property, - const std::string& backupProperty, - const std::string& format, - const std::string& default_value = "") { - std::string value = GetProperty(property, default_value); - if (!value.empty()) { - return StringPrintf(format.c_str(), value.c_str()); - } - return MapPropertyToArg(backupProperty, format, default_value); -} - // Determines which binary we should use for execution (the debug or non-debug version). // e.g. dex2oatd vs dex2oat static const char* select_execution_binary(const char* binary, const char* debug_binary, @@ -311,9 +217,6 @@ const char* select_execution_binary( static const char* RUNTIME_NATIVE_BOOT_NAMESPACE = "runtime_native_boot"; // Feature flag name for running the JIT in Zygote experiment, b/119800099. static const char* ENABLE_JITZYGOTE_IMAGE = "enable_apex_image"; -// Location of the JIT Zygote image. -static const char* kJitZygoteImage = - "boot.art:/nonx/boot-framework.art!/system/etc/boot-image.prof"; // Phenotype property name for enabling profiling the boot class path. static const char* PROFILE_BOOT_CLASS_PATH = "profilebootclasspath"; @@ -328,289 +231,6 @@ static bool IsBootClassPathProfilingEnable() { return profile_boot_class_path == "true"; } -class RunDex2Oat : public ExecVHelper { - public: - RunDex2Oat(int zip_fd, - int oat_fd, - int input_vdex_fd, - int output_vdex_fd, - int image_fd, - const char* input_file_name, - const char* output_file_name, - int swap_fd, - const char* instruction_set, - const char* compiler_filter, - bool debuggable, - bool post_bootcomplete, - bool for_restore, - bool background_job_compile, - int profile_fd, - const char* class_loader_context, - const std::string& class_loader_context_fds, - int target_sdk_version, - bool enable_hidden_api_checks, - bool generate_compact_dex, - int dex_metadata_fd, - const char* compilation_reason) { - // Get the relative path to the input file. - const char* relative_input_file_name = get_location_from_path(input_file_name); - - std::string dex2oat_Xms_arg = MapPropertyToArg("dalvik.vm.dex2oat-Xms", "-Xms%s"); - std::string dex2oat_Xmx_arg = MapPropertyToArg("dalvik.vm.dex2oat-Xmx", "-Xmx%s"); - - std::string threads_format = "-j%s"; - std::string dex2oat_threads_arg = post_bootcomplete - ? (for_restore - ? MapPropertyToArgWithBackup( - "dalvik.vm.restore-dex2oat-threads", - "dalvik.vm.dex2oat-threads", - threads_format) - : MapPropertyToArg("dalvik.vm.dex2oat-threads", threads_format)) - : MapPropertyToArg("dalvik.vm.boot-dex2oat-threads", threads_format); - std::string cpu_set_format = "--cpu-set=%s"; - std::string dex2oat_cpu_set_arg = post_bootcomplete - ? (for_restore - ? MapPropertyToArgWithBackup( - "dalvik.vm.restore-dex2oat-cpu-set", - "dalvik.vm.dex2oat-cpu-set", - cpu_set_format) - : MapPropertyToArg("dalvik.vm.dex2oat-cpu-set", cpu_set_format)) - : MapPropertyToArg("dalvik.vm.boot-dex2oat-cpu-set", cpu_set_format); - - std::string bootclasspath; - char* dex2oat_bootclasspath = getenv("DEX2OATBOOTCLASSPATH"); - if (dex2oat_bootclasspath != nullptr) { - bootclasspath = StringPrintf("-Xbootclasspath:%s", dex2oat_bootclasspath); - } - // If DEX2OATBOOTCLASSPATH is not in the environment, dex2oat is going to query - // BOOTCLASSPATH. - - const std::string dex2oat_isa_features_key = - StringPrintf("dalvik.vm.isa.%s.features", instruction_set); - std::string instruction_set_features_arg = - MapPropertyToArg(dex2oat_isa_features_key, "--instruction-set-features=%s"); - - const std::string dex2oat_isa_variant_key = - StringPrintf("dalvik.vm.isa.%s.variant", instruction_set); - std::string instruction_set_variant_arg = - MapPropertyToArg(dex2oat_isa_variant_key, "--instruction-set-variant=%s"); - - const char* dex2oat_norelocation = "-Xnorelocate"; - - const std::string dex2oat_flags = GetProperty("dalvik.vm.dex2oat-flags", ""); - std::vector<std::string> dex2oat_flags_args = SplitBySpaces(dex2oat_flags); - ALOGV("dalvik.vm.dex2oat-flags=%s\n", dex2oat_flags.c_str()); - - // If we are booting without the real /data, don't spend time compiling. - std::string vold_decrypt = GetProperty("vold.decrypt", ""); - bool skip_compilation = vold_decrypt == "trigger_restart_min_framework" || - vold_decrypt == "1"; - - std::string updatable_bcp_packages = - MapPropertyToArg("dalvik.vm.dex2oat-updatable-bcp-packages-file", - "--updatable-bcp-packages-file=%s"); - if (updatable_bcp_packages.empty()) { - // Make dex2oat fail by providing non-existent file name. - updatable_bcp_packages = "--updatable-bcp-packages-file=/nonx/updatable-bcp-packages.txt"; - } - - std::string resolve_startup_string_arg = - MapPropertyToArg("persist.device_config.runtime.dex2oat_resolve_startup_strings", - "--resolve-startup-const-strings=%s"); - if (resolve_startup_string_arg.empty()) { - // If empty, fall back to system property. - resolve_startup_string_arg = - MapPropertyToArg("dalvik.vm.dex2oat-resolve-startup-strings", - "--resolve-startup-const-strings=%s"); - } - - const std::string image_block_size_arg = - MapPropertyToArg("dalvik.vm.dex2oat-max-image-block-size", - "--max-image-block-size=%s"); - - const bool generate_debug_info = GetBoolProperty("debug.generate-debug-info", false); - - std::string image_format_arg; - if (image_fd >= 0) { - image_format_arg = MapPropertyToArg("dalvik.vm.appimageformat", "--image-format=%s"); - } - - std::string dex2oat_large_app_threshold_arg = - MapPropertyToArg("dalvik.vm.dex2oat-very-large", "--very-large-app-threshold=%s"); - - - - // Decide whether to use dex2oat64. - bool use_dex2oat64 = false; - // Check whether the device even supports 64-bit ABIs. - if (!GetProperty("ro.product.cpu.abilist64", "").empty()) { - use_dex2oat64 = GetBoolProperty("dalvik.vm.dex2oat64.enabled", false); - } - const char* dex2oat_bin = select_execution_binary( - (use_dex2oat64 ? kDex2oat64Path : kDex2oat32Path), - (use_dex2oat64 ? kDex2oatDebug64Path : kDex2oatDebug32Path), - background_job_compile); - - bool generate_minidebug_info = kEnableMinidebugInfo && - GetBoolProperty(kMinidebugInfoSystemProperty, kMinidebugInfoSystemPropertyDefault); - - std::string boot_image; - std::string use_jitzygote_image = - server_configurable_flags::GetServerConfigurableFlag(RUNTIME_NATIVE_BOOT_NAMESPACE, - ENABLE_JITZYGOTE_IMAGE, - /*default_value=*/ ""); - - if (use_jitzygote_image == "true" || IsBootClassPathProfilingEnable()) { - boot_image = StringPrintf("--boot-image=%s", kJitZygoteImage); - } else { - boot_image = MapPropertyToArg("dalvik.vm.boot-image", "--boot-image=%s"); - } - - // clang FORTIFY doesn't let us use strlen in constant array bounds, so we - // use arraysize instead. - std::string zip_fd_arg = StringPrintf("--zip-fd=%d", zip_fd); - std::string zip_location_arg = StringPrintf("--zip-location=%s", relative_input_file_name); - std::string input_vdex_fd_arg = StringPrintf("--input-vdex-fd=%d", input_vdex_fd); - std::string output_vdex_fd_arg = StringPrintf("--output-vdex-fd=%d", output_vdex_fd); - std::string oat_fd_arg = StringPrintf("--oat-fd=%d", oat_fd); - std::string oat_location_arg = StringPrintf("--oat-location=%s", output_file_name); - std::string instruction_set_arg = StringPrintf("--instruction-set=%s", instruction_set); - std::string dex2oat_compiler_filter_arg; - std::string dex2oat_swap_fd; - std::string dex2oat_image_fd; - std::string target_sdk_version_arg; - if (target_sdk_version != 0) { - target_sdk_version_arg = StringPrintf("-Xtarget-sdk-version:%d", target_sdk_version); - } - std::string class_loader_context_arg; - std::string class_loader_context_fds_arg; - if (class_loader_context != nullptr) { - class_loader_context_arg = StringPrintf("--class-loader-context=%s", - class_loader_context); - if (!class_loader_context_fds.empty()) { - class_loader_context_fds_arg = StringPrintf("--class-loader-context-fds=%s", - class_loader_context_fds.c_str()); - } - } - - if (swap_fd >= 0) { - dex2oat_swap_fd = StringPrintf("--swap-fd=%d", swap_fd); - } - if (image_fd >= 0) { - dex2oat_image_fd = StringPrintf("--app-image-fd=%d", image_fd); - } - - // Compute compiler filter. - bool have_dex2oat_relocation_skip_flag = false; - if (skip_compilation) { - dex2oat_compiler_filter_arg = "--compiler-filter=extract"; - have_dex2oat_relocation_skip_flag = true; - } else if (compiler_filter != nullptr) { - dex2oat_compiler_filter_arg = StringPrintf("--compiler-filter=%s", compiler_filter); - } - - if (dex2oat_compiler_filter_arg.empty()) { - dex2oat_compiler_filter_arg = MapPropertyToArg("dalvik.vm.dex2oat-filter", - "--compiler-filter=%s"); - } - - // Check whether all apps should be compiled debuggable. - if (!debuggable) { - debuggable = GetProperty("dalvik.vm.always_debuggable", "") == "1"; - } - std::string profile_arg; - if (profile_fd != -1) { - profile_arg = StringPrintf("--profile-file-fd=%d", profile_fd); - } - - // Get the directory of the apk to pass as a base classpath directory. - std::string base_dir; - std::string apk_dir(input_file_name); - unsigned long dir_index = apk_dir.rfind('/'); - bool has_base_dir = dir_index != std::string::npos; - if (has_base_dir) { - apk_dir = apk_dir.substr(0, dir_index); - base_dir = StringPrintf("--classpath-dir=%s", apk_dir.c_str()); - } - - std::string dex_metadata_fd_arg = "--dm-fd=" + std::to_string(dex_metadata_fd); - - std::string compilation_reason_arg = compilation_reason == nullptr - ? "" - : std::string("--compilation-reason=") + compilation_reason; - - ALOGV("Running %s in=%s out=%s\n", dex2oat_bin, relative_input_file_name, output_file_name); - - // Disable cdex if update input vdex is true since this combination of options is not - // supported. - const bool disable_cdex = !generate_compact_dex || (input_vdex_fd == output_vdex_fd); - - AddArg(zip_fd_arg); - AddArg(zip_location_arg); - AddArg(input_vdex_fd_arg); - AddArg(output_vdex_fd_arg); - AddArg(oat_fd_arg); - AddArg(oat_location_arg); - AddArg(instruction_set_arg); - - AddArg(instruction_set_variant_arg); - AddArg(instruction_set_features_arg); - - AddArg(boot_image); - - AddRuntimeArg(bootclasspath); - AddRuntimeArg(dex2oat_Xms_arg); - AddRuntimeArg(dex2oat_Xmx_arg); - - AddArg(updatable_bcp_packages); - AddArg(resolve_startup_string_arg); - AddArg(image_block_size_arg); - AddArg(dex2oat_compiler_filter_arg); - AddArg(dex2oat_threads_arg); - AddArg(dex2oat_cpu_set_arg); - AddArg(dex2oat_swap_fd); - AddArg(dex2oat_image_fd); - - if (generate_debug_info) { - AddArg("--generate-debug-info"); - } - if (debuggable) { - AddArg("--debuggable"); - } - AddArg(image_format_arg); - AddArg(dex2oat_large_app_threshold_arg); - - if (have_dex2oat_relocation_skip_flag) { - AddRuntimeArg(dex2oat_norelocation); - } - AddArg(profile_arg); - AddArg(base_dir); - AddArg(class_loader_context_arg); - AddArg(class_loader_context_fds_arg); - if (generate_minidebug_info) { - AddArg(kMinidebugDex2oatFlag); - } - if (disable_cdex) { - AddArg(kDisableCompactDexFlag); - } - AddRuntimeArg(target_sdk_version_arg); - if (enable_hidden_api_checks) { - AddRuntimeArg("-Xhidden-api-policy:enabled"); - } - - if (dex_metadata_fd > -1) { - AddArg(dex_metadata_fd_arg); - } - - AddArg(compilation_reason_arg); - - // Do not add args after dex2oat_flags, they should override others for debugging. - args_.insert(args_.end(), dex2oat_flags_args.begin(), dex2oat_flags_args.end()); - - PrepareArgs(dex2oat_bin); - } -}; - /* * Whether dexopt should use a swap file when compiling an APK. * @@ -868,6 +488,7 @@ class RunProfman : public ExecVHelper { /*for_boot_image*/false); } + using ExecVHelper::Exec; // To suppress -Wno-overloaded-virtual void Exec() { ExecVHelper::Exec(DexoptReturnCodes::kProfmanExec); } @@ -1022,7 +643,7 @@ bool dump_profiles(int32_t uid, const std::string& pkgname, const std::string& p PLOG(ERROR) << "installd cannot open " << code_path.c_str(); return false; } - dex_locations.push_back(get_location_from_path(code_path.c_str())); + dex_locations.push_back(Basename(code_path)); apk_fds.push_back(std::move(apk_fd)); @@ -2220,9 +1841,29 @@ int dexopt(const char* dex_path, uid_t uid, const char* pkgname, const char* ins } } + std::string jitzygote_flag = server_configurable_flags::GetServerConfigurableFlag( + RUNTIME_NATIVE_BOOT_NAMESPACE, + ENABLE_JITZYGOTE_IMAGE, + /*default_value=*/ ""); + bool use_jitzygote_image = jitzygote_flag == "true" || IsBootClassPathProfilingEnable(); + + // Decide whether to use dex2oat64. + bool use_dex2oat64 = false; + // Check whether the device even supports 64-bit ABIs. + if (!GetProperty("ro.product.cpu.abilist64", "").empty()) { + use_dex2oat64 = GetBoolProperty("dalvik.vm.dex2oat64.enabled", false); + } + const char* dex2oat_bin = select_execution_binary( + (use_dex2oat64 ? kDex2oat64Path : kDex2oat32Path), + (use_dex2oat64 ? kDex2oatDebug64Path : kDex2oatDebug32Path), + background_job_compile); + + auto execv_helper = std::make_unique<ExecVHelper>(); + LOG(VERBOSE) << "DexInv: --- BEGIN '" << dex_path << "' ---"; - RunDex2Oat runner(input_fd.get(), + RunDex2Oat runner(dex2oat_bin, execv_helper.get()); + runner.Initialize(input_fd.get(), out_oat_fd.get(), in_vdex_fd.get(), out_vdex_fd.get(), @@ -2235,7 +1876,6 @@ int dexopt(const char* dex_path, uid_t uid, const char* pkgname, const char* ins debuggable, boot_complete, for_restore, - background_job_compile, reference_profile_fd.get(), class_loader_context, join_fds(context_input_fds), @@ -2243,6 +1883,7 @@ int dexopt(const char* dex_path, uid_t uid, const char* pkgname, const char* ins enable_hidden_api_checks, generate_compact_dex, dex_metadata_fd.get(), + use_jitzygote_image, compilation_reason); pid_t pid = fork(); diff --git a/cmds/installd/execv_helper.cpp b/cmds/installd/execv_helper.cpp new file mode 100644 index 0000000000..a2d240abb9 --- /dev/null +++ b/cmds/installd/execv_helper.cpp @@ -0,0 +1,67 @@ +/* + * 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. + */ +#define LOG_TAG "installd" + +#include "execv_helper.h" + +#include <stdlib.h> +#include <unistd.h> + +#include <string> + +#include <android-base/logging.h> +#include <android-base/properties.h> + +namespace android { +namespace installd { + +// Store a placeholder for the binary name. +ExecVHelper::ExecVHelper() : args_(1u, std::string()) {} + +ExecVHelper::~ExecVHelper() {} + +void ExecVHelper::PrepareArgs(const std::string& bin) { + CHECK(!args_.empty()); + CHECK(args_[0].empty()); + args_[0] = bin; + // Write char* into array. + for (const std::string& arg : args_) { + argv_.push_back(arg.c_str()); + } + argv_.push_back(nullptr); // Add null terminator. +} + +void ExecVHelper::Exec(int exit_code) { + execv(argv_[0], (char * const *)&argv_[0]); + PLOG(ERROR) << "execv(" << argv_[0] << ") failed"; + exit(exit_code); +} + +void ExecVHelper::AddArg(const std::string& arg) { + if (!arg.empty()) { + args_.push_back(arg); + } +} + +void ExecVHelper::AddRuntimeArg(const std::string& arg) { + if (!arg.empty()) { + args_.push_back("--runtime-arg"); + args_.push_back(arg); + } +} + +} // namespace installd +} // namespace android diff --git a/cmds/installd/execv_helper.h b/cmds/installd/execv_helper.h new file mode 100644 index 0000000000..9adfc0e812 --- /dev/null +++ b/cmds/installd/execv_helper.h @@ -0,0 +1,55 @@ +/* + * 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. + */ + +#ifndef ANDROID_INSTALLD_EXECV_HELPER_H +#define ANDROID_INSTALLD_EXECV_HELPER_H + +#include <string> +#include <vector> + +namespace android { +namespace installd { + +// ExecVHelper prepares and holds pointers to parsed command line arguments so that no allocations +// need to be performed between the fork and exec. +class ExecVHelper { + public: + ExecVHelper(); + virtual ~ExecVHelper(); + + [[ noreturn ]] + virtual void Exec(int exit_code); + + void PrepareArgs(const std::string& bin); + + // Add an arg if it's not empty. + void AddArg(const std::string& arg); + + // Add a runtime arg if it's not empty. + void AddRuntimeArg(const std::string& arg); + + protected: + // Holder arrays for backing arg storage. + std::vector<std::string> args_; + + // Argument poiners. + std::vector<const char*> argv_; +}; + +} // namespace installd +} // namespace android + +#endif // ANDROID_INSTALLD_EXECV_HELPER_H diff --git a/cmds/installd/run_dex2oat.cpp b/cmds/installd/run_dex2oat.cpp new file mode 100644 index 0000000000..8cac58f91f --- /dev/null +++ b/cmds/installd/run_dex2oat.cpp @@ -0,0 +1,379 @@ +/* + * 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. + */ +#define LOG_TAG "installd" + +#include "run_dex2oat.h" + +#include <memory> +#include <string> +#include <vector> + +#include <android-base/file.h> +#include <android-base/logging.h> +#include <android-base/properties.h> +#include <android-base/scopeguard.h> +#include <android-base/stringprintf.h> +#include <android-base/strings.h> +#include <log/log.h> +#include <server_configurable_flags/get_flags.h> + +using android::base::Basename; +using android::base::StringPrintf; + +namespace android { +namespace installd { + +namespace { + +// Should minidebug info be included in compiled artifacts? Even if this value is +// "true," usage might still be conditional to other constraints, e.g., system +// property overrides. +static constexpr bool kEnableMinidebugInfo = true; + +static constexpr const char* kMinidebugInfoSystemProperty = "dalvik.vm.dex2oat-minidebuginfo"; +static constexpr bool kMinidebugInfoSystemPropertyDefault = false; +static constexpr const char* kMinidebugDex2oatFlag = "--generate-mini-debug-info"; +static constexpr const char* kDisableCompactDexFlag = "--compact-dex-level=none"; + +// Location of the JIT Zygote image. +static const char* kJitZygoteImage = + "boot.art:/nonx/boot-framework.art!/system/etc/boot-image.prof"; + +std::vector<std::string> SplitBySpaces(const std::string& str) { + if (str.empty()) { + return {}; + } + return android::base::Split(str, " "); +} + +} // namespace + +RunDex2Oat::RunDex2Oat(const char* dex2oat_bin, ExecVHelper* execv_helper) + : dex2oat_bin_(dex2oat_bin), execv_helper_(execv_helper) {} + +void RunDex2Oat::Initialize(int zip_fd, + int oat_fd, + int input_vdex_fd, + int output_vdex_fd, + int image_fd, + const char* input_file_name, + const char* output_file_name, + int swap_fd, + const char* instruction_set, + const char* compiler_filter, + bool debuggable, + bool post_bootcomplete, + bool for_restore, + int profile_fd, + const char* class_loader_context, + const std::string& class_loader_context_fds, + int target_sdk_version, + bool enable_hidden_api_checks, + bool generate_compact_dex, + int dex_metadata_fd, + bool use_jitzygote_image, + const char* compilation_reason) { + // Get the relative path to the input file. + std::string input_basename = Basename(input_file_name); + + std::string dex2oat_Xms_arg = MapPropertyToArg("dalvik.vm.dex2oat-Xms", "-Xms%s"); + std::string dex2oat_Xmx_arg = MapPropertyToArg("dalvik.vm.dex2oat-Xmx", "-Xmx%s"); + + std::string threads_format = "-j%s"; + std::string dex2oat_threads_arg = post_bootcomplete + ? (for_restore + ? MapPropertyToArgWithBackup( + "dalvik.vm.restore-dex2oat-threads", + "dalvik.vm.dex2oat-threads", + threads_format) + : MapPropertyToArg("dalvik.vm.dex2oat-threads", threads_format)) + : MapPropertyToArg("dalvik.vm.boot-dex2oat-threads", threads_format); + std::string cpu_set_format = "--cpu-set=%s"; + std::string dex2oat_cpu_set_arg = post_bootcomplete + ? (for_restore + ? MapPropertyToArgWithBackup( + "dalvik.vm.restore-dex2oat-cpu-set", + "dalvik.vm.dex2oat-cpu-set", + cpu_set_format) + : MapPropertyToArg("dalvik.vm.dex2oat-cpu-set", cpu_set_format)) + : MapPropertyToArg("dalvik.vm.boot-dex2oat-cpu-set", cpu_set_format); + + std::string bootclasspath; + char* dex2oat_bootclasspath = getenv("DEX2OATBOOTCLASSPATH"); + if (dex2oat_bootclasspath != nullptr) { + bootclasspath = StringPrintf("-Xbootclasspath:%s", dex2oat_bootclasspath); + } + // If DEX2OATBOOTCLASSPATH is not in the environment, dex2oat is going to query + // BOOTCLASSPATH. + + const std::string dex2oat_isa_features_key = + StringPrintf("dalvik.vm.isa.%s.features", instruction_set); + std::string instruction_set_features_arg = + MapPropertyToArg(dex2oat_isa_features_key, "--instruction-set-features=%s"); + + const std::string dex2oat_isa_variant_key = + StringPrintf("dalvik.vm.isa.%s.variant", instruction_set); + std::string instruction_set_variant_arg = + MapPropertyToArg(dex2oat_isa_variant_key, "--instruction-set-variant=%s"); + + const char* dex2oat_norelocation = "-Xnorelocate"; + + const std::string dex2oat_flags = GetProperty("dalvik.vm.dex2oat-flags", ""); + std::vector<std::string> dex2oat_flags_args = SplitBySpaces(dex2oat_flags); + ALOGV("dalvik.vm.dex2oat-flags=%s\n", dex2oat_flags.c_str()); + + // If we are booting without the real /data, don't spend time compiling. + std::string vold_decrypt = GetProperty("vold.decrypt", ""); + bool skip_compilation = vold_decrypt == "trigger_restart_min_framework" || + vold_decrypt == "1"; + + std::string updatable_bcp_packages = + MapPropertyToArg("dalvik.vm.dex2oat-updatable-bcp-packages-file", + "--updatable-bcp-packages-file=%s"); + if (updatable_bcp_packages.empty()) { + // Make dex2oat fail by providing non-existent file name. + updatable_bcp_packages = "--updatable-bcp-packages-file=/nonx/updatable-bcp-packages.txt"; + } + + std::string resolve_startup_string_arg = + MapPropertyToArg("persist.device_config.runtime.dex2oat_resolve_startup_strings", + "--resolve-startup-const-strings=%s"); + if (resolve_startup_string_arg.empty()) { + // If empty, fall back to system property. + resolve_startup_string_arg = + MapPropertyToArg("dalvik.vm.dex2oat-resolve-startup-strings", + "--resolve-startup-const-strings=%s"); + } + + const std::string image_block_size_arg = + MapPropertyToArg("dalvik.vm.dex2oat-max-image-block-size", + "--max-image-block-size=%s"); + + const bool generate_debug_info = GetBoolProperty("debug.generate-debug-info", false); + + std::string image_format_arg; + if (image_fd >= 0) { + image_format_arg = MapPropertyToArg("dalvik.vm.appimageformat", "--image-format=%s"); + } + + std::string dex2oat_large_app_threshold_arg = + MapPropertyToArg("dalvik.vm.dex2oat-very-large", "--very-large-app-threshold=%s"); + + bool generate_minidebug_info = kEnableMinidebugInfo && + GetBoolProperty(kMinidebugInfoSystemProperty, kMinidebugInfoSystemPropertyDefault); + + std::string boot_image; + if (use_jitzygote_image) { + boot_image = StringPrintf("--boot-image=%s", kJitZygoteImage); + } else { + boot_image = MapPropertyToArg("dalvik.vm.boot-image", "--boot-image=%s"); + } + + // clang FORTIFY doesn't let us use strlen in constant array bounds, so we + // use arraysize instead. + std::string zip_fd_arg = StringPrintf("--zip-fd=%d", zip_fd); + std::string zip_location_arg = StringPrintf("--zip-location=%s", input_basename.c_str()); + std::string input_vdex_fd_arg = StringPrintf("--input-vdex-fd=%d", input_vdex_fd); + std::string output_vdex_fd_arg = StringPrintf("--output-vdex-fd=%d", output_vdex_fd); + std::string oat_fd_arg = StringPrintf("--oat-fd=%d", oat_fd); + std::string oat_location_arg = StringPrintf("--oat-location=%s", output_file_name); + std::string instruction_set_arg = StringPrintf("--instruction-set=%s", instruction_set); + std::string dex2oat_compiler_filter_arg; + std::string dex2oat_swap_fd; + std::string dex2oat_image_fd; + std::string target_sdk_version_arg; + if (target_sdk_version != 0) { + target_sdk_version_arg = StringPrintf("-Xtarget-sdk-version:%d", target_sdk_version); + } + std::string class_loader_context_arg; + std::string class_loader_context_fds_arg; + if (class_loader_context != nullptr) { + class_loader_context_arg = StringPrintf("--class-loader-context=%s", + class_loader_context); + if (!class_loader_context_fds.empty()) { + class_loader_context_fds_arg = StringPrintf("--class-loader-context-fds=%s", + class_loader_context_fds.c_str()); + } + } + + if (swap_fd >= 0) { + dex2oat_swap_fd = StringPrintf("--swap-fd=%d", swap_fd); + } + if (image_fd >= 0) { + dex2oat_image_fd = StringPrintf("--app-image-fd=%d", image_fd); + } + + // Compute compiler filter. + bool have_dex2oat_relocation_skip_flag = false; + if (skip_compilation) { + dex2oat_compiler_filter_arg = "--compiler-filter=extract"; + have_dex2oat_relocation_skip_flag = true; + } else if (compiler_filter != nullptr) { + dex2oat_compiler_filter_arg = StringPrintf("--compiler-filter=%s", compiler_filter); + } + + if (dex2oat_compiler_filter_arg.empty()) { + dex2oat_compiler_filter_arg = MapPropertyToArg("dalvik.vm.dex2oat-filter", + "--compiler-filter=%s"); + } + + // Check whether all apps should be compiled debuggable. + if (!debuggable) { + debuggable = GetProperty("dalvik.vm.always_debuggable", "") == "1"; + } + std::string profile_arg; + if (profile_fd != -1) { + profile_arg = StringPrintf("--profile-file-fd=%d", profile_fd); + } + + // Get the directory of the apk to pass as a base classpath directory. + std::string base_dir; + std::string apk_dir(input_file_name); + unsigned long dir_index = apk_dir.rfind('/'); + bool has_base_dir = dir_index != std::string::npos; + if (has_base_dir) { + apk_dir = apk_dir.substr(0, dir_index); + base_dir = StringPrintf("--classpath-dir=%s", apk_dir.c_str()); + } + + std::string dex_metadata_fd_arg = "--dm-fd=" + std::to_string(dex_metadata_fd); + + std::string compilation_reason_arg = compilation_reason == nullptr + ? "" + : std::string("--compilation-reason=") + compilation_reason; + + ALOGV("Running %s in=%s out=%s\n", dex2oat_bin_.c_str(), input_basename.c_str(), + output_file_name); + + // Disable cdex if update input vdex is true since this combination of options is not + // supported. + const bool disable_cdex = !generate_compact_dex || (input_vdex_fd == output_vdex_fd); + + AddArg(zip_fd_arg); + AddArg(zip_location_arg); + AddArg(input_vdex_fd_arg); + AddArg(output_vdex_fd_arg); + AddArg(oat_fd_arg); + AddArg(oat_location_arg); + AddArg(instruction_set_arg); + + AddArg(instruction_set_variant_arg); + AddArg(instruction_set_features_arg); + + AddArg(boot_image); + + AddRuntimeArg(bootclasspath); + AddRuntimeArg(dex2oat_Xms_arg); + AddRuntimeArg(dex2oat_Xmx_arg); + + AddArg(updatable_bcp_packages); + AddArg(resolve_startup_string_arg); + AddArg(image_block_size_arg); + AddArg(dex2oat_compiler_filter_arg); + AddArg(dex2oat_threads_arg); + AddArg(dex2oat_cpu_set_arg); + AddArg(dex2oat_swap_fd); + AddArg(dex2oat_image_fd); + + if (generate_debug_info) { + AddArg("--generate-debug-info"); + } + if (debuggable) { + AddArg("--debuggable"); + } + AddArg(image_format_arg); + AddArg(dex2oat_large_app_threshold_arg); + + if (have_dex2oat_relocation_skip_flag) { + AddRuntimeArg(dex2oat_norelocation); + } + AddArg(profile_arg); + AddArg(base_dir); + AddArg(class_loader_context_arg); + AddArg(class_loader_context_fds_arg); + if (generate_minidebug_info) { + AddArg(kMinidebugDex2oatFlag); + } + if (disable_cdex) { + AddArg(kDisableCompactDexFlag); + } + AddRuntimeArg(target_sdk_version_arg); + if (enable_hidden_api_checks) { + AddRuntimeArg("-Xhidden-api-policy:enabled"); + } + + if (dex_metadata_fd > -1) { + AddArg(dex_metadata_fd_arg); + } + + AddArg(compilation_reason_arg); + + // Do not add args after dex2oat_flags, they should override others for debugging. + for (auto it = dex2oat_flags_args.begin(); it != dex2oat_flags_args.end(); ++it) { + AddArg(*it); + } + + execv_helper_->PrepareArgs(dex2oat_bin_); +} + +RunDex2Oat::~RunDex2Oat() {} + +void RunDex2Oat::Exec(int exit_code) { + LOG(ERROR) << "RunDex2Oat::Exec"; + execv_helper_->Exec(exit_code); +} + +void RunDex2Oat::AddArg(const std::string& arg) { + execv_helper_->AddArg(arg); +} + +void RunDex2Oat::AddRuntimeArg(const std::string& arg) { + execv_helper_->AddRuntimeArg(arg); +} + +std::string RunDex2Oat::GetProperty(const std::string& key, + const std::string& default_value) { + return android::base::GetProperty(key, default_value); +} + +bool RunDex2Oat::GetBoolProperty(const std::string& key, bool default_value) { + return android::base::GetBoolProperty(key, default_value); +} + +std::string RunDex2Oat::MapPropertyToArg(const std::string& property, + const std::string& format, + const std::string& default_value) { + std::string prop = GetProperty(property, default_value); + if (!prop.empty()) { + return StringPrintf(format.c_str(), prop.c_str()); + } + return ""; +} + +std::string RunDex2Oat::MapPropertyToArgWithBackup( + const std::string& property, + const std::string& backupProperty, + const std::string& format, + const std::string& default_value) { + std::string value = GetProperty(property, default_value); + if (!value.empty()) { + return StringPrintf(format.c_str(), value.c_str()); + } + return MapPropertyToArg(backupProperty, format, default_value); +} + +} // namespace installd +} // namespace android diff --git a/cmds/installd/run_dex2oat.h b/cmds/installd/run_dex2oat.h new file mode 100644 index 0000000000..e4450b0d24 --- /dev/null +++ b/cmds/installd/run_dex2oat.h @@ -0,0 +1,82 @@ +/* + * 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. + */ + +#ifndef ANDROID_INSTALLD_RUN_DEX2OAT_H +#define ANDROID_INSTALLD_RUN_DEX2OAT_H + +#include <memory> +#include <string> + +#include "execv_helper.h" + +namespace android { +namespace installd { + +class RunDex2Oat { + public: + explicit RunDex2Oat(const char* dex2oat_bin, ExecVHelper* execv_helper); + virtual ~RunDex2Oat(); + + void Initialize(int zip_fd, + int oat_fd, + int input_vdex_fd, + int output_vdex_fd, + int image_fd, + const char* input_file_name, + const char* output_file_name, + int swap_fd, + const char* instruction_set, + const char* compiler_filter, + bool debuggable, + bool post_bootcomplete, + bool for_restore, + int profile_fd, + const char* class_loader_context, + const std::string& class_loader_context_fds, + int target_sdk_version, + bool enable_hidden_api_checks, + bool generate_compact_dex, + int dex_metadata_fd, + bool use_jitzygote_image, + const char* compilation_reason); + + void Exec(int exit_code); + + protected: + virtual std::string GetProperty(const std::string& key, const std::string& default_value); + virtual bool GetBoolProperty(const std::string& key, bool default_value); + + private: + void AddArg(const std::string& arg); + void AddRuntimeArg(const std::string& arg); + + std::string MapPropertyToArg(const std::string& property, + const std::string& format, + const std::string& default_value = ""); + + std::string MapPropertyToArgWithBackup(const std::string& property, + const std::string& backupProperty, + const std::string& format, + const std::string& default_value = ""); + + const std::string dex2oat_bin_; + ExecVHelper* execv_helper_; // not owned +}; + +} // namespace installd +} // namespace android + +#endif // ANDROID_INSTALLD_RUN_DEX2OAT_H diff --git a/cmds/installd/run_dex2oat_test.cpp b/cmds/installd/run_dex2oat_test.cpp new file mode 100644 index 0000000000..b1f429d01f --- /dev/null +++ b/cmds/installd/run_dex2oat_test.cpp @@ -0,0 +1,588 @@ +/* + * 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" + +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}}"; + + static constexpr int ZIP_FD = 1; + static constexpr int OAT_FD = 2; + static constexpr int INPUT_VDEX_FD = 3; + static constexpr int OUTPUT_VDEX_FD = 4; + static constexpr int IMAGE_FD = 5; + static constexpr int PROFILE_FD = 6; + static constexpr int DEX_METADATA_FD = 7; + static constexpr int SWAP_FD = 8; + + 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_file_name = INPUT_PATH; + args->zip_fd = ZIP_FD; + args->output_file_name = OUTPUT_PATH; + args->oat_fd = OAT_FD; + args->input_vdex_fd = INPUT_VDEX_FD; + args->output_vdex_fd = OUTPUT_VDEX_FD; + args->instruction_set = "arm64"; + args->compilation_reason = "rundex2oattest"; + return args; + } + + int zip_fd = -1; + int oat_fd = -1; + int input_vdex_fd = -1; + int output_vdex_fd = -1; + int image_fd = -1; + const char* input_file_name = nullptr; + const char* output_file_name = nullptr; + 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; + int profile_fd = -1; + 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; + int dex_metadata_fd = -1; + 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; + } + }; + + 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; + default_expected_flags_["--updatable-bcp-packages-file"] = + "=/nonx/updatable-bcp-packages.txt"; + + // 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->zip_fd, + args->oat_fd, + args->input_vdex_fd, + args->output_vdex_fd, + args->image_fd, + args->input_file_name, + args->output_file_name, + args->swap_fd, + args->instruction_set, + args->compiler_filter, + args->debuggable, + args->post_bootcomplete, + args->for_restore, + args->profile_fd, + args->class_loader_context, + args->class_loader_context_fds, + args->target_sdk_version, + args->enable_hidden_api_checks, + args->generate_compact_dex, + args->dex_metadata_fd, + 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->image_fd = IMAGE_FD; + args->profile_fd = PROFILE_FD; + 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, DEX2OATBOOTCLASSPATH) { + ASSERT_EQ(nullptr, getenv("DEX2OATBOOTCLASSPATH")); + ASSERT_EQ(0, setenv("DEX2OATBOOTCLASSPATH", "foobar", /*override=*/ false)) + << "Failed to setenv: " << strerror(errno); + + CallRunDex2Oat(RunDex2OatArgs::MakeDefaultTestArgs()); + + SetExpectedFlagUsed("-Xbootclasspath", ":foobar"); + VerifyExpectedFlags(); + + ASSERT_EQ(0, unsetenv("DEX2OATBOOTCLASSPATH")) + << "Failed to setenv: " << strerror(errno); +} + +TEST_F(RunDex2OatTest, UpdatableBootClassPath) { + setSystemProperty("dalvik.vm.dex2oat-updatable-bcp-packages-file", "/path/to/file"); + CallRunDex2Oat(RunDex2OatArgs::MakeDefaultTestArgs()); + + SetExpectedFlagUsed("--updatable-bcp-packages-file", "=/path/to/file"); + 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_fd = INPUT_VDEX_FD; + args->output_vdex_fd = INPUT_VDEX_FD; + 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 diff --git a/cmds/installd/run_dex2oat_test.xml b/cmds/installd/run_dex2oat_test.xml new file mode 100644 index 0000000000..2467fe4e83 --- /dev/null +++ b/cmds/installd/run_dex2oat_test.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> +<configuration description="Unittest of run_dex2oat"> + <option name="test-suite-tag" value="apct" /> + <option name="test-suite-tag" value="apct-native" /> + + <option name="null-device" value="true" /> + <test class="com.android.tradefed.testtype.HostGTest" > + <option name="module-name" value="run_dex2oat_test" /> + </test> +</configuration> |