OSDN Git Service

Add and use loaded class profiling
authorMathieu Chartier <mathieuc@google.com>
Thu, 10 Dec 2015 00:38:30 +0000 (16:38 -0800)
committerMathieu Chartier <mathieuc@google.com>
Fri, 26 Feb 2016 21:07:39 +0000 (13:07 -0800)
Class profiling is a way to keep track of which classes are resolved.
From here the compiler can use this information to generate a smaller
app image.

TODO: Add tests for profile stuff.

Bug: 22858531

(cherry picked from commit 8913fc1a27df8cf3b37fd99e94d87f290591328e)

Change-Id: Ifcd09230cbdc266305bc1247e0d31e7920eb353e

13 files changed:
compiler/driver/compiler_driver.cc
compiler/driver/compiler_driver_test.cc
dex2oat/dex2oat.cc
profman/profile_assistant_test.cc
runtime/class_linker.cc
runtime/class_linker.h
runtime/dex_cache_resolved_classes.h [new file with mode: 0644]
runtime/gc/space/image_space.cc
runtime/jit/offline_profiling_info.cc
runtime/jit/offline_profiling_info.h
runtime/jit/profile_compilation_info_test.cc
runtime/jit/profile_saver.cc
runtime/jit/profile_saver.h

index a9fec30..3100b6d 100644 (file)
@@ -384,7 +384,9 @@ CompilerDriver::CompilerDriver(
 
   compiler_->Init();
 
-  CHECK_EQ(boot_image_, image_classes_.get() != nullptr);
+  if (boot_image_) {
+    CHECK(image_classes_.get() != nullptr) << "Expected image classes for boot image";
+  }
 }
 
 CompilerDriver::~CompilerDriver() {
@@ -868,12 +870,13 @@ void CompilerDriver::PreCompile(jobject class_loader,
 }
 
 bool CompilerDriver::IsImageClass(const char* descriptor) const {
-  if (!IsBootImage()) {
-    // NOTE: Currently only reachable from InitImageMethodVisitor for the app image case.
-    return true;
-  } else {
+  if (image_classes_ != nullptr) {
+    // If we have a set of image classes, use those.
     return image_classes_->find(descriptor) != image_classes_->end();
   }
+  // No set of image classes, assume we include all the classes.
+  // NOTE: Currently only reachable from InitImageMethodVisitor for the app image case.
+  return !IsBootImage();
 }
 
 bool CompilerDriver::IsClassToCompile(const char* descriptor) const {
index 4785885..0037564 100644 (file)
@@ -250,8 +250,8 @@ class CompilerDriverProfileTest : public CompilerDriverTest {
     ProfileCompilationInfo info;
     for (const std::unique_ptr<const DexFile>& dex_file : dex_files) {
       std::string key = ProfileCompilationInfo::GetProfileDexFileKey(dex_file->GetLocation());
-      profile_info_.AddData(key, dex_file->GetLocationChecksum(), 1);
-      profile_info_.AddData(key, dex_file->GetLocationChecksum(), 2);
+      profile_info_.AddMethodIndex(key, dex_file->GetLocationChecksum(), 1);
+      profile_info_.AddMethodIndex(key, dex_file->GetLocationChecksum(), 2);
     }
     return &profile_info_;
   }
index dfcb4bc..d9a2f30 100644 (file)
@@ -1263,6 +1263,24 @@ class Dex2Oat FINAL {
     dex_caches_.clear();
   }
 
+  void LoadClassProfileDescriptors() {
+    if (profile_compilation_info_ != nullptr && app_image_) {
+      Runtime* runtime = Runtime::Current();
+      CHECK(runtime != nullptr);
+      std::set<DexCacheResolvedClasses> resolved_classes(
+          profile_compilation_info_->GetResolvedClasses());
+      image_classes_.reset(new std::unordered_set<std::string>(
+          runtime->GetClassLinker()->GetClassDescriptorsForProfileKeys(resolved_classes)));
+      VLOG(compiler) << "Loaded " << image_classes_->size()
+                     << " image class descriptors from profile";
+      if (VLOG_IS_ON(compiler)) {
+        for (const std::string& s : *image_classes_) {
+          LOG(INFO) << "Image class " << s;
+        }
+      }
+    }
+  }
+
   // Set up the environment for compilation. Includes starting the runtime and loading/opening the
   // boot class path.
   bool Setup() {
@@ -1610,7 +1628,10 @@ class Dex2Oat FINAL {
         // The non moving space is right after the oat file. Put the preferred app image location
         // right after the non moving space so that we ideally get a continuous immune region for
         // the GC.
-        const size_t non_moving_space_capacity = heap->GetNonMovingSpace()->Capacity();
+        // Use the default non moving space capacity since dex2oat does not have a separate non-
+        // moving space. This means the runtime's non moving space space size will be as large
+        // as the growth limit for dex2oat, but smaller in the zygote.
+        const size_t non_moving_space_capacity = gc::Heap::kDefaultNonMovingSpaceCapacity;
         image_base_ += non_moving_space_capacity;
         VLOG(compiler) << "App image base=" << reinterpret_cast<void*>(image_base_);
       }
@@ -2475,6 +2496,7 @@ static void b13564922() {
 }
 
 static int CompileImage(Dex2Oat& dex2oat) {
+  dex2oat.LoadClassProfileDescriptors();
   dex2oat.Compile();
 
   if (!dex2oat.WriteOatFiles()) {
index 3faa8eb..b0d5df2 100644 (file)
@@ -37,8 +37,8 @@ class ProfileAssistantTest : public CommonRuntimeTest {
     std::string dex_location2 = "location2" + id;
     uint32_t dex_location_checksum2 = 10 * checksum;
     for (uint16_t i = start_method_index; i < start_method_index + number_of_methods; i++) {
-      ASSERT_TRUE(info->AddData(dex_location1, dex_location_checksum1, i));
-      ASSERT_TRUE(info->AddData(dex_location2, dex_location_checksum2, i));
+      ASSERT_TRUE(info->AddMethodIndex(dex_location1, dex_location_checksum1, i));
+      ASSERT_TRUE(info->AddMethodIndex(dex_location2, dex_location_checksum2, i));
     }
     ASSERT_TRUE(info->Save(GetFd(profile)));
     ASSERT_EQ(0, profile.GetFile()->Flush());
index cd4daeb..b5e6532 100644 (file)
@@ -58,6 +58,7 @@
 #include "interpreter/interpreter.h"
 #include "jit/jit.h"
 #include "jit/jit_code_cache.h"
+#include "jit/offline_profiling_info.h"
 #include "leb128.h"
 #include "linear_alloc.h"
 #include "mirror/class.h"
@@ -1615,7 +1616,8 @@ bool ClassLinker::AddImageSpace(
           VLOG(image) << name->ToModifiedUtf8();
         }
         *error_msg = "Rejecting application image due to class loader mismatch";
-        return false;
+        // Ignore class loader mismatch for now since these would just use possibly incorrect
+        // oat code anyways. The structural class check should be done in the parent.
       }
     }
   }
@@ -7628,6 +7630,116 @@ void ClassLinker::CleanupClassLoaders() {
   }
 }
 
+std::set<DexCacheResolvedClasses> ClassLinker::GetResolvedClasses(bool ignore_boot_classes) {
+  ScopedObjectAccess soa(Thread::Current());
+  ScopedAssertNoThreadSuspension ants(soa.Self(), __FUNCTION__);
+  std::set<DexCacheResolvedClasses> ret;
+  VLOG(class_linker) << "Collecting resolved classes";
+  const uint64_t start_time = NanoTime();
+  ReaderMutexLock mu(soa.Self(), *DexLock());
+  // Loop through all the dex caches and inspect resolved classes.
+  for (const ClassLinker::DexCacheData& data : GetDexCachesData()) {
+    if (soa.Self()->IsJWeakCleared(data.weak_root)) {
+      continue;
+    }
+    mirror::DexCache* dex_cache =
+        down_cast<mirror::DexCache*>(soa.Self()->DecodeJObject(data.weak_root));
+    if (dex_cache == nullptr) {
+      continue;
+    }
+    const DexFile* dex_file = dex_cache->GetDexFile();
+    const std::string& location = dex_file->GetLocation();
+    const size_t num_class_defs = dex_file->NumClassDefs();
+    // Use the resolved types, this will miss array classes.
+    const size_t num_types = dex_file->NumTypeIds();
+    VLOG(class_linker) << "Collecting class profile for dex file " << location
+                       << " types=" << num_types << " class_defs=" << num_class_defs;
+    DexCacheResolvedClasses resolved_classes(dex_file->GetLocation(),
+                                             dex_file->GetLocationChecksum());
+    size_t num_resolved = 0;
+    std::unordered_set<uint16_t> class_set;
+    CHECK_EQ(num_types, dex_cache->NumResolvedTypes());
+    for (size_t i = 0; i < num_types; ++i) {
+      mirror::Class* klass = dex_cache->GetResolvedType(i);
+      // Filter out null class loader since that is the boot class loader.
+      if (klass == nullptr || (ignore_boot_classes && klass->GetClassLoader() == nullptr)) {
+        continue;
+      }
+      ++num_resolved;
+      DCHECK(!klass->IsProxyClass());
+      DCHECK(klass->IsResolved());
+      mirror::DexCache* klass_dex_cache = klass->GetDexCache();
+      if (klass_dex_cache == dex_cache) {
+        const size_t class_def_idx = klass->GetDexClassDefIndex();
+        DCHECK(klass->IsResolved());
+        CHECK_LT(class_def_idx, num_class_defs);
+        class_set.insert(class_def_idx);
+      }
+    }
+
+    if (!class_set.empty()) {
+      auto it = ret.find(resolved_classes);
+      if (it != ret.end()) {
+        // Already have the key, union the class def idxs.
+        it->AddClasses(class_set.begin(), class_set.end());
+      } else {
+        resolved_classes.AddClasses(class_set.begin(), class_set.end());
+        ret.insert(resolved_classes);
+      }
+    }
+
+    VLOG(class_linker) << "Dex location " << location << " has " << num_resolved << " / "
+                       << num_class_defs << " resolved classes";
+  }
+  VLOG(class_linker) << "Collecting class profile took " << PrettyDuration(NanoTime() - start_time);
+  return ret;
+}
+
+std::unordered_set<std::string> ClassLinker::GetClassDescriptorsForProfileKeys(
+    const std::set<DexCacheResolvedClasses>& classes) {
+  std::unordered_set<std::string> ret;
+  Thread* const self = Thread::Current();
+  std::unordered_map<std::string, const DexFile*> location_to_dex_file;
+  ScopedObjectAccess soa(self);
+  ScopedAssertNoThreadSuspension ants(soa.Self(), __FUNCTION__);
+  ReaderMutexLock mu(self, *DexLock());
+  for (const ClassLinker::DexCacheData& data : GetDexCachesData()) {
+    if (!self->IsJWeakCleared(data.weak_root)) {
+      mirror::DexCache* dex_cache =
+          down_cast<mirror::DexCache*>(soa.Self()->DecodeJObject(data.weak_root));
+      if (dex_cache != nullptr) {
+        const DexFile* dex_file = dex_cache->GetDexFile();
+        // There could be duplicates if two dex files with the same location are mapped.
+        location_to_dex_file.emplace(
+            ProfileCompilationInfo::GetProfileDexFileKey(dex_file->GetLocation()), dex_file);
+      }
+    }
+  }
+  for (const DexCacheResolvedClasses& info : classes) {
+    const std::string& profile_key = info.GetDexLocation();
+    auto found = location_to_dex_file.find(profile_key);
+    if (found != location_to_dex_file.end()) {
+      const DexFile* dex_file = found->second;
+      VLOG(profiler) << "Found opened dex file for " << dex_file->GetLocation() << " with "
+                     << info.GetClasses().size() << " classes";
+      DCHECK_EQ(dex_file->GetLocationChecksum(), info.GetLocationChecksum());
+      for (uint16_t class_def_idx : info.GetClasses()) {
+        if (class_def_idx >= dex_file->NumClassDefs()) {
+          LOG(WARNING) << "Class def index " << class_def_idx << " >= " << dex_file->NumClassDefs();
+          continue;
+        }
+        const DexFile::TypeId& type_id = dex_file->GetTypeId(
+            dex_file->GetClassDef(class_def_idx).class_idx_);
+        const char* descriptor = dex_file->GetTypeDescriptor(type_id);
+        ret.insert(descriptor);
+      }
+    } else {
+      VLOG(class_linker) << "Failed to find opened dex file for profile key " << profile_key;
+    }
+  }
+  return ret;
+}
+
 // Instantiate ResolveMethod.
 template ArtMethod* ClassLinker::ResolveMethod<ClassLinker::kForceICCECheck>(
     const DexFile& dex_file,
index aa55dac..729617d 100644 (file)
 #ifndef ART_RUNTIME_CLASS_LINKER_H_
 #define ART_RUNTIME_CLASS_LINKER_H_
 
+#include <set>
 #include <string>
 #include <unordered_map>
+#include <unordered_set>
 #include <utility>
 #include <vector>
 
@@ -27,6 +29,7 @@
 #include "base/macros.h"
 #include "base/mutex.h"
 #include "class_table.h"
+#include "dex_cache_resolved_classes.h"
 #include "dex_file.h"
 #include "gc_root.h"
 #include "jni.h"
@@ -589,6 +592,13 @@ class ClassLinker {
   static bool ShouldUseInterpreterEntrypoint(ArtMethod* method, const void* quick_code)
       SHARED_REQUIRES(Locks::mutator_lock_);
 
+  std::set<DexCacheResolvedClasses> GetResolvedClasses(bool ignore_boot_classes)
+      REQUIRES(!dex_lock_);
+
+  std::unordered_set<std::string> GetClassDescriptorsForProfileKeys(
+      const std::set<DexCacheResolvedClasses>& classes)
+      REQUIRES(!dex_lock_);
+
   struct DexCacheData {
     // Weak root to the DexCache. Note: Do not decode this unnecessarily or else class unloading may
     // not work properly.
diff --git a/runtime/dex_cache_resolved_classes.h b/runtime/dex_cache_resolved_classes.h
new file mode 100644 (file)
index 0000000..80c12cb
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_DEX_CACHE_RESOLVED_CLASSES_H_
+#define ART_RUNTIME_DEX_CACHE_RESOLVED_CLASSES_H_
+
+#include <string>
+#include <unordered_set>
+#include <vector>
+
+namespace art {
+
+// Data structure for passing around which classes belonging to a dex cache / dex file are resolved.
+class DexCacheResolvedClasses {
+ public:
+  DexCacheResolvedClasses(const std::string& dex_location, uint32_t location_checksum)
+      : dex_location_(dex_location),
+        location_checksum_(location_checksum) {}
+
+  // Only compare the key elements, ignore the resolved classes.
+  int Compare(const DexCacheResolvedClasses& other) const {
+    if (location_checksum_ != other.location_checksum_) {
+      return static_cast<int>(location_checksum_ - other.location_checksum_);
+    }
+    return dex_location_.compare(other.dex_location_);
+  }
+
+  template <class InputIt>
+  void AddClasses(InputIt begin, InputIt end) const {
+    classes_.insert(begin, end);
+  }
+
+  const std::string& GetDexLocation() const {
+    return dex_location_;
+  }
+
+  uint32_t GetLocationChecksum() const {
+    return location_checksum_;
+  }
+
+  const std::unordered_set<uint16_t>& GetClasses() const {
+    return classes_;
+  }
+
+ private:
+  const std::string dex_location_;
+  const uint32_t location_checksum_;
+  // Array of resolved class def indexes.
+  mutable std::unordered_set<uint16_t> classes_;
+};
+
+inline bool operator<(const DexCacheResolvedClasses& a, const DexCacheResolvedClasses& b) {
+  return a.Compare(b) < 0;
+}
+
+}  // namespace art
+
+#endif  // ART_RUNTIME_DEX_CACHE_RESOLVED_CLASSES_H_
index 4ef36a4..5aaf104 100644 (file)
@@ -1278,7 +1278,7 @@ ImageSpace* ImageSpace::Init(const char* image_filename,
                                      PROT_READ | PROT_WRITE,
                                      /*low_4gb*/true,
                                      /*reuse*/false,
-                                     out_error_msg));
+                                     /*out*/out_error_msg));
       if (map != nullptr) {
         const size_t stored_size = image_header->GetDataSize();
         const size_t write_offset = sizeof(ImageHeader);  // Skip the header.
index 747b112..67c9b5f 100644 (file)
@@ -48,9 +48,11 @@ std::string ProfileCompilationInfo::GetProfileDexFileKey(const std::string& dex_
   }
 }
 
-bool ProfileCompilationInfo::SaveProfilingInfo(const std::string& filename,
-                                               const std::vector<ArtMethod*>& methods) {
-  if (methods.empty()) {
+bool ProfileCompilationInfo::SaveProfilingInfo(
+    const std::string& filename,
+    const std::vector<ArtMethod*>& methods,
+    const std::set<DexCacheResolvedClasses>& resolved_classes) {
+  if (methods.empty() && resolved_classes.empty()) {
     VLOG(profiler) << "No info to save to " << filename;
     return true;
   }
@@ -71,14 +73,17 @@ bool ProfileCompilationInfo::SaveProfilingInfo(const std::string& filename,
   }
   {
     ScopedObjectAccess soa(Thread::Current());
-    for (auto it = methods.begin(); it != methods.end(); it++) {
-      const DexFile* dex_file = (*it)->GetDexFile();
-      if (!info.AddData(GetProfileDexFileKey(dex_file->GetLocation()),
-                        dex_file->GetLocationChecksum(),
-                        (*it)->GetDexMethodIndex())) {
+    for (ArtMethod* method : methods) {
+      const DexFile* dex_file = method->GetDexFile();
+      if (!info.AddMethodIndex(GetProfileDexFileKey(dex_file->GetLocation()),
+                               dex_file->GetLocationChecksum(),
+                               method->GetDexMethodIndex())) {
         return false;
       }
     }
+    for (const DexCacheResolvedClasses& dex_cache : resolved_classes) {
+      info.AddResolvedClasses(dex_cache);
+    }
   }
 
   if (!flock.GetFile()->ClearContent()) {
@@ -116,13 +121,14 @@ static bool WriteToFile(int fd, const std::ostringstream& os) {
 
 static constexpr const char kFieldSeparator = ',';
 static constexpr const char kLineSeparator = '\n';
+static constexpr const char* kClassesMarker = "classes";
 
 /**
  * Serialization format:
- *    dex_location1,dex_location_checksum1,method_id11,method_id12...
- *    dex_location2,dex_location_checksum2,method_id21,method_id22...
+ *    dex_location1,dex_location_checksum1,method_id11,method_id12...,classes,class_id1,class_id2...
+ *    dex_location2,dex_location_checksum2,method_id21,method_id22...,classes,class_id1,class_id2...
  * e.g.
- *    app.apk,131232145,11,23,454,54
+ *    app.apk,131232145,11,23,454,54,classes,1,2,4,1234
  *    app.apk:classes5.dex,218490184,39,13,49,1
  **/
 bool ProfileCompilationInfo::Save(int fd) {
@@ -133,11 +139,20 @@ bool ProfileCompilationInfo::Save(int fd) {
   for (const auto& it : info_) {
     const std::string& dex_location = it.first;
     const DexFileData& dex_data = it.second;
+    if (dex_data.method_set.empty() && dex_data.class_set.empty()) {
+      continue;
+    }
 
     os << dex_location << kFieldSeparator << dex_data.checksum;
     for (auto method_it : dex_data.method_set) {
       os << kFieldSeparator << method_it;
     }
+    if (!dex_data.class_set.empty()) {
+      os << kFieldSeparator << kClassesMarker;
+      for (auto class_id : dex_data.class_set) {
+        os << kFieldSeparator << class_id;
+      }
+    }
     os << kLineSeparator;
   }
 
@@ -168,18 +183,50 @@ static void SplitString(const std::string& s, char separator, std::vector<std::s
   }
 }
 
-bool ProfileCompilationInfo::AddData(const std::string& dex_location,
-                                     uint32_t checksum,
-                                     uint16_t method_idx) {
+ProfileCompilationInfo::DexFileData* ProfileCompilationInfo::GetOrAddDexFileData(
+    const std::string& dex_location,
+    uint32_t checksum) {
   auto info_it = info_.find(dex_location);
   if (info_it == info_.end()) {
     info_it = info_.Put(dex_location, DexFileData(checksum));
   }
   if (info_it->second.checksum != checksum) {
     LOG(WARNING) << "Checksum mismatch for dex " << dex_location;
+    return nullptr;
+  }
+  return &info_it->second;
+}
+
+bool ProfileCompilationInfo::AddResolvedClasses(const DexCacheResolvedClasses& classes) {
+  const std::string dex_location = GetProfileDexFileKey(classes.GetDexLocation());
+  const uint32_t checksum = classes.GetLocationChecksum();
+  DexFileData* const data = GetOrAddDexFileData(dex_location, checksum);
+  if (data == nullptr) {
     return false;
   }
-  info_it->second.method_set.insert(method_idx);
+  data->class_set.insert(classes.GetClasses().begin(), classes.GetClasses().end());
+  return true;
+}
+
+bool ProfileCompilationInfo::AddMethodIndex(const std::string& dex_location,
+                                            uint32_t checksum,
+                                            uint16_t method_idx) {
+  DexFileData* const data = GetOrAddDexFileData(dex_location, checksum);
+  if (data == nullptr) {
+    return false;
+  }
+  data->method_set.insert(method_idx);
+  return true;
+}
+
+bool ProfileCompilationInfo::AddClassIndex(const std::string& dex_location,
+                                           uint32_t checksum,
+                                           uint16_t class_idx) {
+  DexFileData* const data = GetOrAddDexFileData(dex_location, checksum);
+  if (data == nullptr) {
+    return false;
+  }
+  data->class_set.insert(class_idx);
   return true;
 }
 
@@ -198,12 +245,30 @@ bool ProfileCompilationInfo::ProcessLine(const std::string& line) {
   }
 
   for (size_t i = 2; i < parts.size(); i++) {
+    if (parts[i] == kClassesMarker) {
+      ++i;
+      // All of the remaining idx are class def indexes.
+      for (++i; i < parts.size(); ++i) {
+        uint32_t class_def_idx;
+        if (!ParseInt(parts[i].c_str(), &class_def_idx)) {
+          LOG(WARNING) << "Cannot parse class_def_idx " << parts[i];
+          return false;
+        } else if (class_def_idx >= std::numeric_limits<uint16_t>::max()) {
+          LOG(WARNING) << "Class def idx " << class_def_idx << " is larger than uint16_t max";
+          return false;
+        }
+        if (!AddClassIndex(dex_location, checksum, class_def_idx)) {
+          return false;
+        }
+      }
+      break;
+    }
     uint32_t method_idx;
     if (!ParseInt(parts[i].c_str(), &method_idx)) {
       LOG(WARNING) << "Cannot parse method_idx " << parts[i];
       return false;
     }
-    if (!AddData(dex_location, checksum, method_idx)) {
+    if (!AddMethodIndex(dex_location, checksum, method_idx)) {
       return false;
     }
   }
@@ -280,6 +345,8 @@ bool ProfileCompilationInfo::Load(const ProfileCompilationInfo& other) {
     }
     info_it->second.method_set.insert(other_dex_data.method_set.begin(),
                                       other_dex_data.method_set.end());
+    info_it->second.class_set.insert(other_dex_data.class_set.begin(),
+                                     other_dex_data.class_set.end());
   }
   return true;
 }
@@ -347,4 +414,16 @@ bool ProfileCompilationInfo::Equals(const ProfileCompilationInfo& other) {
   return info_.Equals(other.info_);
 }
 
+std::set<DexCacheResolvedClasses> ProfileCompilationInfo::GetResolvedClasses() const {
+  std::set<DexCacheResolvedClasses> ret;
+  for (auto&& pair : info_) {
+    const std::string& profile_key = pair.first;
+    const DexFileData& data = pair.second;
+    DexCacheResolvedClasses classes(profile_key, data.checksum);
+    classes.AddClasses(data.class_set.begin(), data.class_set.end());
+    ret.insert(classes);
+  }
+  return ret;
+}
+
 }  // namespace art
index edc591c..ee7ce27 100644 (file)
@@ -21,6 +21,7 @@
 #include <vector>
 
 #include "atomic.h"
+#include "dex_cache_resolved_classes.h"
 #include "dex_file.h"
 #include "method_reference.h"
 #include "safe_map.h"
@@ -28,6 +29,7 @@
 namespace art {
 
 class ArtMethod;
+class DexCacheProfileData;
 
 // TODO: rename file.
 /**
@@ -43,7 +45,8 @@ class ProfileCompilationInfo {
   // Note that the saving proceeds only if the file can be locked for exclusive access.
   // If not (the locking is not blocking), the function does not save and returns false.
   static bool SaveProfilingInfo(const std::string& filename,
-                                const std::vector<ArtMethod*>& methods);
+                                const std::vector<ArtMethod*>& methods,
+                                const std::set<DexCacheResolvedClasses>& resolved_classes);
 
   // Loads profile information from the given file descriptor.
   bool Load(int fd);
@@ -68,14 +71,17 @@ class ProfileCompilationInfo {
   bool Equals(const ProfileCompilationInfo& other);
   static std::string GetProfileDexFileKey(const std::string& dex_location);
 
- private:
-  bool AddData(const std::string& dex_location, uint32_t checksum, uint16_t method_idx);
-  bool ProcessLine(const std::string& line);
+  // Returns the class descriptors for all of the classes in the profiles' class sets.
+  // Note the dex location is actually the profile key, the caller needs to call back in to the
+  // profile info stuff to generate a map back to the dex location.
+  std::set<DexCacheResolvedClasses> GetResolvedClasses() const;
 
+ private:
   struct DexFileData {
     explicit DexFileData(uint32_t location_checksum) : checksum(location_checksum) {}
     uint32_t checksum;
     std::set<uint16_t> method_set;
+    std::set<uint16_t> class_set;
 
     bool operator==(const DexFileData& other) const {
       return checksum == other.checksum && method_set == other.method_set;
@@ -84,6 +90,13 @@ class ProfileCompilationInfo {
 
   using DexFileToProfileInfoMap = SafeMap<const std::string, DexFileData>;
 
+  DexFileData* GetOrAddDexFileData(const std::string& dex_location, uint32_t checksum);
+  bool AddMethodIndex(const std::string& dex_location, uint32_t checksum, uint16_t method_idx);
+  bool AddClassIndex(const std::string& dex_location, uint32_t checksum, uint16_t class_idx);
+  bool AddResolvedClasses(const DexCacheResolvedClasses& classes)
+      SHARED_REQUIRES(Locks::mutator_lock_);
+  bool ProcessLine(const std::string& line);
+
   friend class ProfileCompilationInfoTest;
   friend class CompilerDriverProfileTest;
   friend class ProfileAssistantTest;
index 482ea06..fdd8c6e 100644 (file)
@@ -53,7 +53,7 @@ class ProfileCompilationInfoTest : public CommonRuntimeTest {
                uint32_t checksum,
                uint16_t method_index,
                ProfileCompilationInfo* info) {
-    return info->AddData(dex_location, checksum, method_index);
+    return info->AddMethodIndex(dex_location, checksum, method_index);
   }
 
   uint32_t GetFd(const ScratchFile& file) {
@@ -73,8 +73,11 @@ TEST_F(ProfileCompilationInfoTest, SaveArtMethods) {
   ASSERT_NE(class_loader, nullptr);
 
   // Save virtual methods from Main.
+  std::set<DexCacheResolvedClasses> resolved_classes;
   std::vector<ArtMethod*> main_methods = GetVirtualMethods(class_loader, "LMain;");
-  ASSERT_TRUE(ProfileCompilationInfo::SaveProfilingInfo(profile.GetFilename(), main_methods));
+  ASSERT_TRUE(ProfileCompilationInfo::SaveProfilingInfo(profile.GetFilename(),
+                                                        main_methods,
+                                                        resolved_classes));
 
   // Check that what we saved is in the profile.
   ProfileCompilationInfo info1;
@@ -89,7 +92,9 @@ TEST_F(ProfileCompilationInfoTest, SaveArtMethods) {
 
   // Save virtual methods from Second.
   std::vector<ArtMethod*> second_methods = GetVirtualMethods(class_loader, "LSecond;");
-  ASSERT_TRUE(ProfileCompilationInfo::SaveProfilingInfo(profile.GetFilename(), second_methods));
+  ASSERT_TRUE(ProfileCompilationInfo::SaveProfilingInfo(profile.GetFilename(),
+                                                        second_methods,
+                                                        resolved_classes));
 
   // Check that what we saved is in the profile (methods form Main and Second).
   ProfileCompilationInfo info2;
index b1a5a4b..ab26f6f 100644 (file)
@@ -32,6 +32,7 @@ static constexpr const uint64_t kMinimumTimeBetweenCodeCacheUpdatesNs = 2000 * k
 static constexpr const uint64_t kRandomDelayMaxMs = 20 * 1000;  // 20 seconds
 static constexpr const uint64_t kMaxBackoffMs = 5 * 60 * 1000;  // 5 minutes
 static constexpr const uint64_t kSavePeriodMs = 10 * 1000;  // 10 seconds
+static constexpr const uint64_t kInitialDelayMs = 2 * 1000;  // 2 seconds
 static constexpr const double kBackoffCoef = 1.5;
 
 static constexpr const uint32_t kMinimumNrOrMethodsToSave = 10;
@@ -45,6 +46,7 @@ ProfileSaver::ProfileSaver(const std::string& output_filename,
     : jit_code_cache_(jit_code_cache),
       code_cache_last_update_time_ns_(0),
       shutting_down_(false),
+      first_profile_(true),
       wait_lock_("ProfileSaver wait lock"),
       period_condition_("ProfileSaver period condition", wait_lock_) {
   AddTrackedLocations(output_filename, code_paths);
@@ -56,13 +58,18 @@ void ProfileSaver::Run() {
 
   uint64_t save_period_ms = kSavePeriodMs;
   VLOG(profiler) << "Save profiling information every " << save_period_ms << " ms";
-  while (true) {
-    if (ShuttingDown(self)) {
-      break;
-    }
 
-    uint64_t random_sleep_delay_ms = rand() % kRandomDelayMaxMs;
-    uint64_t sleep_time_ms = save_period_ms + random_sleep_delay_ms;
+  bool first_iteration = true;
+  while (!ShuttingDown(self)) {
+    uint64_t sleep_time_ms;
+    if (first_iteration) {
+      // Sleep less long for the first iteration since we want to record loaded classes shortly
+      // after app launch.
+      sleep_time_ms = kInitialDelayMs;
+    } else {
+      const uint64_t random_sleep_delay_ms = rand() % kRandomDelayMaxMs;
+      sleep_time_ms = save_period_ms + random_sleep_delay_ms;
+    }
     {
       MutexLock mu(self, wait_lock_);
       period_condition_.TimedWait(self, sleep_time_ms, 0);
@@ -81,13 +88,14 @@ void ProfileSaver::Run() {
       // Reset the period to the initial value as it's highly likely to JIT again.
       save_period_ms = kSavePeriodMs;
     }
+    first_iteration = false;
   }
 }
 
 bool ProfileSaver::ProcessProfilingInfo() {
   uint64_t last_update_time_ns = jit_code_cache_->GetLastUpdateTimeNs();
-  if (last_update_time_ns - code_cache_last_update_time_ns_
-      < kMinimumTimeBetweenCodeCacheUpdatesNs) {
+  if (!first_profile_ && last_update_time_ns - code_cache_last_update_time_ns_
+          < kMinimumTimeBetweenCodeCacheUpdatesNs) {
     VLOG(profiler) << "Not enough time has passed since the last code cache update."
         << "Last update: " << last_update_time_ns
         << " Last save: " << code_cache_last_update_time_ns_;
@@ -113,19 +121,27 @@ bool ProfileSaver::ProcessProfilingInfo() {
       ScopedObjectAccess soa(Thread::Current());
       jit_code_cache_->GetCompiledArtMethods(locations, methods);
     }
-    if (methods.size() < kMinimumNrOrMethodsToSave) {
+    // Always save for the first one for loaded classes profile.
+    if (methods.size() < kMinimumNrOrMethodsToSave && !first_profile_) {
       VLOG(profiler) << "Not enough information to save to: " << filename
           <<" Nr of methods: " << methods.size();
       return false;
     }
 
-    if (!ProfileCompilationInfo::SaveProfilingInfo(filename, methods)) {
+    std::set<DexCacheResolvedClasses> resolved_classes;
+    if (first_profile_) {
+      ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
+      resolved_classes = class_linker->GetResolvedClasses(/*ignore boot classes*/true);
+    }
+
+    if (!ProfileCompilationInfo::SaveProfilingInfo(filename, methods, resolved_classes)) {
       LOG(WARNING) << "Could not save profiling info to " << filename;
       return false;
     }
 
     VLOG(profiler) << "Profile process time: " << PrettyDuration(NanoTime() - start);
   }
+  first_profile_ = false;
   return true;
 }
 
index 3342790..21017c1 100644 (file)
@@ -74,6 +74,7 @@ class ProfileSaver {
       GUARDED_BY(Locks::profiler_lock_);
   uint64_t code_cache_last_update_time_ns_;
   bool shutting_down_ GUARDED_BY(Locks::profiler_lock_);
+  bool first_profile_ = true;
 
   // Save period condition support.
   Mutex wait_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;