/* * 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. */ // A test to verify that the compilation artifacts built in the system image for all system server // jars are used. It will fail if odrefresh has run (in which case, artifacts in /data will be used // instead) or the artifacts in the system image are rejected by the runtime. This test should only // run on a clean system without any APEX (including com.android.art.testing) installed on data, // which otherwise will trigger odrefresh. #include #include #include #include #include #include #include #include #include #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" #include "oat_file_assistant.h" #include "procinfo/process_map.h" namespace art { using ::android::base::Error; using ::testing::IsSupersetOf; constexpr const char* kZygote32 = "zygote"; constexpr const char* kZygote64 = "zygote64"; std::vector GetListFromEnv(const std::string& name) { const char* env_value = getenv(name.c_str()); if (env_value == nullptr || strlen(env_value) == 0) { return {}; } return android::base::Split(env_value, ":"); } android::base::Result>> GetZygoteNamesAndIsas() { std::vector> 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> GetZygoteExpectedArtifacts(InstructionSet isa) { std::vector jars = GetListFromEnv("DEX2OATBOOTCLASSPATH"); if (jars.empty()) { return Errorf("Environment variable `DEX2OATBOOTCLASSPATH` is not defined or empty"); } std::string error_msg; std::string first_mainline_jar = GetFirstMainlineFrameworkLibraryFilename(&error_msg); if (first_mainline_jar.empty()) { return Error() << error_msg; } jars.push_back(std::move(first_mainline_jar)); std::string art_root = GetArtRoot(); std::string android_root = GetAndroidRoot(); std::vector 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"); std::string dir = android::base::StartsWith(jar, art_root) ? GetPrebuiltPrimaryBootImageDir() : 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> GetSystemServerExpectedArtifacts() { std::vector jars = GetListFromEnv("SYSTEMSERVERCLASSPATH"); if (jars.empty()) { return Errorf("Environment variable `SYSTEMSERVERCLASSPATH` is not defined or empty"); } std::vector standalone_jars = GetListFromEnv("STANDALONE_SYSTEMSERVER_JARS"); std::move(standalone_jars.begin(), standalone_jars.end(), std::back_inserter(jars)); if (kRuntimeISA == InstructionSet::kNone) { return Errorf("Unable to get system server ISA"); } std::vector artifacts; for (const std::string& jar : jars) { std::string error_msg; std::string odex_file; if (!OatFileAssistant::DexLocationToOdexFilename(jar, kRuntimeISA, &odex_file, &error_msg)) { return Errorf("Failed to get odex filename: {}", error_msg); } if (!OS::FileExists(odex_file.c_str())) { if (errno == EACCES) { return ErrnoErrorf("Failed to stat() {}", odex_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(odex_file); } return artifacts; } android::base::Result> GetMappedFiles(pid_t pid, const std::string& extension, uint16_t flags) { std::vector maps; if (!android::procinfo::ReadProcessMaps(pid, &maps)) { return ErrnoErrorf("Failed to get mapped memory regions of pid {}", pid); } std::vector 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> GetZygoteMappedOatFiles( const std::string& zygote_name) { std::vector 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> GetSystemServerArtifactsMappedOdexes() { std::vector pids = art::GetPidByName("system_server"); if (pids.size() != 1) { return Errorf("There should be exactly one `system_server` process, found {}", pids.size()); } // 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>> 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> expected_artifacts = GetZygoteExpectedArtifacts(isa); ASSERT_RESULT_OK(expected_artifacts); if (expected_artifacts->empty()) { // Skip the test if dexpreopting is disabled. return; } android::base::Result> mapped_oat_files = GetZygoteMappedOatFiles(zygote_name); ASSERT_RESULT_OK(mapped_oat_files); EXPECT_THAT(mapped_oat_files.value(), IsSupersetOf(expected_artifacts.value())); } } TEST(DexpreoptTest, ForSystemServer) { android::base::Result> expected_artifacts = GetSystemServerExpectedArtifacts(); ASSERT_RESULT_OK(expected_artifacts); if (expected_artifacts->empty()) { // Skip the test if dexpreopting is disabled. return; } android::base::Result> mapped_odexes = GetSystemServerArtifactsMappedOdexes(); ASSERT_RESULT_OK(mapped_odexes); EXPECT_THAT(mapped_odexes.value(), IsSupersetOf(expected_artifacts.value())); } } // namespace art