Merge "Ensure GetThreadState only counts user-code suspensions"
diff --git a/compiler/dex/verification_results.cc b/compiler/dex/verification_results.cc
index 04ceca0..183c955 100644
--- a/compiler/dex/verification_results.cc
+++ b/compiler/dex/verification_results.cc
@@ -46,6 +46,10 @@
 
 void VerificationResults::ProcessVerifiedMethod(verifier::MethodVerifier* method_verifier) {
   DCHECK(method_verifier != nullptr);
+  if (!compiler_options_->IsAnyCompilationEnabled()) {
+    // Verified methods are only required for quickening and compilation.
+    return;
+  }
   MethodReference ref = method_verifier->GetMethodReference();
   std::unique_ptr<const VerifiedMethod> verified_method(VerifiedMethod::Create(method_verifier));
   if (verified_method == nullptr) {
@@ -98,6 +102,7 @@
 
 const VerifiedMethod* VerificationResults::GetVerifiedMethod(MethodReference ref) {
   const VerifiedMethod* ret = nullptr;
+  DCHECK(compiler_options_->IsAnyCompilationEnabled());
   if (atomic_verified_methods_.Get(ref, &ret)) {
     return ret;
   }
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index bb64755..c04e45d 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -890,17 +890,18 @@
                                 TimingLogger* timings) {
   CheckThreadPools();
 
-  for (const DexFile* dex_file : dex_files) {
-    // Can be already inserted if the caller is CompileOne. This happens for gtests.
-    if (!compiled_methods_.HaveDexFile(dex_file)) {
-      compiled_methods_.AddDexFile(dex_file);
-    }
-  }
-
   LoadImageClasses(timings);
   VLOG(compiler) << "LoadImageClasses: " << GetMemoryUsageString(false);
 
   if (compiler_options_->IsAnyCompilationEnabled()) {
+    // Avoid adding the dex files in the case where we aren't going to add compiled methods.
+    // This reduces RAM usage for this case.
+    for (const DexFile* dex_file : dex_files) {
+      // Can be already inserted if the caller is CompileOne. This happens for gtests.
+      if (!compiled_methods_.HaveDexFile(dex_file)) {
+        compiled_methods_.AddDexFile(dex_file);
+      }
+    }
     // Resolve eagerly to prepare for compilation.
     Resolve(class_loader, dex_files, timings);
     VLOG(compiler) << "Resolve: " << GetMemoryUsageString(false);
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index 7cb3166..7aef785 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -158,26 +158,61 @@
   const void* source_;
 };
 
+// OatClassHeader is the header only part of the oat class that is required even when compilation
+// is not enabled.
+class OatWriter::OatClassHeader {
+ public:
+  OatClassHeader(uint32_t offset,
+                 uint32_t num_non_null_compiled_methods,
+                 uint32_t num_methods,
+                 mirror::Class::Status status)
+      : status_(status),
+        offset_(offset) {
+    // We just arbitrarily say that 0 methods means kOatClassNoneCompiled and that we won't use
+    // kOatClassAllCompiled unless there is at least one compiled method. This means in an
+    // interpreter only system, we can assert that all classes are kOatClassNoneCompiled.
+    if (num_non_null_compiled_methods == 0) {
+      type_ = kOatClassNoneCompiled;
+    } else if (num_non_null_compiled_methods == num_methods) {
+      type_ = kOatClassAllCompiled;
+    } else {
+      type_ = kOatClassSomeCompiled;
+    }
+  }
+
+  bool Write(OatWriter* oat_writer, OutputStream* out, const size_t file_offset) const;
+
+  static size_t SizeOf() {
+    return sizeof(status_) + sizeof(type_);
+  }
+
+  // Data to write.
+  static_assert(mirror::Class::Status::kStatusMax < (1 << 16), "class status won't fit in 16bits");
+  int16_t status_;
+
+  static_assert(OatClassType::kOatClassMax < (1 << 16), "oat_class type won't fit in 16bits");
+  uint16_t type_;
+
+  // Offset of start of OatClass from beginning of OatHeader. It is
+  // used to validate file position when writing.
+  uint32_t offset_;
+};
+
+// The actual oat class body contains the information about compiled methods. It is only required
+// for compiler filters that have any compilation.
 class OatWriter::OatClass {
  public:
-  OatClass(size_t offset,
-           const dchecked_vector<CompiledMethod*>& compiled_methods,
+  OatClass(const dchecked_vector<CompiledMethod*>& compiled_methods,
            uint32_t num_non_null_compiled_methods,
-           mirror::Class::Status status);
+           uint16_t oat_class_type);
   OatClass(OatClass&& src) = default;
-  size_t GetOatMethodOffsetsOffsetFromOatHeader(size_t class_def_method_index_) const;
-  size_t GetOatMethodOffsetsOffsetFromOatClass(size_t class_def_method_index_) const;
   size_t SizeOf() const;
-  bool Write(OatWriter* oat_writer, OutputStream* out, const size_t file_offset) const;
+  bool Write(OatWriter* oat_writer, OutputStream* out) const;
 
   CompiledMethod* GetCompiledMethod(size_t class_def_method_index) const {
     return compiled_methods_[class_def_method_index];
   }
 
-  // Offset of start of OatClass from beginning of OatHeader. It is
-  // used to validate file position when writing.
-  size_t offset_;
-
   // CompiledMethods for each class_def_method_index, or null if no method is available.
   dchecked_vector<CompiledMethod*> compiled_methods_;
 
@@ -188,13 +223,6 @@
   dchecked_vector<uint32_t> oat_method_offsets_offsets_from_oat_class_;
 
   // Data to write.
-
-  static_assert(mirror::Class::Status::kStatusMax < (1 << 16), "class status won't fit in 16bits");
-  int16_t status_;
-
-  static_assert(OatClassType::kOatClassMax < (1 << 16), "oat_class type won't fit in 16bits");
-  uint16_t type_;
-
   uint32_t method_bitmap_size_;
 
   // bit vector indexed by ClassDef method index. When
@@ -482,6 +510,11 @@
   return locations;
 }
 
+bool OatWriter::MayHaveCompiledMethods() const {
+  return CompilerFilter::IsAnyCompilationEnabled(
+      GetCompilerDriver()->GetCompilerOptions().GetCompilerFilter());
+}
+
 bool OatWriter::WriteAndOpenDexFiles(
     File* vdex_file,
     OutputStream* oat_rodata,
@@ -663,7 +696,10 @@
 
   bool StartClass(const DexFile* dex_file, size_t class_def_index) OVERRIDE {
     DexMethodVisitor::StartClass(dex_file, class_def_index);
-    DCHECK_LT(oat_class_index_, writer_->oat_classes_.size());
+    if (kIsDebugBuild && writer_->MayHaveCompiledMethods()) {
+      // There are no oat classes if there aren't any compiled methods.
+      CHECK_LT(oat_class_index_, writer_->oat_classes_.size());
+    }
     method_offsets_index_ = 0u;
     return true;
   }
@@ -726,7 +762,11 @@
     for (const OatDexFile& oat_dex_file : writer_->oat_dex_files_) {
       num_classes += oat_dex_file.class_offsets_.size();
     }
-    writer_->oat_classes_.reserve(num_classes);
+    // If we aren't compiling only reserve headers.
+    writer_->oat_class_headers_.reserve(num_classes);
+    if (writer->MayHaveCompiledMethods()) {
+      writer->oat_classes_.reserve(num_classes);
+    }
     compiled_methods_.reserve(256u);
     // If there are any classes, the class offsets allocation aligns the offset.
     DCHECK(num_classes == 0u || IsAligned<4u>(offset));
@@ -770,11 +810,19 @@
       }
     }
 
-    writer_->oat_classes_.emplace_back(offset_,
-                                       compiled_methods_,
-                                       num_non_null_compiled_methods_,
-                                       status);
-    offset_ += writer_->oat_classes_.back().SizeOf();
+    writer_->oat_class_headers_.emplace_back(offset_,
+                                             num_non_null_compiled_methods_,
+                                             compiled_methods_.size(),
+                                             status);
+    OatClassHeader& header = writer_->oat_class_headers_.back();
+    offset_ += header.SizeOf();
+    if (writer_->MayHaveCompiledMethods()) {
+      writer_->oat_classes_.emplace_back(compiled_methods_,
+                                         num_non_null_compiled_methods_,
+                                         header.type_);
+      offset_ += writer_->oat_classes_.back().SizeOf();
+    }
+
     return DexMethodVisitor::EndClass();
   }
 
@@ -1671,7 +1719,7 @@
       if (UNLIKELY(!visitor->StartClass(dex_file, class_def_index))) {
         return false;
       }
-      if (compiler_driver_->GetCompilerOptions().IsAnyCompilationEnabled()) {
+      if (MayHaveCompiledMethods()) {
         const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index);
         const uint8_t* class_data = dex_file->GetClassData(class_def);
         if (class_data != nullptr) {  // ie not an empty class, such as a marker interface
@@ -1739,21 +1787,21 @@
   offset = visitor.GetOffset();
 
   // Update oat_dex_files_.
-  auto oat_class_it = oat_classes_.begin();
+  auto oat_class_it = oat_class_headers_.begin();
   for (OatDexFile& oat_dex_file : oat_dex_files_) {
     for (uint32_t& class_offset : oat_dex_file.class_offsets_) {
-      DCHECK(oat_class_it != oat_classes_.end());
+      DCHECK(oat_class_it != oat_class_headers_.end());
       class_offset = oat_class_it->offset_;
       ++oat_class_it;
     }
   }
-  CHECK(oat_class_it == oat_classes_.end());
+  CHECK(oat_class_it == oat_class_headers_.end());
 
   return offset;
 }
 
 size_t OatWriter::InitOatMaps(size_t offset) {
-  if (!compiler_driver_->GetCompilerOptions().IsAnyCompilationEnabled()) {
+  if (!MayHaveCompiledMethods()) {
     return offset;
   }
   {
@@ -2291,14 +2339,24 @@
 }
 
 size_t OatWriter::WriteClasses(OutputStream* out, size_t file_offset, size_t relative_offset) {
-  for (OatClass& oat_class : oat_classes_) {
+  const bool may_have_compiled = MayHaveCompiledMethods();
+  if (may_have_compiled) {
+    CHECK_EQ(oat_class_headers_.size(), oat_classes_.size());
+  }
+  for (size_t i = 0; i < oat_class_headers_.size(); ++i) {
     // If there are any classes, the class offsets allocation aligns the offset.
     DCHECK_ALIGNED(relative_offset, 4u);
     DCHECK_OFFSET();
-    if (!oat_class.Write(this, out, oat_data_offset_)) {
+    if (!oat_class_headers_[i].Write(this, out, oat_data_offset_)) {
       return 0u;
     }
-    relative_offset += oat_class.SizeOf();
+    relative_offset += oat_class_headers_[i].SizeOf();
+    if (may_have_compiled) {
+      if (!oat_classes_[i].Write(this, out)) {
+        return 0u;
+      }
+      relative_offset += oat_classes_[i].SizeOf();
+    }
   }
   return relative_offset;
 }
@@ -3181,37 +3239,21 @@
   return true;
 }
 
-OatWriter::OatClass::OatClass(size_t offset,
-                              const dchecked_vector<CompiledMethod*>& compiled_methods,
+OatWriter::OatClass::OatClass(const dchecked_vector<CompiledMethod*>& compiled_methods,
                               uint32_t num_non_null_compiled_methods,
-                              mirror::Class::Status status)
+                              uint16_t oat_class_type)
     : compiled_methods_(compiled_methods) {
-  uint32_t num_methods = compiled_methods.size();
+  const uint32_t num_methods = compiled_methods.size();
   CHECK_LE(num_non_null_compiled_methods, num_methods);
 
-  offset_ = offset;
   oat_method_offsets_offsets_from_oat_class_.resize(num_methods);
 
-  // Since both kOatClassNoneCompiled and kOatClassAllCompiled could
-  // apply when there are 0 methods, we just arbitrarily say that 0
-  // methods means kOatClassNoneCompiled and that we won't use
-  // kOatClassAllCompiled unless there is at least one compiled
-  // method. This means in an interpretter only system, we can assert
-  // that all classes are kOatClassNoneCompiled.
-  if (num_non_null_compiled_methods == 0) {
-    type_ = kOatClassNoneCompiled;
-  } else if (num_non_null_compiled_methods == num_methods) {
-    type_ = kOatClassAllCompiled;
-  } else {
-    type_ = kOatClassSomeCompiled;
-  }
-
-  status_ = status;
   method_offsets_.resize(num_non_null_compiled_methods);
   method_headers_.resize(num_non_null_compiled_methods);
 
-  uint32_t oat_method_offsets_offset_from_oat_class = sizeof(type_) + sizeof(status_);
-  if (type_ == kOatClassSomeCompiled) {
+  uint32_t oat_method_offsets_offset_from_oat_class = OatClassHeader::SizeOf();
+  // We only create this instance if there are at least some compiled.
+  if (oat_class_type == kOatClassSomeCompiled) {
     method_bitmap_.reset(new BitVector(num_methods, false, Allocator::GetMallocAllocator()));
     method_bitmap_size_ = method_bitmap_->GetSizeOf();
     oat_method_offsets_offset_from_oat_class += sizeof(method_bitmap_size_);
@@ -3228,38 +3270,22 @@
     } else {
       oat_method_offsets_offsets_from_oat_class_[i] = oat_method_offsets_offset_from_oat_class;
       oat_method_offsets_offset_from_oat_class += sizeof(OatMethodOffsets);
-      if (type_ == kOatClassSomeCompiled) {
+      if (oat_class_type == kOatClassSomeCompiled) {
         method_bitmap_->SetBit(i);
       }
     }
   }
 }
 
-size_t OatWriter::OatClass::GetOatMethodOffsetsOffsetFromOatHeader(
-    size_t class_def_method_index_) const {
-  uint32_t method_offset = GetOatMethodOffsetsOffsetFromOatClass(class_def_method_index_);
-  if (method_offset == 0) {
-    return 0;
-  }
-  return offset_ + method_offset;
-}
-
-size_t OatWriter::OatClass::GetOatMethodOffsetsOffsetFromOatClass(
-    size_t class_def_method_index_) const {
-  return oat_method_offsets_offsets_from_oat_class_[class_def_method_index_];
-}
-
 size_t OatWriter::OatClass::SizeOf() const {
-  return sizeof(status_)
-          + sizeof(type_)
-          + ((method_bitmap_size_ == 0) ? 0 : sizeof(method_bitmap_size_))
+  return ((method_bitmap_size_ == 0) ? 0 : sizeof(method_bitmap_size_))
           + method_bitmap_size_
           + (sizeof(method_offsets_[0]) * method_offsets_.size());
 }
 
-bool OatWriter::OatClass::Write(OatWriter* oat_writer,
-                                OutputStream* out,
-                                const size_t file_offset) const {
+bool OatWriter::OatClassHeader::Write(OatWriter* oat_writer,
+                                      OutputStream* out,
+                                      const size_t file_offset) const {
   DCHECK_OFFSET_();
   if (!out->WriteFully(&status_, sizeof(status_))) {
     PLOG(ERROR) << "Failed to write class status to " << out->GetLocation();
@@ -3272,9 +3298,11 @@
     return false;
   }
   oat_writer->size_oat_class_type_ += sizeof(type_);
+  return true;
+}
 
+bool OatWriter::OatClass::Write(OatWriter* oat_writer, OutputStream* out) const {
   if (method_bitmap_size_ != 0) {
-    CHECK_EQ(kOatClassSomeCompiled, type_);
     if (!out->WriteFully(&method_bitmap_size_, sizeof(method_bitmap_size_))) {
       PLOG(ERROR) << "Failed to write method bitmap size to " << out->GetLocation();
       return false;
diff --git a/compiler/oat_writer.h b/compiler/oat_writer.h
index 024a3e8..7b3c31c 100644
--- a/compiler/oat_writer.h
+++ b/compiler/oat_writer.h
@@ -239,12 +239,13 @@
     return ArrayRef<const debug::MethodDebugInfo>(method_info_);
   }
 
-  const CompilerDriver* GetCompilerDriver() {
+  const CompilerDriver* GetCompilerDriver() const {
     return compiler_driver_;
   }
 
  private:
   class DexFileSource;
+  class OatClassHeader;
   class OatClass;
   class OatDexFile;
 
@@ -327,6 +328,8 @@
   void SetMultiOatRelativePatcherAdjustment();
   void CloseSources();
 
+  bool MayHaveCompiledMethods() const;
+
   enum class WriteState {
     kAddingDexFileSources,
     kPrepareLayout,
@@ -410,6 +413,7 @@
   // data to write
   std::unique_ptr<OatHeader> oat_header_;
   dchecked_vector<OatDexFile> oat_dex_files_;
+  dchecked_vector<OatClassHeader> oat_class_headers_;
   dchecked_vector<OatClass> oat_classes_;
   std::unique_ptr<const std::vector<uint8_t>> jni_dlsym_lookup_;
   std::unique_ptr<const std::vector<uint8_t>> quick_generic_jni_trampoline_;
diff --git a/test/595-profile-saving/profile-saving.cc b/test/595-profile-saving/profile-saving.cc
index 00aa944..ae3dad8 100644
--- a/test/595-profile-saving/profile-saving.cc
+++ b/test/595-profile-saving/profile-saving.cc
@@ -48,9 +48,8 @@
   ProfileSaver::ForceProcessProfiles();
 }
 
-extern "C" JNIEXPORT jboolean JNICALL Java_Main_profileHasMethod(JNIEnv* env,
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_presentInProfile(JNIEnv* env,
                                                                  jclass,
-                                                                 jboolean hot,
                                                                  jstring filename,
                                                                  jobject method) {
   ScopedUtfChars filename_chars(env, filename);
@@ -59,7 +58,7 @@
   ObjPtr<mirror::Executable> exec = soa.Decode<mirror::Executable>(method);
   ArtMethod* art_method = exec->GetArtMethod();
   return ProfileSaver::HasSeenMethod(std::string(filename_chars.c_str()),
-                                     hot != JNI_FALSE,
+                                     /*hot*/ true,
                                      MethodReference(art_method->GetDexFile(),
                                                      art_method->GetDexMethodIndex()));
 }
diff --git a/test/595-profile-saving/src/Main.java b/test/595-profile-saving/src/Main.java
index 197c4e7..18c0598 100644
--- a/test/595-profile-saving/src/Main.java
+++ b/test/595-profile-saving/src/Main.java
@@ -42,14 +42,6 @@
         System.out.println("Class loader does not match boot class");
       }
       testAddMethodToProfile(file, bootMethod);
-
-      // Test a sampled method that is only warm and not hot.
-      Method reflectMethod = Main.class.getDeclaredMethod("testReflectionInvoke");
-      reflectMethod.invoke(null);
-      testSampledMethodInProfile(file, reflectMethod);
-      if (staticObj == null) {
-        throw new AssertionError("Object was not set");
-      }
     } finally {
       if (file != null) {
         file.delete();
@@ -57,38 +49,23 @@
     }
   }
 
-  static Object staticObj = null;
-
-  static void testReflectionInvoke() {
-    staticObj = new Object();
-  }
-
   static void testAddMethodToProfile(File file, Method m) {
     // Make sure we have a profile info for this method without the need to loop.
     ensureProfilingInfo(m);
-    // Make sure the profile gets processed.
+    // Make sure the profile gets saved.
     ensureProfileProcessing();
     // Verify that the profile was saved and contains the method.
-    if (!profileHasMethod(true, file.getPath(), m)) {
+    if (!presentInProfile(file.getPath(), m)) {
       throw new RuntimeException("Method with index " + m + " not in the profile");
     }
   }
 
-  static void testSampledMethodInProfile(File file, Method m) {
-    // Make sure the profile gets processed.
-    ensureProfileProcessing();
-    // Verify that the profile was saved and contains the method.
-    if (!profileHasMethod(false, file.getPath(), m)) {
-      throw new RuntimeException("Method with index " + m + " not sampled in the profile");
-    }
-  }
-
   // Ensure a method has a profiling info.
   public static native void ensureProfilingInfo(Method method);
   // Ensures the profile saver does its usual processing.
   public static native void ensureProfileProcessing();
-  // Checks if the profile saver has the method as a warm/sampled method.
-  public static native boolean profileHasMethod(boolean hot, String profile, Method method);
+  // Checks if the profiles saver knows about the method.
+  public static native boolean presentInProfile(String profile, Method method);
 
   private static final String TEMP_FILE_NAME_PREFIX = "dummy";
   private static final String TEMP_FILE_NAME_SUFFIX = "-file";