243 lines
		
	
	
		
			8.9 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			243 lines
		
	
	
		
			8.9 KiB
		
	
	
	
		
			C++
		
	
	
	
| /*
 | |
|  * 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 <stdlib.h>
 | |
| #include <sys/mman.h>
 | |
| #include <sys/types.h>
 | |
| 
 | |
| #include <algorithm>
 | |
| #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"
 | |
| #include "oat_file_assistant.h"
 | |
| #include "procinfo/process_map.h"
 | |
| 
 | |
| namespace art {
 | |
| 
 | |
| 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) {
 | |
|     return {};
 | |
|   }
 | |
|   return android::base::Split(env_value, ":");
 | |
| }
 | |
| 
 | |
| 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");
 | |
|     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<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");
 | |
|   }
 | |
|   std::vector<std::string> 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<std::string> 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<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());
 | |
|   }
 | |
|   // 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()));
 | |
|   }
 | |
| }
 | |
| 
 | |
| TEST(DexpreoptTest, ForSystemServer) {
 | |
|   android::base::Result<std::vector<std::string>> expected_artifacts =
 | |
|       GetSystemServerExpectedArtifacts();
 | |
|   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_odexes =
 | |
|       GetSystemServerArtifactsMappedOdexes();
 | |
|   ASSERT_RESULT_OK(mapped_odexes);
 | |
| 
 | |
|   EXPECT_THAT(expected_artifacts.value(), IsSubsetOf(mapped_odexes.value()));
 | |
| }
 | |
| 
 | |
| }  // namespace art
 |