Better error reporting when loading dex files
Collect all partial error messages and return them as cause
exceptions for the top-level exception returned.
Change-Id: I9661b8aed2a571dc88bf0f06d447108eeaed1409
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 78b7cc0..e690b30 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -692,7 +692,7 @@
while (true) {
file_.reset(OS::OpenFileWithFlags(filename, O_CREAT | O_RDWR));
if (file_.get() == NULL) {
- *error_msg = StringPrintf("Failed to open file '%s'", filename);
+ *error_msg = StringPrintf("Failed to open file '%s': %s", filename, strerror(errno));
return false;
}
int flock_result = TEMP_FAILURE_RETRY(flock(file_->Fd(), LOCK_EX));
@@ -741,51 +741,57 @@
const DexFile* ClassLinker::FindOrCreateOatFileForDexLocation(const char* dex_location,
uint32_t dex_location_checksum,
const char* oat_location,
- std::string* error_msg) {
+ std::vector<std::string>* error_msgs) {
// We play a locking game here so that if two different processes
// race to generate (or worse, one tries to open a partial generated
// file) we will be okay. This is actually common with apps that use
// DexClassLoader to work around the dex method reference limit and
// that have a background service running in a separate process.
ScopedFlock scoped_flock;
- if (!scoped_flock.Init(oat_location, error_msg)) {
+ std::string error_msg;
+ if (!scoped_flock.Init(oat_location, &error_msg)) {
+ error_msgs->push_back(error_msg);
return nullptr;
}
// Check if we already have an up-to-date output file
const DexFile* dex_file = FindDexFileInOatLocation(dex_location, dex_location_checksum,
- oat_location, error_msg);
+ oat_location, &error_msg);
if (dex_file != nullptr) {
return dex_file;
}
- VLOG(class_linker) << "Failed to find dex file '" << dex_location << "' in oat location '"
- << oat_location << "': " << *error_msg;
- error_msg->clear();
+ std::string compound_msg = StringPrintf("Failed to find dex file '%s' in oat location '%s': %s",
+ dex_location, oat_location, error_msg.c_str());
+ VLOG(class_linker) << compound_msg;
+ error_msgs->push_back(compound_msg);
// Generate the output oat file for the dex file
VLOG(class_linker) << "Generating oat file " << oat_location << " for " << dex_location;
- if (!GenerateOatFile(dex_location, scoped_flock.GetFile().Fd(), oat_location, error_msg)) {
- CHECK(!error_msg->empty());
+ if (!GenerateOatFile(dex_location, scoped_flock.GetFile().Fd(), oat_location, &error_msg)) {
+ CHECK(!error_msg.empty());
+ error_msgs->push_back(error_msg);
return nullptr;
}
const OatFile* oat_file = OatFile::Open(oat_location, oat_location, NULL,
!Runtime::Current()->IsCompiler(),
- error_msg);
+ &error_msg);
if (oat_file == nullptr) {
- *error_msg = StringPrintf("Failed to open generated oat file '%s': %s",
- oat_location, error_msg->c_str());
+ compound_msg = StringPrintf("\nFailed to open generated oat file '%s': %s",
+ oat_location, error_msg.c_str());
+ error_msgs->push_back(compound_msg);
return nullptr;
}
oat_file = RegisterOatFile(oat_file);
const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_location,
&dex_location_checksum);
if (oat_dex_file == nullptr) {
- *error_msg = StringPrintf("Failed to find dex file '%s' (checksum 0x%x) in generated out file "
- "'%s'", dex_location, dex_location_checksum, oat_location);
+ error_msg = StringPrintf("\nFailed to find dex file '%s' (checksum 0x%x) in generated out file "
+ "'%s'", dex_location, dex_location_checksum, oat_location);
+ error_msgs->push_back(error_msg);
return nullptr;
}
- const DexFile* result = oat_dex_file->OpenDexFile(error_msg);
- CHECK(result != nullptr) << *error_msg;
+ const DexFile* result = oat_dex_file->OpenDexFile(&error_msg);
+ CHECK(result != nullptr) << error_msgs << ", " << error_msg;
CHECK_EQ(dex_location_checksum, result->GetLocationChecksum())
<< "dex_location=" << dex_location << " oat_location=" << oat_location << std::hex
<< " dex_location_checksum=" << dex_location_checksum
@@ -880,27 +886,34 @@
const DexFile* ClassLinker::FindDexFileInOatFileFromDexLocation(const char* dex_location,
const uint32_t* const dex_location_checksum,
- std::string* error_msg) {
+ std::vector<std::string>* error_msgs) {
const OatFile* open_oat_file = FindOpenedOatFileFromDexLocation(dex_location,
dex_location_checksum);
if (open_oat_file != nullptr) {
const OatFile::OatDexFile* oat_dex_file = open_oat_file->GetOatDexFile(dex_location,
dex_location_checksum);
- return oat_dex_file->OpenDexFile(error_msg);
+ std::string error_msg;
+ const DexFile* ret = oat_dex_file->OpenDexFile(&error_msg);
+ if (ret == nullptr) {
+ error_msgs->push_back(error_msg);
+ }
+ return ret;
}
// Look for an existing file next to dex. for example, for
// /foo/bar/baz.jar, look for /foo/bar/baz.odex.
std::string odex_filename(OatFile::DexFilenameToOdexFilename(dex_location));
bool open_failed;
+ std::string error_msg;
const DexFile* dex_file = VerifyAndOpenDexFileFromOatFile(odex_filename, dex_location,
- error_msg, &open_failed);
+ &error_msg, &open_failed);
if (dex_file != nullptr) {
return dex_file;
}
if (dex_location_checksum == nullptr) {
- *error_msg = StringPrintf("Failed to open oat file from %s and no classes.dex found in %s: %s",
- odex_filename.c_str(), dex_location, error_msg->c_str());
+ error_msgs->push_back(StringPrintf("Failed to open oat file from %s and no classes.dex found in"
+ "%s: %s", odex_filename.c_str(), dex_location,
+ error_msg.c_str()));
return nullptr;
}
@@ -914,14 +927,15 @@
if (!open_failed && TEMP_FAILURE_RETRY(unlink(cache_location.c_str())) != 0) {
PLOG(FATAL) << "Failed to remove obsolete oat file from " << cache_location;
}
- VLOG(class_linker) << "Failed to open oat file from " << odex_filename
- << " (error '" << *error_msg << "') or " << cache_location
- << " (error '" << cache_error_msg << "').";
+ std::string compound_msg = StringPrintf("Failed to open oat file from %s (error '%s') or %s "
+ "(error '%s').", odex_filename.c_str(), error_msg.c_str(),
+ cache_location.c_str(), cache_error_msg.c_str());
+ VLOG(class_linker) << compound_msg;
+ error_msgs->push_back(compound_msg);
// Try to generate oat file if it wasn't found or was obsolete.
- error_msg->clear();
return FindOrCreateOatFileForDexLocation(dex_location, *dex_location_checksum,
- cache_location.c_str(), error_msg);
+ cache_location.c_str(), error_msgs);
}
const OatFile* ClassLinker::FindOpenedOatFileFromOatLocation(const std::string& oat_location) {
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 701e62e..d684ad5 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -273,14 +273,14 @@
const DexFile* FindOrCreateOatFileForDexLocation(const char* dex_location,
uint32_t dex_location_checksum,
const char* oat_location,
- std::string* error_msg)
+ std::vector<std::string>* error_msgs)
LOCKS_EXCLUDED(dex_lock_, Locks::mutator_lock_);
// Find a DexFile within an OatFile given a DexFile location. Note
// that this returns null if the location checksum of the DexFile
// does not match the OatFile.
const DexFile* FindDexFileInOatFileFromDexLocation(const char* location,
const uint32_t* const location_checksum,
- std::string* error_msg)
+ std::vector<std::string>* error_msgs)
LOCKS_EXCLUDED(dex_lock_, Locks::mutator_lock_);
diff --git a/runtime/common_throws.cc b/runtime/common_throws.cc
index 4b6d82b..315f274 100644
--- a/runtime/common_throws.cc
+++ b/runtime/common_throws.cc
@@ -66,6 +66,28 @@
}
}
+static void ThrowWrappedException(const ThrowLocation* throw_location,
+ const char* exception_descriptor,
+ mirror::Class* referrer, const char* fmt, va_list* args = NULL)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ std::ostringstream msg;
+ if (args != NULL) {
+ std::string vmsg;
+ StringAppendV(&vmsg, fmt, *args);
+ msg << vmsg;
+ } else {
+ msg << fmt;
+ }
+ AddReferrerLocation(msg, referrer);
+ Thread* self = Thread::Current();
+ if (throw_location == NULL) {
+ ThrowLocation computed_throw_location = self->GetCurrentLocationForThrow();
+ self->ThrowNewWrappedException(computed_throw_location, exception_descriptor, msg.str().c_str());
+ } else {
+ self->ThrowNewWrappedException(*throw_location, exception_descriptor, msg.str().c_str());
+ }
+}
+
// AbstractMethodError
void ThrowAbstractMethodError(mirror::ArtMethod* method) {
@@ -243,6 +265,13 @@
va_end(args);
}
+void ThrowWrappedIOException(const char* fmt, ...) {
+ va_list args;
+ va_start(args, fmt);
+ ThrowWrappedException(NULL, "Ljava/io/IOException;", NULL, fmt, &args);
+ va_end(args);
+}
+
// LinkageError
void ThrowLinkageError(mirror::Class* referrer, const char* fmt, ...) {
diff --git a/runtime/common_throws.h b/runtime/common_throws.h
index c06763e..ebedae0 100644
--- a/runtime/common_throws.h
+++ b/runtime/common_throws.h
@@ -126,6 +126,9 @@
void ThrowIOException(const char* fmt, ...) __attribute__((__format__(__printf__, 1, 2)))
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR;
+void ThrowWrappedIOException(const char* fmt, ...) __attribute__((__format__(__printf__, 1, 2)))
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR;
+
// LinkageError
void ThrowLinkageError(mirror::Class* referrer, const char* fmt, ...)
diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc
index 15a5779..6af16f4 100644
--- a/runtime/native/dalvik_system_DexFile.cc
+++ b/runtime/native/dalvik_system_DexFile.cc
@@ -104,6 +104,7 @@
uint32_t dex_location_checksum;
uint32_t* dex_location_checksum_pointer = &dex_location_checksum;
+ std::vector<std::string> error_msgs;
std::string error_msg;
if (!DexFile::GetChecksum(sourceName.c_str(), dex_location_checksum_pointer, &error_msg)) {
dex_location_checksum_pointer = NULL;
@@ -113,9 +114,8 @@
const DexFile* dex_file;
if (outputName.c_str() == nullptr) {
// FindOrCreateOatFileForDexLocation can tolerate a missing dex_location_checksum
- error_msg.clear();
dex_file = linker->FindDexFileInOatFileFromDexLocation(sourceName.c_str(),
- dex_location_checksum_pointer, &error_msg);
+ dex_location_checksum_pointer, &error_msgs);
} else {
// FindOrCreateOatFileForDexLocation requires the dex_location_checksum
if (dex_location_checksum_pointer == NULL) {
@@ -125,12 +125,19 @@
return 0;
}
dex_file = linker->FindOrCreateOatFileForDexLocation(sourceName.c_str(), dex_location_checksum,
- outputName.c_str(), &error_msg);
+ outputName.c_str(), &error_msgs);
}
if (dex_file == nullptr) {
ScopedObjectAccess soa(env);
- CHECK(!error_msg.empty());
- ThrowIOException("%s", error_msg.c_str());
+ CHECK(!error_msgs.empty());
+ // The most important message is at the end. So set up nesting by going forward, which will
+ // wrap the existing exception as a cause for the following one.
+ auto it = error_msgs.begin();
+ auto itEnd = error_msgs.end();
+ for ( ; it != itEnd; ++it) {
+ ThrowWrappedIOException("%s", it->c_str());
+ }
+
return 0;
}
return static_cast<jlong>(reinterpret_cast<uintptr_t>(dex_file));