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));