Reduce string allocations in ProfileCompilationInfo.

Use std::string_view internally to avoid std::string
allocations but keep external ProfileCompilationInfo API
unchanged with std::string to avoid object lifetime issues,
with the exception of `OfflineProfileMethodInfo` provided
to the compiler for AOT compilation.

Test: m test-art-host-gtest
Test: testrunner.py --host --optimizing
Test: boots.
Bug: 181943478
Change-Id: If75c3ce16581760773bdaa3e94334c9c567745de
diff --git a/libprofile/profile/profile_compilation_info.cc b/libprofile/profile/profile_compilation_info.cc
index ddebd6d..4b8fb9d 100644
--- a/libprofile/profile/profile_compilation_info.cc
+++ b/libprofile/profile/profile_compilation_info.cc
@@ -171,7 +171,8 @@
 // Note: this is OK because we don't store profiles of different apps into the same file.
 // Apps with split apks don't cause trouble because each split has a different name and will not
 // collide with other entries.
-std::string ProfileCompilationInfo::GetProfileDexFileBaseKey(const std::string& dex_location) {
+std::string_view ProfileCompilationInfo::GetProfileDexFileBaseKeyView(
+    std::string_view dex_location) {
   DCHECK(!dex_location.empty());
   size_t last_sep_index = dex_location.find_last_of('/');
   if (last_sep_index == std::string::npos) {
@@ -182,12 +183,23 @@
   }
 }
 
-std::string ProfileCompilationInfo::GetBaseKeyFromAugmentedKey(
-    const std::string& profile_key) {
+std::string ProfileCompilationInfo::GetProfileDexFileBaseKey(const std::string& dex_location) {
+  // Note: Conversions between std::string and std::string_view.
+  return std::string(GetProfileDexFileBaseKeyView(dex_location));
+}
+
+std::string_view ProfileCompilationInfo::GetBaseKeyViewFromAugmentedKey(
+    std::string_view profile_key) {
   size_t pos = profile_key.rfind(kSampleMetadataSeparator);
   return (pos == std::string::npos) ? profile_key : profile_key.substr(0, pos);
 }
 
+std::string ProfileCompilationInfo::GetBaseKeyFromAugmentedKey(
+    const std::string& profile_key) {
+  // Note: Conversions between std::string and std::string_view.
+  return std::string(GetBaseKeyViewFromAugmentedKey(profile_key));
+}
+
 std::string ProfileCompilationInfo::MigrateAnnotationInfo(
     const std::string& base_key,
     const std::string& augmented_key) {
@@ -681,9 +693,9 @@
       const DexFile* dex_file,
       const ProfileSampleAnnotation& annotation) const {
   if (annotation == ProfileSampleAnnotation::kNone) {
-    std::string profile_key = GetProfileDexFileBaseKey(dex_file->GetLocation());
+    std::string_view profile_key = GetProfileDexFileBaseKeyView(dex_file->GetLocation());
     for (const DexFileData* dex_data : info_) {
-      if (profile_key == GetBaseKeyFromAugmentedKey(dex_data->profile_key)) {
+      if (profile_key == GetBaseKeyViewFromAugmentedKey(dex_data->profile_key)) {
         if (!ChecksumMatch(dex_data->checksum, dex_file->GetLocationChecksum())) {
           return nullptr;
         }
@@ -701,9 +713,9 @@
 void ProfileCompilationInfo::FindAllDexData(
     const DexFile* dex_file,
     /*out*/ std::vector<const ProfileCompilationInfo::DexFileData*>* result) const {
-  std::string profile_key = GetProfileDexFileBaseKey(dex_file->GetLocation());
+  std::string_view profile_key = GetProfileDexFileBaseKeyView(dex_file->GetLocation());
   for (const DexFileData* dex_data : info_) {
-    if (profile_key == GetBaseKeyFromAugmentedKey(dex_data->profile_key)) {
+    if (profile_key == GetBaseKeyViewFromAugmentedKey(dex_data->profile_key)) {
       if (ChecksumMatch(dex_data->checksum, dex_file->GetLocationChecksum())) {
         result->push_back(dex_data);
       }
@@ -1105,13 +1117,13 @@
 }
 
 bool ProfileCompilationInfo::VerifyProfileData(const std::vector<const DexFile*>& dex_files) {
-  std::unordered_map<std::string, const DexFile*> key_to_dex_file;
+  std::unordered_map<std::string_view, const DexFile*> key_to_dex_file;
   for (const DexFile* dex_file : dex_files) {
-    key_to_dex_file.emplace(GetProfileDexFileBaseKey(dex_file->GetLocation()), dex_file);
+    key_to_dex_file.emplace(GetProfileDexFileBaseKeyView(dex_file->GetLocation()), dex_file);
   }
   for (const DexFileData* dex_data : info_) {
     // We need to remove any annotation from the key during verification.
-    const auto it = key_to_dex_file.find(GetBaseKeyFromAugmentedKey(dex_data->profile_key));
+    const auto it = key_to_dex_file.find(GetBaseKeyViewFromAugmentedKey(dex_data->profile_key));
     if (it == key_to_dex_file.end()) {
       // It is okay if profile contains data for additional dex files.
       continue;
@@ -1681,7 +1693,7 @@
     os << " [checksum=" << std::hex << dex_data->checksum << "]" << std::dec;
     const DexFile* dex_file = nullptr;
     for (const DexFile* current : dex_files) {
-      if (GetBaseKeyFromAugmentedKey(dex_data->profile_key) == current->GetLocation() &&
+      if (GetBaseKeyViewFromAugmentedKey(dex_data->profile_key) == current->GetLocation() &&
           dex_data->checksum == current->GetLocationChecksum()) {
         dex_file = current;
       }
diff --git a/libprofile/profile/profile_compilation_info.h b/libprofile/profile/profile_compilation_info.h
index 7a994a6..622bec1 100644
--- a/libprofile/profile/profile_compilation_info.h
+++ b/libprofile/profile/profile_compilation_info.h
@@ -92,10 +92,13 @@
   // (see compiler/optimizing/inliner.cc).
 
   // A profile reference to the dex file (profile key, dex checksum and number of methods).
+  //
+  // This contains references to internal std::string data of a profile key. The profile
+  // key must not be modified or destroyed for the entire lifetime of a `DexReference`.
   struct DexReference {
     DexReference() : dex_checksum(0), num_method_ids(0) {}
 
-    DexReference(const std::string& key, uint32_t checksum, uint32_t num_methods)
+    DexReference(std::string_view key, uint32_t checksum, uint32_t num_methods)
         : profile_key(key), dex_checksum(checksum), num_method_ids(num_methods) {}
 
     bool operator==(const DexReference& other) const {
@@ -106,11 +109,11 @@
 
     bool MatchesDex(const DexFile* dex_file) const {
       return dex_checksum == dex_file->GetLocationChecksum() &&
-             GetBaseKeyFromAugmentedKey(profile_key) ==
-                 GetProfileDexFileBaseKey(dex_file->GetLocation());
+             GetBaseKeyViewFromAugmentedKey(profile_key) ==
+                 GetProfileDexFileBaseKeyView(dex_file->GetLocation());
     }
 
-    std::string profile_key;
+    std::string_view profile_key;
     uint32_t dex_checksum;
     uint32_t num_method_ids;
   };
@@ -286,9 +289,15 @@
   };
 
   // Encodes the full set of inline caches for a given method.
-  // The dex_references vector is indexed according to the ClassReference::dex_profile_index.
+  //
+  // The `dex_references` vector is indexed according to the ClassReference::dex_profile_index.
   // i.e. the dex file of any ClassReference present in the inline caches can be found at
   // dex_references[ClassReference::dex_profile_index].
+  //
+  // The `dex_references` contains references to internal std::string data of profile keys.
+  // Those profile keys must not be modified or destroyed for the entire lifetime of the
+  // `OfflineProfileMethodInfo`. To ensure that, the `ProfileCompilationInfo` should not
+  // be modified or destroyed while an `OfflineProfileMethodInfo` is in use.
   struct OfflineProfileMethodInfo {
     explicit OfflineProfileMethodInfo(const InlineCacheMap* inline_cache_map)
         : inline_caches(inline_cache_map) {}
@@ -935,6 +944,13 @@
   // Returns the threshold size (in bytes) which will cause save/load failures.
   size_t GetSizeErrorThresholdBytes() const;
 
+  // Implementation of `GetProfileDexFileBaseKey()` but returning a subview
+  // referencing the same underlying data to avoid excessive heap allocations.
+  static std::string_view GetProfileDexFileBaseKeyView(std::string_view dex_location);
+
+  // Implementation of `GetBaseKeyFromAugmentedKey()` but returning a subview
+  // referencing the same underlying data to avoid excessive heap allocations.
+  static std::string_view GetBaseKeyViewFromAugmentedKey(std::string_view dex_location);
 
   // Returns the augmented profile key associated with the given dex location.
   // The return key will contain a serialized form of the information from the provided
diff --git a/runtime/jit/profiling_info_test.cc b/runtime/jit/profiling_info_test.cc
index 319a3e1..efaea84 100644
--- a/runtime/jit/profiling_info_test.cc
+++ b/runtime/jit/profiling_info_test.cc
@@ -172,9 +172,14 @@
         dex_pc_data.AddClass(dex_profile_index, class_ref.TypeIndex());
         if (dex_profile_index >= offline_pmi.dex_references.size()) {
           // This is a new dex.
-          const std::string& dex_key = ProfileCompilationInfo::GetProfileDexFileBaseKey(
-              class_ref.dex_file->GetLocation());
-          offline_pmi.dex_references.emplace_back(dex_key,
+          const std::string& location = class_ref.dex_file->GetLocation();
+          std::string dex_key = ProfileCompilationInfo::GetProfileDexFileBaseKey(location);
+          // The `dex_key` is a temporary that shall cease to exist soon. Create a view
+          // using the dex file's location as the backing data.
+          CHECK(EndsWith(location, dex_key));
+          size_t dex_key_start = location.size() - dex_key.size();
+          std::string_view dex_key_view(location.data() + dex_key_start, dex_key.size());
+          offline_pmi.dex_references.emplace_back(dex_key_view,
                                                   class_ref.dex_file->GetLocationChecksum(),
                                                   class_ref.dex_file->NumMethodIds());
         }