Add code dumping to oatdump.

Change-Id: I7dccdffb3bac422fee52a4bd82dfac220da91722
diff --git a/src/dex_instruction.cc b/src/dex_instruction.cc
index 2bb53d6..31e9d3a 100644
--- a/src/dex_instruction.cc
+++ b/src/dex_instruction.cc
@@ -290,28 +290,28 @@
   std::ostringstream os;
   const char* opcode = kInstructionNames[insn.opcode_];
   switch (Format()) {
-    case k10x: os << opcode; break;
-    case k12x: os << opcode << " v" << insn.vA_ << ", v" << insn.vB_; break;
-    case k11n: os << opcode << " v" << insn.vA_ << ", #+" << insn.vB_; break;
-    case k11x: os << opcode << " v" << insn.vA_; break;
-    case k10t: os << opcode << " +" << (int)insn.vA_; break;
-    case k20bc: os << opcode << " " << insn.vA_ << ", kind@" << insn.vB_; break;
-    case k20t: os << opcode << " +" << (int)insn.vA_; break;
-    case k22x: os << opcode << " v" << insn.vA_ << ", v" << insn.vB_; break;
-    case k21t: os << opcode << " v" << insn.vA_ << ", +" << insn.vB_; break;
-    case k21s: os << opcode << " v" << insn.vA_ << ", #+" << insn.vB_; break;
-    case k21h: os << opcode << " v" << insn.vA_ << ", #+" << insn.vB_ << "00000[00000000]"; break;
-    case k21c: os << opcode << " " << insn.vA_ << ", thing@" << insn.vB_; break;
-    case k23x: os << opcode << " v" << insn.vA_ << ", v" << insn.vB_ << ", v" << insn.vC_; break;
-    case k22b: os << opcode << " v" << insn.vA_ << ", v" << insn.vB_ << ", #+" << insn.vC_; break;
-    case k22t: os << opcode << " v" << insn.vA_ << ", v" << insn.vB_ << ", +" << insn.vC_; break;
-    case k22s: os << opcode << " v" << insn.vA_ << ", v" << insn.vB_ << ", #+" << insn.vC_; break;
-    case k22c: os << opcode << " v" << insn.vA_ << ", v" << insn.vB_ << ", thing@" << insn.vC_; break;
-    case k32x: os << opcode << " v" << insn.vA_ << ", v" << insn.vB_; break;
-    case k30t: os << opcode << " +" << (int)insn.vA_; break;
-    case k31t: os << opcode << " v" << insn.vA_ << ", +" << insn.vB_; break;
-    case k31i: os << opcode << " v" << insn.vA_ << ", #+" << insn.vB_; break;
-    case k31c: os << opcode << " v" << insn.vA_ << ", thing@" << insn.vB_; break;
+    case k10x:  os << opcode; break;
+    case k12x:  os << StringPrintf("%s v%d, v%d", opcode, insn.vA_, insn.vB_); break;
+    case k11n:  os << StringPrintf("%s v%d, #%+d", opcode, insn.vA_, insn.vB_); break;
+    case k11x:  os << StringPrintf("%s v%d", opcode, insn.vA_); break;
+    case k10t:  os << StringPrintf("%s %+d", opcode, insn.vA_); break;
+    case k20bc: os << StringPrintf("%s %d, kind@%d", opcode, insn.vA_, insn.vB_); break;
+    case k20t:  os << StringPrintf("%s %+d", opcode, insn.vA_); break;
+    case k22x:  os << StringPrintf("%s v%d, v%d", opcode, insn.vA_, insn.vB_); break;
+    case k21t:  os << StringPrintf("%s v%d, %+d", opcode, insn.vA_, insn.vB_); break;
+    case k21s:  os << StringPrintf("%s v%d, #%+d", opcode, insn.vA_, insn.vB_); break;
+    case k21h:  os << StringPrintf("%s v%d, #%+d00000[00000000]", opcode, insn.vA_, insn.vB_); break;
+    case k21c:  os << StringPrintf("%s v%d, thing@%d", opcode, insn.vA_, insn.vB_); break;
+    case k23x:  os << StringPrintf("%s v%d, v%d, v%d", opcode, insn.vA_, insn.vB_, insn.vC_); break;
+    case k22b:  os << StringPrintf("%s v%d, v%d, #%+d", opcode, insn.vA_, insn.vB_, insn.vC_); break;
+    case k22t:  os << StringPrintf("%s v%d, v%d, %+d", opcode, insn.vA_, insn.vB_, insn.vC_); break;
+    case k22s:  os << StringPrintf("%s v%d, v%d, #%+d", opcode, insn.vA_, insn.vB_, insn.vC_); break;
+    case k22c:  os << StringPrintf("%s v%d, v%d, thing@%d", opcode, insn.vA_, insn.vB_, insn.vC_); break;
+    case k32x:  os << StringPrintf("%s v%d, v%d", opcode, insn.vA_, insn.vB_); break;
+    case k30t:  os << StringPrintf("%s %+d", opcode, insn.vA_); break;
+    case k31t:  os << StringPrintf("%s v%d, %+d", opcode, insn.vA_, insn.vB_); break;
+    case k31i:  os << StringPrintf("%s v%d, #%+d", opcode, insn.vA_, insn.vB_); break;
+    case k31c:  os << StringPrintf("%s v%d, thing@%d", opcode, insn.vA_, insn.vB_); break;
     case k35c: {
       switch (insn.opcode_) {
         case INVOKE_VIRTUAL:
@@ -321,11 +321,17 @@
         case INVOKE_INTERFACE:
           if (file != NULL) {
             const DexFile::MethodId& meth_id = file->GetMethodId(insn.vB_);
-            os << opcode << " {v" << insn.arg_[0] << ", v" << insn.arg_[1] << ", v" << insn.arg_[2]
-                         << ", v" << insn.arg_[3] << ", v" << insn.arg_[4] << "}, "
-                         << file->GetMethodDeclaringClassDescriptor(meth_id) << "."
-                         << file->GetMethodName(meth_id) << file->GetMethodSignature(meth_id)
-                         << " // method@" << insn.vB_;
+            os << opcode << " {";
+            for (size_t i = 0; i < insn.vA_; ++i) {
+              if (i != 0) {
+                os << ", ";
+              }
+              os << "v" << insn.arg_[i];
+            }
+            os << "}, "
+               << file->GetMethodDeclaringClassDescriptor(meth_id) << "."
+               << file->GetMethodName(meth_id) << file->GetMethodSignature(meth_id)
+               << " // method@" << insn.vB_;
             break;
           }  // else fall-through
         default:
@@ -335,8 +341,8 @@
       }
       break;
     }
-    case k3rc: os << opcode << " {v" << insn.vC_ << " .. v" << (insn.vC_+ insn.vA_ - 1) << "}, method@" << insn.vB_; break;
-    case k51l: os << opcode << " v" << insn.vA_ << ", #+" << insn.vB_; break;
+    case k3rc: os << StringPrintf("%s, {v%d .. v%d}, method@%d", opcode, insn.vC_, (insn.vC_+ insn.vA_ - 1), insn.vB_); break;
+    case k51l: os << StringPrintf("%s v%d, #%+d", opcode, insn.vA_, insn.vB_); break;
     default: os << " unknown format (" << DumpHex(5) << ")"; break;
   }
   return os.str();
diff --git a/src/oat_file.h b/src/oat_file.h
index 83560e8..831934a 100644
--- a/src/oat_file.h
+++ b/src/oat_file.h
@@ -221,7 +221,7 @@
 
   friend class OatClass;
   friend class OatDexFile;
-  friend class OatDump;  // For GetBase and GetLimit
+  friend class OatDumper;  // For GetBase and GetLimit
   DISALLOW_COPY_AND_ASSIGN(OatFile);
 };
 
diff --git a/src/oatdump.cc b/src/oatdump.cc
index 7c9fa92..337c907 100644
--- a/src/oatdump.cc
+++ b/src/oatdump.cc
@@ -24,6 +24,7 @@
 #include <vector>
 
 #include "class_linker.h"
+#include "dex_instruction.h"
 #include "file.h"
 #include "image.h"
 #include "object_utils.h"
@@ -79,11 +80,9 @@
   "kClassRoots",
 };
 
-class OatDump {
+class OatDumper {
  public:
-  static void Dump(const std::string& oat_filename,
-                   std::ostream& os,
-                   const OatFile& oat_file) {
+  void Dump(const std::string& oat_filename, std::ostream& os, const OatFile& oat_file) {
     const OatHeader& oat_header = oat_file.GetOatHeader();
 
     os << "MAGIC:\n";
@@ -107,6 +106,8 @@
     os << std::flush;
 
     std::vector<const OatFile::OatDexFile*> oat_dex_files = oat_file.GetOatDexFiles();
+    AddAllOffsets(oat_file, oat_dex_files);
+
     for (size_t i = 0; i < oat_dex_files.size(); i++) {
       const OatFile::OatDexFile* oat_dex_file = oat_dex_files[i];
       CHECK(oat_dex_file != NULL);
@@ -115,9 +116,54 @@
   }
 
  private:
-  static void DumpOatDexFile(std::ostream& os,
-                             const OatFile& oat_file,
-                             const OatFile::OatDexFile& oat_dex_file) {
+  void AddAllOffsets(const OatFile& oat_file, std::vector<const OatFile::OatDexFile*>& oat_dex_files) {
+    // We don't know the length of the code for each method, but we need to know where to stop
+    // when disassembling. What we do know is that a region of code will be followed by some other
+    // region, so if we keep a sorted sequence of the start of each region, we can infer the length
+    // of a piece of code by using upper_bound to find the start of the next region.
+    for (size_t i = 0; i < oat_dex_files.size(); i++) {
+      const OatFile::OatDexFile* oat_dex_file = oat_dex_files[i];
+      CHECK(oat_dex_file != NULL);
+      UniquePtr<const DexFile> dex_file(oat_dex_file->OpenDexFile());
+      if (dex_file.get() == NULL) {
+        return;
+      }
+      for (size_t class_def_index = 0; class_def_index < dex_file->NumClassDefs(); class_def_index++) {
+        const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index);
+        UniquePtr<const OatFile::OatClass> oat_class(oat_dex_file->GetOatClass(class_def_index));
+        const byte* class_data = dex_file->GetClassData(class_def);
+        if (class_data != NULL) {
+          ClassDataItemIterator it(*dex_file, class_data);
+          SkipAllFields(it);
+          uint32_t class_method_index = 0;
+          while (it.HasNextDirectMethod()) {
+            AddOffsets(oat_class->GetOatMethod(class_method_index++));
+            it.Next();
+          }
+          while (it.HasNextVirtualMethod()) {
+            AddOffsets(oat_class->GetOatMethod(class_method_index++));
+            it.Next();
+          }
+        }
+      }
+    }
+
+    // If the last thing in the file is code for a method, there won't be an offset for the "next"
+    // thing. Instead of having a special case in the upper_bound code, let's just add an entry
+    // for the end of the file.
+    offsets_.insert(static_cast<uint32_t>(oat_file.End() - oat_file.Begin()));
+  }
+
+  void AddOffsets(const OatFile::OatMethod& oat_method) {
+    offsets_.insert(oat_method.GetCodeOffset());
+    offsets_.insert(oat_method.GetMappingTableOffset());
+    offsets_.insert(oat_method.GetVmapTableOffset());
+    offsets_.insert(oat_method.GetGcMapOffset());
+    offsets_.insert(oat_method.GetInvokeStubOffset());
+  }
+
+  void DumpOatDexFile(std::ostream& os, const OatFile& oat_file,
+                      const OatFile::OatDexFile& oat_dex_file) {
     os << "OAT DEX FILE:\n";
     os << StringPrintf("location: %s\n", oat_dex_file.GetDexFileLocation().c_str());
     os << StringPrintf("checksum: %08x\n", oat_dex_file.GetDexFileLocationChecksum());
@@ -139,52 +185,51 @@
     os << std::flush;
   }
 
-  static void DumpOatClass(std::ostream& os,
-                           const OatFile& oat_file,
-                           const OatFile::OatClass& oat_class,
-                           const DexFile& dex_file,
-                           const DexFile::ClassDef& class_def) {
-    const byte* class_data = dex_file.GetClassData(class_def);
-    if (class_data == NULL) {  // empty class such as a marker interface?
-      return;
-    }
-    ClassDataItemIterator it(dex_file, class_data);
-
-    // just skipping through the fields to advance class_data
+  static void SkipAllFields(ClassDataItemIterator& it) {
     while (it.HasNextStaticField()) {
       it.Next();
     }
     while (it.HasNextInstanceField()) {
       it.Next();
     }
+  }
 
-    uint32_t method_index = 0;
+  void DumpOatClass(std::ostream& os, const OatFile& oat_file, const OatFile::OatClass& oat_class,
+      const DexFile& dex_file, const DexFile::ClassDef& class_def) {
+    const byte* class_data = dex_file.GetClassData(class_def);
+    if (class_data == NULL) {  // empty class such as a marker interface?
+      return;
+    }
+    ClassDataItemIterator it(dex_file, class_data);
+    SkipAllFields(it);
+
+    uint32_t class_method_index = 0;
     while (it.HasNextDirectMethod()) {
-      const OatFile::OatMethod oat_method = oat_class.GetOatMethod(method_index);
-      DumpOatMethod(os, method_index, oat_file, oat_method, dex_file, it.GetMemberIndex());
-      method_index++;
+      const OatFile::OatMethod oat_method = oat_class.GetOatMethod(class_method_index);
+      DumpOatMethod(os, class_method_index, oat_file, oat_method, dex_file,
+          it.GetMemberIndex(), it.GetMethodCodeItem());
+      class_method_index++;
       it.Next();
     }
     while (it.HasNextVirtualMethod()) {
-      const OatFile::OatMethod oat_method = oat_class.GetOatMethod(method_index);
-      DumpOatMethod(os, method_index, oat_file, oat_method, dex_file, it.GetMemberIndex());
-      method_index++;
+      const OatFile::OatMethod oat_method = oat_class.GetOatMethod(class_method_index);
+      DumpOatMethod(os, class_method_index, oat_file, oat_method, dex_file,
+          it.GetMemberIndex(), it.GetMethodCodeItem());
+      class_method_index++;
       it.Next();
     }
     DCHECK(!it.HasNext());
     os << std::flush;
   }
-  static void DumpOatMethod(std::ostream& os,
-                            uint32_t method_index,
-                            const OatFile& oat_file,
-                            const OatFile::OatMethod& oat_method,
-                            const DexFile& dex_file,
-                            uint32_t method_idx) {
-    const DexFile::MethodId& method_id = dex_file.GetMethodId(method_idx);
+
+  void DumpOatMethod(std::ostream& os, uint32_t class_method_index, const OatFile& oat_file,
+                     const OatFile::OatMethod& oat_method, const DexFile& dex_file,
+                     uint32_t dex_method_idx, const DexFile::CodeItem* code_item) {
+    const DexFile::MethodId& method_id = dex_file.GetMethodId(dex_method_idx);
     const char* name = dex_file.GetMethodName(method_id);
     std::string signature(dex_file.GetMethodSignature(method_id));
-    os << StringPrintf("\t%d: %s %s (method_idx=%d)\n",
-                       method_index, name, signature.c_str(), method_idx);
+    os << StringPrintf("\t%d: %s %s (dex_method_idx=%d)\n",
+                       class_method_index, name, signature.c_str(), dex_method_idx);
     os << StringPrintf("\t\tcode: %p (offset=%08x)\n",
                        oat_method.GetCode(), oat_method.GetCodeOffset());
     os << StringPrintf("\t\tframe_size_in_bytes: %zd\n",
@@ -195,6 +240,7 @@
                        oat_method.GetFpSpillMask());
     os << StringPrintf("\t\tmapping_table: %p (offset=%08x)\n",
                        oat_method.GetMappingTable(), oat_method.GetMappingTableOffset());
+    DumpMappingTable(os, oat_file, oat_method, dex_file, code_item);
     os << StringPrintf("\t\tvmap_table: %p (offset=%08x)\n",
                        oat_method.GetVmapTable(), oat_method.GetVmapTableOffset());
     os << StringPrintf("\t\tgc_map: %p (offset=%08x)\n",
@@ -202,6 +248,48 @@
     os << StringPrintf("\t\tinvoke_stub: %p (offset=%08x)\n",
                        oat_method.GetInvokeStub(), oat_method.GetInvokeStubOffset());
   }
+
+  void DumpMappingTable(std::ostream& os,
+      const OatFile& oat_file, const OatFile::OatMethod& oat_method,
+      const DexFile& dex_file, const DexFile::CodeItem* code_item) {
+    const uint32_t* raw_table = oat_method.GetMappingTable();
+    const void* code = oat_method.GetCode();
+    if (raw_table == NULL || code == NULL) {
+      return;
+    }
+
+    uint32_t length = *raw_table;
+    ++raw_table;
+
+    for (size_t i = 0; i < length; i += 2) {
+      uint32_t dex_pc = raw_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());
+
+      // TODO: this is thumb2-specific.
+      const uint8_t* native_pc = reinterpret_cast<const uint8_t*>(code) + raw_table[i];
+      const uint8_t* end_native_pc = NULL;
+      if (i + 2 < length) {
+        end_native_pc = reinterpret_cast<const uint8_t*>(code) + raw_table[i + 2];
+      } else {
+        const uint8_t* oat_begin = reinterpret_cast<const uint8_t*>(oat_file.Begin());
+        uint32_t last_offset = static_cast<uint32_t>(native_pc - oat_begin);
+
+        typedef std::set<uint32_t>::iterator It;
+        It it = offsets_.lower_bound(last_offset);
+        CHECK(it != offsets_.end());
+        end_native_pc = reinterpret_cast<const uint8_t*>(oat_begin) + *it;
+      }
+
+      // TODO: insert disassembler here.
+      CHECK(native_pc <= end_native_pc);
+      for (; native_pc < end_native_pc; native_pc += 2) {
+        os << StringPrintf("\t\t\t%p: 0x%04x\n", native_pc, *reinterpret_cast<const uint16_t*>(native_pc));
+      }
+    }
+  }
+
+  std::set<uint32_t> offsets_;
 };
 
 class ImageDump {
@@ -292,7 +380,8 @@
     os << "\n";
     os << std::flush;
 
-    OatDump::Dump(oat_location, os, *oat_file);
+    OatDumper oat_dumper;
+    oat_dumper.Dump(oat_location, os, *oat_file);
   }
 
  private:
@@ -632,7 +721,8 @@
       fprintf(stderr, "Failed to open oat file from %s\n", oat_filename);
       return EXIT_FAILURE;
     }
-    OatDump::Dump(oat_filename, *os, *oat_file);
+    OatDumper oat_dumper;
+    oat_dumper.Dump(oat_filename, *os, *oat_file);
     return EXIT_SUCCESS;
   }