Dump maps inline in disassembled code.

In pursuit of Bug: 7250540, dump mapping and GC map tables inline such
as:

0x607333a8: f8dfe11c    ldr.w   lr, [pc, #284]  ; 0x6076416d
0x607333ac: 1c05        mov     r5, r0
0x607333ae: f8df0144    ldr.w   r0, [pc, #324]  ; 0x6003ba08
0x607333b2: 9a0b        ldr     r2, [sp, #44]
0x607333b4: f04f0b2f    orr     r11, pc, ThumbExpand(47)
0x607333b8: 1c29        mov     r1, r5
0x607333ba: 465b        mov     r3, r11
0x607333bc: 2900        cmp     r1, #0
0x607333be: f0008070    beq.w   +224 (0x607334a2)
0x607333c2: 47f0        blx     lr
suspend point dex PC: 44
GC map objects:  v2 (r7), v3 (r5), v6 ([sp + #84]), v7 (r6)
...

As GC map and mapping tables are inline, don't dump them.
Also dump dex instructions before code.

Change-Id: I9f0c04182a4cda6844027eae22e8151f2827dc99
diff --git a/src/oatdump.cc b/src/oatdump.cc
index e4fc930..88dbaae 100644
--- a/src/oatdump.cc
+++ b/src/oatdump.cc
@@ -293,38 +293,40 @@
     os << StringPrintf("\t%d: %s (dex_method_idx=%d)\n",
                        class_method_index, PrettyMethod(dex_method_idx, dex_file, true).c_str(),
                        dex_method_idx);
-    os << StringPrintf("\t\tframe_size_in_bytes: %zd\n",
-                       oat_method.GetFrameSizeInBytes());
-    os << StringPrintf("\t\tcore_spill_mask: 0x%08x",
-                       oat_method.GetCoreSpillMask());
+    os << StringPrintf("\t\tframe_size_in_bytes: %zd\n", oat_method.GetFrameSizeInBytes());
+    os << StringPrintf("\t\tcore_spill_mask: 0x%08x\n", oat_method.GetCoreSpillMask());
     DumpSpillMask(os, oat_method.GetCoreSpillMask(), false);
-    os << StringPrintf("\n\t\tfp_spill_mask: 0x%08x",
-                       oat_method.GetFpSpillMask());
+    os << StringPrintf("\n\t\tfp_spill_mask: 0x%08x\n", oat_method.GetFpSpillMask());
     DumpSpillMask(os, oat_method.GetFpSpillMask(), true);
-    os << StringPrintf("\n\t\tmapping_table: %p (offset=0x%08x)\n",
-                       oat_method.GetMappingTable(), oat_method.GetMappingTableOffset());
-    DumpMappingTable(os, oat_method);
     os << StringPrintf("\t\tvmap_table: %p (offset=0x%08x)\n",
                        oat_method.GetVmapTable(), oat_method.GetVmapTableOffset());
-    DumpVmap(os, oat_method.GetVmapTable(), oat_method.GetCoreSpillMask(),
-             oat_method.GetFpSpillMask());
-    os << StringPrintf("\t\tgc_map: %p (offset=0x%08x)\n",
-                       oat_method.GetNativeGcMap(), oat_method.GetNativeGcMapOffset());
-    DumpGcMap(os, oat_method.GetCode(), oat_method.GetNativeGcMap());
+    DumpVmap(os, oat_method);
+    const bool kDumpRawMappingTable = false;
+    if (kDumpRawMappingTable) {
+      os << StringPrintf("\t\tmapping_table: %p (offset=0x%08x)\n",
+                         oat_method.GetMappingTable(), oat_method.GetMappingTableOffset());
+      DumpMappingTable(os, oat_method);
+    }
+    const bool kDumpRawGcMap = false;
+    if (kDumpRawGcMap) {
+      os << StringPrintf("\t\tgc_map: %p (offset=0x%08x)\n",
+                         oat_method.GetNativeGcMap(), oat_method.GetNativeGcMapOffset());
+      DumpGcMap(os, oat_method, code_item);
+    }
+    os << "\t\tDEX CODE:\n";
+    DumpDexCode(os, dex_file, code_item);
     os << StringPrintf("\t\tCODE: %p (offset=0x%08x size=%d)%s\n",
                        oat_method.GetCode(),
                        oat_method.GetCodeOffset(),
                        oat_method.GetCodeSize(),
                        oat_method.GetCode() != NULL ? "..." : "");
-    DumpCode(os, oat_method.GetCode(), oat_method.GetCodeSize(), oat_method.GetMappingTable(),
-             dex_file, code_item);
+    DumpCode(os, oat_method, code_item);
     os << StringPrintf("\t\tINVOKE STUB: %p (offset=0x%08x size=%d)%s\n",
                        oat_method.GetInvokeStub(),
                        oat_method.GetInvokeStubOffset(),
                        oat_method.GetInvokeStubSize(),
                        oat_method.GetInvokeStub() != NULL ? "..." : "");
-    DumpCode(os, reinterpret_cast<const void*>(oat_method.GetInvokeStub()),
-             oat_method.GetInvokeStubSize(), NULL, dex_file, NULL);
+    DumpInvokeStub(os, oat_method);
   }
 
   void DumpSpillMask(std::ostream& os, uint32_t spill_mask, bool is_float) {
@@ -350,8 +352,8 @@
     os << ")";
   }
 
-  void DumpVmap(std::ostream& os, const uint16_t* raw_table, uint32_t core_spill_mask,
-                uint32_t fp_spill_mask) {
+  void DumpVmap(std::ostream& os, const OatFile::OatMethod& oat_method) {
+    const uint16_t* raw_table = oat_method.GetVmapTable();
     if (raw_table == NULL) {
       return;
     }
@@ -362,12 +364,12 @@
       uint16_t dex_reg = vmap_table[i];
       size_t matches = 0;
       size_t spill_shifts = 0;
-      uint32_t spill_mask = core_spill_mask;
+      uint32_t spill_mask = oat_method.GetCoreSpillMask();
       bool processing_fp = false;
       while (matches != (i + 1)) {
         if (spill_mask == 0) {
           CHECK(!processing_fp);
-          spill_mask = fp_spill_mask;
+          spill_mask = oat_method.GetFpSpillMask();
           processing_fp = true;
         }
         matches += spill_mask & 1;  // Add 1 if the low bit is set
@@ -388,11 +390,43 @@
     os << "\n";
   }
 
-  void DumpGcMap(std::ostream& os, const void* code, const uint8_t* gc_map_raw) {
+  void DescribeVReg(std::ostream& os, const OatFile::OatMethod& oat_method,
+                    const DexFile::CodeItem* code_item, size_t reg) {
+    const uint16_t* raw_table = oat_method.GetVmapTable();
+    if (raw_table != NULL) {
+      const VmapTable vmap_table(raw_table);
+      uint32_t vmap_offset;
+      if (vmap_table.IsInContext(reg, vmap_offset)) {
+        // Compute the register we need to load from the context
+        uint32_t spill_mask = oat_method.GetCoreSpillMask();
+        CHECK_LT(vmap_offset, static_cast<uint32_t>(__builtin_popcount(spill_mask)));
+        uint32_t matches = 0;
+        uint32_t spill_shifts = 0;
+        while (matches != (vmap_offset + 1)) {
+          DCHECK_NE(spill_mask, 0u);
+          matches += spill_mask & 1;  // Add 1 if the low bit is set
+          spill_mask >>= 1;
+          spill_shifts++;
+        }
+        spill_shifts--;  // wind back one as we want the last match
+        os << "r" << spill_shifts;
+      } else {
+        uint32_t offset = StackVisitor::GetVRegOffset(code_item, oat_method.GetCoreSpillMask(),
+                                                      oat_method.GetFpSpillMask(),
+                                                      oat_method.GetFrameSizeInBytes(), reg);
+        os << "[sp + #" << offset << "]";
+      }
+    }
+  }
+
+  void DumpGcMap(std::ostream& os, const OatFile::OatMethod& oat_method,
+                 const DexFile::CodeItem* code_item) {
+    const uint8_t* gc_map_raw = oat_method.GetNativeGcMap();
     if (gc_map_raw == NULL) {
       return;
     }
     NativePcOffsetToReferenceMap map(gc_map_raw);
+    const void* code = oat_method.GetCode();
     for (size_t entry = 0; entry < map.NumEntries(); entry++) {
       const uint8_t* native_pc = reinterpret_cast<const uint8_t*>(code) +
                                  map.GetNativePcOffset(entry);
@@ -403,10 +437,14 @@
       for (size_t reg = 0; reg < num_regs; reg++) {
         if (((reg_bitmap[reg / 8] >> (reg % 8)) & 0x01) != 0) {
           if (first) {
-            os << "  v" << reg;
+            os << "  v" << reg << " (";
+            DescribeVReg(os, oat_method, code_item, reg);
+            os << ")";
             first = false;
           } else {
-            os << ", v" << reg;
+            os << ", v" << reg << " (";
+            DescribeVReg(os, oat_method, code_item, reg);
+            os << ")";
           }
         }
       }
@@ -427,67 +465,115 @@
     uint32_t pc_to_dex_entries = *raw_table;
     ++raw_table;
 
-    os << "\t\t{";
+    os << "\t\tsuspend point mappings {";
     for (size_t i = 0; i < length; i += 2) {
       const uint8_t* native_pc = reinterpret_cast<const uint8_t*>(code) + raw_table[i];
       uint32_t dex_pc = raw_table[i + 1];
       os << StringPrintf("%p -> 0x%04x", native_pc, dex_pc);
       if (i + 2 == pc_to_dex_entries) {
         // Separate the pc -> dex from dex -> pc sections
-        os << "}\n\t\t{";
+        os << "}\n\t\tcatch entry mappings {";
       } else if (i + 2 < length) {
         os << ", ";
       }
     }
-    os << "}\n" << std::flush;
+    os << "}\n";
   }
 
-  void DumpCode(std::ostream& os, const void* code, int code_size,
-                const uint32_t* raw_mapping_table,
-                const DexFile& dex_file, const DexFile::CodeItem* code_item) {
-    if (code == NULL || code_size == 0) {
-      return;
-    }
-
-    const uint8_t* native_pc = reinterpret_cast<const uint8_t*>(code);
-    const uint8_t* end_native_pc = native_pc + code_size;
-
-    /*
-     * TODO: the mapping table is no longer useful for identifying Dalvik opcodes.  This was
-     * a nice feature, so we ought to come up with another mechanism (at least when debugging).
-     * Keeping the old Dalvik disassembly code for reference.
-     */
-    disassembler_->Dump(os, native_pc, end_native_pc);
-    (void)raw_mapping_table;
-    (void)dex_file;
-    (void)code_item;
-
-#if 0
-    if (raw_mapping_table == NULL) {
-      // code but no mapping table is most likely caused by code created by the JNI compiler
-      disassembler_->Dump(os, native_pc, end_native_pc);
-      return;
-    }
-
-    uint32_t length = *raw_mapping_table;
-    ++raw_mapping_table;
-
-    for (size_t i = 0; i < length; i += 2) {
-      uint32_t dex_pc = raw_mapping_table[i + 1];
-      const Instruction* instruction = Instruction::At(&code_item->insns_[dex_pc]);
-      os << StringPrintf("\t\t0x%04x: %s\n", dex_pc, instruction->DumpString(&dex_file).c_str());
-
-      const uint8_t* cur_pc = reinterpret_cast<const uint8_t*>(code) + raw_mapping_table[i];
-      const uint8_t* cur_pc_end = NULL;
-      if (i + 2 < length) {
-        cur_pc_end = reinterpret_cast<const uint8_t*>(code) + raw_mapping_table[i + 2];
+  void DumpMappingAtOffset(std::ostream& os, const OatFile::OatMethod& oat_method, size_t offset,
+                           bool suspend_point_mapping) {
+    const uint32_t* raw_table = oat_method.GetMappingTable();
+    if (raw_table != NULL) {
+      ++raw_table;
+      uint32_t length = *raw_table;
+      ++raw_table;
+      uint32_t pc_to_dex_entries = *raw_table;
+      ++raw_table;
+      size_t start, end;
+      if (suspend_point_mapping) {
+        start = 0;
+        end = pc_to_dex_entries;
       } else {
-        cur_pc_end = end_native_pc;
+        start = pc_to_dex_entries;
+        end = length;
       }
-      CHECK(cur_pc < cur_pc_end);
-      disassembler_->Dump(os, cur_pc, cur_pc_end);
+      for (size_t i = start; i < end; i += 2) {
+        if (offset == raw_table[i]) {
+          if (suspend_point_mapping) {
+            os << "\t\t\tsuspend point dex PC: ";
+          } else {
+            os << "\t\t\tcatch entry dex PC: ";
+          }
+          os << raw_table[i + 1] << "\n";
+          return;
+        }
+      }
     }
-#endif
+  }
+
+  void DumpGcMapAtOffset(std::ostream& os, const OatFile::OatMethod& oat_method,
+                         const DexFile::CodeItem* code_item, size_t offset) {
+    const uint8_t* gc_map_raw = oat_method.GetNativeGcMap();
+    if (gc_map_raw != NULL) {
+      NativePcOffsetToReferenceMap map(gc_map_raw);
+      if (map.HasEntry(offset)) {
+        size_t num_regs = map.RegWidth() * 8;
+        const uint8_t* reg_bitmap = map.FindBitMap(offset);
+        bool first = true;
+        for (size_t reg = 0; reg < num_regs; reg++) {
+          if (((reg_bitmap[reg / 8] >> (reg % 8)) & 0x01) != 0) {
+            if (first) {
+              os << "\t\t\tGC map objects:  v" << reg << " (";
+              DescribeVReg(os, oat_method, code_item, reg);
+              os << ")";
+              first = false;
+            } else {
+              os << ", v" << reg << " (";
+              DescribeVReg(os, oat_method, code_item, reg);
+              os << ")";
+            }
+          }
+        }
+        if (!first) {
+          os << "\n";
+        }
+      }
+    }
+  }
+
+  void DumpDexCode(std::ostream& os, const DexFile& dex_file, const DexFile::CodeItem* code_item) {
+    if (code_item != NULL) {
+      size_t i = 0;
+      while (i < code_item->insns_size_in_code_units_) {
+        const Instruction* instruction = Instruction::At(&code_item->insns_[i]);
+        os << StringPrintf("\t\t\t0x%04x: %s\n", i, instruction->DumpString(&dex_file).c_str());
+        i += instruction->SizeInCodeUnits();
+      }
+    }
+  }
+
+  void DumpCode(std::ostream& os, const OatFile::OatMethod& oat_method,
+                const DexFile::CodeItem* code_item) {
+    const void* code = oat_method.GetCode();
+    size_t code_size = oat_method.GetCodeSize();
+    if (code == NULL || code_size == 0) {
+      os << "\t\t\tNO CODE!\n";
+      return;
+    }
+    const uint8_t* native_pc = reinterpret_cast<const uint8_t*>(code);
+    size_t offset = 0;
+    while (offset < code_size) {
+      DumpMappingAtOffset(os, oat_method, offset, false);
+      offset += disassembler_->Dump(os, native_pc + offset);
+      DumpMappingAtOffset(os, oat_method, offset, true);
+      DumpGcMapAtOffset(os, oat_method, code_item, offset);
+    }
+  }
+
+  void DumpInvokeStub(std::ostream& os, const OatFile::OatMethod& oat_method) {
+    const uint8_t* begin = reinterpret_cast<const uint8_t*>(oat_method.GetInvokeStub());
+    const uint8_t* end = begin + oat_method.GetInvokeStubSize();
+    disassembler_->Dump(os, begin, end);
   }
 
   const std::string host_prefix_;