Reland^2: Create an abstraction for dexopt tasks
Cause of the revert / broken test:
buildbot-build.sh does not build libbinder_ndk.so. Add the missing
dependency to copy to the chroot.
Original message:
Instead of letting odrefresh building dex2oat cmdline arguments, hide
the specifics of cmdline flags behind an API abstraction. The API
defines the only necessary parameters for the actual dexopt task (e.g.
compiling BCP extension, system server) to proceed. The introduced API
is private within ART.
libdexopt.so is created to contain the API and implementation. It is put
under art/artd/ in the sense that the future ART services can reuse and
evolve the code.
This change should not have any effective change to the compilation
results. Given the large size of the change, the dex2oat cmdline is
purposely ordered in the same way as before. The compiled artifacts
should not change at all.
Also, art_odrefresh_test is no longer built for the "second" ABI. This
is to make it consistent to odrefresh, so that we won't need to test
unused variation and build "second" for the dependencies.
Bug: 193668901
Test: 1. Revert this change, run `odrefresh --force-compile`, capture
the checksum of all output files.
2. Apply this change, repeat the same step.
3. Saw all the file hashes remain the same (except for
cache-info.xml where the timestamp changed).
Test: atest ComposHostTestCases
Test: atest odsign_e2e_tests
Test: atest art_standalone_odrefresh_tests
Test: art/build/apex/runtests.sh
Test: lunch full-eng; m; art/build/apex/runtests.sh
Test: with master-art, art/tools/run-gtests.sh \
/apex/com.android.art/bin/art/arm64/art_odrefresh_tests
Test: TH
Change-Id: I3bf805fba925c197ec10508a67f0a16e74d64c62
diff --git a/artd/Android.bp b/artd/Android.bp
index 6338fa5..e57d22c 100644
--- a/artd/Android.bp
+++ b/artd/Android.bp
@@ -45,6 +45,76 @@
],
}
+cc_defaults {
+ name: "libdexopt_defaults",
+ defaults: ["art_defaults"],
+ srcs: [
+ "libdexopt.cc",
+ ],
+ shared_libs: [
+ "artd-private-aidl-ndk",
+ "libbase",
+ "libbinder_ndk",
+ "liblog",
+ ],
+ export_include_dirs: [
+ "include/libdexopt",
+ ],
+ local_include_dirs: [
+ "include/libdexopt",
+ ],
+ visibility: [
+ "//art:__subpackages__",
+ ],
+}
+
+art_cc_library {
+ name: "libdexopt",
+ defaults: ["libdexopt_defaults"],
+ shared_libs: [
+ "libart",
+ "libartbase",
+ ],
+ apex_available: [
+ "com.android.art",
+ "com.android.art.debug",
+ ],
+}
+
+art_cc_library {
+ name: "libdexoptd",
+ defaults: ["libdexopt_defaults"],
+ shared_libs: [
+ "libartd",
+ "libartbased",
+ ],
+ apex_available: [
+ "com.android.art.debug",
+ ],
+}
+
+art_cc_defaults {
+ name: "art_artd_tests_defaults",
+ defaults: [
+ "libdexopt_defaults",
+ ],
+ srcs: [
+ "libdexopt_test.cc",
+ ],
+ static_libs: [
+ "libgmock",
+ ],
+}
+
+art_cc_test {
+ name: "art_standalone_artd_tests",
+ defaults: [
+ "art_standalone_gtest_defaults",
+ "art_artd_tests_defaults",
+ ],
+ test_config_template: "art_artd_tests.xml",
+}
+
prebuilt_etc {
name: "com.android.art.artd.init.rc",
src: "artd.rc",
diff --git a/artd/art_artd_tests.xml b/artd/art_artd_tests.xml
new file mode 100644
index 0000000..9129d98
--- /dev/null
+++ b/artd/art_artd_tests.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 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.
+-->
+<!-- Note: This test config file for {MODULE} is generated from a template. -->
+<configuration description="Runs {MODULE}.">
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+ <option name="cleanup" value="true" />
+ <option name="push" value="{MODULE}->/data/local/tmp/{MODULE}/{MODULE}" />
+ <option name="append-bitness" value="true" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp/{MODULE}" />
+ <option name="module-name" value="{MODULE}" />
+ <option name="ld-library-path-32" value="/apex/com.android.art/lib" />
+ <option name="ld-library-path-64" value="/apex/com.android.art/lib64" />
+
+ <option name="gtest-env" value="DEX2OATBOOTCLASSPATH=/apex/com.android.art/javalib/core-foo.jar:/system/framework/framework.jar" />
+ <option name="gtest-env" value="BOOTCLASSPATH=/apex/com.android.art/javalib/core-bar.jar:/system/framework/framework.jar:/apex/com.android.bar/javalib/bar.jar" />
+ </test>
+
+ <!-- When this test is run in a Mainline context (e.g. with `mts-tradefed`), only enable it if
+ one of the Mainline modules below is present on the device used for testing. -->
+ <object type="module_controller" class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController">
+ <!-- ART Mainline Module (internal version). -->
+ <option name="mainline-module-package-name" value="com.google.android.art" />
+ <!-- ART Mainline Module (external (AOSP) version). -->
+ <option name="mainline-module-package-name" value="com.android.art" />
+ </object>
+</configuration>
diff --git a/artd/binder/Android.bp b/artd/binder/Android.bp
index 94f3f17..17678af 100644
--- a/artd/binder/Android.bp
+++ b/artd/binder/Android.bp
@@ -40,6 +40,32 @@
apex_available: [
"com.android.art",
"com.android.art.debug",
+ "com.android.compos",
+ ],
+ // TODO(b/177273468): Increment to next version when possible
+ min_sdk_version: "S",
+ },
+ },
+ unstable: true,
+ visibility: [
+ "//system/tools/aidl/build",
+ "//art:__subpackages__",
+ ],
+}
+
+aidl_interface {
+ name: "artd-private-aidl",
+ srcs: [
+ "private/**/*.aidl",
+ ],
+ host_supported: true,
+ local_include_dir: "private",
+ backend: {
+ ndk: {
+ enabled: true,
+ apex_available: [
+ "com.android.art",
+ "com.android.art.debug",
],
// TODO(b/177273468): Increment to next version when possible
min_sdk_version: "S",
diff --git a/artd/binder/private/com/android/art/CompilerFilter.aidl b/artd/binder/private/com/android/art/CompilerFilter.aidl
new file mode 100644
index 0000000..1e2902a
--- /dev/null
+++ b/artd/binder/private/com/android/art/CompilerFilter.aidl
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+package com.android.art;
+
+@Backing(type="int")
+enum CompilerFilter {
+ UNSUPPORTED = 0,
+ VERIFY,
+ SPEED,
+ SPEED_PROFILE,
+}
+
diff --git a/artd/binder/private/com/android/art/DexoptBcpExtArgs.aidl b/artd/binder/private/com/android/art/DexoptBcpExtArgs.aidl
new file mode 100644
index 0000000..43fd922
--- /dev/null
+++ b/artd/binder/private/com/android/art/DexoptBcpExtArgs.aidl
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+package com.android.art;
+
+import com.android.art.Isa;
+
+/**
+ * Arguments for dexopt of BCP extension.
+ *
+ * <h1>Security Considerations:</h1>
+ *
+ * <p>On Android, both the data provider and consumer are currently assumed in the same trusting
+ * domain * (e.g. since they are in the same process and the boundary is function call).
+ *
+ * <p>Compilation OS, on the contrary, plays role of data consumer and can't trust the data provided
+ * by * the potentially malicious clients. The data must be validated before use.
+ *
+ * <p>When adding a new field, make sure the value space can be validated. See "SECURITY:" below for
+ * examples.
+ *
+ * {@hide}
+ */
+parcelable DexoptBcpExtArgs {
+ // SECURITY: File descriptor are currently integers. They are assumed trusted in Android right
+ // now. For CompOS, they are verified transparently to the compiler, thus can also be assumed
+ // trusted.
+ int[] dexFds;
+ // Note: It's more ideal to put bootClasspaths and bootClasspathFds in a Map<path, fd>, but Map
+ // is only supported by the Java backend of AIDL. As a result, we need to maintain both arrays
+ // manually.
+ String[] bootClasspaths;
+ int[] bootClasspathFds;
+ int profileFd = -1;
+ int dirtyImageObjectsFd = -1;
+ // Output file descriptors
+ int oatFd = -1;
+ int vdexFd = -1;
+ int imageFd = -1;
+
+ // TODO(victorhsieh): Try to reconstruct behind the API, otherwise reasonable the security.
+ String[] dexPaths;
+ String oatLocation;
+
+ // SECURITY: The server may accept the request to produce code for the specified architecture,
+ // if supported.
+ Isa isa = Isa.UNSUPPORTED;
+
+ // SECURITY: Computational resource should not affect the compilation results.
+ int[] cpuSet;
+ int threads;
+}
diff --git a/artd/binder/private/com/android/art/DexoptSystemServerArgs.aidl b/artd/binder/private/com/android/art/DexoptSystemServerArgs.aidl
new file mode 100644
index 0000000..b351c67
--- /dev/null
+++ b/artd/binder/private/com/android/art/DexoptSystemServerArgs.aidl
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+package com.android.art;
+
+import com.android.art.CompilerFilter;
+import com.android.art.Isa;
+
+/**
+ * Arguments for dexopt of a system server JAR.
+ *
+ * <h1>Security Considerations:</h1>
+ *
+ * <p>On Android, both the data provider and consumer are currently assumed in the same trusting
+ * domain * (e.g. since they are in the same process and the boundary is function call).
+ *
+ * <p>Compilation OS, on the contrary, plays role of data consumer and can't trust the data provided
+ * by * the potentially malicious clients. The data must be validated before use.
+ *
+ * <p>When adding a new field, make sure the value space can be validated. See "SECURITY:" below for
+ * examples.
+ *
+ * {@hide}
+ */
+parcelable DexoptSystemServerArgs {
+ // SECURITY: File descriptor are currently integers. They are assumed trusted in Android right
+ // now. For CompOS, they are verified transparently to the compiler, thus can also be assumed
+ // trusted.
+ int dexFd = -1;
+ int profileFd = -1;
+ // Note: It's more ideal to put bootClasspaths and bootClasspathFds in a Map<path, fd>
+ // (similarly to the other bootClasspath*Fds), but Map is only supported by the Java backend of
+ // AIDL. As a result, we need to maintain both arrays manually. Note that this array is not used
+ // as -Xbootclasspath but as a container of the mapping keys for the arrays below.
+ String[] bootClasspaths;
+ int[] bootClasspathFds;
+ int[] bootClasspathImageFds;
+ int[] bootClasspathVdexFds;
+ int[] bootClasspathOatFds;
+ int[] classloaderFds;
+ // Output file descriptors
+ int imageFd = -1;
+ int vdexFd = -1;
+ int oatFd = -1;
+
+ // TODO(victorhsieh): Try to reconstruct behind the API, otherwise reasonable the security.
+ String dexPath;
+ String oatLocation;
+ String[] classloaderContext;
+ boolean isBootImageOnSystem;
+
+ // SECURITY: The server may accept the request to produce code for the specified architecture,
+ // if supported.
+ Isa isa = Isa.UNSUPPORTED;
+
+ // SECURITY: The server may apply compiler filter as long as the compilation can be truthful.
+ // For example, given that the profile is valid, the produced code should still be translated
+ // correctly. Effectively, what a malicious attacker can do is to produce less/more efficient
+ // code.
+ CompilerFilter compilerFilter = CompilerFilter.UNSUPPORTED;
+
+ // SECURITY: Computational resource should not affect the compilation results.
+ int[] cpuSet;
+ int threads;
+}
diff --git a/artd/binder/private/com/android/art/Isa.aidl b/artd/binder/private/com/android/art/Isa.aidl
new file mode 100644
index 0000000..3a2bd36
--- /dev/null
+++ b/artd/binder/private/com/android/art/Isa.aidl
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+package com.android.art;
+
+@Backing(type="int")
+enum Isa {
+ UNSUPPORTED = 0,
+ ARM,
+ ARM64,
+ THUMB2,
+ X86,
+ X86_64,
+}
diff --git a/artd/include/libdexopt/libdexopt.h b/artd/include/libdexopt/libdexopt.h
new file mode 100644
index 0000000..2e958be
--- /dev/null
+++ b/artd/include/libdexopt/libdexopt.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2021 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 ART_ARTD_INCLUDE_LIBDEXOPT_LIBDEXOPT_H_
+#define ART_ARTD_INCLUDE_LIBDEXOPT_LIBDEXOPT_H_
+
+#include <string>
+#include <vector>
+
+#include "android-base/result.h"
+
+#include "aidl/com/android/art/DexoptBcpExtArgs.h"
+#include "aidl/com/android/art/DexoptSystemServerArgs.h"
+
+namespace art {
+
+android::base::Result<void> AddDex2oatArgsFromBcpExtensionArgs(
+ const aidl::com::android::art::DexoptBcpExtArgs& dexopt_args,
+ std::vector<std::string>& dex2oat_args);
+
+android::base::Result<void> AddDex2oatArgsFromSystemServerArgs(
+ const aidl::com::android::art::DexoptSystemServerArgs& dexopt_args,
+ std::vector<std::string>& dex2oat_args);
+
+} // namespace art
+
+#endif // ART_ARTD_INCLUDE_LIBDEXOPT_LIBDEXOPT_H_
diff --git a/artd/libdexopt.cc b/artd/libdexopt.cc
new file mode 100644
index 0000000..c064c2e
--- /dev/null
+++ b/artd/libdexopt.cc
@@ -0,0 +1,337 @@
+/*
+ * Copyright (C) 2021 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 "libdexopt.h"
+#define LOG_TAG "libdexopt"
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "android-base/stringprintf.h"
+#include "android-base/strings.h"
+#include "android-base/result.h"
+#include "base/file_utils.h"
+#include "log/log.h"
+
+#include "aidl/com/android/art/CompilerFilter.h"
+#include "aidl/com/android/art/DexoptBcpExtArgs.h"
+#include "aidl/com/android/art/DexoptSystemServerArgs.h"
+#include "aidl/com/android/art/Isa.h"
+
+namespace art {
+
+namespace {
+
+using aidl::com::android::art::CompilerFilter;
+using aidl::com::android::art::DexoptBcpExtArgs;
+using aidl::com::android::art::DexoptSystemServerArgs;
+using aidl::com::android::art::Isa;
+using android::base::Error;
+using android::base::Result;
+
+std::string GetBootImage() {
+ // Typically "/apex/com.android.art/javalib/boot.art".
+ return art::GetArtRoot() + "/javalib/boot.art";
+}
+
+std::string GetEnvironmentVariableOrDie(const char* name) {
+ const char* value = getenv(name);
+ LOG_ALWAYS_FATAL_IF(value == nullptr, "%s is not defined.", name);
+ return value;
+}
+
+std::string GetDex2oatBootClasspath() {
+ return GetEnvironmentVariableOrDie("DEX2OATBOOTCLASSPATH");
+}
+
+std::string GetBootClasspath() {
+ return GetEnvironmentVariableOrDie("BOOTCLASSPATH");
+}
+
+std::string ToInstructionSetString(Isa isa) {
+ switch (isa) {
+ case Isa::ARM:
+ case Isa::THUMB2:
+ return "arm";
+ case Isa::ARM64:
+ return "arm64";
+ case Isa::X86:
+ return "x86";
+ case Isa::X86_64:
+ return "x86_64";
+ default:
+ UNREACHABLE();
+ }
+}
+
+const char* CompilerFilterAidlToString(CompilerFilter compiler_filter) {
+ switch (compiler_filter) {
+ case CompilerFilter::SPEED_PROFILE:
+ return "speed-profile";
+ case CompilerFilter::SPEED:
+ return"speed";
+ case CompilerFilter::VERIFY:
+ return "verify";
+ default:
+ UNREACHABLE();
+ }
+}
+
+Result<void> AddBootClasspath(/*inout*/ std::vector<std::string>& cmdline,
+ const std::string& bootclasspath_env,
+ const std::vector<std::string>& boot_classpaths,
+ const std::vector<int>& boot_classpath_fds) {
+ if (boot_classpaths.empty()) {
+ return Errorf("Missing BCP files");
+ }
+
+ if (boot_classpaths.size() != boot_classpath_fds.size()) {
+ return Errorf("Number of BCP paths (%d) != number of FDs (%d)", boot_classpaths.size(),
+ boot_classpath_fds.size());
+ }
+
+ cmdline.emplace_back("--runtime-arg");
+ cmdline.emplace_back("-Xbootclasspath:" + bootclasspath_env);
+
+ // Construct a path->fd map from both arrays. If the client provides duplicated paths, only one
+ // will be used. This is fine since the client may not be trusted any way.
+ std::map<std::string, int> bcp_map;
+ auto zip = [](const std::string &path, int fd) { return std::make_pair(path, fd); };
+ std::transform(boot_classpaths.begin(), boot_classpaths.end(), boot_classpath_fds.begin(),
+ std::inserter(bcp_map, bcp_map.end()), zip);
+
+ std::vector<std::string> jar_paths = android::base::Split(bootclasspath_env, ":");
+ std::stringstream ss;
+ ss << "-Xbootclasspathfds";
+ for (auto& jar_path : jar_paths) {
+ auto iter = bcp_map.find(jar_path);
+ if (iter == bcp_map.end()) {
+ ss << ":-1";
+ } else {
+ ss << ":" << std::to_string(iter->second);
+ bcp_map.erase(iter);
+ }
+ }
+ cmdline.emplace_back("--runtime-arg");
+ cmdline.emplace_back(ss.str());
+
+ if (!bcp_map.empty()) {
+ std::stringstream error_ss;
+ for (const auto &[key, _] : bcp_map) {
+ error_ss << key << ":";
+ }
+ return Error() << "Residual BCP paths: " << error_ss.str();
+ }
+ return {};
+}
+
+Result<void> AddCompiledBootClasspathFdsIfAny(/*inout*/ std::vector<std::string>& cmdline,
+ const DexoptSystemServerArgs& args) {
+ // Result
+ if ((args.bootClasspathImageFds.size() != args.bootClasspathOatFds.size()) ||
+ (args.bootClasspathImageFds.size() != args.bootClasspathVdexFds.size()) ||
+ (args.bootClasspathImageFds.size() != args.bootClasspaths.size())) {
+ return Errorf("Inconsistent FD numbers of BCP artifacts: jar/image/vdex/oat: %d/%d/%d/%d",
+ args.bootClasspaths.size(), args.bootClasspathImageFds.size(),
+ args.bootClasspathVdexFds.size(), args.bootClasspathOatFds.size());
+ }
+
+ if (!args.bootClasspathImageFds.empty()) {
+ cmdline.emplace_back("--runtime-arg");
+ cmdline.emplace_back(
+ "-Xbootclasspathimagefds:" + android::base::Join(args.bootClasspathImageFds, ':'));
+ cmdline.emplace_back("--runtime-arg");
+ cmdline.emplace_back(
+ "-Xbootclasspathoatfds:" + android::base::Join(args.bootClasspathOatFds, ':'));
+ cmdline.emplace_back("--runtime-arg");
+ cmdline.emplace_back(
+ "-Xbootclasspathvdexfds:" + android::base::Join(args.bootClasspathVdexFds, ':'));
+ }
+ return {};
+}
+
+void AddDex2OatConcurrencyArguments(/*inout*/ std::vector<std::string>& cmdline,
+ int threads,
+ const std::vector<int>& cpu_set) {
+ if (threads > 0) {
+ cmdline.emplace_back(android::base::StringPrintf("-j%d", threads));
+ }
+ if (!cpu_set.empty()) {
+ cmdline.emplace_back("--cpu-set=" + android::base::Join(cpu_set, ','));
+ }
+}
+
+void AddDex2OatCommonOptions(/*inout*/ std::vector<std::string>& cmdline) {
+ cmdline.emplace_back("--android-root=out/empty");
+ cmdline.emplace_back("--abort-on-hard-verifier-error");
+ cmdline.emplace_back("--no-abort-on-soft-verifier-error");
+ cmdline.emplace_back("--compilation-reason=boot");
+ cmdline.emplace_back("--image-format=lz4");
+ cmdline.emplace_back("--force-determinism");
+ cmdline.emplace_back("--resolve-startup-const-strings=true");
+}
+
+void AddDex2OatDebugInfo(/*inout*/ std::vector<std::string>& cmdline) {
+ cmdline.emplace_back("--generate-mini-debug-info");
+ cmdline.emplace_back("--strip");
+}
+
+} // namespace
+
+Result<void> AddDex2oatArgsFromBcpExtensionArgs(const DexoptBcpExtArgs& args,
+ /*out*/ std::vector<std::string>& cmdline) {
+ // Common dex2oat flags
+ AddDex2OatCommonOptions(cmdline);
+ AddDex2OatDebugInfo(cmdline);
+
+ cmdline.emplace_back("--instruction-set=" + ToInstructionSetString(args.isa));
+
+ if (args.profileFd >= 0) {
+ cmdline.emplace_back(android::base::StringPrintf("--profile-file-fd=%d", args.profileFd));
+ cmdline.emplace_back("--compiler-filter=speed-profile");
+ } else {
+ cmdline.emplace_back("--compiler-filter=speed");
+ }
+
+ // Compile as a single image for fewer files and slightly less memory overhead.
+ cmdline.emplace_back("--single-image");
+
+ // Set boot-image and expectation of compiling boot classpath extensions.
+ cmdline.emplace_back("--boot-image=" + GetBootImage());
+
+ if (args.dirtyImageObjectsFd >= 0) {
+ cmdline.emplace_back(android::base::StringPrintf("--dirty-image-objects-fd=%d",
+ args.dirtyImageObjectsFd));
+ }
+
+ if (args.dexPaths.size() != args.dexFds.size()) {
+ return Errorf("Mismatched number of dexPaths (%d) and dexFds (%d)",
+ args.dexPaths.size(),
+ args.dexFds.size());
+ }
+ for (unsigned int i = 0; i < args.dexPaths.size(); ++i) {
+ cmdline.emplace_back("--dex-file=" + args.dexPaths[i]);
+ cmdline.emplace_back(android::base::StringPrintf("--dex-fd=%d", args.dexFds[i]));
+ }
+
+ std::string bcp_env = GetDex2oatBootClasspath();
+ auto result = AddBootClasspath(cmdline, bcp_env, args.bootClasspaths, args.bootClasspathFds);
+ if (!result.ok()) {
+ return result.error();
+ }
+
+ cmdline.emplace_back("--oat-location=" + args.oatLocation);
+
+ // Output files
+ if (args.imageFd < 0) {
+ return Error() << "imageFd is missing";
+ }
+ cmdline.emplace_back(android::base::StringPrintf("--image-fd=%d", args.imageFd));
+ if (args.vdexFd < 0) {
+ return Error() << "vdexFd is missing";
+ }
+ cmdline.emplace_back(android::base::StringPrintf("--output-vdex-fd=%d", args.vdexFd));
+ if (args.oatFd < 0) {
+ return Error() << "oatFd is missing";
+ }
+ cmdline.emplace_back(android::base::StringPrintf("--oat-fd=%d", args.oatFd));
+
+ AddDex2OatConcurrencyArguments(cmdline, args.threads, args.cpuSet);
+
+ return {};
+}
+
+Result<void> AddDex2oatArgsFromSystemServerArgs(const DexoptSystemServerArgs& args,
+ /*out*/ std::vector<std::string>& cmdline) {
+ cmdline.emplace_back("--dex-file=" + args.dexPath);
+ cmdline.emplace_back(android::base::StringPrintf("--dex-fd=%d", args.dexFd));
+
+ // Common dex2oat flags
+ AddDex2OatCommonOptions(cmdline);
+ AddDex2OatDebugInfo(cmdline);
+
+ cmdline.emplace_back("--instruction-set=" + ToInstructionSetString(args.isa));
+
+ if (args.compilerFilter == CompilerFilter::SPEED_PROFILE) {
+ if (args.profileFd < 0) {
+ return Error() << "profileFd is missing";
+ }
+ cmdline.emplace_back(android::base::StringPrintf("--profile-file-fd=%d", args.profileFd));
+ cmdline.emplace_back("--compiler-filter=speed-profile");
+ } else {
+ cmdline.emplace_back("--compiler-filter=" +
+ std::string(CompilerFilterAidlToString(args.compilerFilter)));
+ }
+
+ // Output files
+ if (args.imageFd < 0) {
+ return Error() << "imageFd is missing";
+ }
+ cmdline.emplace_back(android::base::StringPrintf("--app-image-fd=%d", args.imageFd));
+ if (args.vdexFd < 0) {
+ return Error() << "vdexFd is missing";
+ }
+ cmdline.emplace_back(android::base::StringPrintf("--output-vdex-fd=%d", args.vdexFd));
+ if (args.oatFd < 0) {
+ return Error() << "oatFd is missing";
+ }
+ cmdline.emplace_back(android::base::StringPrintf("--oat-fd=%d", args.oatFd));
+ cmdline.emplace_back("--oat-location=" + args.oatLocation);
+
+ std::string bcp_env = GetBootClasspath();
+ auto result = AddBootClasspath(cmdline, bcp_env, args.bootClasspaths, args.bootClasspathFds);
+ if (!result.ok()) {
+ return result.error();
+ }
+ result = AddCompiledBootClasspathFdsIfAny(cmdline, args);
+ if (!result.ok()) {
+ return result.error();
+ }
+
+ if (args.classloaderFds.empty()) {
+ cmdline.emplace_back("--class-loader-context=PCL[]");
+ } else {
+ const std::string context_path = android::base::Join(args.classloaderContext, ':');
+ cmdline.emplace_back("--class-loader-context=PCL[" + context_path + "]");
+ cmdline.emplace_back("--class-loader-context-fds=" +
+ android::base::Join(args.classloaderFds, ':'));
+ }
+
+ // Derive boot image
+ // b/197176583
+ // If the boot extension artifacts are not on /data, then boot extensions are not re-compiled
+ // and the artifacts must exist on /system.
+ std::vector<std::string> jar_paths = android::base::Split(GetDex2oatBootClasspath(), ":");
+ auto iter = std::find_if_not(jar_paths.begin(), jar_paths.end(), &LocationIsOnArtModule);
+ if (iter == jar_paths.end()) {
+ return Error() << "Missing BCP extension compatible JAR";
+ }
+ const std::string& first_boot_extension_compatible_jars = *iter;
+ // TODO(197176583): Support compiling against BCP extension in /system.
+ const std::string extension_image = GetBootImagePath(args.isBootImageOnSystem,
+ first_boot_extension_compatible_jars);
+ if (extension_image.empty()) {
+ return Error() << "Can't identify the first boot extension compatible jar";
+ }
+ cmdline.emplace_back("--boot-image=" + GetBootImage() + ":" + extension_image);
+
+ AddDex2OatConcurrencyArguments(cmdline, args.threads, args.cpuSet);
+
+ return {};
+}
+
+} // namespace art
diff --git a/artd/libdexopt_test.cc b/artd/libdexopt_test.cc
new file mode 100644
index 0000000..7536dcd
--- /dev/null
+++ b/artd/libdexopt_test.cc
@@ -0,0 +1,327 @@
+/*
+ * Copyright (C) 2021 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 "libdexopt.h"
+
+#include <string>
+#include <vector>
+
+#include "android-base/result.h"
+#include "android-base/strings.h"
+#include "base/common_art_test.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+#include "aidl/com/android/art/CompilerFilter.h"
+#include "aidl/com/android/art/DexoptBcpExtArgs.h"
+#include "aidl/com/android/art/DexoptSystemServerArgs.h"
+#include "aidl/com/android/art/Isa.h"
+
+namespace art {
+
+using ::testing::AllOf;
+using ::testing::Contains;
+using ::testing::HasSubstr;
+using ::testing::Not;
+using aidl::com::android::art::CompilerFilter;
+using aidl::com::android::art::DexoptBcpExtArgs;
+using aidl::com::android::art::DexoptSystemServerArgs;
+using aidl::com::android::art::Isa;
+using android::base::Result;
+
+std::string GetEnvironmentVariableOrDie(const char* name) {
+ const char* value = getenv(name);
+ EXPECT_NE(value, nullptr);
+ return value;
+}
+
+// See art_artd_tests.xml for *CLASSPATH setup.
+class LibDexoptTest : public CommonArtTest {
+ protected:
+ void SetUp() override {
+ CommonArtTest::SetUp();
+
+ default_bcp_ext_args_.dexFds = {10, 11};
+ default_bcp_ext_args_.bootClasspaths = android::base::Split(
+ GetEnvironmentVariableOrDie("DEX2OATBOOTCLASSPATH"), ":"); // from art_artd_tests.xml
+ default_bcp_ext_args_.bootClasspathFds = {21, 22};
+ default_bcp_ext_args_.profileFd = 30;
+ default_bcp_ext_args_.dirtyImageObjectsFd = 31;
+ default_bcp_ext_args_.imageFd = 90;
+ default_bcp_ext_args_.vdexFd = 91;
+ default_bcp_ext_args_.oatFd = 92;
+ default_bcp_ext_args_.dexPaths = {"/path/to/foo.jar", "/path/to/bar.jar"};
+ default_bcp_ext_args_.oatLocation = "/oat/location/bar.odex";
+ default_bcp_ext_args_.isa = Isa::X86_64;
+ default_bcp_ext_args_.cpuSet = {0, 1};
+ default_bcp_ext_args_.threads = 42;
+ ASSERT_EQ(default_bcp_ext_args_.bootClasspaths.size(),
+ default_bcp_ext_args_.bootClasspathFds.size());
+
+ default_system_server_args_.dexFd = 10;
+ default_system_server_args_.profileFd = 11;
+ default_system_server_args_.bootClasspaths = android::base::Split(
+ GetEnvironmentVariableOrDie("BOOTCLASSPATH"), ":"); // from art_artd_tests.xml
+ default_system_server_args_.bootClasspathFds = {21, 22, 23};
+ default_system_server_args_.bootClasspathImageFds = {-1, 31, -1};
+ default_system_server_args_.bootClasspathVdexFds = {-1, 32, -1};
+ default_system_server_args_.bootClasspathOatFds = {-1, 33, -1};
+ default_system_server_args_.classloaderFds = {40, 41};
+ default_system_server_args_.classloaderContext = {"/cl/abc.jar", "/cl/def.jar"};
+ default_system_server_args_.imageFd = 90;
+ default_system_server_args_.vdexFd = 91;
+ default_system_server_args_.oatFd = 92;
+ default_system_server_args_.dexPath = "/path/to/foo.jar";
+ default_system_server_args_.oatLocation = "/oat/location/bar.odex";
+ default_system_server_args_.isa = Isa::X86_64;
+ default_system_server_args_.compilerFilter = CompilerFilter::SPEED_PROFILE;
+ default_system_server_args_.cpuSet = {0, 1};
+ default_system_server_args_.threads = 42;
+ default_system_server_args_.isBootImageOnSystem = true;
+ ASSERT_EQ(default_system_server_args_.bootClasspaths.size(),
+ default_system_server_args_.bootClasspathFds.size());
+ }
+
+ void TearDown() override {
+ CommonArtTest::TearDown();
+ }
+
+ std::vector<std::string> Dex2oatArgsFromBcpExtensionArgs(const DexoptBcpExtArgs& args) {
+ std::vector<std::string> cmdline;
+ Result<void> result = AddDex2oatArgsFromBcpExtensionArgs(args, cmdline);
+ EXPECT_TRUE(result.ok()) << "Failed expectation: " << result.error().message();
+ return cmdline;
+ }
+
+ std::vector<std::string> Dex2oatArgsFromSystemServerArgs(const DexoptSystemServerArgs& args) {
+ std::vector<std::string> cmdline;
+ Result<void> result = AddDex2oatArgsFromSystemServerArgs(args, cmdline);
+ EXPECT_TRUE(result.ok()) << "Failed expectation: " << result.error().message();
+ return cmdline;
+ }
+
+ bool DexoptBcpExtArgsIsInvalid(const DexoptBcpExtArgs& args) {
+ std::vector<std::string> cmdline;
+ Result<void> result = AddDex2oatArgsFromBcpExtensionArgs(args, cmdline);
+ return result.ok();
+ }
+
+ bool DexoptSystemServerArgsIsInvalid(const DexoptSystemServerArgs& args) {
+ std::vector<std::string> cmdline;
+ Result<void> result = AddDex2oatArgsFromSystemServerArgs(args, cmdline);
+ return result.ok();
+ }
+
+ DexoptBcpExtArgs default_bcp_ext_args_;
+ DexoptSystemServerArgs default_system_server_args_;
+};
+
+TEST_F(LibDexoptTest, AddDex2oatArgsFromBcpExtensionArgs) {
+ // Test basics with default args
+ {
+ std::vector<std::string> cmdline = Dex2oatArgsFromBcpExtensionArgs(default_bcp_ext_args_);
+
+ EXPECT_THAT(cmdline, AllOf(
+ Contains("--dex-fd=10"),
+ Contains("--dex-fd=11"),
+ Contains("--dex-file=/path/to/foo.jar"),
+ Contains("--dex-file=/path/to/bar.jar"),
+ Contains(HasSubstr("-Xbootclasspath:")),
+ Contains("-Xbootclasspathfds:21:22"),
+
+ Contains("--profile-file-fd=30"),
+ Contains("--compiler-filter=speed-profile"),
+
+ Contains("--image-fd=90"),
+ Contains("--output-vdex-fd=91"),
+ Contains("--oat-fd=92"),
+ Contains("--oat-location=/oat/location/bar.odex"),
+
+ Contains("--dirty-image-objects-fd=31"),
+ Contains("--instruction-set=x86_64"),
+ Contains("--cpu-set=0,1"),
+ Contains("-j42")));
+ }
+
+ // No profile
+ {
+ auto args = default_bcp_ext_args_;
+ args.profileFd = -1;
+ std::vector<std::string> cmdline = Dex2oatArgsFromBcpExtensionArgs(args);
+
+ EXPECT_THAT(cmdline, AllOf(
+ Not(Contains(HasSubstr("--profile-file-fd="))),
+ Contains("--compiler-filter=speed")));
+ }
+
+ // No dirty image objects fd
+ {
+ auto args = default_bcp_ext_args_;
+ args.dirtyImageObjectsFd = -1;
+ std::vector<std::string> cmdline = Dex2oatArgsFromBcpExtensionArgs(args);
+
+ EXPECT_THAT(cmdline, Not(Contains(HasSubstr("--dirty-image-objects-fd"))));
+ }
+}
+
+TEST_F(LibDexoptTest, AddDex2oatArgsFromBcpExtensionArgs_InvalidArguments) {
+ // Mismatched dex number
+ {
+ auto args = default_bcp_ext_args_;
+ args.dexPaths = {"/path/to/foo.jar", "/path/to/bar.jar"};
+ args.dexFds = {100, 101, 102};
+
+ EXPECT_FALSE(DexoptBcpExtArgsIsInvalid(args));
+ }
+
+ // Mismatched classpath arguments
+ {
+ auto args = default_bcp_ext_args_;
+ args.bootClasspathFds.emplace_back(200);
+
+ EXPECT_FALSE(DexoptBcpExtArgsIsInvalid(args));
+ }
+
+ // Mismatched classpath arguments
+ {
+ auto args = default_bcp_ext_args_;
+ args.bootClasspaths.emplace_back("/unrecognized/jar");
+
+ EXPECT_FALSE(DexoptBcpExtArgsIsInvalid(args));
+ }
+
+ // Missing output fds
+ {
+ auto args = default_bcp_ext_args_;
+ args.imageFd = -1;
+ EXPECT_FALSE(DexoptBcpExtArgsIsInvalid(args));
+
+ args = default_bcp_ext_args_;
+ args.vdexFd = -1;
+ EXPECT_FALSE(DexoptBcpExtArgsIsInvalid(args));
+
+ args = default_bcp_ext_args_;
+ args.oatFd = -1;
+ EXPECT_FALSE(DexoptBcpExtArgsIsInvalid(args));
+ }
+}
+
+TEST_F(LibDexoptTest, AddDex2oatArgsFromSystemServerArgs) {
+ // Test basics with default args
+ {
+ std::vector<std::string> cmdline = Dex2oatArgsFromSystemServerArgs(default_system_server_args_);
+
+ EXPECT_THAT(cmdline, AllOf(
+ Contains("--dex-fd=10"),
+ Contains("--dex-file=/path/to/foo.jar"),
+ Contains(HasSubstr("-Xbootclasspath:")),
+ Contains("-Xbootclasspathfds:21:22:23"),
+ Contains("-Xbootclasspathimagefds:-1:31:-1"),
+ Contains("-Xbootclasspathvdexfds:-1:32:-1"),
+ Contains("-Xbootclasspathoatfds:-1:33:-1"),
+
+ Contains("--profile-file-fd=11"),
+ Contains("--compiler-filter=speed-profile"),
+
+ Contains("--app-image-fd=90"),
+ Contains("--output-vdex-fd=91"),
+ Contains("--oat-fd=92"),
+ Contains("--oat-location=/oat/location/bar.odex"),
+
+ Contains("--class-loader-context-fds=40:41"),
+ Contains("--class-loader-context=PCL[/cl/abc.jar:/cl/def.jar]"),
+
+ Contains("--instruction-set=x86_64"),
+ Contains("--cpu-set=0,1"),
+ Contains("-j42")));
+ }
+
+ // Test different compiler filters
+ {
+ // speed
+ auto args = default_system_server_args_;
+ args.compilerFilter = CompilerFilter::SPEED;
+ std::vector<std::string> cmdline = Dex2oatArgsFromSystemServerArgs(args);
+
+ EXPECT_THAT(cmdline, AllOf(
+ Not(Contains(HasSubstr("--profile-file-fd="))),
+ Contains("--compiler-filter=speed")));
+
+ // verify
+ args = default_system_server_args_;
+ args.compilerFilter = CompilerFilter::VERIFY;
+ cmdline = Dex2oatArgsFromSystemServerArgs(args);
+
+ EXPECT_THAT(cmdline, AllOf(
+ Not(Contains(HasSubstr("--profile-file-fd="))),
+ Contains("--compiler-filter=verify")));
+ }
+
+ // Test classloader context
+ {
+ auto args = default_system_server_args_;
+ args.classloaderFds = {};
+ args.classloaderContext = {};
+ std::vector<std::string> cmdline = Dex2oatArgsFromSystemServerArgs(args);
+
+ EXPECT_THAT(cmdline, AllOf(
+ Not(Contains(HasSubstr("--class-loader-context-fds"))),
+ Contains("--class-loader-context=PCL[]")));
+ }
+}
+
+TEST_F(LibDexoptTest, AddDex2oatArgsFromSystemServerArgs_InvalidArguments) {
+ // Mismatched classpath arguments
+ {
+ auto args = default_system_server_args_;
+ args.bootClasspathFds.emplace_back(200);
+
+ EXPECT_FALSE(DexoptSystemServerArgsIsInvalid(args));
+ }
+
+ // Unrecognized jar path
+ {
+ auto args = default_system_server_args_;
+ args.bootClasspaths.emplace_back("/unrecognized/jar");
+
+ EXPECT_FALSE(DexoptSystemServerArgsIsInvalid(args));
+ }
+
+ // speed-profile without profile fd
+ {
+ auto args = default_system_server_args_;
+ args.compilerFilter = CompilerFilter::SPEED_PROFILE;
+ args.profileFd = -1;
+ EXPECT_FALSE(DexoptSystemServerArgsIsInvalid(args));
+ }
+
+ // Missing output fds
+ {
+ auto args = default_system_server_args_;
+ args.imageFd = -1;
+ EXPECT_FALSE(DexoptSystemServerArgsIsInvalid(args));
+
+ args = default_system_server_args_;
+ args.vdexFd = -1;
+ EXPECT_FALSE(DexoptSystemServerArgsIsInvalid(args));
+
+ args = default_system_server_args_;
+ args.oatFd = -1;
+ EXPECT_FALSE(DexoptSystemServerArgsIsInvalid(args));
+ }
+}
+
+} // namespace art
diff --git a/build/apex/Android.bp b/build/apex/Android.bp
index eb8476c..0d877f8 100644
--- a/build/apex/Android.bp
+++ b/build/apex/Android.bp
@@ -372,12 +372,16 @@
"art_libdexfile_support_tests",
"art_libprofile_tests",
"art_oatdump_tests",
- "art_odrefresh_tests",
"art_profman_tests",
"art_runtime_compiler_tests",
"art_runtime_tests",
"art_sigchain_tests",
]
+art_gtests_first_only = [
+ // We only ship "first" for odrefresh, so don't bother test the "second". This also avoids a
+ // problem where some dependencies may only build "first" and doesn't have "second".
+ "art_odrefresh_tests",
+]
// "Testing" version of the ART APEX module (containing both release
// and debug variants, additional tools, and ART gtests), for testing
@@ -387,7 +391,14 @@
defaults: ["com.android.art-devel-defaults"],
file_contexts: ":com.android.art.debug-file_contexts",
certificate: ":com.android.art.certificate",
- tests: art_gtests,
+ multilib: {
+ both: {
+ tests: art_gtests,
+ },
+ first: {
+ tests: art_gtests_first_only,
+ },
+ },
binaries: ["signal_dumper"], // Need signal_dumper for run-tests.
updatable: false,
}
diff --git a/build/apex/art_apex_test.py b/build/apex/art_apex_test.py
index 7b1bf8a..64904de 100755
--- a/build/apex/art_apex_test.py
+++ b/build/apex/art_apex_test.py
@@ -493,6 +493,8 @@
# Check internal libraries for ART.
self._checker.check_prefer64_library('artd-aidl-ndk')
+ self._checker.check_prefer64_library('artd-private-aidl-ndk')
+ self._checker.check_prefer64_library('libdexopt')
self._checker.check_native_library('libadbconnection')
self._checker.check_native_library('libart')
self._checker.check_native_library('libart-compiler')
@@ -711,13 +713,15 @@
self._checker.check_art_test_executable('art_libdexfile_tests')
self._checker.check_art_test_executable('art_libprofile_tests')
self._checker.check_art_test_executable('art_oatdump_tests')
- self._checker.check_art_test_executable('art_odrefresh_tests')
+ self._checker.check_art_test_executable('art_odrefresh_tests',
+ multilib=MULTILIB_FIRST)
self._checker.check_art_test_executable('art_profman_tests')
self._checker.check_art_test_executable('art_runtime_compiler_tests')
self._checker.check_art_test_executable('art_runtime_tests')
self._checker.check_art_test_executable('art_sigchain_tests')
# Check ART test (internal) libraries.
+ self._checker.check_prefer64_library('libdexoptd')
self._checker.check_native_library('libartd-gtest')
self._checker.check_native_library('libartd-simulator-container')
diff --git a/libartbase/base/file_utils.cc b/libartbase/base/file_utils.cc
index 884bca1..0780fe0 100644
--- a/libartbase/base/file_utils.cc
+++ b/libartbase/base/file_utils.cc
@@ -358,6 +358,18 @@
return GetDefaultBootImageLocation(android_root, /*deny_art_apex_data_files=*/false);
}
+std::string GetBootImagePath(bool on_system, const std::string& jar_path) {
+ if (on_system) {
+ const std::string jar_name = android::base::Basename(jar_path);
+ const std::string image_name = ReplaceFileExtension(jar_name, "art");
+ // Typically "/system/framework/boot-framework.art".
+ return StringPrintf("%s/framework/boot-%s", GetAndroidRoot().c_str(), image_name.c_str());
+ } else {
+ // Typically "/data/misc/apexdata/com.android.art/dalvik-cache/boot-framework.art".
+ return GetApexDataBootImage(jar_path);
+ }
+}
+
static std::string GetDalvikCacheDirectory(std::string_view root_directory,
std::string_view sub_directory = {}) {
static constexpr std::string_view kDalvikCache = "dalvik-cache";
diff --git a/libartbase/base/file_utils.h b/libartbase/base/file_utils.h
index 3c11ac3..6fc1caa 100644
--- a/libartbase/base/file_utils.h
+++ b/libartbase/base/file_utils.h
@@ -77,6 +77,9 @@
std::string GetDefaultBootImageLocation(const std::string& android_root,
bool deny_art_apex_data_files);
+// Returns the boot image path of the provided jar, on /system or /data.
+std::string GetBootImagePath(bool on_system, const std::string& jar_path);
+
// Return true if we found the dalvik cache and stored it in the dalvik_cache argument.
// `have_android_data` will be set to true if we have an ANDROID_DATA that exists,
// `dalvik_cache_exists` will be true if there is a dalvik-cache directory that is present.
diff --git a/odrefresh/Android.bp b/odrefresh/Android.bp
index 9016719..a8ec76c 100644
--- a/odrefresh/Android.bp
+++ b/odrefresh/Android.bp
@@ -29,6 +29,7 @@
"odrefresh.cc",
"odr_common.cc",
"odr_compilation_log.cc",
+ "odr_dexopt.cc",
"odr_fs_utils.cc",
"odr_metrics.cc",
"odr_metrics_record.cc",
@@ -41,6 +42,7 @@
"art-odrefresh-operator-srcs",
],
shared_libs: [
+ "artd-private-aidl-ndk",
"libartpalette",
"libbase",
"liblog",
@@ -104,6 +106,7 @@
shared_libs: [
"libart",
"libartbase",
+ "libdexopt",
],
apex_available: [
"com.android.art",
@@ -124,6 +127,7 @@
shared_libs: [
"libartd",
"libartbased",
+ "libdexoptd",
],
apex_available: [
"com.android.art.debug",
@@ -176,6 +180,11 @@
static_libs: [
"libgmock",
],
+ target: {
+ android: {
+ compile_multilib: "first",
+ },
+ },
}
// Version of ART gtest `art_odrefresh_tests` bundled with the ART APEX on target.
@@ -189,6 +198,7 @@
host_supported: false,
shared_libs: [
"libdexfiled",
+ "libdexoptd",
],
test_config_template: "art_odrefresh_tests.xml",
}
@@ -202,6 +212,7 @@
],
shared_libs: [
"libdexfile",
+ "libdexopt",
],
test_config_template: "art_odrefresh_tests.xml",
}
diff --git a/odrefresh/odr_config.h b/odrefresh/odr_config.h
index 48327f6..1fd1900 100644
--- a/odrefresh/odr_config.h
+++ b/odrefresh/odr_config.h
@@ -53,7 +53,7 @@
std::string program_name_;
std::string system_server_classpath_;
ZygoteKind zygote_kind_;
- std::string compilation_os_address_;
+ int compilation_os_address_ = -1;
std::string boot_classpath_;
// Staging directory for artifacts. The directory must exist and will be automatically removed
@@ -123,10 +123,8 @@
const std::string& GetSystemServerClasspath() const {
return system_server_classpath_;
}
- bool UseCompilationOs() const { return !compilation_os_address_.empty(); }
- const std::string& GetCompilationOsAddress() const {
- return compilation_os_address_;
- }
+ bool UseCompilationOs() const { return compilation_os_address_ >= 0; }
+ int GetCompilationOsAddress() const { return compilation_os_address_; }
const std::string& GetStagingDir() const {
return staging_dir_;
}
@@ -140,7 +138,7 @@
void SetDryRun() { dry_run_ = true; }
void SetIsa(const InstructionSet isa) { isa_ = isa; }
- void SetCompilationOsAddress(const std::string& address) { compilation_os_address_ = address; }
+ void SetCompilationOsAddress(int address) { compilation_os_address_ = address; }
void SetSystemServerClasspath(const std::string& classpath) {
system_server_classpath_ = classpath;
diff --git a/odrefresh/odr_dexopt.cc b/odrefresh/odr_dexopt.cc
new file mode 100644
index 0000000..99e0814
--- /dev/null
+++ b/odrefresh/odr_dexopt.cc
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2021 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 "odr_dexopt.h"
+
+#include <vector>
+
+#include "android-base/logging.h"
+#include <android-base/result.h>
+#include "android-base/strings.h"
+#include "exec_utils.h"
+#include "log/log.h"
+#include "odr_config.h"
+#include "libdexopt.h"
+
+#include "aidl/com/android/art/DexoptBcpExtArgs.h"
+#include "aidl/com/android/art/DexoptSystemServerArgs.h"
+
+namespace art {
+namespace odrefresh {
+
+using aidl::com::android::art::DexoptBcpExtArgs;
+using aidl::com::android::art::DexoptSystemServerArgs;
+using android::base::Result;
+
+namespace {
+
+int ExecAndReturnCode(ExecUtils* exec_utils,
+ std::vector<std::string>& cmdline,
+ time_t timeout_secs,
+ /*out*/ bool* timed_out,
+ /*out*/ std::string* error_msg) {
+ LOG(DEBUG) << "odr_dexopt cmdline: " << android::base::Join(cmdline, ' ') << " [timeout "
+ << timeout_secs << "s]";
+ return exec_utils->ExecAndReturnCode(cmdline, timeout_secs, timed_out, error_msg);
+}
+
+class OdrDexoptLocal final : public OdrDexopt {
+ public:
+ static OdrDexoptLocal* Create(const std::string& dex2oat_path,
+ std::unique_ptr<ExecUtils> exec_utils) {
+ return new OdrDexoptLocal(dex2oat_path, std::move(exec_utils));
+ }
+
+ int DexoptBcpExtension(const DexoptBcpExtArgs& args,
+ time_t timeout_secs,
+ /*out*/ bool* timed_out,
+ /*out*/ std::string* error_msg) override {
+ std::vector<std::string> cmdline = { dex2oat_path_ };
+ auto result = art::AddDex2oatArgsFromBcpExtensionArgs(args, cmdline);
+ if (!result.ok()) {
+ LOG(ERROR) << "Dexopt (local) failed: " << result.error().message() << ", cmdline: "
+ << android::base::Join(cmdline, ' ');
+ return -1;
+ }
+ return ExecAndReturnCode(exec_utils_.get(), cmdline, timeout_secs, timed_out, error_msg);
+ }
+
+ int DexoptSystemServer(const DexoptSystemServerArgs& args,
+ time_t timeout_secs,
+ /*out*/ bool* timed_out,
+ /*out*/ std::string* error_msg) override {
+ std::vector<std::string> cmdline = { dex2oat_path_ };
+ auto result = art::AddDex2oatArgsFromSystemServerArgs(args, cmdline);
+ if (!result.ok()) {
+ LOG(ERROR) << "Dexopt (local) failed: " << result.error().message() << ", cmdline: "
+ << android::base::Join(cmdline, ' ');
+ return -1;
+ }
+ return ExecAndReturnCode(exec_utils_.get(), cmdline, timeout_secs, timed_out, error_msg);
+ }
+
+ private:
+ OdrDexoptLocal(const std::string& dex2oat_path, std::unique_ptr<ExecUtils> exec_utils)
+ : dex2oat_path_(dex2oat_path), exec_utils_(std::move(exec_utils)) {}
+
+ std::string dex2oat_path_;
+ std::unique_ptr<ExecUtils> exec_utils_;
+};
+
+class OdrDexoptCompilationOS final : public OdrDexopt {
+ public:
+ static OdrDexoptCompilationOS* Create(int cid, std::unique_ptr<ExecUtils> exec_utils) {
+ return new OdrDexoptCompilationOS(cid, std::move(exec_utils));
+ }
+
+ int DexoptBcpExtension(const DexoptBcpExtArgs& args,
+ time_t timeout_secs,
+ /*out*/ bool* timed_out,
+ /*out*/ std::string* error_msg) override {
+ std::vector<int> input_fds, output_fds;
+ collectFdsFromDexoptBcpExtensionArgs(input_fds, output_fds, args);
+
+ std::vector<std::string> cmdline;
+ AppendPvmExecArgs(cmdline, input_fds, output_fds);
+
+ // Original dex2oat flags
+ cmdline.push_back("/apex/com.android.art/bin/dex2oat64");
+ auto result = AddDex2oatArgsFromBcpExtensionArgs(args, cmdline);
+ if (!result.ok()) {
+ LOG(ERROR) << "Dexopt (CompOS) failed: " << result.error().message() << ", cmdline: "
+ << android::base::Join(cmdline, ' ');
+ return -1;
+ }
+
+ return ExecAndReturnCode(exec_utils_.get(), cmdline, timeout_secs, timed_out, error_msg);
+ }
+
+ int DexoptSystemServer(const DexoptSystemServerArgs& args,
+ time_t timeout_secs,
+ /*out*/ bool* timed_out,
+ /*out*/ std::string* error_msg) override {
+ std::vector<int> input_fds, output_fds;
+ collectFdsFromDexoptSystemServerArgs(input_fds, output_fds, args);
+
+ std::vector<std::string> cmdline;
+ AppendPvmExecArgs(cmdline, input_fds, output_fds);
+
+ // Original dex2oat flags
+ cmdline.push_back("/apex/com.android.art/bin/dex2oat64");
+ auto result = AddDex2oatArgsFromSystemServerArgs(args, cmdline);
+ if (!result.ok()) {
+ LOG(ERROR) << "Dexopt (CompOS) failed: " << result.error().message() << ", cmdline: "
+ << android::base::Join(cmdline, ' ');
+ return -1;
+ }
+
+ LOG(DEBUG) << "DexoptSystemServer cmdline: " << android::base::Join(cmdline, ' ')
+ << " [timeout " << timeout_secs << "s]";
+ return ExecAndReturnCode(exec_utils_.get(), cmdline, timeout_secs, timed_out, error_msg);
+ }
+
+ private:
+ OdrDexoptCompilationOS(int cid, std::unique_ptr<ExecUtils> exec_utils)
+ : cid_(cid), exec_utils_(std::move(exec_utils)) {}
+
+ void AppendPvmExecArgs(/*inout*/ std::vector<std::string>& cmdline,
+ const std::vector<int>& input_fds,
+ const std::vector<int>& output_fds) {
+ cmdline.emplace_back("/apex/com.android.compos/bin/pvm_exec");
+ cmdline.emplace_back("--cid=" + std::to_string(cid_));
+ cmdline.emplace_back("--in-fd=" + android::base::Join(input_fds, ','));
+ cmdline.emplace_back("--out-fd=" + android::base::Join(output_fds, ','));
+ cmdline.emplace_back("--");
+ }
+
+ void collectFdsFromDexoptBcpExtensionArgs(/*inout*/ std::vector<int>& input_fds,
+ /*inout*/ std::vector<int>& output_fds,
+ const DexoptBcpExtArgs& args) {
+ // input
+ insertOnlyNonNegative(input_fds, args.dexFds);
+ insertIfNonNegative(input_fds, args.profileFd);
+ insertIfNonNegative(input_fds, args.dirtyImageObjectsFd);
+ insertOnlyNonNegative(input_fds, args.bootClasspathFds);
+ // output
+ insertIfNonNegative(output_fds, args.imageFd);
+ insertIfNonNegative(output_fds, args.vdexFd);
+ insertIfNonNegative(output_fds, args.oatFd);
+ }
+
+ void collectFdsFromDexoptSystemServerArgs(/*inout*/ std::vector<int>& input_fds,
+ /*inout*/ std::vector<int>& output_fds,
+ const DexoptSystemServerArgs& args) {
+ // input
+ insertIfNonNegative(input_fds, args.dexFd);
+ insertIfNonNegative(input_fds, args.profileFd);
+ insertOnlyNonNegative(input_fds, args.bootClasspathFds);
+ insertOnlyNonNegative(input_fds, args.bootClasspathImageFds);
+ insertOnlyNonNegative(input_fds, args.bootClasspathVdexFds);
+ insertOnlyNonNegative(input_fds, args.bootClasspathOatFds);
+ insertOnlyNonNegative(input_fds, args.classloaderFds);
+ // output
+ insertIfNonNegative(output_fds, args.imageFd);
+ insertIfNonNegative(output_fds, args.vdexFd);
+ insertIfNonNegative(output_fds, args.oatFd);
+ }
+
+ void insertIfNonNegative(/*inout*/ std::vector<int>& vec, int n) {
+ if (n < 0) {
+ return;
+ }
+ vec.emplace_back(n);
+ }
+
+ void insertOnlyNonNegative(/*inout*/ std::vector<int>& vec, const std::vector<int>& ns) {
+ std::copy_if(ns.begin(), ns.end(), std::back_inserter(vec), [](int n) { return n >= 0; });
+ }
+
+ int cid_;
+ std::unique_ptr<ExecUtils> exec_utils_;
+};
+
+} // namespace
+
+// static
+std::unique_ptr<OdrDexopt> OdrDexopt::Create(const OdrConfig& config,
+ std::unique_ptr<ExecUtils> exec_utils) {
+ if (config.UseCompilationOs()) {
+ int cid = config.GetCompilationOsAddress();
+ return std::unique_ptr<OdrDexopt>(OdrDexoptCompilationOS::Create(cid, std::move(exec_utils)));
+ } else {
+ return std::unique_ptr<OdrDexopt>(OdrDexoptLocal::Create(config.GetDex2Oat(),
+ std::move(exec_utils)));
+ }
+}
+
+} // namespace odrefresh
+} // namespace art
diff --git a/odrefresh/odr_dexopt.h b/odrefresh/odr_dexopt.h
new file mode 100644
index 0000000..2db3b4a
--- /dev/null
+++ b/odrefresh/odr_dexopt.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2021 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 ART_ODREFRESH_ODR_DEXOPT_H_
+#define ART_ODREFRESH_ODR_DEXOPT_H_
+
+#include <memory>
+#include <string>
+#include <time.h>
+
+#include "aidl/com/android/art/DexoptBcpExtArgs.h"
+#include "aidl/com/android/art/DexoptSystemServerArgs.h"
+
+namespace art {
+
+class ExecUtils;
+
+namespace odrefresh {
+
+using aidl::com::android::art::DexoptBcpExtArgs;
+using aidl::com::android::art::DexoptSystemServerArgs;
+
+class OdrConfig;
+
+class OdrDexopt {
+ public:
+ static std::unique_ptr<OdrDexopt> Create(const OdrConfig& confg,
+ std::unique_ptr<ExecUtils> exec_utils);
+
+ virtual ~OdrDexopt() {}
+
+ virtual int DexoptBcpExtension(const DexoptBcpExtArgs& args,
+ time_t timeout_secs,
+ /*out*/ bool* timed_out,
+ /*out*/ std::string* error_msg) = 0;
+ virtual int DexoptSystemServer(const DexoptSystemServerArgs& args,
+ time_t timeout_secs,
+ /*out*/ bool* timed_out,
+ /*out*/ std::string* error_msg) = 0;
+};
+
+} // namespace odrefresh
+} // namespace art
+
+#endif // ART_ODREFRESH_ODR_DEXOPT_H_
diff --git a/odrefresh/odrefresh.cc b/odrefresh/odrefresh.cc
index a984413..36fbf75 100644
--- a/odrefresh/odrefresh.cc
+++ b/odrefresh/odrefresh.cc
@@ -50,7 +50,9 @@
#include "android-base/file.h"
#include "android-base/logging.h"
#include "android-base/macros.h"
+#include "android-base/parseint.h"
#include "android-base/properties.h"
+#include "android-base/result.h"
#include "android-base/stringprintf.h"
#include "android-base/strings.h"
#include "android/log.h"
@@ -66,23 +68,36 @@
#include "dex/art_dex_file_loader.h"
#include "dexoptanalyzer.h"
#include "exec_utils.h"
+#include "libdexopt.h"
#include "log/log.h"
#include "odr_artifacts.h"
#include "odr_common.h"
#include "odr_compilation_log.h"
#include "odr_config.h"
+#include "odr_dexopt.h"
#include "odr_fs_utils.h"
#include "odr_metrics.h"
#include "odrefresh/odrefresh.h"
#include "palette/palette.h"
#include "palette/palette_types.h"
+#include "aidl/com/android/art/CompilerFilter.h"
+#include "aidl/com/android/art/DexoptBcpExtArgs.h"
+#include "aidl/com/android/art/DexoptSystemServerArgs.h"
+#include "aidl/com/android/art/Isa.h"
+
namespace art {
namespace odrefresh {
namespace apex = com::android::apex;
namespace art_apex = com::android::art;
+using aidl::com::android::art::CompilerFilter;
+using aidl::com::android::art::DexoptBcpExtArgs;
+using aidl::com::android::art::DexoptSystemServerArgs;
+using aidl::com::android::art::Isa;
+using android::base::Result;
+
namespace {
// Name of cache info file in the ART Apex artifact cache.
@@ -283,93 +298,65 @@
return true;
}
-void AddDex2OatCommonOptions(/*inout*/ std::vector<std::string>* args) {
- args->emplace_back("--android-root=out/empty");
- args->emplace_back("--abort-on-hard-verifier-error");
- args->emplace_back("--no-abort-on-soft-verifier-error");
- args->emplace_back("--compilation-reason=boot");
- args->emplace_back("--image-format=lz4");
- args->emplace_back("--force-determinism");
- args->emplace_back("--resolve-startup-const-strings=true");
-}
+bool PrepareDex2OatConcurrencyArguments(/*out*/ int* threads, /*out*/ std::vector<int>* cpu_set) {
+ DCHECK(threads);
+ DCHECK(cpu_set && cpu_set->empty());
+ *threads = android::base::GetIntProperty("dalvik.vm.boot-dex2oat-threads",
+ /*default_value=*/ 0,
+ /*min=*/ 1);
-void AddDex2OatConcurrencyArguments(/*inout*/ std::vector<std::string>* args) {
- static constexpr std::pair<const char*, const char*> kPropertyArgPairs[] = {
- std::make_pair("dalvik.vm.boot-dex2oat-cpu-set", "--cpu-set="),
- std::make_pair("dalvik.vm.boot-dex2oat-threads", "-j"),
- };
- for (auto property_arg_pair : kPropertyArgPairs) {
- auto [property, arg] = property_arg_pair;
- std::string value = android::base::GetProperty(property, {});
- if (!value.empty()) {
- args->push_back(arg + value);
- }
+ std::string cpu_set_spec = android::base::GetProperty("dalvik.vm.boot-dex2oat-cpu-set", "");
+ if (cpu_set_spec.empty()) {
+ return true;
}
+ for (auto& str : android::base::Split(cpu_set_spec, ",")) {
+ int id;
+ if (!android::base::ParseInt(str, &id, 0)) {
+ LOG(ERROR) << "Invalid CPU set spec: " << cpu_set_spec;
+ return false;
+ }
+ cpu_set->push_back(id);
+ }
+ return true;
}
-void AddDex2OatDebugInfo(/*inout*/ std::vector<std::string>* args) {
- args->emplace_back("--generate-mini-debug-info");
- args->emplace_back("--strip");
-}
-
-void AddDex2OatInstructionSet(/*inout*/ std::vector<std::string>* args, InstructionSet isa) {
- const char* isa_str = GetInstructionSetString(isa);
- args->emplace_back(Concatenate({"--instruction-set=", isa_str}));
-}
-
-void AddDex2OatProfileAndCompilerFilter(
- /*inout*/ std::vector<std::string>* args,
- /*inout*/ std::vector<std::unique_ptr<File>>* output_files,
- const std::string& profile_path) {
+bool PrepareDex2OatProfileIfExists(/*inout*/ int* profile_fd,
+ /*inout*/ std::vector<std::unique_ptr<File>>* output_files,
+ const std::string& profile_path) {
std::unique_ptr<File> profile_file(OS::OpenFileForReading(profile_path.c_str()));
if (profile_file && profile_file->IsOpened()) {
- args->emplace_back(android::base::StringPrintf("--profile-file-fd=%d", profile_file->Fd()));
- args->emplace_back("--compiler-filter=speed-profile");
+ *profile_fd = profile_file->Fd();
output_files->push_back(std::move(profile_file));
+ return true;
} else {
- args->emplace_back("--compiler-filter=speed");
+ return false;
}
}
-bool AddBootClasspathFds(/*inout*/ std::vector<std::string>& args,
- /*inout*/ std::vector<std::unique_ptr<File>>& output_files,
- const std::vector<std::string>& bcp_jars) {
- auto bcp_fds = std::vector<std::string>();
+bool PrepareBootClasspathFds(/*inout*/ std::vector<int>& boot_classpath_fds,
+ /*inout*/ std::vector<std::unique_ptr<File>>& output_files,
+ const std::vector<std::string>& bcp_jars) {
for (const std::string& jar : bcp_jars) {
std::unique_ptr<File> jar_file(OS::OpenFileForReading(jar.c_str()));
if (!jar_file || !jar_file->IsValid()) {
LOG(ERROR) << "Failed to open a BCP jar " << jar;
return false;
}
- bcp_fds.push_back(std::to_string(jar_file->Fd()));
+ boot_classpath_fds.emplace_back(jar_file->Fd());
output_files.push_back(std::move(jar_file));
}
- args.emplace_back("--runtime-arg");
- args.emplace_back(Concatenate({"-Xbootclasspathfds:", android::base::Join(bcp_fds, ':')}));
return true;
}
-std::string GetBootImagePath(bool on_system, const std::string& jar_path) {
- if (on_system) {
- const std::string jar_name = android::base::Basename(jar_path);
- const std::string image_name = ReplaceFileExtension(jar_name, "art");
- // Typically "/system/framework/boot-framework.art".
- return Concatenate({GetAndroidRoot(), "/framework/boot-", image_name});
- } else {
- // Typically "/data/misc/apexdata/com.android.art/dalvik-cache/boot-framework.art".
- return GetApexDataBootImage(jar_path);
- }
-}
-
-void AddCompiledBootClasspathFdsIfAny(
- /*inout*/ std::vector<std::string>& args,
+void PrepareCompiledBootClasspathFdsIfAny(
+ /*inout*/ DexoptSystemServerArgs& dexopt_args,
/*inout*/ std::vector<std::unique_ptr<File>>& output_files,
const std::vector<std::string>& bcp_jars,
const InstructionSet isa,
bool on_system) {
- std::vector<std::string> bcp_image_fds;
- std::vector<std::string> bcp_oat_fds;
- std::vector<std::string> bcp_vdex_fds;
+ std::vector<int> bcp_image_fds;
+ std::vector<int> bcp_oat_fds;
+ std::vector<int> bcp_vdex_fds;
std::vector<std::unique_ptr<File>> opened_files;
bool added_any = false;
for (const std::string& jar : bcp_jars) {
@@ -377,46 +364,40 @@
image_path = image_path.empty() ? "" : GetSystemImageFilename(image_path.c_str(), isa);
std::unique_ptr<File> image_file(OS::OpenFileForReading(image_path.c_str()));
if (image_file && image_file->IsValid()) {
- bcp_image_fds.push_back(std::to_string(image_file->Fd()));
+ bcp_image_fds.push_back(image_file->Fd());
opened_files.push_back(std::move(image_file));
added_any = true;
} else {
- bcp_image_fds.push_back("-1");
+ bcp_image_fds.push_back(-1);
}
std::string oat_path = ReplaceFileExtension(image_path, "oat");
std::unique_ptr<File> oat_file(OS::OpenFileForReading(oat_path.c_str()));
if (oat_file && oat_file->IsValid()) {
- bcp_oat_fds.push_back(std::to_string(oat_file->Fd()));
+ bcp_oat_fds.push_back(oat_file->Fd());
opened_files.push_back(std::move(oat_file));
added_any = true;
} else {
- bcp_oat_fds.push_back("-1");
+ bcp_oat_fds.push_back(-1);
}
std::string vdex_path = ReplaceFileExtension(image_path, "vdex");
std::unique_ptr<File> vdex_file(OS::OpenFileForReading(vdex_path.c_str()));
if (vdex_file && vdex_file->IsValid()) {
- bcp_vdex_fds.push_back(std::to_string(vdex_file->Fd()));
+ bcp_vdex_fds.push_back(vdex_file->Fd());
opened_files.push_back(std::move(vdex_file));
added_any = true;
} else {
- bcp_vdex_fds.push_back("-1");
+ bcp_vdex_fds.push_back(-1);
}
}
// Add same amount of FDs as BCP JARs, or none.
if (added_any) {
std::move(opened_files.begin(), opened_files.end(), std::back_inserter(output_files));
- args.emplace_back("--runtime-arg");
- args.emplace_back(
- Concatenate({"-Xbootclasspathimagefds:", android::base::Join(bcp_image_fds, ':')}));
- args.emplace_back("--runtime-arg");
- args.emplace_back(
- Concatenate({"-Xbootclasspathoatfds:", android::base::Join(bcp_oat_fds, ':')}));
- args.emplace_back("--runtime-arg");
- args.emplace_back(
- Concatenate({"-Xbootclasspathvdexfds:", android::base::Join(bcp_vdex_fds, ':')}));
+ dexopt_args.bootClasspathImageFds = bcp_image_fds;
+ dexopt_args.bootClasspathVdexFds = bcp_vdex_fds;
+ dexopt_args.bootClasspathOatFds = bcp_oat_fds;
}
}
@@ -424,20 +405,6 @@
return Concatenate({staging_dir, "/", android::base::Basename(path)});
}
-std::string JoinFilesAsFDs(const std::vector<std::unique_ptr<File>>& files, char delimiter) {
- std::stringstream output;
- bool is_first = true;
- for (const auto& f : files) {
- if (is_first) {
- is_first = false;
- } else {
- output << delimiter;
- }
- output << std::to_string(f->Fd());
- }
- return output.str();
-}
-
WARN_UNUSED bool CheckCompilationSpace() {
// Check the available storage space against an arbitrary threshold because dex2oat does not
// report when it runs out of storage space and we do not want to completely fill
@@ -469,20 +436,52 @@
return GetArtRoot() + "/javalib/boot.art";
}
+Isa InstructionSetToAidlIsa(InstructionSet isa) {
+ switch (isa) {
+ case InstructionSet::kArm:
+ return Isa::ARM;
+ case InstructionSet::kThumb2:
+ return Isa::THUMB2;
+ case InstructionSet::kArm64:
+ return Isa::ARM64;
+ case InstructionSet::kX86:
+ return Isa::X86;
+ case InstructionSet::kX86_64:
+ return Isa::X86_64;
+ default:
+ UNREACHABLE();
+ }
+}
+
+CompilerFilter CompilerFilterStringToAidl(const std::string& compiler_filter) {
+ if (compiler_filter == "speed-profile") {
+ return CompilerFilter::SPEED_PROFILE;
+ } else if (compiler_filter == "speed") {
+ return CompilerFilter::SPEED;
+ } else if (compiler_filter == "verify") {
+ return CompilerFilter::VERIFY;
+ } else {
+ return CompilerFilter::UNSUPPORTED;
+ }
+}
+
} // namespace
OnDeviceRefresh::OnDeviceRefresh(const OdrConfig& config)
: OnDeviceRefresh(config,
Concatenate({kOdrefreshArtifactDirectory, "/", kCacheInfoFile}),
- std::make_unique<ExecUtils>()) {}
+ std::make_unique<ExecUtils>(),
+ std::move(OdrDexopt::Create(config, std::make_unique<ExecUtils>()))) {}
OnDeviceRefresh::OnDeviceRefresh(const OdrConfig& config,
const std::string& cache_info_filename,
- std::unique_ptr<ExecUtils> exec_utils)
+ std::unique_ptr<ExecUtils> exec_utils,
+ std::unique_ptr<OdrDexopt> odr_dexopt)
: config_{config},
cache_info_filename_{cache_info_filename},
start_time_{time(nullptr)},
- exec_utils_{std::move(exec_utils)} {
+ exec_utils_{std::move(exec_utils)},
+ odr_dexopt_{std::move(odr_dexopt)} {
for (const std::string& jar : android::base::Split(config_.GetDex2oatBootClasspath(), ":")) {
// Boot class path extensions are those not in the ART APEX. Updatable APEXes should not
// have DEX files in the DEX2OATBOOTCLASSPATH. At the time of writing i18n is a non-updatable
@@ -1274,28 +1273,22 @@
uint32_t* dex2oat_invocation_count,
std::string* error_msg) const {
ScopedOdrCompilationTimer compilation_timer(metrics);
- std::vector<std::string> args;
- args.push_back(config_.GetDex2Oat());
- AddDex2OatCommonOptions(&args);
- AddDex2OatConcurrencyArguments(&args);
- AddDex2OatDebugInfo(&args);
- AddDex2OatInstructionSet(&args, isa);
+ DexoptBcpExtArgs dexopt_args;
+ dexopt_args.isa = InstructionSetToAidlIsa(isa);
std::vector<std::unique_ptr<File>> readonly_files_raii;
const std::string boot_profile_file(GetAndroidRoot() + "/etc/boot-image.prof");
- AddDex2OatProfileAndCompilerFilter(&args, &readonly_files_raii, boot_profile_file);
-
- // Compile as a single image for fewer files and slightly less memory overhead.
- args.emplace_back("--single-image");
-
- // Set boot-image and expectation of compiling boot classpath extensions.
- args.emplace_back("--boot-image=" + GetBootImage());
+ if (!PrepareDex2OatProfileIfExists(&dexopt_args.profileFd, &readonly_files_raii,
+ boot_profile_file)) {
+ LOG(ERROR) << "Missing expected profile for boot extension: " << boot_profile_file;
+ return false;
+ }
const std::string dirty_image_objects_file(GetAndroidRoot() + "/etc/dirty-image-objects");
if (OS::FileExists(dirty_image_objects_file.c_str())) {
std::unique_ptr<File> file(OS::OpenFileForReading(dirty_image_objects_file.c_str()));
- args.emplace_back(android::base::StringPrintf("--dirty-image-objects-fd=%d", file->Fd()));
+ dexopt_args.dirtyImageObjectsFd = file->Fd();
readonly_files_raii.push_back(std::move(file));
} else {
LOG(WARNING) << "Missing dirty objects file : " << QuotePath(dirty_image_objects_file);
@@ -1303,16 +1296,17 @@
// Add boot extensions to compile.
for (const std::string& component : boot_extension_compilable_jars_) {
- args.emplace_back("--dex-file=" + component);
std::unique_ptr<File> file(OS::OpenFileForReading(component.c_str()));
- args.emplace_back(android::base::StringPrintf("--dex-fd=%d", file->Fd()));
+ dexopt_args.dexPaths.emplace_back(component);
+ dexopt_args.dexFds.emplace_back(file->Fd());
readonly_files_raii.push_back(std::move(file));
}
- args.emplace_back("--runtime-arg");
- args.emplace_back(Concatenate({"-Xbootclasspath:", config_.GetDex2oatBootClasspath()}));
auto bcp_jars = android::base::Split(config_.GetDex2oatBootClasspath(), ":");
- if (!AddBootClasspathFds(args, readonly_files_raii, bcp_jars)) {
+ dexopt_args.bootClasspaths = bcp_jars;
+ if (!PrepareBootClasspathFds(dexopt_args.bootClasspathFds,
+ readonly_files_raii,
+ bcp_jars)) {
return false;
}
@@ -1321,19 +1315,18 @@
CHECK_EQ(GetApexDataOatFilename(boot_extension_compilable_jars_.front().c_str(), isa),
artifacts.OatPath());
- args.emplace_back("--oat-location=" + artifacts.OatPath());
- const std::pair<const std::string, const char*> location_kind_pairs[] = {
- std::make_pair(artifacts.ImagePath(), "image"),
- std::make_pair(artifacts.OatPath(), "oat"),
- std::make_pair(artifacts.VdexPath(), "output-vdex")};
-
+ dexopt_args.oatLocation = artifacts.OatPath();
+ const std::pair<const std::string, int*> location_kind_pairs[] = {
+ std::make_pair(artifacts.ImagePath(), &dexopt_args.imageFd),
+ std::make_pair(artifacts.OatPath(), &dexopt_args.oatFd),
+ std::make_pair(artifacts.VdexPath(), &dexopt_args.vdexFd)};
std::vector<std::unique_ptr<File>> staging_files;
for (const auto& location_kind_pair : location_kind_pairs) {
- auto& [location, kind] = location_kind_pair;
+ auto& [location, out_ptr] = location_kind_pair;
const std::string staging_location = GetStagingLocation(staging_dir, location);
std::unique_ptr<File> staging_file(OS::CreateEmptyFile(staging_location.c_str()));
if (staging_file == nullptr) {
- PLOG(ERROR) << "Failed to create " << kind << " file: " << staging_location;
+ PLOG(ERROR) << "Failed to create file: " << staging_location;
metrics.SetStatus(OdrMetrics::Status::kIoError);
EraseFiles(staging_files);
return false;
@@ -1346,7 +1339,7 @@
return false;
}
- args.emplace_back(android::base::StringPrintf("--%s-fd=%d", kind, staging_file->Fd()));
+ *out_ptr = staging_file->Fd();
staging_files.emplace_back(std::move(staging_file));
}
@@ -1356,28 +1349,22 @@
return false;
}
- if (config_.UseCompilationOs()) {
- std::vector<std::string> prefix_args = {
- "/apex/com.android.compos/bin/pvm_exec",
- "--cid=" + config_.GetCompilationOsAddress(),
- "--in-fd=" + JoinFilesAsFDs(readonly_files_raii, ','),
- "--out-fd=" + JoinFilesAsFDs(staging_files, ','),
- "--",
- };
- args.insert(args.begin(), prefix_args.begin(), prefix_args.end());
+ if (!PrepareDex2OatConcurrencyArguments(&dexopt_args.threads, &dexopt_args.cpuSet)) {
+ return false;
}
const time_t timeout = GetSubprocessTimeout();
- const std::string cmd_line = android::base::Join(args, ' ');
- LOG(INFO) << "Compiling boot extensions (" << isa << "): " << cmd_line << " [timeout " << timeout
- << "s]";
+ LOG(INFO) << "Compiling boot extensions (" << isa << "): " << dexopt_args.toString()
+ << " [timeout " << timeout << "s]";
if (config_.GetDryRun()) {
LOG(INFO) << "Compilation skipped (dry-run).";
return true;
}
bool timed_out = false;
- int dex2oat_exit_code = exec_utils_->ExecAndReturnCode(args, timeout, &timed_out, error_msg);
+ int dex2oat_exit_code = odr_dexopt_->DexoptBcpExtension(
+ dexopt_args, timeout, &timed_out, error_msg);
+
if (dex2oat_exit_code != 0) {
if (timed_out) {
metrics.SetStatus(OdrMetrics::Status::kTimeLimitExceeded);
@@ -1410,26 +1397,29 @@
const InstructionSet isa = config_.GetSystemServerIsa();
for (const std::string& jar : systemserver_compilable_jars_) {
std::vector<std::unique_ptr<File>> readonly_files_raii;
- std::vector<std::string> args;
- args.emplace_back(dex2oat);
- args.emplace_back("--dex-file=" + jar);
+ DexoptSystemServerArgs dexopt_args;
+ dexopt_args.isa = InstructionSetToAidlIsa(isa);
std::unique_ptr<File> dex_file(OS::OpenFileForReading(jar.c_str()));
- args.emplace_back(android::base::StringPrintf("--dex-fd=%d", dex_file->Fd()));
+
+ dexopt_args.dexPath = jar;
+ dexopt_args.dexFd = dex_file->Fd();
readonly_files_raii.push_back(std::move(dex_file));
- AddDex2OatCommonOptions(&args);
- AddDex2OatConcurrencyArguments(&args);
- AddDex2OatDebugInfo(&args);
- AddDex2OatInstructionSet(&args, isa);
+ dexopt_args.isa = InstructionSetToAidlIsa(isa);
const std::string jar_name(android::base::Basename(jar));
const std::string profile = Concatenate({GetAndroidRoot(), "/framework/", jar_name, ".prof"});
std::string compiler_filter =
android::base::GetProperty("dalvik.vm.systemservercompilerfilter", "speed");
if (compiler_filter == "speed-profile") {
- AddDex2OatProfileAndCompilerFilter(&args, &readonly_files_raii, profile);
+ // Use speed-profile only if profile is provided, otherwise fallback to speed.
+ if (PrepareDex2OatProfileIfExists(&dexopt_args.profileFd, &readonly_files_raii, profile)) {
+ dexopt_args.compilerFilter = CompilerFilter::SPEED_PROFILE;
+ } else {
+ dexopt_args.compilerFilter = CompilerFilter::SPEED;
+ }
} else {
- args.emplace_back("--compiler-filter=" + compiler_filter);
+ dexopt_args.compilerFilter = CompilerFilterStringToAidl(compiler_filter);
}
const std::string image_location = GetSystemServerImagePath(/*on_system=*/false, jar);
@@ -1445,31 +1435,32 @@
OdrArtifacts artifacts = OdrArtifacts::ForSystemServer(image_location);
CHECK_EQ(artifacts.OatPath(), GetApexDataOdexFilename(jar.c_str(), isa));
- const std::pair<const std::string, const char*> location_kind_pairs[] = {
- std::make_pair(artifacts.ImagePath(), "app-image"),
- std::make_pair(artifacts.OatPath(), "oat"),
- std::make_pair(artifacts.VdexPath(), "output-vdex")};
+ const std::pair<const std::string, int*> location_kind_pairs[] = {
+ std::make_pair(artifacts.ImagePath(), &dexopt_args.imageFd),
+ std::make_pair(artifacts.OatPath(), &dexopt_args.oatFd),
+ std::make_pair(artifacts.VdexPath(), &dexopt_args.vdexFd)};
std::vector<std::unique_ptr<File>> staging_files;
for (const auto& location_kind_pair : location_kind_pairs) {
- auto& [location, kind] = location_kind_pair;
+ auto& [location, out_ptr] = location_kind_pair;
const std::string staging_location = GetStagingLocation(staging_dir, location);
std::unique_ptr<File> staging_file(OS::CreateEmptyFile(staging_location.c_str()));
if (staging_file == nullptr) {
- PLOG(ERROR) << "Failed to create " << kind << " file: " << staging_location;
+ PLOG(ERROR) << "Failed to create file: " << staging_location;
metrics.SetStatus(OdrMetrics::Status::kIoError);
EraseFiles(staging_files);
return false;
}
- args.emplace_back(android::base::StringPrintf("--%s-fd=%d", kind, staging_file->Fd()));
+ *out_ptr = staging_file->Fd();
staging_files.emplace_back(std::move(staging_file));
}
- args.emplace_back("--oat-location=" + artifacts.OatPath());
+ dexopt_args.oatLocation = artifacts.OatPath();
- args.emplace_back("--runtime-arg");
- args.emplace_back(Concatenate({"-Xbootclasspath:", config_.GetBootClasspath()}));
auto bcp_jars = android::base::Split(config_.GetBootClasspath(), ":");
- if (!AddBootClasspathFds(args, readonly_files_raii, bcp_jars)) {
+ dexopt_args.bootClasspaths = bcp_jars;
+ if (!PrepareBootClasspathFds(dexopt_args.bootClasspathFds,
+ readonly_files_raii,
+ bcp_jars)) {
return false;
}
std::string unused_error_msg;
@@ -1477,11 +1468,11 @@
// and the artifacts must exist on /system.
bool boot_image_on_system =
!BootExtensionArtifactsExist(/*on_system=*/false, isa, &unused_error_msg);
- AddCompiledBootClasspathFdsIfAny(
- args, readonly_files_raii, bcp_jars, isa, boot_image_on_system);
+ PrepareCompiledBootClasspathFdsIfAny(
+ dexopt_args, readonly_files_raii, bcp_jars, isa, boot_image_on_system);
+ dexopt_args.isBootImageOnSystem = boot_image_on_system;
- const std::string context_path = android::base::Join(classloader_context, ':');
- args.emplace_back(Concatenate({"--class-loader-context=PCL[", context_path, "]"}));
+ dexopt_args.classloaderContext = classloader_context;
if (!classloader_context.empty()) {
std::vector<int> fds;
for (const std::string& path : classloader_context) {
@@ -1494,33 +1485,25 @@
fds.emplace_back(file->Fd());
readonly_files_raii.emplace_back(std::move(file));
}
- const std::string context_fds = android::base::Join(fds, ':');
- args.emplace_back(Concatenate({"--class-loader-context-fds=", context_fds}));
+ dexopt_args.classloaderFds = fds;
}
- std::string extension_image = GetBootImageExtensionImage(boot_image_on_system);
- args.emplace_back(Concatenate({"--boot-image=", GetBootImage(), ":", extension_image}));
- if (config_.UseCompilationOs()) {
- std::vector<std::string> prefix_args = {
- "/apex/com.android.compos/bin/pvm_exec",
- "--cid=" + config_.GetCompilationOsAddress(),
- "--in-fd=" + JoinFilesAsFDs(readonly_files_raii, ','),
- "--out-fd=" + JoinFilesAsFDs(staging_files, ','),
- "--",
- };
- args.insert(args.begin(), prefix_args.begin(), prefix_args.end());
+ if (!PrepareDex2OatConcurrencyArguments(&dexopt_args.threads, &dexopt_args.cpuSet)) {
+ return false;
}
const time_t timeout = GetSubprocessTimeout();
- const std::string cmd_line = android::base::Join(args, ' ');
- LOG(INFO) << "Compiling " << jar << ": " << cmd_line << " [timeout " << timeout << "s]";
+ LOG(INFO) << "Compiling " << jar << ": " << dexopt_args.toString() << " [timeout " << timeout
+ << "s]";
if (config_.GetDryRun()) {
LOG(INFO) << "Compilation skipped (dry-run).";
return true;
}
bool timed_out = false;
- int dex2oat_exit_code = exec_utils_->ExecAndReturnCode(args, timeout, &timed_out, error_msg);
+ int dex2oat_exit_code = odr_dexopt_->DexoptSystemServer(
+ dexopt_args, timeout, &timed_out, error_msg);
+
if (dex2oat_exit_code != 0) {
if (timed_out) {
metrics.SetStatus(OdrMetrics::Status::kTimeLimitExceeded);
diff --git a/odrefresh/odrefresh.h b/odrefresh/odrefresh.h
index 2a7164b..d842688 100644
--- a/odrefresh/odrefresh.h
+++ b/odrefresh/odrefresh.h
@@ -28,6 +28,7 @@
#include "exec_utils.h"
#include "odr_artifacts.h"
#include "odr_config.h"
+#include "odr_dexopt.h"
#include "odr_metrics.h"
#include "odrefresh/odrefresh.h"
@@ -41,7 +42,8 @@
// Constructor with injections. For testing and internal use only.
OnDeviceRefresh(const OdrConfig& config,
const std::string& cache_info_filename,
- std::unique_ptr<ExecUtils> exec_utils);
+ std::unique_ptr<ExecUtils> exec_utils,
+ std::unique_ptr<OdrDexopt> odr_dexopt);
// Returns the exit code, a list of ISAs that boot extensions should be compiled for, and a
// boolean indicating whether the system server should be compiled.
@@ -180,6 +182,8 @@
std::unique_ptr<ExecUtils> exec_utils_;
+ std::unique_ptr<OdrDexopt> odr_dexopt_;
+
DISALLOW_COPY_AND_ASSIGN(OnDeviceRefresh);
};
diff --git a/odrefresh/odrefresh_main.cc b/odrefresh/odrefresh_main.cc
index d234e0d..2135224 100644
--- a/odrefresh/odrefresh_main.cc
+++ b/odrefresh/odrefresh_main.cc
@@ -19,6 +19,7 @@
#include <string>
#include <string_view>
+#include "android-base/parseint.h"
#include "android-base/properties.h"
#include "android-base/stringprintf.h"
#include "android-base/strings.h"
@@ -200,7 +201,11 @@
const char* arg = argv[n];
std::string value;
if (ArgumentMatches(arg, "--use-compilation-os=", &value)) {
- config->SetCompilationOsAddress(value);
+ int cid;
+ if (!android::base::ParseInt(value, &cid)) {
+ ArgumentError("Failed to parse CID: %s", value.c_str());
+ }
+ config->SetCompilationOsAddress(cid);
} else if (!InitializeCommonConfig(arg, config)) {
UsageError("Unrecognized argument: '%s'", arg);
}
diff --git a/odrefresh/odrefresh_test.cc b/odrefresh/odrefresh_test.cc
index bbcca60..5094843 100644
--- a/odrefresh/odrefresh_test.cc
+++ b/odrefresh/odrefresh_test.cc
@@ -22,6 +22,7 @@
#include <functional>
#include <memory>
#include <string_view>
+#include <utility>
#include <vector>
#include "android-base/parseint.h"
@@ -39,24 +40,35 @@
#include "odr_artifacts.h"
#include "odr_common.h"
#include "odr_config.h"
+#include "odr_dexopt.h"
#include "odr_fs_utils.h"
#include "odr_metrics.h"
#include "odrefresh/odrefresh.h"
+#include "aidl/com/android/art/CompilerFilter.h"
+#include "aidl/com/android/art/DexoptBcpExtArgs.h"
+#include "aidl/com/android/art/DexoptSystemServerArgs.h"
+#include "aidl/com/android/art/Isa.h"
+
namespace art {
namespace odrefresh {
using ::testing::AllOf;
using ::testing::Contains;
-using ::testing::HasSubstr;
-using ::testing::Not;
+using ::testing::Eq;
+using ::testing::Field;
+using ::testing::Ge;
using ::testing::Return;
+using aidl::com::android::art::CompilerFilter;
+using aidl::com::android::art::DexoptBcpExtArgs;
+using aidl::com::android::art::DexoptSystemServerArgs;
+using aidl::com::android::art::Isa;
constexpr int kReplace = 1;
void CreateEmptyFile(const std::string& name) {
File* file = OS::CreateEmptyFile(name.c_str());
- ASSERT_TRUE(file != nullptr);
+ ASSERT_TRUE(file != nullptr) << "Cannot create file " << name;
file->Release();
delete file;
}
@@ -73,42 +85,32 @@
return android::base::ScopeGuard([=]() { android::base::SetProperty(key, old_value); });
}
-class MockExecUtils : public ExecUtils {
+class MockOdrDexopt : public OdrDexopt {
public:
// A workaround to avoid MOCK_METHOD on a method with an `std::string*` parameter, which will lead
// to a conflict between gmock and android-base/logging.h (b/132668253).
- int ExecAndReturnCode(std::vector<std::string>& arg_vector,
- time_t,
- bool*,
- std::string*) const override {
- return DoExecAndReturnCode(arg_vector);
+ int DexoptBcpExtension(const DexoptBcpExtArgs& args,
+ time_t,
+ bool*,
+ std::string*) override {
+ return DoDexoptBcpExtension(args);
}
- MOCK_METHOD(int, DoExecAndReturnCode, (std::vector<std::string> & arg_vector), (const));
+ int DexoptSystemServer(const DexoptSystemServerArgs& args,
+ time_t,
+ bool*,
+ std::string*) override {
+ return DoDexoptSystemServer(args);
+ }
+
+ MOCK_METHOD(int, DoDexoptBcpExtension, (const DexoptBcpExtArgs&));
+ MOCK_METHOD(int, DoDexoptSystemServer, (const DexoptSystemServerArgs&));
};
-// Matches a flag that starts with `flag` and is a colon-separated list that contains an element
-// that matches `matcher`.
-MATCHER_P2(FlagContains, flag, matcher, "") {
- std::string_view value = arg;
- if (!android::base::ConsumePrefix(&value, flag)) {
- return false;
- }
- for (std::string_view s : SplitString(value, ':')) {
- if (ExplainMatchResult(matcher, s, result_listener)) {
- return true;
- }
- }
- return false;
-}
-
// Matches an FD of a file whose path matches `matcher`.
MATCHER_P(FdOf, matcher, "") {
char path[PATH_MAX];
- int fd;
- if (!android::base::ParseInt(std::string{arg}, &fd)) {
- return false;
- }
+ int fd = arg;
std::string proc_path = android::base::StringPrintf("/proc/self/fd/%d", fd);
ssize_t len = readlink(proc_path.c_str(), path, sizeof(path));
if (len < 0) {
@@ -148,6 +150,11 @@
dalvik_cache_dir_ = art_apex_data_path + "/dalvik-cache";
ASSERT_TRUE(EnsureDirectoryExists(dalvik_cache_dir_ + "/x86_64"));
+ std::string system_etc_dir = Concatenate({android_root_path, "/etc"});
+ ASSERT_TRUE(EnsureDirectoryExists(system_etc_dir));
+ boot_profile_file_ = system_etc_dir + "/boot-image.prof";
+ CreateEmptyFile(boot_profile_file_);
+
framework_dir_ = android_root_path + "/framework";
framework_jar_ = framework_dir_ + "/framework.jar";
location_provider_jar_ = framework_dir_ + "/com.android.location.provider.jar";
@@ -177,12 +184,7 @@
ASSERT_TRUE(EnsureDirectoryExists(staging_dir));
config_.SetStagingDir(staging_dir);
- auto mock_exec_utils = std::make_unique<MockExecUtils>();
- mock_exec_utils_ = mock_exec_utils.get();
-
metrics_ = std::make_unique<OdrMetrics>(dalvik_cache_dir_);
- odrefresh_ = std::make_unique<OnDeviceRefresh>(
- config_, dalvik_cache_dir_ + "/cache-info.xml", std::move(mock_exec_utils));
}
void TearDown() override {
@@ -195,19 +197,27 @@
CommonArtTest::TearDown();
}
+ std::pair<std::unique_ptr<OnDeviceRefresh>, MockOdrDexopt*> CreateOdRefresh() {
+ auto mock_odr_dexopt = std::make_unique<MockOdrDexopt>();
+ MockOdrDexopt* mock_odr_dexopt_ptr = mock_odr_dexopt.get();
+ auto odrefresh = std::make_unique<OnDeviceRefresh>(
+ config_, dalvik_cache_dir_ + "/cache-info.xml", std::make_unique<ExecUtils>(),
+ std::move(mock_odr_dexopt));
+ return std::make_pair(std::move(odrefresh), mock_odr_dexopt_ptr);
+ }
+
std::unique_ptr<ScratchDir> temp_dir_;
std::unique_ptr<ScopedUnsetEnvironmentVariable> android_root_env_;
std::unique_ptr<ScopedUnsetEnvironmentVariable> android_art_root_env_;
std::unique_ptr<ScopedUnsetEnvironmentVariable> art_apex_data_env_;
OdrConfig config_;
- MockExecUtils* mock_exec_utils_;
std::unique_ptr<OdrMetrics> metrics_;
- std::unique_ptr<OnDeviceRefresh> odrefresh_;
std::string framework_jar_;
std::string location_provider_jar_;
std::string services_jar_;
std::string dalvik_cache_dir_;
std::string framework_dir_;
+ std::string boot_profile_file_;
};
TEST_F(OdRefreshTest, OdrefreshArtifactDirectory) {
@@ -225,63 +235,116 @@
}
{
- // Defaults to "speed".
+ auto [odrefresh, mock_odr_dexopt] = CreateOdRefresh();
+
+ // Test setup: default compiler filter should be "speed".
+ auto guard = ScopedSetProperty("dalvik.vm.systemservercompilerfilter", "");
+
EXPECT_CALL(
- *mock_exec_utils_,
- DoExecAndReturnCode(AllOf(Contains(Concatenate({"--dex-file=", location_provider_jar_})),
- Not(Contains(HasSubstr("--profile-file-fd="))),
- Contains("--compiler-filter=speed"))))
- .WillOnce(Return(0));
- EXPECT_CALL(*mock_exec_utils_,
- DoExecAndReturnCode(AllOf(Contains(Concatenate({"--dex-file=", services_jar_})),
- Not(Contains(HasSubstr("--profile-file-fd="))),
- Contains("--compiler-filter=speed"))))
- .WillOnce(Return(0));
- EXPECT_EQ(odrefresh_->Compile(
+ *mock_odr_dexopt,
+ DoDexoptSystemServer(AllOf(
+ Field(&DexoptSystemServerArgs::dexPath, Eq(location_provider_jar_)),
+ Field(&DexoptSystemServerArgs::compilerFilter, Eq(CompilerFilter::SPEED)))))
+ .WillOnce(Return(0))
+ .RetiresOnSaturation();
+ EXPECT_CALL(
+ *mock_odr_dexopt,
+ DoDexoptSystemServer(AllOf(
+ Field(&DexoptSystemServerArgs::dexPath, Eq(services_jar_)),
+ Field(&DexoptSystemServerArgs::compilerFilter, Eq(CompilerFilter::SPEED)))))
+ .WillOnce(Return(0))
+ .RetiresOnSaturation();
+ EXPECT_EQ(odrefresh->Compile(
*metrics_, /*compile_boot_extensions=*/{}, /*compile_system_server=*/true),
ExitCode::kCompilationSuccess);
}
{
+ auto [odrefresh, mock_odr_dexopt] = CreateOdRefresh();
+
+ // Test setup: with "speed-profile" compiler filter in the request, only apply if there is a
+ // profile, otherwise fallback to speed.
auto guard = ScopedSetProperty("dalvik.vm.systemservercompilerfilter", "speed-profile");
+
// services.jar has a profile, while location.provider.jar does not.
EXPECT_CALL(
- *mock_exec_utils_,
- DoExecAndReturnCode(AllOf(Contains(Concatenate({"--dex-file=", location_provider_jar_})),
- Not(Contains(HasSubstr("--profile-file-fd="))),
- Contains("--compiler-filter=speed"))))
- .WillOnce(Return(0));
- EXPECT_CALL(*mock_exec_utils_,
- DoExecAndReturnCode(AllOf(Contains(Concatenate({"--dex-file=", services_jar_})),
- Contains(HasSubstr("--profile-file-fd=")),
- Contains("--compiler-filter=speed-profile"))))
- .WillOnce(Return(0));
- EXPECT_EQ(odrefresh_->Compile(
+ *mock_odr_dexopt,
+ DoDexoptSystemServer(AllOf(
+ Field(&DexoptSystemServerArgs::dexPath, Eq(services_jar_)),
+ Field(&DexoptSystemServerArgs::profileFd, Ge(0)),
+ Field(&DexoptSystemServerArgs::compilerFilter, Eq(CompilerFilter::SPEED_PROFILE)))))
+ .WillOnce(Return(0))
+ .RetiresOnSaturation();
+ EXPECT_CALL(
+ *mock_odr_dexopt,
+ DoDexoptSystemServer(AllOf(
+ Field(&DexoptSystemServerArgs::dexPath, Eq(location_provider_jar_)),
+ Field(&DexoptSystemServerArgs::compilerFilter, Eq(CompilerFilter::SPEED)))))
+ .WillOnce(Return(0))
+ .RetiresOnSaturation();
+ EXPECT_EQ(odrefresh->Compile(
*metrics_, /*compile_boot_extensions=*/{}, /*compile_system_server=*/true),
ExitCode::kCompilationSuccess);
}
{
+ auto [odrefresh, mock_odr_dexopt] = CreateOdRefresh();
+
+ // Test setup: "verify" compiler filter should be simply applied.
auto guard = ScopedSetProperty("dalvik.vm.systemservercompilerfilter", "verify");
+
EXPECT_CALL(
- *mock_exec_utils_,
- DoExecAndReturnCode(AllOf(Contains(Concatenate({"--dex-file=", location_provider_jar_})),
- Not(Contains(HasSubstr("--profile-file-fd="))),
- Contains("--compiler-filter=verify"))))
- .WillOnce(Return(0));
- EXPECT_CALL(*mock_exec_utils_,
- DoExecAndReturnCode(AllOf(Contains(Concatenate({"--dex-file=", services_jar_})),
- Not(Contains(HasSubstr("--profile-file-fd="))),
- Contains("--compiler-filter=verify"))))
- .WillOnce(Return(0));
- EXPECT_EQ(odrefresh_->Compile(
+ *mock_odr_dexopt,
+ DoDexoptSystemServer(AllOf(
+ Field(&DexoptSystemServerArgs::dexPath, Eq(location_provider_jar_)),
+ Field(&DexoptSystemServerArgs::compilerFilter, Eq(CompilerFilter::VERIFY)))))
+ .WillOnce(Return(0))
+ .RetiresOnSaturation();
+ EXPECT_CALL(
+ *mock_odr_dexopt,
+ DoDexoptSystemServer(AllOf(
+ Field(&DexoptSystemServerArgs::dexPath, Eq(services_jar_)),
+ Field(&DexoptSystemServerArgs::compilerFilter, Eq(CompilerFilter::VERIFY)))))
+ .WillOnce(Return(0))
+ .RetiresOnSaturation();
+ EXPECT_EQ(odrefresh->Compile(
*metrics_, /*compile_boot_extensions=*/{}, /*compile_system_server=*/true),
ExitCode::kCompilationSuccess);
}
}
+TEST_F(OdRefreshTest, OutputFilesAndIsa) {
+ auto [odrefresh, mock_odr_dexopt] = CreateOdRefresh();
+
+ EXPECT_CALL(
+ *mock_odr_dexopt,
+ DoDexoptBcpExtension(AllOf(
+ Field(&DexoptBcpExtArgs::isa, Eq(Isa::X86_64)),
+ Field(&DexoptBcpExtArgs::imageFd, Ge(0)),
+ Field(&DexoptBcpExtArgs::vdexFd, Ge(0)),
+ Field(&DexoptBcpExtArgs::oatFd, Ge(0)))))
+ .WillOnce(Return(0));
+
+ EXPECT_CALL(
+ *mock_odr_dexopt,
+ DoDexoptSystemServer(AllOf(
+ Field(&DexoptSystemServerArgs::isa, Eq(Isa::X86_64)),
+ Field(&DexoptSystemServerArgs::imageFd, Ge(0)),
+ Field(&DexoptSystemServerArgs::vdexFd, Ge(0)),
+ Field(&DexoptSystemServerArgs::oatFd, Ge(0)))))
+ .Times(2)
+ .WillRepeatedly(Return(0));
+
+ EXPECT_EQ(odrefresh->Compile(*metrics_,
+ /*compile_boot_extensions=*/{InstructionSet::kX86_64},
+ /*compile_system_server=*/true),
+ ExitCode::kCompilationSuccess);
+}
+
TEST_F(OdRefreshTest, CompileChoosesBootImage) {
{
+ auto [odrefresh, mock_odr_dexopt] = CreateOdRefresh();
+
// Boot image is on /data.
OdrArtifacts artifacts =
OdrArtifacts::ForBootImageExtension(dalvik_cache_dir_ + "/x86_64/boot-framework.art");
@@ -290,20 +353,25 @@
auto file3 = ScopedCreateEmptyFile(artifacts.OatPath());
EXPECT_CALL(
- *mock_exec_utils_,
- DoExecAndReturnCode(AllOf(
- Contains(FlagContains("--boot-image=", dalvik_cache_dir_ + "/boot-framework.art")),
- Contains(FlagContains("-Xbootclasspathimagefds:", FdOf(artifacts.ImagePath()))),
- Contains(FlagContains("-Xbootclasspathvdexfds:", FdOf(artifacts.VdexPath()))),
- Contains(FlagContains("-Xbootclasspathoatfds:", FdOf(artifacts.OatPath()))))))
+ *mock_odr_dexopt,
+ DoDexoptSystemServer(AllOf(
+ Field(&DexoptSystemServerArgs::isBootImageOnSystem, Eq(false)),
+ Field(&DexoptSystemServerArgs::bootClasspathImageFds,
+ Contains(FdOf(artifacts.ImagePath()))),
+ Field(&DexoptSystemServerArgs::bootClasspathVdexFds,
+ Contains(FdOf(artifacts.VdexPath()))),
+ Field(&DexoptSystemServerArgs::bootClasspathOatFds,
+ Contains(FdOf(artifacts.OatPath()))))))
.Times(2)
.WillRepeatedly(Return(0));
- EXPECT_EQ(odrefresh_->Compile(
+ EXPECT_EQ(odrefresh->Compile(
*metrics_, /*compile_boot_extensions=*/{}, /*compile_system_server=*/true),
ExitCode::kCompilationSuccess);
}
{
+ auto [odrefresh, mock_odr_dexopt] = CreateOdRefresh();
+
// Boot image is on /system.
OdrArtifacts artifacts =
OdrArtifacts::ForBootImageExtension(framework_dir_ + "/x86_64/boot-framework.art");
@@ -311,15 +379,19 @@
auto file2 = ScopedCreateEmptyFile(artifacts.VdexPath());
auto file3 = ScopedCreateEmptyFile(artifacts.OatPath());
- EXPECT_CALL(*mock_exec_utils_,
- DoExecAndReturnCode(AllOf(
- Contains(FlagContains("--boot-image=", framework_dir_ + "/boot-framework.art")),
- Contains(FlagContains("-Xbootclasspathimagefds:", FdOf(artifacts.ImagePath()))),
- Contains(FlagContains("-Xbootclasspathvdexfds:", FdOf(artifacts.VdexPath()))),
- Contains(FlagContains("-Xbootclasspathoatfds:", FdOf(artifacts.OatPath()))))))
+ EXPECT_CALL(
+ *mock_odr_dexopt,
+ DoDexoptSystemServer(AllOf(
+ Field(&DexoptSystemServerArgs::isBootImageOnSystem, Eq(true)),
+ Field(&DexoptSystemServerArgs::bootClasspathImageFds,
+ Contains(FdOf(artifacts.ImagePath()))),
+ Field(&DexoptSystemServerArgs::bootClasspathVdexFds,
+ Contains(FdOf(artifacts.VdexPath()))),
+ Field(&DexoptSystemServerArgs::bootClasspathOatFds,
+ Contains(FdOf(artifacts.OatPath()))))))
.Times(2)
.WillRepeatedly(Return(0));
- EXPECT_EQ(odrefresh_->Compile(
+ EXPECT_EQ(odrefresh->Compile(
*metrics_, /*compile_boot_extensions=*/{}, /*compile_system_server=*/true),
ExitCode::kCompilationSuccess);
}
diff --git a/tools/buildbot-build.sh b/tools/buildbot-build.sh
index d5fee20..3e0bfa6 100755
--- a/tools/buildbot-build.sh
+++ b/tools/buildbot-build.sh
@@ -118,6 +118,8 @@
# chroot environment. The *.libraries.txt targets are required by
# the source linkerconfig but not included in the prebuilt one.
make_command+=" linkerconfig conv_linker_config sanitizer.libraries.txt vndkcorevariant.libraries.txt"
+ # Additional dependency in /system
+ make_command+=" libbinder_ndk"
# Additional targets needed for the chroot environment.
make_command+=" event-log-tags"
# Needed to extract prebuilt APEXes.