Merge "Send ThreadEnd after clearing ThreadGroup."
diff --git a/dex2oat/dex2oat_image_test.cc b/dex2oat/dex2oat_image_test.cc
index 7ddf1c1..1f644c1 100644
--- a/dex2oat/dex2oat_image_test.cc
+++ b/dex2oat/dex2oat_image_test.cc
@@ -311,7 +311,11 @@
   {
     ProfileCompilationInfo profile;
     VisitLibcoreDexes([&profile](MethodReference ref) {
-      EXPECT_TRUE(profile.AddMethodIndex(ProfileCompilationInfo::MethodHotness::kFlagHot, ref));
+      uint32_t flags = ProfileCompilationInfo::MethodHotness::kFlagHot |
+          ProfileCompilationInfo::MethodHotness::kFlagStartup;
+      EXPECT_TRUE(profile.AddMethodIndex(
+          static_cast<ProfileCompilationInfo::MethodHotness::Flag>(flags),
+          ref));
     }, [&profile](TypeReference ref) {
       EXPECT_TRUE(profile.AddClassForDex(ref));
     }, kMethodFrequency, kTypeFrequency);
diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc
index a80dbf6..dfbe31a 100644
--- a/dex2oat/linker/oat_writer.cc
+++ b/dex2oat/linker/oat_writer.cc
@@ -961,8 +961,8 @@
       if (method_hotness.IsHot() &&
               !method_hotness.IsStartup() && !method_hotness.IsPostStartup()) {
         std::string name = method_reference.PrettyMethod();
-        LOG(WARNING) << "Method " << name << " had a Hot method that wasn't marked "
-                     << "either start-up or post-startup. Possible corrupted profile?";
+        LOG(FATAL) << "Method " << name << " had a Hot method that wasn't marked "
+                   << "either start-up or post-startup. Possible corrupted profile?";
         // This is not fatal, so only warn.
       }
     }
diff --git a/runtime/interpreter/shadow_frame.h b/runtime/interpreter/shadow_frame.h
index 6903af2..80fdadb 100644
--- a/runtime/interpreter/shadow_frame.h
+++ b/runtime/interpreter/shadow_frame.h
@@ -188,9 +188,7 @@
       const uint32_t* vreg_ptr = &vregs_[i];
       ref = reinterpret_cast<const StackReference<mirror::Object>*>(vreg_ptr)->AsMirrorPtr();
     }
-    if (kUseReadBarrier) {
-      ReadBarrier::AssertToSpaceInvariant(ref);
-    }
+    ReadBarrier::MaybeAssertToSpaceInvariant(ref);
     if (kVerifyFlags & kVerifyReads) {
       VerifyObject(ref);
     }
@@ -256,9 +254,7 @@
     if (kVerifyFlags & kVerifyWrites) {
       VerifyObject(val);
     }
-    if (kUseReadBarrier) {
-      ReadBarrier::AssertToSpaceInvariant(val);
-    }
+    ReadBarrier::MaybeAssertToSpaceInvariant(val);
     uint32_t* vreg = &vregs_[i];
     reinterpret_cast<StackReference<mirror::Object>*>(vreg)->Assign(val);
     if (HasReferenceArray()) {
diff --git a/runtime/jit/profile_compilation_info.cc b/runtime/jit/profile_compilation_info.cc
index 1ed7889..5601317 100644
--- a/runtime/jit/profile_compilation_info.cc
+++ b/runtime/jit/profile_compilation_info.cc
@@ -1704,7 +1704,10 @@
       if (m < (number_of_methods / kFavorSplit)) {
         method_idx %= kFavorFirstN;
       }
-      info.AddMethodIndex(MethodHotness::kFlagHot,
+      // Alternate between startup and post startup.
+      uint32_t flags = MethodHotness::kFlagHot;
+      flags |= ((m & 1) != 0) ? MethodHotness::kFlagPostStartup : MethodHotness::kFlagStartup;
+      info.AddMethodIndex(static_cast<MethodHotness::Flag>(flags),
                           profile_key,
                           /*method_idx*/ 0,
                           method_idx,
@@ -1761,8 +1764,13 @@
       if (number_of_methods - i == methods_required_in_profile ||
           std::rand() % (number_of_methods - i - methods_required_in_profile) == 0) {
         uint32_t method_index = (method_start_index + i) % number_of_methods;
-        info.AddMethodIndex(MethodHotness::kFlagHot, MethodReference(dex_file.get(),
-                                                                     method_index));
+        // Alternate between startup and post startup.
+        uint32_t flags = MethodHotness::kFlagHot;
+        flags |= ((method_index & 1) != 0)
+            ? MethodHotness::kFlagPostStartup
+            : MethodHotness::kFlagStartup;
+        info.AddMethodIndex(static_cast<MethodHotness::Flag>(flags),
+                            MethodReference(dex_file.get(), method_index));
         methods_required_in_profile--;
       }
     }
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index b2f6c03..9a42c29 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -635,29 +635,35 @@
   }
 
   if (boot_image_tables != nullptr) {
-    // Map boot image tables into the .bss. The reserved size must match size of the tables.
-    size_t reserved_size = static_cast<size_t>(boot_image_tables_end - boot_image_tables);
-    size_t tables_size = 0u;
-    for (gc::space::ImageSpace* space : Runtime::Current()->GetHeap()->GetBootImageSpaces()) {
-      tables_size += space->GetImageHeader().GetBootImageConstantTablesSize();
-      DCHECK_ALIGNED(tables_size, kPageSize);
-    }
-    if (tables_size != reserved_size) {
-      *error_msg = StringPrintf("In oat file '%s' found unexpected boot image table sizes, "
-                                    " %zu bytes, should be %zu.",
-                                GetLocation().c_str(),
-                                reserved_size,
-                                tables_size);
-      return false;
-    }
-    for (gc::space::ImageSpace* space : Runtime::Current()->GetHeap()->GetBootImageSpaces()) {
-      uint32_t current_tables_size = space->GetImageHeader().GetBootImageConstantTablesSize();
-      if (current_tables_size != 0u && !MapConstantTables(space, boot_image_tables)) {
+    Runtime* runtime = Runtime::Current();
+    if (UNLIKELY(runtime == nullptr)) {
+      // This must be oatdump without boot image. Make sure the .bss is inaccessible.
+      mprotect(const_cast<uint8_t*>(BssBegin()), BssSize(), PROT_NONE);
+    } else {
+      // Map boot image tables into the .bss. The reserved size must match size of the tables.
+      size_t reserved_size = static_cast<size_t>(boot_image_tables_end - boot_image_tables);
+      size_t tables_size = 0u;
+      for (gc::space::ImageSpace* space : runtime->GetHeap()->GetBootImageSpaces()) {
+        tables_size += space->GetImageHeader().GetBootImageConstantTablesSize();
+        DCHECK_ALIGNED(tables_size, kPageSize);
+      }
+      if (tables_size != reserved_size) {
+        *error_msg = StringPrintf("In oat file '%s' found unexpected boot image table sizes, "
+                                      " %zu bytes, should be %zu.",
+                                  GetLocation().c_str(),
+                                  reserved_size,
+                                  tables_size);
         return false;
       }
-      boot_image_tables += current_tables_size;
+      for (gc::space::ImageSpace* space : Runtime::Current()->GetHeap()->GetBootImageSpaces()) {
+        uint32_t current_tables_size = space->GetImageHeader().GetBootImageConstantTablesSize();
+        if (current_tables_size != 0u && !MapConstantTables(space, boot_image_tables)) {
+          return false;
+        }
+        boot_image_tables += current_tables_size;
+      }
+      DCHECK(boot_image_tables == boot_image_tables_end);
     }
-    DCHECK(boot_image_tables == boot_image_tables_end);
   }
   return true;
 }
diff --git a/runtime/quick_exception_handler.cc b/runtime/quick_exception_handler.cc
index d8b6237..f94923e 100644
--- a/runtime/quick_exception_handler.cc
+++ b/runtime/quick_exception_handler.cc
@@ -166,10 +166,9 @@
                 << line_number << ")";
     }
   }
-  if (clear_exception_) {
-    // Exception was cleared as part of delivery.
-    DCHECK(!self_->IsExceptionPending());
-  } else {
+  // Exception was cleared as part of delivery.
+  DCHECK(!self_->IsExceptionPending());
+  if (!clear_exception_) {
     // Put exception back in root set with clear throw location.
     self_->SetException(exception_ref.Get());
   }
diff --git a/runtime/quick_exception_handler.h b/runtime/quick_exception_handler.h
index 8090f9b..12b63c9 100644
--- a/runtime/quick_exception_handler.h
+++ b/runtime/quick_exception_handler.h
@@ -112,6 +112,10 @@
     handler_dex_pc_ = dex_pc;
   }
 
+  bool GetClearException() const {
+    return clear_exception_;
+  }
+
   void SetClearException(bool clear_exception) {
     clear_exception_ = clear_exception;
   }
diff --git a/runtime/read_barrier.h b/runtime/read_barrier.h
index 45e78bc..00674b2 100644
--- a/runtime/read_barrier.h
+++ b/runtime/read_barrier.h
@@ -89,6 +89,14 @@
   static void AssertToSpaceInvariant(GcRootSource* gc_root_source, mirror::Object* ref)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
+  // Without the holder object, and only with the read barrier configuration (no-op otherwise).
+  static void MaybeAssertToSpaceInvariant(mirror::Object* ref)
+      REQUIRES_SHARED(Locks::mutator_lock_) {
+    if (kUseReadBarrier) {
+      AssertToSpaceInvariant(ref);
+    }
+  }
+
   // ALWAYS_INLINE on this caused a performance regression b/26744236.
   static mirror::Object* Mark(mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_);
 
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 198edc7..524e73d 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -3080,6 +3080,8 @@
     UNREACHABLE();
   }
 
+  ReadBarrier::MaybeAssertToSpaceInvariant(exception.Ptr());
+
   // This is a real exception: let the instrumentation know about it.
   instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
   if (instrumentation->HasExceptionThrownListeners() &&
@@ -3121,6 +3123,15 @@
   QuickExceptionHandler exception_handler(this, false);
   exception_handler.FindCatch(exception);
   exception_handler.UpdateInstrumentationStack();
+  if (exception_handler.GetClearException()) {
+    // Exception was cleared as part of delivery.
+    DCHECK(!IsExceptionPending());
+  } else {
+    // Exception was put back with a throw location.
+    DCHECK(IsExceptionPending());
+    // Check the to-space invariant on the re-installed exception (if applicable).
+    ReadBarrier::MaybeAssertToSpaceInvariant(GetException());
+  }
   exception_handler.DoLongJump();
 }
 
diff --git a/test/638-checker-inline-caches/profile b/test/638-checker-inline-caches/profile
index 1ca6d7b..7756a16 100644
--- a/test/638-checker-inline-caches/profile
+++ b/test/638-checker-inline-caches/profile
@@ -1,6 +1,6 @@
-LMain;->inlineMonomorphicSubA(LSuper;)I+LSubA;
-LMain;->inlinePolymophicSubASubB(LSuper;)I+LSubA;,LSubB;
-LMain;->inlinePolymophicCrossDexSubASubC(LSuper;)I+LSubA;,LSubC;
-LMain;->inlineMegamorphic(LSuper;)I+LSubA;,LSubB;,LSubC;,LSubD;,LSubE;
-LMain;->inlineMissingTypes(LSuper;)I+missing_types
-LMain;->noInlineCache(LSuper;)I
+HSLMain;->inlineMonomorphicSubA(LSuper;)I+LSubA;
+HSLMain;->inlinePolymophicSubASubB(LSuper;)I+LSubA;,LSubB;
+HSLMain;->inlinePolymophicCrossDexSubASubC(LSuper;)I+LSubA;,LSubC;
+HSLMain;->inlineMegamorphic(LSuper;)I+LSubA;,LSubB;,LSubC;,LSubD;,LSubE;
+HSLMain;->inlineMissingTypes(LSuper;)I+missing_types
+HSLMain;->noInlineCache(LSuper;)I
diff --git a/test/643-checker-bogus-ic/profile b/test/643-checker-bogus-ic/profile
index cbf7796..540a935 100644
--- a/test/643-checker-bogus-ic/profile
+++ b/test/643-checker-bogus-ic/profile
@@ -1,2 +1,2 @@
-LMain;->inlineMonomorphic(LMain;)I+LUnrelated;
-LMain;->inlinePolymorphic(LMain;)I+LUnrelated;,LMain;
+SHLMain;->inlineMonomorphic(LMain;)I+LUnrelated;
+SHLMain;->inlinePolymorphic(LMain;)I+LUnrelated;,LMain;
diff --git a/test/648-inline-caches-unresolved/profile b/test/648-inline-caches-unresolved/profile
index 92c0a41..06bc8ad 100644
--- a/test/648-inline-caches-unresolved/profile
+++ b/test/648-inline-caches-unresolved/profile
@@ -1 +1 @@
-LMain;->inlineMonomorphicUnresolvedSuper(Ljava/lang/Object;)Ljava/lang/String;+LSubclass;
+SHLMain;->inlineMonomorphicUnresolvedSuper(Ljava/lang/Object;)Ljava/lang/String;+LSubclass;
diff --git a/test/661-oat-writer-layout/expected.txt b/test/661-oat-writer-layout/expected.txt
index db28e4f..b7ad70a 100644
--- a/test/661-oat-writer-layout/expected.txt
+++ b/test/661-oat-writer-layout/expected.txt
@@ -8,15 +8,6 @@
 C::m_a$$$
 C::m_b$$$
 C::m_c$$$
-A::m_a$Hot$$
-A::m_b$Hot$$
-A::m_c$Hot$$
-B::m_a$Hot$$
-B::m_b$Hot$$
-B::m_c$Hot$$
-C::m_a$Hot$$
-C::m_b$Hot$$
-C::m_c$Hot$$
 A::m_a$$Startup$
 A::m_b$$Startup$
 A::m_c$$Startup$
diff --git a/test/661-oat-writer-layout/profile b/test/661-oat-writer-layout/profile
index 5406484..cf307c2 100644
--- a/test/661-oat-writer-layout/profile
+++ b/test/661-oat-writer-layout/profile
@@ -1,60 +1,51 @@
-HLA;->m_a$Hot$$()V
 SLA;->m_a$$Startup$()V
 HSLA;->m_a$Hot$Startup$()V
 PLA;->m_a$$$Poststartup()V
 HPLA;->m_a$Hot$$Poststartup()V
 SPLA;->m_a$$Startup$Poststartup()V
 HSPLA;->m_a$Hot$Startup$Poststartup()V
-HLA;->m_b$Hot$$()V
 SLA;->m_b$$Startup$()V
 HSLA;->m_b$Hot$Startup$()V
 PLA;->m_b$$$Poststartup()V
 HPLA;->m_b$Hot$$Poststartup()V
 SPLA;->m_b$$Startup$Poststartup()V
 HSPLA;->m_b$Hot$Startup$Poststartup()V
-HLA;->m_c$Hot$$()V
 SLA;->m_c$$Startup$()V
 HSLA;->m_c$Hot$Startup$()V
 PLA;->m_c$$$Poststartup()V
 HPLA;->m_c$Hot$$Poststartup()V
 SPLA;->m_c$$Startup$Poststartup()V
 HSPLA;->m_c$Hot$Startup$Poststartup()V
-HLB;->m_a$Hot$$()V
 SLB;->m_a$$Startup$()V
 HSLB;->m_a$Hot$Startup$()V
 PLB;->m_a$$$Poststartup()V
 HPLB;->m_a$Hot$$Poststartup()V
 SPLB;->m_a$$Startup$Poststartup()V
 HSPLB;->m_a$Hot$Startup$Poststartup()V
-HLB;->m_b$Hot$$()V
 SLB;->m_b$$Startup$()V
 HSLB;->m_b$Hot$Startup$()V
 PLB;->m_b$$$Poststartup()V
 HPLB;->m_b$Hot$$Poststartup()V
 SPLB;->m_b$$Startup$Poststartup()V
 HSPLB;->m_b$Hot$Startup$Poststartup()V
-HLB;->m_c$Hot$$()V
 SLB;->m_c$$Startup$()V
 HSLB;->m_c$Hot$Startup$()V
 PLB;->m_c$$$Poststartup()V
 HPLB;->m_c$Hot$$Poststartup()V
 SPLB;->m_c$$Startup$Poststartup()V
 HSPLB;->m_c$Hot$Startup$Poststartup()V
-HLC;->m_a$Hot$$()V
 SLC;->m_a$$Startup$()V
 HSLC;->m_a$Hot$Startup$()V
 PLC;->m_a$$$Poststartup()V
 HPLC;->m_a$Hot$$Poststartup()V
 SPLC;->m_a$$Startup$Poststartup()V
 HSPLC;->m_a$Hot$Startup$Poststartup()V
-HLC;->m_b$Hot$$()V
 SLC;->m_b$$Startup$()V
 HSLC;->m_b$Hot$Startup$()V
 PLC;->m_b$$$Poststartup()V
 HPLC;->m_b$Hot$$Poststartup()V
 SPLC;->m_b$$Startup$Poststartup()V
 HSPLC;->m_b$Hot$Startup$Poststartup()V
-HLC;->m_c$Hot$$()V
 SLC;->m_c$$Startup$()V
 HSLC;->m_c$Hot$Startup$()V
 PLC;->m_c$$$Poststartup()V
diff --git a/test/661-oat-writer-layout/src/Test.java b/test/661-oat-writer-layout/src/Test.java
index db67b48..f862e37 100644
--- a/test/661-oat-writer-layout/src/Test.java
+++ b/test/661-oat-writer-layout/src/Test.java
@@ -13,86 +13,78 @@
 // limitations under the License.
 
 import java.lang.reflect.Method;
+import java.util.ArrayList;
 
 public class Test {
   // Returns list of all methods in Generated.java
   // This is to avoid having to introspect classes with extra code
   // (for example, we ignore <init> methods).
   public static Method[] getTestMethods() throws NoSuchMethodException, SecurityException {
-    Method[] all_methods = new Method[72];
-    all_methods[0] = A.class.getDeclaredMethod("m_a$$$");
-    all_methods[1] = A.class.getDeclaredMethod("m_a$Hot$$");
-    all_methods[2] = A.class.getDeclaredMethod("m_a$$Startup$");
-    all_methods[3] = A.class.getDeclaredMethod("m_a$Hot$Startup$");
-    all_methods[4] = A.class.getDeclaredMethod("m_a$$$Poststartup");
-    all_methods[5] = A.class.getDeclaredMethod("m_a$Hot$$Poststartup");
-    all_methods[6] = A.class.getDeclaredMethod("m_a$$Startup$Poststartup");
-    all_methods[7] = A.class.getDeclaredMethod("m_a$Hot$Startup$Poststartup");
-    all_methods[8] = A.class.getDeclaredMethod("m_b$$$");
-    all_methods[9] = A.class.getDeclaredMethod("m_b$Hot$$");
-    all_methods[10] = A.class.getDeclaredMethod("m_b$$Startup$");
-    all_methods[11] = A.class.getDeclaredMethod("m_b$Hot$Startup$");
-    all_methods[12] = A.class.getDeclaredMethod("m_b$$$Poststartup");
-    all_methods[13] = A.class.getDeclaredMethod("m_b$Hot$$Poststartup");
-    all_methods[14] = A.class.getDeclaredMethod("m_b$$Startup$Poststartup");
-    all_methods[15] = A.class.getDeclaredMethod("m_b$Hot$Startup$Poststartup");
-    all_methods[16] = A.class.getDeclaredMethod("m_c$$$");
-    all_methods[17] = A.class.getDeclaredMethod("m_c$Hot$$");
-    all_methods[18] = A.class.getDeclaredMethod("m_c$$Startup$");
-    all_methods[19] = A.class.getDeclaredMethod("m_c$Hot$Startup$");
-    all_methods[20] = A.class.getDeclaredMethod("m_c$$$Poststartup");
-    all_methods[21] = A.class.getDeclaredMethod("m_c$Hot$$Poststartup");
-    all_methods[22] = A.class.getDeclaredMethod("m_c$$Startup$Poststartup");
-    all_methods[23] = A.class.getDeclaredMethod("m_c$Hot$Startup$Poststartup");
-    all_methods[24] = B.class.getDeclaredMethod("m_a$$$");
-    all_methods[25] = B.class.getDeclaredMethod("m_a$Hot$$");
-    all_methods[26] = B.class.getDeclaredMethod("m_a$$Startup$");
-    all_methods[27] = B.class.getDeclaredMethod("m_a$Hot$Startup$");
-    all_methods[28] = B.class.getDeclaredMethod("m_a$$$Poststartup");
-    all_methods[29] = B.class.getDeclaredMethod("m_a$Hot$$Poststartup");
-    all_methods[30] = B.class.getDeclaredMethod("m_a$$Startup$Poststartup");
-    all_methods[31] = B.class.getDeclaredMethod("m_a$Hot$Startup$Poststartup");
-    all_methods[32] = B.class.getDeclaredMethod("m_b$$$");
-    all_methods[33] = B.class.getDeclaredMethod("m_b$Hot$$");
-    all_methods[34] = B.class.getDeclaredMethod("m_b$$Startup$");
-    all_methods[35] = B.class.getDeclaredMethod("m_b$Hot$Startup$");
-    all_methods[36] = B.class.getDeclaredMethod("m_b$$$Poststartup");
-    all_methods[37] = B.class.getDeclaredMethod("m_b$Hot$$Poststartup");
-    all_methods[38] = B.class.getDeclaredMethod("m_b$$Startup$Poststartup");
-    all_methods[39] = B.class.getDeclaredMethod("m_b$Hot$Startup$Poststartup");
-    all_methods[40] = B.class.getDeclaredMethod("m_c$$$");
-    all_methods[41] = B.class.getDeclaredMethod("m_c$Hot$$");
-    all_methods[42] = B.class.getDeclaredMethod("m_c$$Startup$");
-    all_methods[43] = B.class.getDeclaredMethod("m_c$Hot$Startup$");
-    all_methods[44] = B.class.getDeclaredMethod("m_c$$$Poststartup");
-    all_methods[45] = B.class.getDeclaredMethod("m_c$Hot$$Poststartup");
-    all_methods[46] = B.class.getDeclaredMethod("m_c$$Startup$Poststartup");
-    all_methods[47] = B.class.getDeclaredMethod("m_c$Hot$Startup$Poststartup");
-    all_methods[48] = C.class.getDeclaredMethod("m_a$$$");
-    all_methods[49] = C.class.getDeclaredMethod("m_a$Hot$$");
-    all_methods[50] = C.class.getDeclaredMethod("m_a$$Startup$");
-    all_methods[51] = C.class.getDeclaredMethod("m_a$Hot$Startup$");
-    all_methods[52] = C.class.getDeclaredMethod("m_a$$$Poststartup");
-    all_methods[53] = C.class.getDeclaredMethod("m_a$Hot$$Poststartup");
-    all_methods[54] = C.class.getDeclaredMethod("m_a$$Startup$Poststartup");
-    all_methods[55] = C.class.getDeclaredMethod("m_a$Hot$Startup$Poststartup");
-    all_methods[56] = C.class.getDeclaredMethod("m_b$$$");
-    all_methods[57] = C.class.getDeclaredMethod("m_b$Hot$$");
-    all_methods[58] = C.class.getDeclaredMethod("m_b$$Startup$");
-    all_methods[59] = C.class.getDeclaredMethod("m_b$Hot$Startup$");
-    all_methods[60] = C.class.getDeclaredMethod("m_b$$$Poststartup");
-    all_methods[61] = C.class.getDeclaredMethod("m_b$Hot$$Poststartup");
-    all_methods[62] = C.class.getDeclaredMethod("m_b$$Startup$Poststartup");
-    all_methods[63] = C.class.getDeclaredMethod("m_b$Hot$Startup$Poststartup");
-    all_methods[64] = C.class.getDeclaredMethod("m_c$$$");
-    all_methods[65] = C.class.getDeclaredMethod("m_c$Hot$$");
-    all_methods[66] = C.class.getDeclaredMethod("m_c$$Startup$");
-    all_methods[67] = C.class.getDeclaredMethod("m_c$Hot$Startup$");
-    all_methods[68] = C.class.getDeclaredMethod("m_c$$$Poststartup");
-    all_methods[69] = C.class.getDeclaredMethod("m_c$Hot$$Poststartup");
-    all_methods[70] = C.class.getDeclaredMethod("m_c$$Startup$Poststartup");
-    all_methods[71] = C.class.getDeclaredMethod("m_c$Hot$Startup$Poststartup");
-    return all_methods;
+    ArrayList<Method> all_methods = new ArrayList<Method>();
+    all_methods.add(A.class.getDeclaredMethod("m_a$$$"));
+    all_methods.add(A.class.getDeclaredMethod("m_a$$Startup$"));
+    all_methods.add(A.class.getDeclaredMethod("m_a$Hot$Startup$"));
+    all_methods.add(A.class.getDeclaredMethod("m_a$$$Poststartup"));
+    all_methods.add(A.class.getDeclaredMethod("m_a$Hot$$Poststartup"));
+    all_methods.add(A.class.getDeclaredMethod("m_a$$Startup$Poststartup"));
+    all_methods.add(A.class.getDeclaredMethod("m_a$Hot$Startup$Poststartup"));
+    all_methods.add(A.class.getDeclaredMethod("m_b$$$"));
+    all_methods.add(A.class.getDeclaredMethod("m_b$$Startup$"));
+    all_methods.add(A.class.getDeclaredMethod("m_b$Hot$Startup$"));
+    all_methods.add(A.class.getDeclaredMethod("m_b$$$Poststartup"));
+    all_methods.add(A.class.getDeclaredMethod("m_b$Hot$$Poststartup"));
+    all_methods.add(A.class.getDeclaredMethod("m_b$$Startup$Poststartup"));
+    all_methods.add(A.class.getDeclaredMethod("m_b$Hot$Startup$Poststartup"));
+    all_methods.add(A.class.getDeclaredMethod("m_c$$$"));
+    all_methods.add(A.class.getDeclaredMethod("m_c$$Startup$"));
+    all_methods.add(A.class.getDeclaredMethod("m_c$Hot$Startup$"));
+    all_methods.add(A.class.getDeclaredMethod("m_c$$$Poststartup"));
+    all_methods.add(A.class.getDeclaredMethod("m_c$Hot$$Poststartup"));
+    all_methods.add(A.class.getDeclaredMethod("m_c$$Startup$Poststartup"));
+    all_methods.add(A.class.getDeclaredMethod("m_c$Hot$Startup$Poststartup"));
+    all_methods.add(B.class.getDeclaredMethod("m_a$$$"));
+    all_methods.add(B.class.getDeclaredMethod("m_a$$Startup$"));
+    all_methods.add(B.class.getDeclaredMethod("m_a$Hot$Startup$"));
+    all_methods.add(B.class.getDeclaredMethod("m_a$$$Poststartup"));
+    all_methods.add(B.class.getDeclaredMethod("m_a$Hot$$Poststartup"));
+    all_methods.add(B.class.getDeclaredMethod("m_a$$Startup$Poststartup"));
+    all_methods.add(B.class.getDeclaredMethod("m_a$Hot$Startup$Poststartup"));
+    all_methods.add(B.class.getDeclaredMethod("m_b$$$"));
+    all_methods.add(B.class.getDeclaredMethod("m_b$$Startup$"));
+    all_methods.add(B.class.getDeclaredMethod("m_b$Hot$Startup$"));
+    all_methods.add(B.class.getDeclaredMethod("m_b$$$Poststartup"));
+    all_methods.add(B.class.getDeclaredMethod("m_b$Hot$$Poststartup"));
+    all_methods.add(B.class.getDeclaredMethod("m_b$$Startup$Poststartup"));
+    all_methods.add(B.class.getDeclaredMethod("m_b$Hot$Startup$Poststartup"));
+    all_methods.add(B.class.getDeclaredMethod("m_c$$$"));
+    all_methods.add(B.class.getDeclaredMethod("m_c$$Startup$"));
+    all_methods.add(B.class.getDeclaredMethod("m_c$Hot$Startup$"));
+    all_methods.add(B.class.getDeclaredMethod("m_c$$$Poststartup"));
+    all_methods.add(B.class.getDeclaredMethod("m_c$Hot$$Poststartup"));
+    all_methods.add(B.class.getDeclaredMethod("m_c$$Startup$Poststartup"));
+    all_methods.add(B.class.getDeclaredMethod("m_c$Hot$Startup$Poststartup"));
+    all_methods.add(C.class.getDeclaredMethod("m_a$$$"));
+    all_methods.add(C.class.getDeclaredMethod("m_a$$Startup$"));
+    all_methods.add(C.class.getDeclaredMethod("m_a$Hot$Startup$"));
+    all_methods.add(C.class.getDeclaredMethod("m_a$$$Poststartup"));
+    all_methods.add(C.class.getDeclaredMethod("m_a$Hot$$Poststartup"));
+    all_methods.add(C.class.getDeclaredMethod("m_a$$Startup$Poststartup"));
+    all_methods.add(C.class.getDeclaredMethod("m_a$Hot$Startup$Poststartup"));
+    all_methods.add(C.class.getDeclaredMethod("m_b$$$"));
+    all_methods.add(C.class.getDeclaredMethod("m_b$$Startup$"));
+    all_methods.add(C.class.getDeclaredMethod("m_b$Hot$Startup$"));
+    all_methods.add(C.class.getDeclaredMethod("m_b$$$Poststartup"));
+    all_methods.add(C.class.getDeclaredMethod("m_b$Hot$$Poststartup"));
+    all_methods.add(C.class.getDeclaredMethod("m_b$$Startup$Poststartup"));
+    all_methods.add(C.class.getDeclaredMethod("m_b$Hot$Startup$Poststartup"));
+    all_methods.add(C.class.getDeclaredMethod("m_c$$$"));
+    all_methods.add(C.class.getDeclaredMethod("m_c$$Startup$"));
+    all_methods.add(C.class.getDeclaredMethod("m_c$Hot$Startup$"));
+    all_methods.add(C.class.getDeclaredMethod("m_c$$$Poststartup"));
+    all_methods.add(C.class.getDeclaredMethod("m_c$Hot$$Poststartup"));
+    all_methods.add(C.class.getDeclaredMethod("m_c$$Startup$Poststartup"));
+    all_methods.add(C.class.getDeclaredMethod("m_c$Hot$Startup$Poststartup"));
+    return all_methods.toArray(new Method[all_methods.size()]);
   }
 }
 
diff --git a/test/707-checker-invalid-profile/profile b/test/707-checker-invalid-profile/profile
index 5979dd2..f142c40 100644
--- a/test/707-checker-invalid-profile/profile
+++ b/test/707-checker-invalid-profile/profile
@@ -1,4 +1,4 @@
-LMain;->attemptInlineMonomorphic(LMain;)I+invalid_class
-LMain;->attemptInlinePolymorphic(LMain;)I+LMain;,invalid_class
-LMain;->invalid_method
+SHLMain;->attemptInlineMonomorphic(LMain;)I+invalid_class
+SHLMain;->attemptInlinePolymorphic(LMain;)I+LMain;,invalid_class
+SHLMain;->invalid_method
 invalid_class
\ No newline at end of file
diff --git a/tools/ahat/src/Main.java b/tools/ahat/src/Main.java
index 623a865..31c485d 100644
--- a/tools/ahat/src/Main.java
+++ b/tools/ahat/src/Main.java
@@ -18,6 +18,7 @@
 
 import com.android.ahat.heapdump.AhatSnapshot;
 import com.android.ahat.heapdump.Diff;
+import com.android.ahat.heapdump.HprofFormatException;
 import com.android.ahat.heapdump.Parser;
 import com.android.ahat.proguard.ProguardMap;
 import com.sun.net.httpserver.HttpServer;
@@ -47,7 +48,7 @@
     out.println("");
   }
 
-  public static void main(String[] args) throws Exception {
+  public static void main(String[] args) {
     int port = 7100;
     for (String arg : args) {
       if (arg.equals("--help")) {
@@ -104,34 +105,40 @@
       return;
     }
 
-    // Launch the server before parsing the hprof file so we get
-    // BindExceptions quickly.
-    InetAddress loopback = InetAddress.getLoopbackAddress();
-    InetSocketAddress addr = new InetSocketAddress(loopback, port);
-    HttpServer server = HttpServer.create(addr, 0);
+    try {
+      // Launch the server before parsing the hprof file so we get
+      // BindExceptions quickly.
+      InetAddress loopback = InetAddress.getLoopbackAddress();
+      InetSocketAddress addr = new InetSocketAddress(loopback, port);
+      System.out.println("Preparing " + addr + " ...");
+      HttpServer server = HttpServer.create(addr, 0);
 
-    System.out.println("Processing hprof file...");
-    AhatSnapshot ahat = Parser.parseHeapDump(hprof, map);
+      System.out.println("Processing '" + hprof + "' ...");
+      AhatSnapshot ahat = Parser.parseHeapDump(hprof, map);
 
-    if (hprofbase != null) {
-      System.out.println("Processing baseline hprof file...");
-      AhatSnapshot base = Parser.parseHeapDump(hprofbase, mapbase);
+      if (hprofbase != null) {
+        System.out.println("Processing '" + hprofbase + "' ...");
+        AhatSnapshot base = Parser.parseHeapDump(hprofbase, mapbase);
 
-      System.out.println("Diffing hprof files...");
-      Diff.snapshots(ahat, base);
+        System.out.println("Diffing heap dumps ...");
+        Diff.snapshots(ahat, base);
+      }
+
+      server.createContext("/", new AhatHttpHandler(new OverviewHandler(ahat, hprof, hprofbase)));
+      server.createContext("/rooted", new AhatHttpHandler(new RootedHandler(ahat)));
+      server.createContext("/object", new AhatHttpHandler(new ObjectHandler(ahat)));
+      server.createContext("/objects", new AhatHttpHandler(new ObjectsHandler(ahat)));
+      server.createContext("/site", new AhatHttpHandler(new SiteHandler(ahat)));
+      server.createContext("/bitmap", new BitmapHandler(ahat));
+      server.createContext("/style.css", new StaticHandler("style.css", "text/css"));
+      server.setExecutor(Executors.newFixedThreadPool(1));
+      System.out.println("Server started on localhost:" + port);
+
+      server.start();
+    } catch (HprofFormatException|IOException e) {
+      System.err.println("Unable to launch ahat:");
+      e.printStackTrace();
     }
-
-    server.createContext("/", new AhatHttpHandler(new OverviewHandler(ahat, hprof, hprofbase)));
-    server.createContext("/rooted", new AhatHttpHandler(new RootedHandler(ahat)));
-    server.createContext("/object", new AhatHttpHandler(new ObjectHandler(ahat)));
-    server.createContext("/objects", new AhatHttpHandler(new ObjectsHandler(ahat)));
-    server.createContext("/site", new AhatHttpHandler(new SiteHandler(ahat)));
-    server.createContext("/bitmap", new BitmapHandler(ahat));
-    server.createContext("/style.css", new StaticHandler("style.css", "text/css"));
-    server.setExecutor(Executors.newFixedThreadPool(1));
-    System.out.println("Server started on localhost:" + port);
-
-    server.start();
   }
 }
 
diff --git a/tools/ahat/src/heapdump/HprofFormatException.java b/tools/ahat/src/heapdump/HprofFormatException.java
index 55e8958..0e128cd 100644
--- a/tools/ahat/src/heapdump/HprofFormatException.java
+++ b/tools/ahat/src/heapdump/HprofFormatException.java
@@ -20,4 +20,8 @@
   public HprofFormatException(String msg) {
     super(msg);
   }
+
+  public HprofFormatException(String msg, Exception cause) {
+    super(msg, cause);
+  }
 }
diff --git a/tools/ahat/src/heapdump/Parser.java b/tools/ahat/src/heapdump/Parser.java
index 3d5f95f..756b7d2 100644
--- a/tools/ahat/src/heapdump/Parser.java
+++ b/tools/ahat/src/heapdump/Parser.java
@@ -19,6 +19,7 @@
 import com.android.ahat.proguard.ProguardMap;
 import java.io.File;
 import java.io.IOException;
+import java.nio.BufferUnderflowException;
 import java.nio.ByteBuffer;
 import java.nio.channels.FileChannel;
 import java.nio.charset.StandardCharsets;
@@ -48,7 +49,11 @@
    */
   public static AhatSnapshot parseHeapDump(File hprof, ProguardMap map)
     throws IOException, HprofFormatException {
-    return parseHeapDump(new HprofBuffer(hprof), map);
+    try {
+      return parseHeapDump(new HprofBuffer(hprof), map);
+    } catch (BufferUnderflowException e) {
+      throw new HprofFormatException("Unexpected end of file", e);
+    }
   }
 
   /**
@@ -56,11 +61,15 @@
    */
   public static AhatSnapshot parseHeapDump(ByteBuffer hprof, ProguardMap map)
     throws IOException, HprofFormatException {
-    return parseHeapDump(new HprofBuffer(hprof), map);
+    try {
+      return parseHeapDump(new HprofBuffer(hprof), map);
+    } catch (BufferUnderflowException e) {
+      throw new HprofFormatException("Unexpected end of file", e);
+    }
   }
 
   private static AhatSnapshot parseHeapDump(HprofBuffer hprof, ProguardMap map)
-    throws IOException, HprofFormatException {
+    throws IOException, HprofFormatException, BufferUnderflowException {
     // Read, and mostly ignore, the hprof header info.
     {
       StringBuilder format = new StringBuilder();
@@ -409,6 +418,12 @@
                   break;
                 }
 
+                case 0x8a: { // ROOT FINALIZING (ANDROID)
+                  long objectId = hprof.getId();
+                  roots.add(new RootData(objectId, RootType.FINALIZING));
+                  break;
+                }
+
                 case 0x8b: { // ROOT DEBUGGER (ANDROID)
                   long objectId = hprof.getId();
                   roots.add(new RootData(objectId, RootType.DEBUGGER));
diff --git a/tools/ahat/src/heapdump/RootType.java b/tools/ahat/src/heapdump/RootType.java
index 7165b83..af552ea 100644
--- a/tools/ahat/src/heapdump/RootType.java
+++ b/tools/ahat/src/heapdump/RootType.java
@@ -29,7 +29,8 @@
   DEBUGGER        (1 <<  9),
   VM_INTERNAL     (1 << 10),
   UNKNOWN         (1 << 11),
-  JNI_MONITOR     (1 << 12);
+  JNI_MONITOR     (1 << 12),
+  FINALIZING      (1 << 13);
 
   public final int mask;
 
diff --git a/tools/ahat/test-dump/L.hprof b/tools/ahat/test-dump/L.hprof
index cf82557..1acdf79 100644
--- a/tools/ahat/test-dump/L.hprof
+++ b/tools/ahat/test-dump/L.hprof
Binary files differ
diff --git a/tools/ahat/test-dump/README.txt b/tools/ahat/test-dump/README.txt
index 344271c..e7ea584 100644
--- a/tools/ahat/test-dump/README.txt
+++ b/tools/ahat/test-dump/README.txt
@@ -1,5 +1,7 @@
 
 Main.java - A program used to generate a heap dump used for tests.
-L.hprof - A version of the test dump generated on Android L.
+L.hprof - A version of the test dump generated on Android L,
+          with one of the ROOT_DEBUGGER records manually changed to a
+          ROOT_FINALIZING record.
 O.hprof - A version of the test dump generated on Android O.
 RI.hprof - A version of the test dump generated on the reference implementation.