OatFileAssistant::OatFileAssistant(constchar* dex_location, const InstructionSet isa,
ClassLoaderContext* context, bool load_executable, bool only_load_trusted_executable,
OatFileAssistantContext* ofa_context, int vdex_fd, int oat_fd, int zip_fd)
: context_(context),
isa_(isa),
load_executable_(load_executable),
only_load_trusted_executable_(only_load_trusted_executable),
zip_fd_(zip_fd),
logger_(ArtLogger::Default()) {
CHECK(dex_location != nullptr) << "OatFileAssistant: null dex location";
CHECK_IMPLIES(load_executable, context != nullptr) << "Loading executable without a context";
if (zip_fd < 0) {
CHECK_LE(oat_fd, 0) << "zip_fd must be provided with valid oat_fd. zip_fd=" << zip_fd
<< " oat_fd=" << oat_fd;
CHECK_LE(vdex_fd, 0) << "zip_fd must be provided with valid vdex_fd. zip_fd=" << zip_fd
<< " vdex_fd=" << vdex_fd;
CHECK(!UseFdToReadFiles());
} else {
CHECK(UseFdToReadFiles());
}
dex_location_.assign(dex_location);
Runtime* runtime = Runtime::Current();
if (load_executable_ && runtime == nullptr) {
LOG(WARNING) << "OatFileAssistant: Load executable specified, "
<< "but no active runtime is found. Will not attempt to load executable.";
load_executable_ = false;
}
if (load_executable_ && isa != kRuntimeQuickCodeISA) {
LOG(WARNING) << "OatFileAssistant: Load executable specified, "
<< "but isa is not kRuntimeQuickCodeISA. Will not attempt to load executable.";
load_executable_ = false;
}
if (ofa_context == nullptr) {
CHECK(runtime != nullptr) << "runtime_options is not provided, and no active runtime is found.";
ofa_context_ = std::make_unique<OatFileAssistantContext>(runtime);
} else {
ofa_context_ = ofa_context;
}
if (runtime == nullptr) { // We need `MemMap` for mapping files. We don't have to initialize it when there is a runtime // because the runtime initializes it.
MemMap::Init();
}
// Get the odex filename.
std::string error_msg;
std::string odex_file_name; if (!DexLocationToOdexFilename(dex_location_, isa_, &odex_file_name, &error_msg)) {
LOG(WARNING) << "Failed to determine odex file name: " << error_msg;
}
// Get the oat filename.
std::string oat_file_name; if (!UseFdToReadFiles()) { if (!DexLocationToOatFilename(dex_location_,
isa_,
GetRuntimeOptions().deny_art_apex_data_files,
&oat_file_name,
&error_msg)) { if (kIsTargetAndroid) { // No need to warn on host. We are probably in oatdump, where we only need OatFileAssistant // to validate BCP checksums.
LOG(WARNING) << "Failed to determine oat file name for dex location " << dex_location_
<< ": " << error_msg;
}
}
}
if (!oat_file_name.empty() && !UseFdToReadFiles()) { // The oat location. This is for apps on readonly filesystems (typically, system apps and // incremental apps). This must be prioritized over the odex location, because the odex location // probably has the dexpreopt artifacts for such apps.
info_list_.push_back(std::make_unique<OatFileInfoBackedByOat>(this,
oat_file_name, /*is_oat_location=*/true, /*use_fd=*/false));
info_list_.push_back(
std::make_unique<OatFileInfoBackedBySdm>(this,
GetSdmFilename(dex_location_, isa), /*is_oat_location=*/true,
GetDmFilename(dex_location_),
GetSdcFilename(oat_file_name)));
}
if (!odex_file_name.empty()) { // The odex location, which is the most common.
info_list_.push_back(std::make_unique<OatFileInfoBackedByOat>(this,
odex_file_name, /*is_oat_location=*/false,
UseFdToReadFiles(),
zip_fd,
vdex_fd,
oat_fd));
info_list_.push_back(
std::make_unique<OatFileInfoBackedBySdm>(this,
GetSdmFilename(dex_location_, isa), /*is_oat_location=*/false,
GetDmFilename(dex_location_),
GetSdcFilename(odex_file_name)));
}
// When there is no odex/oat available (e.g., they are both out of date), we look for a useable // vdex file.
if (!oat_file_name.empty() && !UseFdToReadFiles()) { // The vdex-only file next to 'oat_`.
info_list_.push_back(std::make_unique<OatFileInfoBackedByVdex>(this,
GetVdexFilename(oat_file_name), /*is_oat_location=*/true, /*use_fd=*/false));
}
if (!odex_file_name.empty()) { // The vdex-only file next to `odex_`. // We dup FDs as the odex_ will claim ownership.
info_list_.push_back(std::make_unique<OatFileInfoBackedByVdex>(this,
GetVdexFilename(odex_file_name), /*is_oat_location=*/false,
UseFdToReadFiles(),
DupCloexec(zip_fd),
DupCloexec(vdex_fd)));
}
if (!UseFdToReadFiles()) { // A .dm file may be available, look for it.
info_list_.push_back(
std::make_unique<OatFileInfoBackedByDm>(this, GetDmFilename(dex_location_)));
}
}
// Must be defined outside of the class, to prevent inlining, which causes callers to access hidden // symbols used by the destructor. `NOINLINE` doesn't work.
OatFileAssistant::~OatFileAssistant() = default;
std::unique_ptr<ClassLoaderContext> tmp_context = nullptr; if (context_str.has_value()) {
tmp_context = ClassLoaderContext::Create(context_str.value()); if (tmp_context == nullptr) {
*error_msg = StringPrintf("Class loader context '%s' is invalid", context_str->c_str()); return nullptr;
}
if (!tmp_context->OpenDexFiles(android::base::Dirname(filename), /*context_fds=*/{}, /*only_read_checksums=*/true)) {
*error_msg =
StringPrintf("Failed to load class loader context files for '%s' with context '%s'",
filename.c_str(),
context_str->c_str()); return nullptr;
}
}
auto assistant = std::make_unique<OatFileAssistant>(filename.c_str(),
isa,
tmp_context.get(),
load_executable,
only_load_trusted_executable,
ofa_context);
bool OatFileAssistant::IsInBootClassPath() { // Note: We check the current boot class path, regardless of the ISA // specified by the user. This is okay, because the boot class path should // be the same for all ISAs. // TODO: Can we verify the boot class path is the same for all ISAs? for (const std::string& boot_class_path_location :
GetRuntimeOptions().boot_class_path_locations) { if (boot_class_path_location == dex_location_) {
VLOG(oat) << "Dex location " << dex_location_ << " is in boot class path"; returntrue;
}
} returnfalse;
}
OatFileAssistant::DexoptTrigger OatFileAssistant::GetDexoptTrigger(
CompilerFilter::Filter target_compiler_filter, bool profile_changed, bool downgrade) { if (downgrade) { // The caller's intention is to downgrade the compiler filter. We should only re-compile if the // target compiler filter is worse than the current one. return {.dexopt_comparators = {DexoptComparator::kComparingCompilerFilterReversed},
.custom_comparator_reason = std::nullopt};
}
// This is the usual case. The caller's intention is to see if a better oat file can be generated.
std::vector<DexoptComparator> dexopt_comparators{DexoptComparator::kComparingCompilerFilter}; if (profile_changed && CompilerFilter::DependsOnProfile(target_compiler_filter)) { // Since the profile has been changed, we should re-compile even if the compilation does not // make the compiler filter better.
dexopt_comparators.push_back(DexoptComparator::kCustomTargetIsBetterThanCurrent); return {.dexopt_comparators = std::move(dexopt_comparators),
.custom_comparator_reason = "profile changed"};
}
dexopt_comparators.push_back(DexoptComparator::kComparingPrimaryBootImageStatus);
dexopt_comparators.push_back(DexoptComparator::kComparingExtractionStatus); return {.dexopt_comparators = std::move(dexopt_comparators),
.custom_comparator_reason = std::nullopt};
}
int OatFileAssistant::GetDexOptNeeded(CompilerFilter::Filter target_compiler_filter, bool profile_changed, bool downgrade) {
OatFileInfo& info = GetBestInfo();
DexOptNeeded dexopt_needed = info.GetDexOptNeeded(
target_compiler_filter, GetDexoptTrigger(target_compiler_filter, profile_changed, downgrade)); if (dexopt_needed != kNoDexOptNeeded &&
(info.GetType() == OatFileType::kDm || info.GetType() == OatFileType::kSdm)) { // The usable vdex file is in the DM file. This information cannot be encoded in the integer. // Return kDex2OatFromScratch so that neither the vdex in the "oat" location nor the vdex in the // "odex" location will be picked by installd. return kDex2OatFromScratch;
} if (info.IsOatLocation() || dexopt_needed == kDex2OatFromScratch) { return dexopt_needed;
} return -dexopt_needed;
}
std::unique_ptr<const DexFile> dex_file = oat_dex_file->OpenDexFile(&error_msg); if (dex_file.get() == nullptr) {
LOG(WARNING) << "Failed to open dex file from oat dex file: " << error_msg; returnfalse;
}
out_dex_files->push_back(std::move(dex_file));
// Load the rest of the multidex entries for (size_t i = 1;; i++) {
std::string multidex_dex_location = DexFileLoader::GetMultiDexLocation(dex_location.c_str(), i);
oat_dex_file = oat_file.GetOatDexFile(multidex_dex_location.c_str()); if (oat_dex_file == nullptr) { // There are no more multidex entries to load. break;
}
dex_file = oat_dex_file->OpenDexFile(&error_msg); if (dex_file.get() == nullptr) {
LOG(WARNING) << "Failed to open dex file from oat dex file: " << error_msg; returnfalse;
}
out_dex_files->push_back(std::move(dex_file));
} returntrue;
}
OatFileAssistant::OatStatus OatFileAssistant::OdexFileStatus() { for (const std::unique_ptr<OatFileInfo>& info : info_list_) { if (info->GetType() == OatFileType::kOat && !info->IsOatLocation()) { return info->Status();
}
} return kOatCannotOpen;
}
OatFileAssistant::OatStatus OatFileAssistant::OatFileStatus() { for (const std::unique_ptr<OatFileInfo>& info : info_list_) { if (info->GetType() == OatFileType::kOat && info->IsOatLocation()) { return info->Status();
}
} return kOatCannotOpen;
}
bool OatFileAssistant::DexChecksumUpToDate(const OatFile& file, std::string* error_msg) { if (!file.ContainsDexCode()) { // We've already checked during oat file creation that the dex files loaded // from external files have the same checksums as the ones in the vdex file. returntrue;
}
ScopedTrace trace("DexChecksumUpToDate");
std::optional<std::uint32_t> dex_checksum; if (!GetRequiredDexChecksum(&dex_checksum, error_msg)) { returnfalse;
} if (!dex_checksum.has_value()) {
LOG(WARNING) << "Required dex checksums not found. Assuming dex checksums are up to date."; returntrue;
}
std::vector<const OatDexFile*> oat_dex_files;
uint32_t number_of_dex_files = file.GetOatHeader().GetDexFileCount(); for (uint32_t i = 0; i < number_of_dex_files; i++) {
std::string dex = DexFileLoader::GetMultiDexLocation(dex_location_.c_str(), i); const OatDexFile* oat_dex_file = file.GetOatDexFile(dex.c_str()); if (oat_dex_file == nullptr) {
*error_msg = StringPrintf("failed to find %s in %s", dex.c_str(), file.GetLocation().c_str()); returnfalse;
}
oat_dex_files.push_back(oat_dex_file);
}
uint32_t oat_checksum = DexFileLoader::GetMultiDexChecksum(oat_dex_files);
CHECK(dex_checksum.has_value()); if (dex_checksum != oat_checksum) {
VLOG(oat) << "Checksum does not match: " << std::hex << file.GetLocation() << " ("
<< oat_checksum << ") vs " << dex_location_ << " (" << *dex_checksum << ")"; returnfalse;
}
returntrue;
}
OatFileAssistant::OatStatus OatFileAssistant::GivenOatFileStatus(const OatFile& file, /*out*/ std::string* error_msg) { // Verify the ART_USE_READ_BARRIER state. // TODO: Don't fully reject files due to read barrier state. If they contain // compiled code and are otherwise okay, we should return something like // kOatRelocationOutOfDate. If they don't contain compiled code, the read // barrier state doesn't matter. if (file.GetOatHeader().IsConcurrentCopying() != gUseReadBarrier) {
*error_msg = "Read barrier state mismatch"; return kOatCannotOpen;
}
// Verify the dex checksum. if (!DexChecksumUpToDate(file, error_msg)) {
LOG(ERROR) << *error_msg; return kOatDexOutOfDate;
}
// Verify the image checksum if (!file.IsBackedByVdexOnly() &&
CompilerFilter::DependsOnImageChecksum(current_compiler_filter)) { if (!ValidateBootClassPathChecksums(file, error_msg)) { return kOatBootImageOutOfDate;
} if (!gc::space::ImageSpace::ValidateApexVersions(
file, GetOatFileAssistantContext()->GetApexVersions(), error_msg)) { return kOatBootImageOutOfDate;
}
}
// The constraint is only enforced if the zip has uncompressed dex code. if (only_load_trusted_executable_ &&
!LocationIsTrusted(file.GetLocation(), !GetRuntimeOptions().deny_art_apex_data_files) &&
file.ContainsDexCode() && ZipFileOnlyContainsUncompressedDex()) {
*error_msg = "Oat file has dex code, but APK has uncompressed dex code";
LOG(ERROR) << "Not loading " << dex_location_ << ": " << *error_msg; return kOatDexOutOfDate;
}
if (!ClassLoaderContextIsOkay(file, error_msg)) { return kOatContextOutOfDate;
}
if (file.GetOatHeader().HasAssumeValueSdkInt() &&
file.GetOatHeader().GetAssumeValueSdkInt() != GetRuntimeOptions().sdk_version) {
*error_msg = ART_FORMAT("Assumed value mismatch for SDK_INT (compiled='{}', runtime='{}')",
file.GetOatHeader().GetAssumeValueSdkInt(),
GetRuntimeOptions().sdk_version); return kOatAssumedValuesOutOfDate;
}
bool OatFileAssistant::AnonymousDexVdexLocation(const std::vector<const DexFile::Header*>& headers,
InstructionSet isa, /* out */ std::string* dex_location, /* out */ std::string* vdex_filename) { // Normally, OatFileAssistant should not assume that there is an active runtime. However, we // reference the runtime here. This is okay because we are in a static function that is unrelated // to other parts of OatFileAssistant.
DCHECK(Runtime::Current() != nullptr);
bool OatFileAssistant::IsAnonymousVdexBasename(const std::string& basename) {
DCHECK(basename.find('/') == std::string::npos); // `basename` must have format: <kAnonymousDexPrefix><checksum><kVdexExtension> if (basename.size() < strlen(kAnonymousDexPrefix) + strlen(kVdexExtension) + 1 ||
!basename.starts_with(kAnonymousDexPrefix) || !basename.ends_with(kVdexExtension)) { returnfalse;
} // Check that all characters between the prefix and extension are decimal digits. for (size_t i = strlen(kAnonymousDexPrefix); i < basename.size() - strlen(kVdexExtension); ++i) { if (!std::isdigit(basename[i])) { returnfalse;
}
} returntrue;
}
// For a DEX file on /apex, check if there is an odex file on /system. If so, and the file exists, // use it. if (LocationIsOnApex(location)) { const std::string system_file = GetSystemOdexFilenameForApex(location, isa); if (OS::FileExists(system_file.c_str(), /*check_file_type=*/true)) {
*odex_filename = system_file; returntrue;
} elseif (errno != ENOENT) {
PLOG(ERROR) << "Could not check odex file " << system_file;
}
}
// The odex file name is formed by replacing the dex_location extension with // .odex and inserting an oat/<isa> directory. For example: // location = /foo/bar/baz.jar // odex_location = /foo/bar/oat/<isa>/baz.odex
// Find the directory portion of the dex location and add the oat/<isa> // directory.
size_t pos = location.rfind('/'); if (pos == std::string::npos) {
*error_msg = "Dex location " + location + " has no directory."; returnfalse;
}
std::string dir = location.substr(0, pos + 1); // Add the oat directory.
dir += "oat";
// Add the isa directory
dir += "/" + std::string(GetInstructionSetString(isa));
// Get the base part of the file without the extension.
std::string file = location.substr(pos + 1);
pos = file.rfind('.');
std::string base = pos != std::string::npos ? file.substr(0, pos) : file;
*odex_filename = dir + "/" + base + kOdexExtension; returntrue;
}
// Check if `location` could have an oat file in the ART APEX data directory. If so, and the // file exists, use it. const std::string apex_data_file = GetApexDataOdexFilename(location, isa); if (!apex_data_file.empty() && !deny_art_apex_data_files) { if (OS::FileExists(apex_data_file.c_str(), /*check_file_type=*/true)) {
*oat_filename = apex_data_file; returntrue;
} elseif (errno != ENOENT) {
PLOG(ERROR) << "Could not check odex file " << apex_data_file;
}
}
// If ANDROID_DATA is not set, return false instead of aborting. // This can occur for preopt when using a class loader context. if (GetAndroidDataSafe(error_msg).empty()) {
*error_msg = "GetAndroidDataSafe failed: " + *error_msg; returnfalse;
}
// TODO: The oat file assistant should be the definitive place for // determining the oat file name from the dex location, not // GetDalvikCacheFilename. return GetDalvikCacheFilename(location, dalvik_cache, oat_filename, error_msg);
}
File file(zip_fd_, /*check_usage=*/false);
ArtDexFileLoader dex_loader(&file, dex_location_);
std::optional<uint32_t> checksum2;
std::string error2; if (dex_loader.GetMultiDexChecksum(
&checksum2, &error2, &zip_file_only_contains_uncompressed_dex_)) {
cached_required_dex_checksums_ = checksum2;
cached_required_dex_checksums_error_ = std::nullopt;
} else {
cached_required_dex_checksums_ = std::nullopt;
cached_required_dex_checksums_error_ = error2;
}
file.Release(); // Don't close the file yet (we have only read the checksum).
}
if (cached_required_dex_checksums_error_.has_value()) {
*error = cached_required_dex_checksums_error_.value();
DCHECK(!error->empty()); returnfalse;
}
if (!cached_required_dex_checksums_.has_value()) { // The only valid case here is for APKs without dex files.
VLOG(oat) << "No dex file found in " << dex_location_;
}
*checksum = cached_required_dex_checksums_; returntrue;
}
for (const std::unique_ptr<OatFileInfo>& info : info_list_) { if (VLOG_IS_ON(oat) && info->FileExists()) {
std::string error_msg;
OatStatus status = info->Status(&error_msg);
std::string message = ART_FORMAT("GetBestInfo: {} ({}) is {}",
info->GetLocationDebugString(),
info->DisplayFilename(),
fmt::streamed(status)); const OatFile* file = info->GetFile(); if (file != nullptr) {
message += ART_FORMAT(" with filter '{}' executable '{}'",
fmt::streamed(file->GetCompilerFilter()),
file->IsExecutable());
} if (!info->IsUseable()) {
message += ": " + error_msg;
}
VLOG_TO(logger_, oat) << message;
}
if (info->IsUseable()) { return *info;
}
}
// No usable artifact. Pick the oat or odex if they exist, or empty info if not.
VLOG_TO(logger_, oat) << ART_FORMAT("GetBestInfo: {} has no usable artifacts", dex_location_); for (const std::unique_ptr<OatFileInfo>& info : info_list_) { if (info->GetType() == OatFileType::kOat && info->Status() != kOatCannotOpen) { return *info;
}
} return empty_info_;
}
bool OatFileAssistant::OatFileInfo::IsUseable() {
ScopedTrace trace("IsUseable"); switch (Status()) { case kOatCannotOpen: case kOatDexOutOfDate: case kOatContextOutOfDate: case kOatBootImageOutOfDate: case kOatAssumedValuesOutOfDate: returnfalse;
// In this case, the oat file is not usable. If the caller doesn't seek for a better compiler // filter or force the compilation (e.g., the caller wants to downgrade), then we should not // recompile. if (dexopt_trigger.dexopt_comparators[0] != DexoptComparator::kComparingCompilerFilter &&
dexopt_trigger.dexopt_comparators[0] != DexoptComparator::kCustomTargetIsBetterThanCurrent) { return kNoDexOptNeeded;
}
if (Status() == kOatBootImageOutOfDate) { return kDex2OatForBootImage;
}
std::string error_msg;
std::optional<bool> has_dex_files = oat_file_assistant_->HasDexFiles(&error_msg); if (has_dex_files.has_value()) { if (*has_dex_files) { return kDex2OatFromScratch;
} else { // No dex file, so there is nothing we need to do. return kNoDexOptNeeded;
}
} else { // Unable to open the dex file, so there is nothing we can do.
LOG(WARNING) << error_msg; return kNoDexOptNeeded;
}
}
std::unique_ptr<OatFile> OatFileAssistant::OatFileInfoBackedByVdex::LoadFile(
std::string* error_msg) const { // Check to see if there is a vdex file we can make use of.
std::unique_ptr<VdexFile> vdex; if (use_fd_) { if (vdex_fd_ < 0) {
*error_msg = "vdex_fd not provided"; return nullptr;
} struct stat s; if (fstat(vdex_fd_, &s) < 0) {
*error_msg = ART_FORMAT("Failed getting length of the vdex file: {}", strerror(errno)); return nullptr;
}
vdex = VdexFile::Open(vdex_fd_, /*start=*/0,
s.st_size,
filename_, /*low_4gb=*/false,
error_msg);
} else {
vdex = VdexFile::Open(filename_, /*low_4gb=*/false,
error_msg);
} if (vdex == nullptr) {
*error_msg = ART_FORMAT("Unable to open vdex file: {}", *error_msg); return nullptr;
} return std::unique_ptr<OatFile>(OatFile::OpenFromVdex(zip_fd_,
std::move(vdex),
oat_file_assistant_->dex_location_,
oat_file_assistant_->context_,
kReasonVdex,
error_msg));
}
std::unique_ptr<OatFile> OatFileAssistant::OatFileInfoBackedByDm::LoadFile(
std::string* error_msg) const { // Check to see if there is a vdex file we can make use of.
std::unique_ptr<VdexFile> vdex(VdexFile::OpenFromDm(filename_, error_msg)); if (vdex == nullptr) { return nullptr;
} return std::unique_ptr<OatFile>(OatFile::OpenFromVdex(/*zip_fd=*/-1,
std::move(vdex),
oat_file_assistant_->dex_location_,
oat_file_assistant_->context_,
kReasonVdexDm,
error_msg));
}
for (DexoptComparator dexopt_comparator : dexopt_trigger.dexopt_comparators) { switch (dexopt_comparator) { case DexoptComparator::kComparingCompilerFilter: { if (CompilerFilter::IsBetter(target, current)) {
VLOG_TO(logger, oat) << "Should recompile: target filter is better "
<< filter_comparison_message(); returntrue;
} elseif (CompilerFilter::IsBetter(current, target)) {
VLOG_TO(logger, oat) << "Should not recompile: target filter is worse "
<< filter_comparison_message(); returnfalse;
} continue;
} case DexoptComparator::kComparingCompilerFilterReversed: { if (CompilerFilter::IsBetter(current, target)) {
VLOG_TO(logger, oat) << "Should recompile: target filter is worse "
<< filter_comparison_message(); returntrue;
} elseif (CompilerFilter::IsBetter(target, current)) {
VLOG_TO(logger, oat) << "Should not recompile: target filter is better "
<< filter_comparison_message(); returnfalse;
} continue;
} case DexoptComparator::kComparingPrimaryBootImageStatus: { if (!CompilerFilter::IsAotCompilationEnabled(target)) { continue;
}
constchar* oat_boot_class_path_checksums =
file->GetOatHeader().GetStoreValueByKey(OatHeader::kBootClassPathChecksumsKey); bool current_boot_image_status =
oat_boot_class_path_checksums != nullptr && oat_boot_class_path_checksums[0] == 'i'; bool target_boot_image_status = oat_file_assistant_->IsPrimaryBootImageUsable(); if (!current_boot_image_status && target_boot_image_status) {
VLOG_TO(logger, oat) << "Should recompile: target boot image status is better"; returntrue;
} elseif (current_boot_image_status && !target_boot_image_status) {
VLOG_TO(logger, oat) << "Should not recompile: target boot image status is worse"; returnfalse;
} continue;
} case DexoptComparator::kComparingExtractionStatus: { if (oat_file_assistant_->ZipFileOnlyContainsUncompressedDex()) { continue;
}
bool current_extraction_status = file->ContainsDexCode(); // Target extraction status is always true. if (!current_extraction_status) {
VLOG_TO(logger, oat) << "Should recompile: target extraction status is better"; returntrue;
} continue;
} case DexoptComparator::kCustomTargetIsBetterThanCurrent: {
DCHECK(dexopt_trigger.custom_comparator_reason.has_value());
VLOG_TO(logger, oat) << "Should recompile: " << *dexopt_trigger.custom_comparator_reason; returntrue;
} case DexoptComparator::kCustomTargetIsWorseThanCurrent: {
DCHECK(dexopt_trigger.custom_comparator_reason.has_value());
VLOG_TO(logger, oat) << "Should not recompile: "
<< *dexopt_trigger.custom_comparator_reason; returnfalse;
}
}
}
VLOG_TO(logger, oat) << "Should not recompile"; returnfalse;
}
bool OatFileAssistant::ClassLoaderContextIsOkay(const OatFile& oat_file, /*out*/ std::string* error_msg) const { if (context_ == nullptr) { // The caller requests to skip the check. returntrue;
}
if (oat_file.IsBackedByVdexOnly()) { // Only a vdex file, we don't depend on the class loader context. returntrue;
}
if (!CompilerFilter::IsVerificationEnabled(oat_file.GetCompilerFilter())) { // If verification is not enabled we don't need to verify the class loader context and we // assume it's ok. returntrue;
}
ClassLoaderContext::VerificationResult matches =
context_->VerifyClassLoaderContextMatch(oat_file.GetClassLoaderContext(), /*verify_names=*/true, /*verify_checksums=*/true); if (matches == ClassLoaderContext::VerificationResult::kMismatch) {
*error_msg =
ART_FORMAT("ClassLoaderContext check failed. Context was {}. The expected context is {}",
oat_file.GetClassLoaderContext(),
context_->EncodeContextForOatFile(android::base::Dirname(dex_location_))); returnfalse;
} returntrue;
}
// TODO(calin): we could provide a more refined status here // (e.g. run from uncompressed apk, run with vdex but not oat etc). It will allow us to // track more experiments but adds extra complexity. void OatFileAssistant::GetOptimizationStatus(const std::string& filename,
InstructionSet isa,
std::string* out_compilation_filter,
std::string* out_compilation_reason,
OatFileAssistantContext* ofa_context) { // It may not be possible to load an oat file executable (e.g., selinux restrictions). Load // non-executable and check the status manually.
OatFileAssistant oat_file_assistant(filename.c_str(),
isa, /*context=*/nullptr, /*load_executable=*/false, /*only_load_trusted_executable=*/false,
ofa_context);
std::string odex_location; // unused
std::string odex_status; // unused
Location location; // unused bool is_backed_by_vdex_only; // unused
oat_file_assistant.GetOptimizationStatus(&odex_location,
out_compilation_filter,
out_compilation_reason,
&odex_status,
&location,
&is_backed_by_vdex_only);
}
if (oat_file == nullptr) {
*is_backed_by_vdex_only = false;
std::string error_msg;
std::optional<bool> has_dex_files = HasDexFiles(&error_msg); if (!has_dex_files.has_value()) {
*odex_location = "error";
*compilation_filter = "unknown";
*compilation_reason = "unknown"; // This happens when we cannot open the APK/JAR.
*odex_status = "io-error-no-apk";
} elseif (!has_dex_files.value()) {
*odex_location = "none";
*compilation_filter = "unknown";
*compilation_reason = "unknown"; // This happens when the APK/JAR doesn't contain any DEX file.
*odex_status = "no-dex-code";
} else {
*odex_location = "error";
*compilation_filter = "run-from-apk";
*compilation_reason = "unknown"; // This mostly happens when we cannot open the oat file. // Note that it's different than kOatCannotOpen. // TODO: The design of getting the BestInfo is not ideal, as it's not very clear what's the // difference between a nullptr and kOatcannotOpen. The logic should be revised and improved.
*odex_status = "io-error-no-oat";
} return;
}
// If the oat file is invalid, the vdex file will be picked, so the status is `kOatUpToDate`. If // the vdex file is also invalid, then either `oat_file` is nullptr, or `status` is // `kOatDexOutOfDate`.
DCHECK(status == kOatUpToDate || status == kOatDexOutOfDate);
case kOatCannotOpen: case kOatBootImageOutOfDate: case kOatContextOutOfDate: case kOatAssumedValuesOutOfDate: // These should never happen, but be robust.
*compilation_filter = "unexpected";
*compilation_reason = "unexpected";
*odex_status = "unexpected"; return;
bool OatFileAssistant::ZipFileOnlyContainsUncompressedDex() { // zip_file_only_contains_uncompressed_dex_ is only set during fetching the dex checksums.
std::optional<uint32_t> checksum;
std::string error_msg; if (!GetRequiredDexChecksum(&checksum, &error_msg)) {
LOG(ERROR) << error_msg;
} return zip_file_only_contains_uncompressed_dex_;
}
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.