Revert "Revert "ART: Fix up some multi-image cases""

This reverts commit de38b797c3e5ba3ee44c480db7093386975c51eb.

Fix up imgdiag for std::string and multi-image.

Bug: 26317072
Bug: 26320300

Change-Id: I94ce9528e9fea6fb3231a70c32db02d567143db9
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 3e432c7..7f67ae4 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -265,6 +265,9 @@
     // For code reuse, handle this like a work queue.
     std::vector<std::string> image_file_names;
     image_file_names.push_back(image_file_name);
+    // The loaded spaces. Secondary images may fail to load, in which case we need to remove
+    // already added spaces.
+    std::vector<space::Space*> added_image_spaces;
 
     for (size_t index = 0; index < image_file_names.size(); ++index) {
       std::string& image_name = image_file_names[index];
@@ -277,6 +280,7 @@
       ATRACE_END();
       if (boot_image_space != nullptr) {
         AddSpace(boot_image_space);
+        added_image_spaces.push_back(boot_image_space);
         // Oat files referenced by image files immediately follow them in memory, ensure alloc space
         // isn't going to get in the middle
         uint8_t* oat_file_end_addr = boot_image_space->GetImageHeader().GetOatFileEnd();
@@ -298,66 +302,18 @@
             continue;
           }
 
-          std::vector<std::string> images;
-          Split(boot_classpath, ':', &images);
-
-          // Add the rest into the list. We have to adjust locations, possibly:
-          //
-          // For example, image_file_name is /a/b/c/d/e.art
-          //              images[0] is          f/c/d/e.art
-          // ----------------------------------------------
-          //              images[1] is          g/h/i/j.art  -> /a/b/h/i/j.art
-
-          // Derive pattern.
-          std::vector<std::string> left;
-          Split(image_file_name, '/', &left);
-          std::vector<std::string> right;
-          Split(images[0], '/', &right);
-
-          size_t common = 1;
-          while (common < left.size() && common < right.size()) {
-            if (left[left.size() - common - 1] != right[right.size() - common - 1]) {
-              break;
-            }
-            common++;
-          }
-
-          std::vector<std::string> prefix_vector(left.begin(), left.end() - common);
-          std::string common_prefix = Join(prefix_vector, '/');
-          if (!common_prefix.empty() && common_prefix[0] != '/' && image_file_name[0] == '/') {
-            common_prefix = "/" + common_prefix;
-          }
-
-          // Apply pattern to images[1] .. images[n].
-          for (size_t i = 1; i < images.size(); ++i) {
-            std::string image = images[i];
-
-            size_t rslash = std::string::npos;
-            for (size_t j = 0; j < common; ++j) {
-              if (rslash != std::string::npos) {
-                rslash--;
-              }
-
-              rslash = image.rfind('/', rslash);
-              if (rslash == std::string::npos) {
-                rslash = 0;
-              }
-              if (rslash == 0) {
-                break;
-              }
-            }
-            std::string image_part = image.substr(rslash);
-
-            std::string new_image = common_prefix + (StartsWith(image_part, "/") ? "" : "/") +
-                image_part;
-            image_file_names.push_back(new_image);
-          }
+          space::ImageSpace::CreateMultiImageLocations(image_file_name,
+                                                       boot_classpath,
+                                                       &image_file_names);
         }
       } else {
         LOG(ERROR) << "Could not create image space with image file '" << image_file_name << "'. "
             << "Attempting to fall back to imageless running. Error was: " << error_msg
             << "\nAttempted image: " << image_name;
-        // TODO: Remove already loaded spaces.
+        // Remove already loaded spaces.
+        for (space::Space* loaded_space : added_image_spaces) {
+          RemoveSpace(loaded_space);
+        }
         break;
       }
     }
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index 952759c..dfdbd04 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -523,6 +523,9 @@
           } else if (!ImageCreationAllowed(is_global_cache, &reason)) {
             // Whether we can write to the cache.
             success = false;
+          } else if (secondary_image) {
+            reason = "Should not have to patch secondary image.";
+            success = false;
           } else {
             // Try to relocate.
             success = RelocateImage(image_location, cache_filename.c_str(), image_isa, &reason);
@@ -615,6 +618,9 @@
     return nullptr;
   } else if (!ImageCreationAllowed(is_global_cache, error_msg)) {
     return nullptr;
+  } else if (secondary_image) {
+    *error_msg = "Cannot compile a secondary image.";
+    return nullptr;
   } else if (!GenerateImage(cache_filename, image_isa, error_msg)) {
     *error_msg = StringPrintf("Failed to generate image '%s': %s",
                               cache_filename.c_str(), error_msg->c_str());
@@ -969,6 +975,67 @@
       << ",name=\"" << GetName() << "\"]";
 }
 
+void ImageSpace::CreateMultiImageLocations(const std::string& input_image_file_name,
+                                           const std::string& boot_classpath,
+                                           std::vector<std::string>* image_file_names) {
+  DCHECK(image_file_names != nullptr);
+
+  std::vector<std::string> images;
+  Split(boot_classpath, ':', &images);
+
+  // Add the rest into the list. We have to adjust locations, possibly:
+  //
+  // For example, image_file_name is /a/b/c/d/e.art
+  //              images[0] is          f/c/d/e.art
+  // ----------------------------------------------
+  //              images[1] is          g/h/i/j.art  -> /a/b/h/i/j.art
+
+  // Derive pattern.
+  std::vector<std::string> left;
+  Split(input_image_file_name, '/', &left);
+  std::vector<std::string> right;
+  Split(images[0], '/', &right);
+
+  size_t common = 1;
+  while (common < left.size() && common < right.size()) {
+    if (left[left.size() - common - 1] != right[right.size() - common - 1]) {
+      break;
+    }
+    common++;
+  }
+
+  std::vector<std::string> prefix_vector(left.begin(), left.end() - common);
+  std::string common_prefix = Join(prefix_vector, '/');
+  if (!common_prefix.empty() && common_prefix[0] != '/' && input_image_file_name[0] == '/') {
+    common_prefix = "/" + common_prefix;
+  }
+
+  // Apply pattern to images[1] .. images[n].
+  for (size_t i = 1; i < images.size(); ++i) {
+    std::string image = images[i];
+
+    size_t rslash = std::string::npos;
+    for (size_t j = 0; j < common; ++j) {
+      if (rslash != std::string::npos) {
+        rslash--;
+      }
+
+      rslash = image.rfind('/', rslash);
+      if (rslash == std::string::npos) {
+        rslash = 0;
+      }
+      if (rslash == 0) {
+        break;
+      }
+    }
+    std::string image_part = image.substr(rslash);
+
+    std::string new_image = common_prefix + (StartsWith(image_part, "/") ? "" : "/") +
+        image_part;
+    image_file_names->push_back(new_image);
+  }
+}
+
 }  // namespace space
 }  // namespace gc
 }  // namespace art
diff --git a/runtime/gc/space/image_space.h b/runtime/gc/space/image_space.h
index a54358a..b8ae4a0 100644
--- a/runtime/gc/space/image_space.h
+++ b/runtime/gc/space/image_space.h
@@ -122,6 +122,12 @@
                                 bool* has_data,
                                 bool *is_global_cache);
 
+  // Use the input image filename to adapt the names in the given boot classpath to establish
+  // complete locations for secondary images.
+  static void CreateMultiImageLocations(const std::string& input_image_file_name,
+                                        const std::string& boot_classpath,
+                                        std::vector<std::string>* image_filenames);
+
   // Return the end of the image which includes non-heap objects such as ArtMethods and ArtFields.
   uint8_t* GetImageEnd() const {
     return Begin() + GetImageHeader().GetImageSize();
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index cb80cc9..8543ff4 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -846,7 +846,12 @@
 
 std::string OatFileAssistant::ImageLocation() {
   Runtime* runtime = Runtime::Current();
-  return runtime->GetHeap()->GetBootImageSpaces()[0]->GetImageLocation();
+  const std::vector<gc::space::ImageSpace*>& image_spaces =
+      runtime->GetHeap()->GetBootImageSpaces();
+  if (image_spaces.empty()) {
+    return "";
+  }
+  return image_spaces[0]->GetImageLocation();
 }
 
 const uint32_t* OatFileAssistant::GetRequiredDexChecksum() {
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 98aa8d8..5c72629 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -827,13 +827,9 @@
       const OatHeader& boot_oat_header = oat_file->GetOatHeader();
       const char* boot_cp = boot_oat_header.GetStoreValueByKey(OatHeader::kBootClassPath);
       if (boot_cp != nullptr) {
-        std::vector<std::string> cp;
-        Split(boot_cp, ':', &cp);
-
-        if (cp.size() > 1) {
-          // More images, enqueue (skipping the first).
-          image_locations.insert(image_locations.end(), cp.begin() + 1, cp.end());
-        }
+        gc::space::ImageSpace::CreateMultiImageLocations(image_locations[0],
+                                                         boot_cp,
+                                                         &image_locations);
       }
     }