summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--oatdump/oatdump.cc3
-rw-r--r--runtime/dexopt_test.cc2
-rw-r--r--runtime/oat/oat_file_assistant.cc268
-rw-r--r--runtime/oat/oat_file_assistant.h26
-rw-r--r--test/677-fsi/expected-stderr.txt4
-rw-r--r--test/677-fsi/run.py2
6 files changed, 164 insertions, 141 deletions
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index 13aef63b95..c567a5e730 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -3553,8 +3553,7 @@ struct OatdumpMain : public CmdlineMain<OatdumpArgs> {
/*only_load_trusted_executable=*/false,
ofa_context.get());
- if (!oat_file_assistant.ValidateBootClassPathChecksums(*oat_file)) {
- *error_msg = "BCP checksum check failed";
+ if (!oat_file_assistant.ValidateBootClassPathChecksums(*oat_file, error_msg)) {
return false;
}
diff --git a/runtime/dexopt_test.cc b/runtime/dexopt_test.cc
index edb28823c7..40e10142d7 100644
--- a/runtime/dexopt_test.cc
+++ b/runtime/dexopt_test.cc
@@ -169,7 +169,7 @@ void DexoptTest::GenerateOatForTest(const std::string& dex_location,
context.get(),
/*load_executable=*/false);
- bool match = oat_file_assistant.ValidateBootClassPathChecksums(*odex_file);
+ bool match = oat_file_assistant.ValidateBootClassPathChecksums(*odex_file, &error_msg);
ASSERT_EQ(!with_alternate_image, match) << error_msg;
}
}
diff --git a/runtime/oat/oat_file_assistant.cc b/runtime/oat/oat_file_assistant.cc
index 98e46644c8..e6f9cb15a3 100644
--- a/runtime/oat/oat_file_assistant.cc
+++ b/runtime/oat/oat_file_assistant.cc
@@ -21,6 +21,8 @@
#include <memory>
#include <optional>
#include <sstream>
+#include <string_view>
+#include <utility>
#include <vector>
#include "android-base/file.h"
@@ -49,6 +51,7 @@
#include "gc/space/image_space.h"
#include "image.h"
#include "oat.h"
+#include "oat/oat_file.h"
#include "oat_file_assistant_context.h"
#include "runtime.h"
#include "scoped_thread_state_change-inl.h"
@@ -420,55 +423,51 @@ bool OatFileAssistant::DexChecksumUpToDate(const OatFile& file, std::string* err
return true;
}
-OatFileAssistant::OatStatus OatFileAssistant::GivenOatFileStatus(const OatFile& file) {
+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.
- std::string error_msg;
- if (!DexChecksumUpToDate(file, &error_msg)) {
- LOG(ERROR) << error_msg;
+ if (!DexChecksumUpToDate(file, error_msg)) {
+ LOG(ERROR) << *error_msg;
return kOatDexOutOfDate;
}
CompilerFilter::Filter current_compiler_filter = file.GetCompilerFilter();
// Verify the image checksum
- if (file.IsBackedByVdexOnly()) {
- VLOG(oat) << "Image checksum test skipped for vdex file " << file.GetLocation();
- } else if (CompilerFilter::DependsOnImageChecksum(current_compiler_filter)) {
- if (!ValidateBootClassPathChecksums(file)) {
- VLOG(oat) << "Oat image checksum does not match image checksum.";
+ if (!file.IsBackedByVdexOnly() &&
+ CompilerFilter::DependsOnImageChecksum(current_compiler_filter)) {
+ if (!ValidateBootClassPathChecksums(file, error_msg)) {
return kOatBootImageOutOfDate;
}
if (!gc::space::ImageSpace::ValidateApexVersions(
file.GetOatHeader(),
GetOatFileAssistantContext()->GetApexVersions(),
file.GetLocation(),
- &error_msg)) {
- VLOG(oat) << error_msg;
+ error_msg)) {
return kOatBootImageOutOfDate;
}
- } else {
- VLOG(oat) << "Image checksum test skipped for compiler filter " << current_compiler_filter;
}
// 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()) {
- LOG(ERROR) << "Not loading " << dex_location_
- << ": oat file has dex code, but APK has uncompressed dex code";
+ *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)) {
+ if (!ClassLoaderContextIsOkay(file, error_msg)) {
return kOatContextOutOfDate;
}
@@ -762,29 +761,23 @@ bool OatFileAssistant::ValidateBootClassPathChecksums(OatFileAssistantContext* o
return true;
}
-bool OatFileAssistant::ValidateBootClassPathChecksums(const OatFile& oat_file) {
+bool OatFileAssistant::ValidateBootClassPathChecksums(const OatFile& oat_file,
+ /*out*/ std::string* error_msg) {
// Get the checksums and the BCP from the oat file.
const char* oat_boot_class_path_checksums =
oat_file.GetOatHeader().GetStoreValueByKey(OatHeader::kBootClassPathChecksumsKey);
const char* oat_boot_class_path =
oat_file.GetOatHeader().GetStoreValueByKey(OatHeader::kBootClassPathKey);
if (oat_boot_class_path_checksums == nullptr || oat_boot_class_path == nullptr) {
+ *error_msg = "Missing boot image information from oat file";
return false;
}
- std::string error_msg;
- bool result = ValidateBootClassPathChecksums(GetOatFileAssistantContext(),
- isa_,
- oat_boot_class_path_checksums,
- oat_boot_class_path,
- &error_msg);
- if (!result) {
- VLOG(oat) << "Failed to verify checksums of oat file " << oat_file.GetLocation()
- << " error: " << error_msg;
- return false;
- }
-
- return true;
+ return ValidateBootClassPathChecksums(GetOatFileAssistantContext(),
+ isa_,
+ oat_boot_class_path_checksums,
+ oat_boot_class_path,
+ error_msg);
}
bool OatFileAssistant::IsPrimaryBootImageUsable() {
@@ -794,12 +787,31 @@ bool OatFileAssistant::IsPrimaryBootImageUsable() {
OatFileAssistant::OatFileInfo& OatFileAssistant::GetBestInfo() {
ScopedTrace trace("GetBestInfo");
+ auto log_status = [&](std::string_view location, OatFileInfo* info) {
+ if (!VLOG_IS_ON(oat)) {
+ return;
+ }
+ std::string error_msg;
+ OatStatus status = info->Status(&error_msg);
+ std::string message = ART_FORMAT(
+ "GetBestInfo: {} ({}) is {}", location, 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(oat) << message;
+ };
+
// If the oat location is useable, take it. This must be an app on a readonly filesystem
// (typically, a system app or an incremental app). This must be prioritized over the odex
// location, because the odex location probably has the dexpreopt artifacts.
if (oat_.FileExists()) {
- VLOG(oat) << ART_FORMAT("GetBestInfo checking odex in dalvik-cache ({})",
- oat_.DisplayFilename());
+ log_status("odex in dalvik-cache", &oat_);
if (oat_.IsUseable()) {
return oat_;
}
@@ -807,8 +819,7 @@ OatFileAssistant::OatFileInfo& OatFileAssistant::GetBestInfo() {
// The odex location, which is the most common.
if (odex_.FileExists()) {
- VLOG(oat) << ART_FORMAT("GetBestInfo checking odex next to the dex file ({})",
- odex_.DisplayFilename());
+ log_status("odex next to the dex file", &odex_);
if (odex_.IsUseable()) {
return odex_;
}
@@ -816,8 +827,7 @@ OatFileAssistant::OatFileInfo& OatFileAssistant::GetBestInfo() {
// No odex/oat available, look for a useable vdex file.
if (vdex_for_oat_.FileExists()) {
- VLOG(oat) << ART_FORMAT("GetBestInfo checking vdex in dalvik-cache ({})",
- vdex_for_oat_.DisplayFilename());
+ log_status("vdex in dalvik-cache", &vdex_for_oat_);
if (vdex_for_oat_.IsUseable()) {
return vdex_for_oat_;
}
@@ -832,14 +842,14 @@ OatFileAssistant::OatFileInfo& OatFileAssistant::GetBestInfo() {
// A .dm file may be available, look for it.
if (dm_.FileExists()) {
- VLOG(oat) << ART_FORMAT("GetBestInfo checking dm ({})", dm_.DisplayFilename());
+ log_status("dm", &dm_);
if (dm_.IsUseable()) {
return dm_;
}
}
// No usable artifact. Pick the odex if it exists, or the oat if not.
- VLOG(oat) << "GetBestInfo no usable artifacts";
+ VLOG(oat) << ART_FORMAT("GetBestInfo: {} has no usable artifacts", dex_location_);
return (odex_.Status() == kOatCannotOpen) ? oat_ : odex_;
}
@@ -886,20 +896,22 @@ bool OatFileAssistant::OatFileInfo::IsUseable() {
}
}
-OatFileAssistant::OatStatus OatFileAssistant::OatFileInfo::Status() {
+OatFileAssistant::OatStatus OatFileAssistant::OatFileInfo::Status(/*out*/ std::string* error_msg) {
ScopedTrace trace("Status");
- if (!status_attempted_) {
- status_attempted_ = true;
- const OatFile* file = GetFile();
+ if (!status_.has_value()) {
+ std::string temp_error_msg;
+ const OatFile* file = GetFile(&temp_error_msg);
if (file == nullptr) {
- status_ = kOatCannotOpen;
+ status_ = std::make_pair(kOatCannotOpen, std::move(temp_error_msg));
} else {
- status_ = oat_file_assistant_->GivenOatFileStatus(*file);
- VLOG(oat) << file->GetLocation() << " is " << status_ << " with filter "
- << file->GetCompilerFilter();
+ status_ = std::make_pair(oat_file_assistant_->GivenOatFileStatus(*file, &temp_error_msg),
+ std::move(temp_error_msg));
}
}
- return status_;
+ if (error_msg != nullptr) {
+ *error_msg = status_->second;
+ }
+ return status_->first;
}
OatFileAssistant::DexOptNeeded OatFileAssistant::OatFileInfo::GetDexOptNeeded(
@@ -939,107 +951,110 @@ bool OatFileAssistant::OatFileInfo::FileExists() const {
return use_fd_ || (!filename_.empty() && OS::FileExists(filename_.c_str()));
}
-const OatFile* OatFileAssistant::OatFileInfo::GetFile() {
+const OatFile* OatFileAssistant::OatFileInfo::GetFile(/*out*/ std::string* error_msg) {
CHECK(!file_released_) << "GetFile called after oat file released.";
- if (load_attempted_) {
- return file_.get();
- }
- load_attempted_ = true;
if (!filename_provided_) {
return nullptr;
}
- if (LocationIsOnArtApexData(filename_) &&
- oat_file_assistant_->GetRuntimeOptions().deny_art_apex_data_files) {
- LOG(WARNING) << "OatFileAssistant rejected file " << filename_
- << ": ART apexdata is untrusted.";
- return nullptr;
+ if (!file_.has_value()) {
+ if (LocationIsOnArtApexData(filename_) &&
+ oat_file_assistant_->GetRuntimeOptions().deny_art_apex_data_files) {
+ file_ = std::make_pair(nullptr, "ART apexdata is untrusted");
+ LOG(WARNING) << "OatFileAssistant rejected file " << filename_ << ": " << file_->second;
+ } else {
+ std::string temp_error_msg;
+ file_ = std::make_pair(LoadFile(&temp_error_msg), std::move(temp_error_msg));
+ }
}
- std::string error_msg;
- bool executable = oat_file_assistant_->load_executable_;
+ if (error_msg != nullptr) {
+ *error_msg = file_->second;
+ }
+ return file_->first.get();
+}
+
+std::unique_ptr<OatFile> OatFileAssistant::OatFileInfo::LoadFile(std::string* error_msg) const {
if (filename_.ends_with(kVdexExtension)) {
- executable = false;
// 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) {
- struct stat s;
- int rc = TEMP_FAILURE_RETRY(fstat(vdex_fd_, &s));
- if (rc == -1) {
- error_msg = StringPrintf("Failed getting length of the vdex file %s.", strerror(errno));
- } else {
- vdex = VdexFile::Open(vdex_fd_,
- s.st_size,
- filename_,
- /*low_4gb=*/false,
- &error_msg);
- }
+ 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_,
+ s.st_size,
+ filename_,
+ /*low_4gb=*/false,
+ error_msg);
} else {
vdex = VdexFile::Open(filename_,
/*low_4gb=*/false,
- &error_msg);
+ error_msg);
}
if (vdex == nullptr) {
- VLOG(oat) << "unable to open vdex file " << filename_ << ": " << error_msg;
- } else {
- file_.reset(OatFile::OpenFromVdex(zip_fd_,
- std::move(vdex),
- oat_file_assistant_->dex_location_,
- oat_file_assistant_->context_,
- &error_msg));
+ *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_,
+ error_msg));
} else if (filename_.ends_with(kDmExtension)) {
- executable = false;
// Check to see if there is a vdex file we can make use of.
- std::unique_ptr<ZipArchive> dm_file(ZipArchive::Open(filename_.c_str(), &error_msg));
- if (dm_file != nullptr) {
- std::unique_ptr<VdexFile> vdex(VdexFile::OpenFromDm(filename_, *dm_file, &error_msg));
- if (vdex != nullptr) {
- file_.reset(OatFile::OpenFromVdex(zip_fd_,
- std::move(vdex),
- oat_file_assistant_->dex_location_,
- oat_file_assistant_->context_,
- &error_msg));
- }
+ std::unique_ptr<ZipArchive> dm_file(ZipArchive::Open(filename_.c_str(), error_msg));
+ if (dm_file == nullptr) {
+ return nullptr;
+ }
+ std::unique_ptr<VdexFile> vdex(VdexFile::OpenFromDm(filename_, *dm_file, 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_,
+ error_msg));
} else {
+ bool executable = oat_file_assistant_->load_executable_;
if (executable && oat_file_assistant_->only_load_trusted_executable_) {
executable = LocationIsTrusted(filename_, /*trust_art_apex_data_files=*/true);
}
- VLOG(oat) << "Loading " << filename_ << " with executable: " << executable;
+
if (use_fd_) {
- if (oat_fd_ >= 0 && vdex_fd_ >= 0) {
- ArrayRef<const std::string> dex_locations(&oat_file_assistant_->dex_location_,
- /*size=*/1u);
- file_.reset(OatFile::Open(zip_fd_,
- vdex_fd_,
- oat_fd_,
- filename_,
- executable,
- /*low_4gb=*/false,
- dex_locations,
- /*dex_files=*/{},
- /*reservation=*/nullptr,
- &error_msg));
+ if (oat_fd_ < 0 || vdex_fd_ < 0) {
+ *error_msg = "oat_fd or vdex_fd not provided";
+ return nullptr;
}
+ ArrayRef<const std::string> dex_locations(&oat_file_assistant_->dex_location_,
+ /*size=*/1u);
+ return std::unique_ptr<OatFile>(OatFile::Open(zip_fd_,
+ vdex_fd_,
+ oat_fd_,
+ filename_,
+ executable,
+ /*low_4gb=*/false,
+ dex_locations,
+ /*dex_files=*/{},
+ /*reservation=*/nullptr,
+ error_msg));
} else {
- file_.reset(OatFile::Open(/*zip_fd=*/-1,
- filename_,
- filename_,
- executable,
- /*low_4gb=*/false,
- oat_file_assistant_->dex_location_,
- &error_msg));
+ return std::unique_ptr<OatFile>(OatFile::Open(/*zip_fd=*/-1,
+ filename_,
+ filename_,
+ executable,
+ /*low_4gb=*/false,
+ oat_file_assistant_->dex_location_,
+ error_msg));
}
}
- if (file_.get() == nullptr) {
- VLOG(oat) << "OatFileAssistant test for existing oat file " << filename_ << ": " << error_msg;
- } else {
- VLOG(oat) << "Successfully loaded " << filename_ << " with executable: " << executable;
- }
- return file_.get();
}
bool OatFileAssistant::OatFileInfo::ShouldRecompileForFilter(CompilerFilter::Filter target,
@@ -1103,7 +1118,8 @@ bool OatFileAssistant::OatFileInfo::ShouldRecompileForFilter(CompilerFilter::Fil
return false;
}
-bool OatFileAssistant::ClassLoaderContextIsOkay(const OatFile& oat_file) const {
+bool OatFileAssistant::ClassLoaderContextIsOkay(const OatFile& oat_file,
+ /*out*/ std::string* error_msg) const {
if (context_ == nullptr) {
// The caller requests to skip the check.
return true;
@@ -1125,9 +1141,10 @@ bool OatFileAssistant::ClassLoaderContextIsOkay(const OatFile& oat_file) const {
/*verify_names=*/true,
/*verify_checksums=*/true);
if (matches == ClassLoaderContext::VerificationResult::kMismatch) {
- VLOG(oat) << "ClassLoaderContext check failed. Context was " << oat_file.GetClassLoaderContext()
- << ". The expected context is "
- << context_->EncodeContextForOatFile(android::base::Dirname(dex_location_));
+ *error_msg =
+ ART_FORMAT("ClassLoaderContext check failed. Context was {}. The expected context is {}",
+ oat_file.GetClassLoaderContext(),
+ context_->EncodeContextForOatFile(android::base::Dirname(dex_location_)));
return false;
}
return true;
@@ -1139,9 +1156,8 @@ bool OatFileAssistant::OatFileInfo::IsExecutable() {
}
void OatFileAssistant::OatFileInfo::Reset() {
- load_attempted_ = false;
- file_.reset();
- status_attempted_ = false;
+ file_ = std::nullopt;
+ status_ = std::nullopt;
}
void OatFileAssistant::OatFileInfo::Reset(
@@ -1157,7 +1173,7 @@ void OatFileAssistant::OatFileInfo::Reset(
std::unique_ptr<OatFile> OatFileAssistant::OatFileInfo::ReleaseFile() {
file_released_ = true;
- return std::move(file_);
+ return std::move(file_->first);
}
std::unique_ptr<OatFile> OatFileAssistant::OatFileInfo::ReleaseFileForUse() {
diff --git a/runtime/oat/oat_file_assistant.h b/runtime/oat/oat_file_assistant.h
index 4526843756..85021bea33 100644
--- a/runtime/oat/oat_file_assistant.h
+++ b/runtime/oat/oat_file_assistant.h
@@ -23,6 +23,7 @@
#include <sstream>
#include <string>
#include <string_view>
+#include <utility>
#include <variant>
#include "arch/instruction_set.h"
@@ -356,10 +357,11 @@ class OatFileAssistant {
// anonymous dex file(s) created by AnonymousDexVdexLocation.
EXPORT static bool IsAnonymousVdexBasename(const std::string& basename);
- bool ClassLoaderContextIsOkay(const OatFile& oat_file) const;
+ bool ClassLoaderContextIsOkay(const OatFile& oat_file, /*out*/ std::string* error_msg) const;
// Validates the boot class path checksum of an OatFile.
- EXPORT bool ValidateBootClassPathChecksums(const OatFile& oat_file);
+ EXPORT bool ValidateBootClassPathChecksums(const OatFile& oat_file,
+ /*out*/ std::string* error_msg);
// Validates the given bootclasspath and bootclasspath checksums found in an oat header.
static bool ValidateBootClassPathChecksums(OatFileAssistantContext* ofa_context,
@@ -393,7 +395,8 @@ class OatFileAssistant {
bool IsUseable();
// Returns the status of this oat file.
- OatStatus Status();
+ // Optionally, returns `error_msg` showing why the status is not `kOatUpToDate`.
+ OatStatus Status(/*out*/ std::string* error_msg = nullptr);
// Return the DexOptNeeded value for this oat file with respect to the given target compilation
// filter and dexopt trigger.
@@ -406,7 +409,8 @@ class OatFileAssistant {
// Returns the loaded file.
// Loads the file if needed. Returns null if the file failed to load.
// The caller shouldn't clean up or free the returned pointer.
- const OatFile* GetFile();
+ // Optionally, returns `error_msg` showing why the file failed to load.
+ const OatFile* GetFile(/*out*/ std::string* error_msg = nullptr);
// Returns true if the file is opened executable.
bool IsExecutable();
@@ -439,6 +443,8 @@ class OatFileAssistant {
bool CheckDisableCompactDex();
private:
+ std::unique_ptr<OatFile> LoadFile(std::string* error_msg) const;
+
// Returns true if the oat file is usable but at least one dexopt trigger is matched. This
// function should only be called if the oat file is usable.
bool ShouldRecompileForFilter(CompilerFilter::Filter target,
@@ -463,11 +469,13 @@ class OatFileAssistant {
int vdex_fd_ = -1;
bool use_fd_ = false;
- bool load_attempted_ = false;
- std::unique_ptr<OatFile> file_;
+ // A pair of the loaded file and the error message, if `GetFile` has been attempted.
+ // `std::nullopt` if `GetFile` has not been attempted.
+ std::optional<std::pair<std::unique_ptr<OatFile>, std::string>> file_ = std::nullopt;
- bool status_attempted_ = false;
- OatStatus status_ = OatStatus::kOatCannotOpen;
+ // A pair of the oat status and the error message, if `Status` has been attempted.
+ // `std::nullopt` if `Status` has not been attempted.
+ std::optional<std::pair<OatStatus, std::string>> status_ = std::nullopt;
// For debugging only.
// If this flag is set, the file has been released to the user and the
@@ -491,7 +499,7 @@ class OatFileAssistant {
// Return the status for a given opened oat file with respect to the dex
// location.
- OatStatus GivenOatFileStatus(const OatFile& file);
+ OatStatus GivenOatFileStatus(const OatFile& file, /*out*/ std::string* error_msg);
// Gets the dex checksum required for an up-to-date oat file.
// Returns cached result from GetMultiDexChecksum.
diff --git a/test/677-fsi/expected-stderr.txt b/test/677-fsi/expected-stderr.txt
index 35c39182a5..2295afcf0a 100644
--- a/test/677-fsi/expected-stderr.txt
+++ b/test/677-fsi/expected-stderr.txt
@@ -1,2 +1,2 @@
-oat file has dex code, but APK has uncompressed dex code
-oat file has dex code, but APK has uncompressed dex code
+Oat file has dex code, but APK has uncompressed dex code
+Oat file has dex code, but APK has uncompressed dex code
diff --git a/test/677-fsi/run.py b/test/677-fsi/run.py
index 357692f74e..e124eff4df 100644
--- a/test/677-fsi/run.py
+++ b/test/677-fsi/run.py
@@ -26,7 +26,7 @@ def run(ctx, args):
# Only keep the lines we're interested in.
ctx.run(fr"sed -i '/Hello World/!d' '{args.stdout_file}'")
ctx.run(
- fr"sed -i '/^.*: oat file has dex code, but APK has uncompressed dex code/!d' '{args.stderr_file}'"
+ fr"sed -i '/^.*: Oat file has dex code, but APK has uncompressed dex code/!d' '{args.stderr_file}'"
)
# Remove part of message containing filename.