Update dexpreopt_test to verify boot images.
This ensures that odrefresh does not unexpectedly override the boot
image on /system on the first boot.
Bug: 203492478
Test: Build a system image, flash it to a device, and run
`atest art_standalone_dexpreopt_test`.
Change-Id: Id02e60612e6b3c12876cc8707e6d2be3866a5b6f
diff --git a/libartbase/Android.bp b/libartbase/Android.bp
index 06c8cfc..1f034e6 100644
--- a/libartbase/Android.bp
+++ b/libartbase/Android.bp
@@ -236,13 +236,11 @@
static: {
whole_static_libs: [
"libc++fs",
- "libprocinfo",
],
},
shared: {
static_libs: [
"libc++fs",
- "libprocinfo",
],
},
}
diff --git a/libartbase/base/common_art_test.cc b/libartbase/base/common_art_test.cc
index 20bc87c..593ea25 100644
--- a/libartbase/base/common_art_test.cc
+++ b/libartbase/base/common_art_test.cc
@@ -50,7 +50,6 @@
#include "dex/primitive.h"
#include "gtest/gtest.h"
#include "nativehelper/scoped_local_ref.h"
-#include "procinfo/process.h"
namespace art {
@@ -680,12 +679,16 @@
std::vector<pid_t> GetPidByName(const std::string& process_name) {
std::vector<pid_t> results;
for (pid_t pid : android::base::AllPids{}) {
- android::procinfo::ProcessInfo process_info;
- std::string error;
- if (!android::procinfo::GetProcessInfo(pid, &process_info, &error)) {
+ std::string cmdline;
+ if (!android::base::ReadFileToString(StringPrintf("/proc/%d/cmdline", pid), &cmdline)) {
continue;
}
- if (process_info.name == process_name) {
+ // Take the first argument.
+ size_t pos = cmdline.find('\0');
+ if (pos != std::string::npos) {
+ cmdline.resize(pos);
+ }
+ if (cmdline == process_name) {
results.push_back(pid);
}
}
diff --git a/libartbase/base/common_art_test.h b/libartbase/base/common_art_test.h
index a33c632..6124ed9 100644
--- a/libartbase/base/common_art_test.h
+++ b/libartbase/base/common_art_test.h
@@ -287,8 +287,8 @@
template <typename Param>
using CommonArtTestWithParam = CommonArtTestBase<testing::TestWithParam<Param>>;
-// Returns a list of PIDs of the processes whose process name (the name of the binary without path)
-// fully matches the given name.
+// Returns a list of PIDs of the processes whose process name (the first commandline argument) fully
+// matches the given name.
std::vector<pid_t> GetPidByName(const std::string& process_name);
#define TEST_DISABLED_FOR_TARGET() \
diff --git a/test/dexpreopt/dexpreopt_test.cc b/test/dexpreopt/dexpreopt_test.cc
index 2dd5662..5315937 100644
--- a/test/dexpreopt/dexpreopt_test.cc
+++ b/test/dexpreopt/dexpreopt_test.cc
@@ -28,12 +28,16 @@
#include <iterator>
#include <string>
#include <unordered_set>
+#include <utility>
#include <vector>
+#include "android-base/properties.h"
#include "android-base/result.h"
+#include "android-base/stringprintf.h"
#include "android-base/strings.h"
#include "arch/instruction_set.h"
#include "base/common_art_test.h"
+#include "base/file_utils.h"
#include "base/os.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
@@ -44,6 +48,9 @@
using ::testing::IsSubsetOf;
+constexpr const char* kZygote32 = "zygote";
+constexpr const char* kZygote64 = "zygote64";
+
std::vector<std::string> GetListFromEnv(const std::string& name) {
const char* env_value = getenv(name.c_str());
if (env_value == nullptr || strlen(env_value) == 0) {
@@ -52,7 +59,74 @@
return android::base::Split(env_value, ":");
}
-android::base::Result<std::vector<std::string>> GetSystemServerArtifacts() {
+android::base::Result<std::vector<std::pair<std::string, InstructionSet>>> GetZygoteNamesAndIsas() {
+ std::vector<std::pair<std::string, InstructionSet>> names_and_isas;
+
+ // Possible values are: "zygote32", "zygote64", "zygote32_64", "zygote64_32".
+ std::string zygote_kinds = android::base::GetProperty("ro.zygote", {});
+ if (zygote_kinds.empty()) {
+ return Errorf("Unable to get Zygote kinds");
+ }
+
+ switch (kRuntimeISA) {
+ case InstructionSet::kArm:
+ case InstructionSet::kArm64:
+ if (zygote_kinds.find("32") != std::string::npos) {
+ names_and_isas.push_back(std::make_pair(kZygote32, InstructionSet::kArm));
+ }
+ if (zygote_kinds.find("64") != std::string::npos) {
+ names_and_isas.push_back(std::make_pair(kZygote64, InstructionSet::kArm64));
+ }
+ break;
+ case InstructionSet::kX86:
+ case InstructionSet::kX86_64:
+ if (zygote_kinds.find("32") != std::string::npos) {
+ names_and_isas.push_back(std::make_pair(kZygote32, InstructionSet::kX86));
+ }
+ if (zygote_kinds.find("64") != std::string::npos) {
+ names_and_isas.push_back(std::make_pair(kZygote64, InstructionSet::kX86_64));
+ }
+ break;
+ default:
+ return Errorf("Unknown runtime ISA: {}", GetInstructionSetString(kRuntimeISA));
+ }
+
+ return names_and_isas;
+}
+
+android::base::Result<std::vector<std::string>> GetZygoteExpectedArtifacts(InstructionSet isa) {
+ std::vector<std::string> jars = GetListFromEnv("DEX2OATBOOTCLASSPATH");
+ if (jars.empty()) {
+ return Errorf("Environment variable `DEX2OATBOOTCLASSPATH` is not defined or empty");
+ }
+ std::string art_root = GetArtRoot();
+ std::string android_root = GetAndroidRoot();
+ std::vector<std::string> artifacts;
+ for (size_t i = 0; i < jars.size(); i++) {
+ const std::string& jar = jars[i];
+ std::string basename =
+ i == 0 ? "boot.oat" : "boot-" + ReplaceFileExtension(android::base::Basename(jar), "oat");
+ // TODO(b/211973309): Update this once the primary boot image is moved.
+ std::string dir = android::base::StartsWith(jar, art_root) ? art_root + "/javalib" :
+ android_root + "/framework";
+ std::string oat_file = android::base::StringPrintf(
+ "%s/%s/%s", dir.c_str(), GetInstructionSetString(isa), basename.c_str());
+
+ if (!OS::FileExists(oat_file.c_str())) {
+ if (errno == EACCES) {
+ return ErrnoErrorf("Failed to stat() {}", oat_file);
+ }
+ // Dexpreopting is probably disabled. No need to report missing artifacts here because
+ // artifact generation is already checked at build time.
+ continue;
+ }
+
+ artifacts.push_back(oat_file);
+ }
+ return artifacts;
+}
+
+android::base::Result<std::vector<std::string>> GetSystemServerExpectedArtifacts() {
std::vector<std::string> jars = GetListFromEnv("SYSTEMSERVERCLASSPATH");
if (jars.empty()) {
return Errorf("Environment variable `SYSTEMSERVERCLASSPATH` is not defined or empty");
@@ -85,31 +159,76 @@
return artifacts;
}
+android::base::Result<std::vector<std::string>> GetMappedFiles(pid_t pid,
+ const std::string& extension,
+ uint16_t flags) {
+ std::vector<android::procinfo::MapInfo> maps;
+ if (!android::procinfo::ReadProcessMaps(pid, &maps)) {
+ return ErrnoErrorf("Failed to get mapped memory regions of pid {}", pid);
+ }
+ std::vector<std::string> files;
+ for (const android::procinfo::MapInfo& map : maps) {
+ if ((map.flags & flags) && android::base::EndsWith(map.name, extension)) {
+ files.push_back(map.name);
+ }
+ }
+ return files;
+}
+
+android::base::Result<std::vector<std::string>> GetZygoteMappedOatFiles(
+ const std::string& zygote_name) {
+ std::vector<pid_t> pids = art::GetPidByName(zygote_name);
+ if (pids.empty()) {
+ return Errorf("Unable to find Zygote process: {}", zygote_name);
+ }
+ // OAT files in boot images may not be mmaped with PROT_EXEC if they don't contain executable
+ // code. Checking PROT_READ is sufficient because an OAT file will be unmapped if the runtime
+ // rejects it.
+ return GetMappedFiles(pids[0], ".oat", PROT_READ);
+}
+
android::base::Result<std::vector<std::string>> GetSystemServerArtifactsMappedOdexes() {
std::vector<pid_t> pids = art::GetPidByName("system_server");
if (pids.size() != 1) {
return Errorf("There should be exactly one `system_server` process, found {}", pids.size());
}
- pid_t pid = pids[0];
- std::vector<android::procinfo::MapInfo> maps;
- if (!android::procinfo::ReadProcessMaps(pid, &maps)) {
- return ErrnoErrorf("Failed to get mapped memory regions of `system_server`");
- }
- std::vector<std::string> odexes;
- for (const android::procinfo::MapInfo& map : maps) {
- if ((map.flags & PROT_EXEC) && android::base::EndsWith(map.name, ".odex")) {
- odexes.push_back(map.name);
+ // Unlike boot images, app images don't get unmapped if the runtime rejects them in some cases
+ // (e.g., CLC mismatch). Therefore, we need to check the PROT_EXEC flag to ensure that they are
+ // valid.
+ // The ODEX files always contain executable code because system server jars are compiled with the
+ // "speed" filter.
+ return GetMappedFiles(pids[0], ".odex", PROT_EXEC);
+}
+
+TEST(DexpreoptTest, ForZygote) {
+ android::base::Result<std::vector<std::pair<std::string, InstructionSet>>> zygote_names_and_isas =
+ GetZygoteNamesAndIsas();
+ ASSERT_RESULT_OK(zygote_names_and_isas);
+
+ for (const auto& [zygote_name, isa] : *zygote_names_and_isas) {
+ android::base::Result<std::vector<std::string>> expected_artifacts =
+ GetZygoteExpectedArtifacts(isa);
+ ASSERT_RESULT_OK(expected_artifacts);
+
+ if (expected_artifacts->empty()) {
+ // Skip the test if dexpreopting is disabled.
+ return;
}
+
+ android::base::Result<std::vector<std::string>> mapped_oat_files =
+ GetZygoteMappedOatFiles(zygote_name);
+ ASSERT_RESULT_OK(mapped_oat_files);
+
+ EXPECT_THAT(expected_artifacts.value(), IsSubsetOf(mapped_oat_files.value()));
}
- return odexes;
}
TEST(DexpreoptTest, ForSystemServer) {
- android::base::Result<std::vector<std::string>> system_server_artifacts =
- GetSystemServerArtifacts();
- ASSERT_RESULT_OK(system_server_artifacts);
+ android::base::Result<std::vector<std::string>> expected_artifacts =
+ GetSystemServerExpectedArtifacts();
+ ASSERT_RESULT_OK(expected_artifacts);
- if (system_server_artifacts->empty()) {
+ if (expected_artifacts->empty()) {
// Skip the test if dexpreopting is disabled.
return;
}
@@ -118,7 +237,7 @@
GetSystemServerArtifactsMappedOdexes();
ASSERT_RESULT_OK(mapped_odexes);
- EXPECT_THAT(system_server_artifacts.value(), IsSubsetOf(mapped_odexes.value()));
+ EXPECT_THAT(expected_artifacts.value(), IsSubsetOf(mapped_odexes.value()));
}
} // namespace art