const OatFile* OatFileManager::RegisterOatFile(std::unique_ptr<const OatFile> oat_file, bool in_memory) { // Use class_linker vlog to match the log for dex file registration.
VLOG(class_linker) << "Registered oat file " << oat_file->GetLocation();
PaletteNotifyOatFileLoaded(oat_file->GetLocation().c_str());
WriterMutexLock mu(Thread::Current(), *Locks::oat_file_manager_lock_);
CHECK(in_memory ||
!only_use_system_oat_files_ ||
LocationIsTrusted(oat_file->GetLocation(), !Runtime::Current()->DenyArtApexDataFiles()) ||
!oat_file->IsExecutable())
<< "Registering a non /system oat file: " << oat_file->GetLocation() << " android-root="
<< GetAndroidRoot();
DCHECK(oat_file != nullptr); if (kIsDebugBuild) {
CHECK(oat_files_.find(oat_file) == oat_files_.end()); for (const std::unique_ptr<const OatFile>& existing : oat_files_) {
CHECK_NE(oat_file.get(), existing.get()) << oat_file->GetLocation(); // Check that we don't have an oat file with the same address. Copies of the same oat file // should be loaded at different addresses.
CHECK_NE(oat_file->Begin(), existing->Begin()) << "Oat file already mapped at that location";
}
} const OatFile* ret = oat_file.get();
oat_files_.insert(std::move(oat_file)); return ret;
}
OatFileManager::~OatFileManager() { // Explicitly clear oat_files_ since the OatFile destructor calls back into OatFileManager for // UnRegisterOatFileLocation.
oat_files_.clear();
}
std::vector<const OatFile*> OatFileManager::RegisterImageOatFiles( const std::vector<gc::space::ImageSpace*>& spaces) {
std::vector<const OatFile*> oat_files;
oat_files.reserve(spaces.size()); for (gc::space::ImageSpace* space : spaces) { // The oat file was generated in memory if the image space has a profile. bool in_memory = !space->GetProfileFiles().empty();
oat_files.push_back(RegisterOatFile(space->ReleaseOatFile(), in_memory));
} return oat_files;
}
#ifdefined(__ANDROID__) constchar* process_name = getprogname(); // Some processes would rather take the runtime impact in the interest of memory (b/292210260) if (process_name != nullptr && strstr(process_name, kDisableAppImageKeyword) != nullptr) {
LOG(INFO) << "Skipping app image load for " << process_name; returnfalse;
} #endif
returntrue;
}
// Wrapper for optional heuristic on picking out startup-optimized dex files for madvise. // * If `madvise_dex_using_profile` is disabled, falls back to sequential madvise. //. * If profile metadata hints are missing, falls back to sequential madvise. // * If the target dex has been optimized with accurate startup profiles, the heuristic should // *generally* pick out only those startup dex files to madvise. // * If the startup dex has *not* been optimized with startup profiles, the heuristic will pick // out dex files based on effective startup class/method representation. In practice, this will // often look like the naive greedy algorithm, with possibly a slightly different ordering. static std::vector<size_t> SelectDexFilesToMadvise( const std::vector<std::unique_ptr<const DexFile>>& dex_files) { auto default_greedy_selection = [&] {
std::vector<size_t> indices(dex_files.size());
std::iota(indices.begin(), indices.end(), 0); return indices;
}; if (!com::android::art::rw::flags::madvise_dex_using_profile() || dex_files.empty()) { return default_greedy_selection();
}
if (profile == nullptr) { // We require valid dex profile metadata for all dex when aggregating profile data. // This should only happen when there's no backing OAT file.
VLOG(oat) << "Madvise - Missing OAT profile metadata for dex: " << dex_file->GetLocation(); return default_greedy_selection();
}
// If no startup metadata was found (e.g., speed compilation), fall back to greedy madvise. // In theory, a valid profile could completely lack any startup hints, but it's an edge case // where it's fine to just fall back to the default greedy behavior. if (!has_startup_metadata) { return default_greedy_selection();
}
// Verify we aren't holding the mutator lock, which could starve GC when // hitting the disk.
Thread* const self = Thread::Current();
Locks::mutator_lock_->AssertNotHeld(self);
Runtime* const runtime = Runtime::Current();
// If the class_loader is null there's not much we can do. This happens if a dex files is loaded // directly with DexFile APIs instead of using class loaders. if (class_loader == nullptr) {
LOG(WARNING) << "Opening an oat file without a class loader. "
<< "Are you using the deprecated DexFile APIs?";
} elseif (context != nullptr) { auto oat_file_assistant = std::make_unique<OatFileAssistant>(dex_location,
kRuntimeQuickCodeISA,
context.get(),
runtime->GetOatFilesExecutable(),
only_use_system_oat_files_);
// Get the current optimization status for trace debugging. // Implementation detail note: GetOptimizationStatus will select the same // oat file as GetBestOatFile used below, and in doing so it already pre-populates // some OatFileAssistant internal fields.
std::string odex_location;
std::string compilation_reason;
std::string odex_status;
OatFileAssistant::Location ignored_location; bool ignored_is_backed_by_vdex_only;
oat_file_assistant->GetOptimizationStatus(&odex_location,
&compilation_filter,
&compilation_reason,
&odex_status,
&ignored_location,
&ignored_is_backed_by_vdex_only);
if (oat_file != nullptr) { bool compilation_enabled =
CompilerFilter::IsAotCompilationEnabled(oat_file->GetCompilerFilter()); // Load the dex files from the oat file. bool added_image_space = false; constbool should_madvise = runtime->ShouldMadviseForAppStartup(dex_location); if (should_madvise) {
VLOG(oat) << "Madvising oat file: " << oat_file->GetLocation();
size_t madvise_size_limit = runtime->GetMadviseWillNeedSizeOdex();
Runtime::MadviseFileForRange(madvise_size_limit,
oat_file->Size(),
oat_file->Begin(),
oat_file->End(),
oat_file->GetLocation());
}
ScopedTrace app_image_timing("AppImage:Loading");
// We need to throw away the image space if we are debuggable but the oat-file source of the // image is not otherwise we might get classes with inlined methods or other such things.
std::unique_ptr<gc::space::ImageSpace> image_space; if (ShouldLoadAppImage()) { if (oat_file->IsExecutable()) { // App images generated by the compiler can only be used if the oat file // is executable.
std::string art_file = ReplaceFileExtension(oat_file->GetLocation(), kArtExtension);
DCHECK(!art_file.empty());
std::string error_msg;
image_space = gc::space::ImageSpace::CreateFromAppImage(
art_file.c_str(), oat_file.get(), &error_msg); if (image_space == nullptr && (VLOG_IS_ON(image) || OS::FileExists(art_file.c_str()))) {
LOG(INFO) << "Failed to open app image " << art_file.c_str() << " " << error_msg;
}
} // Load the runtime image if the oat file does not require its own app image. // This logic must be aligned with the one that determines when to // keep runtime images in `ArtManagerLocal.cleanup` in // `art/libartservice/service/java/com/android/server/art/ArtManagerLocal.java`. if (kEnableRuntimeAppImage &&
image_space == nullptr &&
!compilation_enabled &&
!oat_file->RequiresImage()) {
std::string art_file = RuntimeImage::GetRuntimeImagePath(dex_location);
std::string error_msg;
image_space = gc::space::ImageSpace::CreateFromAppImage(
art_file.c_str(), oat_file.get(), &error_msg); if (image_space == nullptr) {
(OS::FileExists(art_file.c_str()) ? LOG_STREAM(INFO) : VLOG_STREAM(image))
<< "Could not load runtime generated app image: " << error_msg;
}
}
} if (image_space != nullptr) {
ScopedTrace image_space_timing("Opening image dex files");
std::string temp_error_msg; if (!image_space->OpenAndSetDexFiles(&dex_files, &temp_error_msg)) {
LOG(INFO) << "Failed to open image dex files: " << temp_error_msg;
dex_files.clear();
image_space.reset();
}
} if (image_space != nullptr) {
ScopedObjectAccess soa(self);
StackHandleScope<1> hs(self);
Handle<mirror::ClassLoader> h_loader(
hs.NewHandle(soa.Decode<mirror::ClassLoader>(class_loader)));
DCHECK(h_loader != nullptr); // Non-null strong reference is decoded as non-null.
std::string temp_error_msg; // Add image space has a race condition since other threads could be reading from the // spaces array.
{
ScopedThreadSuspension sts(self, ThreadState::kSuspended);
gc::ScopedGCCriticalSection gcs(self,
gc::kGcCauseAddRemoveAppImageSpace,
gc::kCollectorTypeAddRemoveAppImageSpace);
ScopedSuspendAll ssa("Add image space");
runtime->GetHeap()->AddSpace(image_space.get());
}
{
ScopedTrace image_space_timing("Adding image space");
added_image_space = runtime->GetClassLinker()->AddImageSpace(
image_space.get(),
h_loader,
context.get(),
dex_files, /*out*/ &temp_error_msg);
} if (added_image_space) { // Successfully added image space to heap, release the map so that it does not get // freed.
image_space.release(); // NOLINT b/117926937
if (oat_file->RequiresImage()) {
LOG(WARNING) << "Loading "
<< oat_file->GetLocation()
<< " non-executable as it requires an image which we failed to load"; // file as non-executable. auto nonexecutable_oat_file_assistant =
std::make_unique<OatFileAssistant>(dex_location,
kRuntimeQuickCodeISA,
context.get(), /*load_executable=*/false,
only_use_system_oat_files_);
oat_file.reset(nonexecutable_oat_file_assistant->GetBestOatFile().release());
// The file could be deleted concurrently (for example background // dexopt, or secondary oat file being deleted by the app). if (oat_file == nullptr) {
LOG(WARNING) << "Failed to reload oat file non-executable " << dex_location;
}
}
if (oat_file != nullptr) {
dex_files = oat_file_assistant->LoadDexFiles(*oat_file.get(), dex_location);
// Register for tracking. for (constauto& dex_file : dex_files) {
dex::tracking::RegisterDexFile(dex_file.get());
}
}
} if (dex_files.empty()) {
ScopedTrace failed_to_open_dex_files("FailedToOpenDexFilesFromOat");
error_msgs->push_back("Failed to open dex files from " + odex_location);
} elseif (should_madvise) {
size_t madvise_size_limit = Runtime::Current()->GetMadviseWillNeedTotalDexSize(); for (const size_t dex_file_index : SelectDexFilesToMadvise(dex_files)) {
DCHECK_LT(dex_file_index, dex_files.size()); constauto& dex_file = dex_files[dex_file_index]; // Prefetch the dex file based on vdex size limit (name should // have been dex size limit).
VLOG(oat) << "Madvising dex file: " << dex_file->GetLocation();
madvise_size_limit -= Runtime::MadviseFileForRange(madvise_size_limit,
dex_file->Size(),
dex_file->Begin(),
dex_file->Begin() + dex_file->Size(),
dex_file->GetLocation()); if (madvise_size_limit == 0) { break;
}
} if (com::android::art::rw::flags::madvise_type_lookup_table() && madvise_size_limit > 0) { // If we have remaining madvise quota, use it to page in the type lookup table if present; // this is typically on the critical path for startup. const VdexFile* vdex_file = oat_file != nullptr ? oat_file->GetVdexFile() : nullptr; if (vdex_file != nullptr && vdex_file->HasTypeLookupTableSection()) { const VdexFile::VdexSectionHeader& section_header =
vdex_file->GetSectionHeader(VdexSection::kTypeLookupTableSection);
VLOG(oat) << "Madvising type lookup table: " << vdex_file->GetName();
madvise_size_limit -=
Runtime::MadviseFileForRange(madvise_size_limit,
section_header.section_size,
vdex_file->Begin() + section_header.section_offset,
vdex_file->End(),
vdex_file->GetName());
}
}
}
if (oat_file != nullptr) {
VLOG(class_linker) << "Registering " << oat_file->GetLocation();
*out_oat_file = RegisterOatFile(std::move(oat_file));
}
} else { // oat_file == nullptr // Verify if any of the dex files being loaded is already in the class path. // If so, report an error with the current stack trace. // Most likely the developer didn't intend to do this because it will waste // performance and memory. if (oat_file_assistant->GetBestStatus() == OatFileAssistant::kOatContextOutOfDate) {
std::set<const DexFile*> already_exists_in_classpath =
context->CheckForDuplicateDexFiles(MakeNonOwningPointerVector(dex_files)); if (!already_exists_in_classpath.empty()) {
ScopedTrace duplicate_dex_files("DuplicateDexFilesInContext"); auto duplicate_it = already_exists_in_classpath.begin();
std::string duplicates = (*duplicate_it)->GetLocation(); for (duplicate_it++ ; duplicate_it != already_exists_in_classpath.end(); duplicate_it++) {
duplicates += "," + (*duplicate_it)->GetLocation();
}
std::ostringstream out;
out << "Trying to load dex files which is already loaded in the same ClassLoader "
<< "hierarchy.\n"
<< "This is a strong indication of bad ClassLoader construct which leads to poor "
<< "performance and wastes memory.\n"
<< "The list of duplicate dex files is: " << duplicates << "\n"
<< "The current class loader context is: "
<< context->EncodeContextForOatFile("") << "\n"
<< "Java stack trace:\n";
// We log this as an ERROR to stress the fact that this is most likely unintended. // Note that ART cannot do anything about it. It is up to the app to fix their logic. // Here we are trying to give a heads up on why the app might have performance issues.
LOG(ERROR) << out.str();
}
}
}
// If we arrive here with an empty dex files list, it means we fail to load // it/them through an .oat file. if (dex_files.empty()) {
std::string error_msg; static constexpr bool kVerifyChecksum = true;
ArtDexFileLoader dex_file_loader(dex_location); if (!dex_file_loader.Open(Runtime::Current()->IsVerificationEnabled(),
kVerifyChecksum, /*out*/ &error_msg,
&dex_files)) {
ScopedTrace fail_to_open_dex_from_apk("FailedToOpenDexFilesFromApk");
LOG(WARNING) << error_msg;
error_msgs->push_back("Failed to open dex files from " + std::string(dex_location)
+ " because: " + error_msg);
}
code_type = runtime->GetAppInfo()->GetRegisteredCodeType(dex_location);
}
if (error_msgs->empty()) { // Remove write permission from DexFile pages. We do this at the end because // OatFile assigns OatDexFile pointer in the DexFile objects. for (std::unique_ptr<const DexFile>& dex_file : dex_files) { if (!dex_file->DisableWrite()) {
error_msgs->push_back("Failed to make dex file " + dex_file->GetLocation() + " read-only");
}
}
}
if (!error_msgs->empty()) { return std::vector<std::unique_ptr<const DexFile>>();
}
// Determine dex/vdex locations and the combined location checksum.
std::string dex_location;
std::string vdex_path; bool has_vdex = OatFileAssistant::AnonymousDexVdexLocation(dex_headers,
kRuntimeQuickCodeISA,
&dex_location,
&vdex_path);
// Attempt to open an existing vdex and check dex file checksums match.
std::unique_ptr<VdexFile> vdex_file = nullptr; if (has_vdex && OS::FileExists(vdex_path.c_str())) {
vdex_file = VdexFile::Open(vdex_path, /*low_4gb=*/false,
&error_msg); if (vdex_file == nullptr) {
LOG(WARNING) << "Failed to open vdex " << vdex_path << ": " << error_msg;
} elseif (!vdex_file->MatchesDexFileChecksums(dex_headers)) {
LOG(WARNING) << "Failed to open vdex " << vdex_path << ": dex file checksum mismatch";
vdex_file.reset(nullptr);
}
}
// Load dex files. Skip structural dex file verification if vdex was found // and dex checksums matched.
std::vector<std::unique_ptr<const DexFile>> dex_files; for (size_t i = 0; i < dex_mem_maps.size(); ++i) { static constexpr bool kVerifyChecksum = true;
ArtDexFileLoader dex_file_loader(std::move(dex_mem_maps[i]),
DexFileLoader::GetMultiDexLocation(dex_location.c_str(), i));
std::unique_ptr<const DexFile> dex_file(dex_file_loader.Open(
dex_headers[i]->checksum_, /* verify= */ (vdex_file == nullptr) && Runtime::Current()->IsVerificationEnabled(),
kVerifyChecksum,
&error_msg)); if (dex_file != nullptr) {
dex::tracking::RegisterDexFile(dex_file.get()); // Register for tracking.
dex_files.push_back(std::move(dex_file));
} else {
error_msgs->push_back("Failed to open dex files from memory: " + error_msg);
}
}
// Check if we should proceed to creating an OatFile instance backed by the vdex. // We need: (a) an existing vdex, (b) class loader (can be null if invoked via reflection), // and (c) no errors during dex file loading. if (vdex_file == nullptr || class_loader == nullptr || !error_msgs->empty()) { return dex_files;
}
// Attempt to create a class loader context, check OpenDexFiles succeeds (prerequisite // for using the context later).
std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::CreateContextForClassLoader(
class_loader,
dex_elements); if (context == nullptr) {
LOG(ERROR) << "Could not create class loader context for " << vdex_path; return dex_files;
}
DCHECK(context->OpenDexFiles())
<< "Context created from already opened dex files should not attempt to open again";
// Initialize an OatFile instance backed by the loaded vdex.
std::unique_ptr<OatFile> oat_file(OatFile::OpenFromVdex(MakeNonOwningPointerVector(dex_files),
std::move(vdex_file),
dex_location,
context.get())); if (oat_file != nullptr) {
VLOG(class_linker) << "Registering " << oat_file->GetLocation();
*out_oat_file = RegisterOatFile(std::move(oat_file));
} return dex_files;
}
// Check how many vdex files exist in the same directory as the vdex file we are about // to write. If more than or equal to kAnonymousVdexCacheSize, unlink the least // recently used one(s) (according to stat-reported atime). staticbool UnlinkLeastRecentlyUsedVdexIfNeeded(const std::string& vdex_path_to_add,
std::string* error_msg) {
std::string basename = android::base::Basename(vdex_path_to_add); if (!OatFileAssistant::IsAnonymousVdexBasename(basename)) { // File is not for in memory dex files. returntrue;
}
if (OS::FileExists(vdex_path_to_add.c_str())) { // File already exists and will be overwritten. // This will not change the number of entries in the cache. returntrue;
}
if (cache.size() < OatFileManager::kAnonymousVdexCacheSize) { returntrue;
}
std::sort(cache.begin(),
cache.end(),
[](constauto& a, constauto& b) { return a.first < b.first; }); for (size_t i = OatFileManager::kAnonymousVdexCacheSize - 1; i < cache.size(); ++i) { if (unlink(cache[i].second.c_str()) != 0) {
*error_msg = "Could not unlink anonymous vdex file " + cache[i].second; returnfalse;
}
}
returntrue;
}
class BackgroundVerificationTask final : public Task { public:
BackgroundVerificationTask(const std::vector<const DexFile*>& dex_files,
jobject class_loader, const std::string& vdex_path)
: dex_files_(dex_files),
vdex_path_(vdex_path) {
Thread* const self = Thread::Current();
ScopedObjectAccess soa(self); // Create a global ref for `class_loader` because it will be accessed from a different thread.
class_loader_ = soa.Vm()->AddGlobalRef(self, soa.Decode<mirror::ClassLoader>(class_loader));
CHECK(class_loader_ != nullptr);
}
// Iterate over all classes and verify them. for (const DexFile* dex_file : dex_files_) { for (uint32_t cdef_idx = 0; cdef_idx < dex_file->NumClassDefs(); cdef_idx++) { const dex::ClassDef& class_def = dex_file->GetClassDef(cdef_idx);
// Take handles inside the loop. The background verification is low priority // and we want to minimize the risk of blocking anyone else.
ScopedObjectAccess soa(self);
StackHandleScope<2> hs(self);
Handle<mirror::ClassLoader> h_loader(hs.NewHandle(
soa.Decode<mirror::ClassLoader>(class_loader_)));
Handle<mirror::Class> h_class =
hs.NewHandle(class_linker->FindClass(self, *dex_file, class_def.class_idx_, h_loader));
if (h_class == nullptr) {
DCHECK(self->IsExceptionPending());
self->ClearException(); continue;
}
if (&h_class->GetDexFile() != dex_file) { // There is a different class in the class path or a parent class loader // with the same descriptor. This `h_class` is not resolvable, skip it. continue;
}
DCHECK(h_class->IsResolved()) << h_class->PrettyDescriptor();
class_linker->VerifyClass(self, &verifier_deps, h_class); if (self->IsExceptionPending()) { // ClassLinker::VerifyClass can throw, but the exception isn't useful here.
self->ClearException();
}
if (h_class->IsVerified()) {
verifier_deps.RecordClassVerified(*dex_file, class_def);
}
}
}
// Delete old vdex files if there are too many in the folder. if (!UnlinkLeastRecentlyUsedVdexIfNeeded(vdex_path_, &error_msg)) {
LOG(ERROR) << "Could not unlink old vdex files " << vdex_path_ << ": " << error_msg; return;
}
// Construct a vdex file and write `verifier_deps` into it. if (!VdexFile::WriteToDisk(vdex_path_,
dex_files_,
verifier_deps,
&error_msg)) {
LOG(ERROR) << "Could not write anonymous vdex " << vdex_path_ << ": " << error_msg; return;
}
}
if (runtime->IsJavaDebuggable()) { // Threads created by ThreadPool ("runtime threads") are not allowed to load // classes when debuggable to match class-initialization semantics // expectations. Do not verify in the background. return;
}
{ // Temporarily create a class loader context to see if we recognize the // chain.
std::unique_ptr<ClassLoaderContext> context(
ClassLoaderContext::CreateContextForClassLoader(class_loader, nullptr)); if (context == nullptr) { // We only run background verification for class loaders we know the lookup // chain. Because the background verification runs on runtime threads, // which do not call Java, we won't be able to load classes when // verifying, which is something the current verifier relies on. return;
}
}
if (!IsSdkVersionSetAndAtLeast(runtime->GetTargetSdkVersion(), SdkVersion::kQ)) { // Do not run for legacy apps as they may depend on the previous class loader behaviour. return;
}
if (runtime->IsShuttingDown(self)) { // Not allowed to create new threads during runtime shutdown. return;
}
if (dex_files.size() < 1) { // Nothing to verify. return;
}
std::string dex_location = dex_files[0]->GetLocation(); const std::string& data_dir = Runtime::Current()->GetProcessDataDirectory(); if (!dex_location.starts_with(data_dir)) { // For now, we only run background verification for secondary dex files. // Running it for primary or split APKs could have some undesirable // side-effects, like overloading the device on app startup. return;
}
std::string error_msg;
std::string odex_filename; if (!OatFileAssistant::DexLocationToOdexFilename(dex_location,
kRuntimeQuickCodeISA,
&odex_filename,
&error_msg)) {
LOG(WARNING) << "Could not get odex filename for " << dex_location << ": " << error_msg; return;
}
if (LocationIsOnArtApexData(odex_filename) && Runtime::Current()->DenyArtApexDataFiles()) { // Ignore vdex file associated with this odex file as the odex file is not trustworthy. return;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.