Extra verbosity and disassembler for oatdump

Disassembler for thumb2 instructions.
Print outlying methods for ART files.
Find oat code methods that require trampolines.
Fix accounting of code and which file contains which bytes.
Dump oat meta-data.

Change-Id: I10f52a4841f57a3f65d61ce0d1bb71bb4eb9f595
diff --git a/build/Android.common.mk b/build/Android.common.mk
index 05228c7..73c5a00 100644
--- a/build/Android.common.mk
+++ b/build/Android.common.mk
@@ -110,7 +110,9 @@
 	src/dex2oat.cc
 
 OATDUMP_SRC_FILES := \
-	src/oatdump.cc
+	src/oatdump.cc \
+	src/disassembler.cc \
+	src/disassembler_arm.cc
 
 OATEXEC_SRC_FILES := \
 	src/oatexec.cc
diff --git a/src/disassembler.cc b/src/disassembler.cc
new file mode 100644
index 0000000..ae781dc
--- /dev/null
+++ b/src/disassembler.cc
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+#include "disassembler.h"
+
+#include <iostream>
+
+#include "disassembler_arm.h"
+#include "logging.h"
+
+namespace art {
+
+Disassembler* Disassembler::Create(InstructionSet instruction_set) {
+  if (instruction_set == kArm || instruction_set == kThumb2) {
+    return new arm::DisassemblerArm();
+  } else {
+    // TODO: give a better fatal message
+    UNIMPLEMENTED(FATAL);
+    return NULL;
+  }
+}
+
+}  // namespace art
diff --git a/src/disassembler.h b/src/disassembler.h
new file mode 100644
index 0000000..de9c8d1
--- /dev/null
+++ b/src/disassembler.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2012 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_SRC_DISASSEMBLER_H_
+#define ART_SRC_DISASSEMBLER_H_
+
+#include "constants.h"
+
+namespace art {
+
+class Disassembler {
+ public:
+  static Disassembler* Create(InstructionSet instruction_set);
+  virtual ~Disassembler() {}
+
+  virtual void Dump(std::ostream& os, const uint8_t* begin, const uint8_t* end) = 0;
+};
+
+}  // namespace art
+
+#endif  // ART_SRC_DISASSEMBLER_H_
diff --git a/src/disassembler_arm.cc b/src/disassembler_arm.cc
new file mode 100644
index 0000000..a2c890d
--- /dev/null
+++ b/src/disassembler_arm.cc
@@ -0,0 +1,482 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+#include "disassembler_arm.h"
+
+#include "stringprintf.h"
+
+#include <iostream>
+
+namespace art {
+namespace arm {
+
+DisassemblerArm::DisassemblerArm() {
+}
+
+
+void DisassemblerArm::Dump(std::ostream& os, const uint8_t* begin, const uint8_t* end) {
+  if ((reinterpret_cast<intptr_t>(begin) & 1) == 0) {
+    for (const uint8_t* cur = begin; cur < end; cur += 4) {
+      DumpArm(os, cur);
+    }
+  } else {
+    // remove thumb specifier bits
+    begin = reinterpret_cast<const uint8_t*>(reinterpret_cast<uintptr_t>(begin) & ~1);
+    end = reinterpret_cast<const uint8_t*>(reinterpret_cast<uintptr_t>(end) & ~1);
+    for (const uint8_t* cur = begin; cur < end;) {
+      cur += DumpThumb16(os, cur);
+    }
+  }
+}
+
+void DisassemblerArm::DumpReg(std::ostream& os, uint32_t reg) {
+  switch (reg) {
+    case 14: os << "LR"; break;
+    case 15: os << "PC"; break;
+    default: os << "r" << reg; break;
+  }
+}
+
+void DisassemblerArm::DumpRegList(std::ostream& os, uint32_t reg_list) {
+  if (reg_list == 0) {
+    os << "<no register list?>";
+    return;
+  }
+  bool first = true;
+  for (size_t i = 0; i < 16; i++) {
+    if ((reg_list & (1 << i)) != 0) {
+      if (first) {
+        os << "{";
+        first = false;
+      } else {
+        os << ", ";
+      }
+      DumpReg(os, i);
+    }
+  }
+  os << "}";
+
+}
+
+static uint32_t ReadU16(const uint8_t* ptr) {
+  return ptr[0] | (ptr[1] << 8);
+}
+
+static uint32_t ReadU32(const uint8_t* ptr) {
+  return ptr[0] | (ptr[1] << 8) | (ptr[2] << 16) | (ptr[3] << 24);
+}
+
+
+void DisassemblerArm::DumpArm(std::ostream& os, const uint8_t* instr_ptr) {
+  os << StringPrintf("\t\t\t%p: %08x\n", instr_ptr, ReadU32(instr_ptr));
+}
+
+size_t DisassemblerArm::DumpThumb32(std::ostream& os, const uint8_t* instr_ptr) {
+  uint32_t instr = (ReadU16(instr_ptr) << 16) | ReadU16(instr_ptr + 2);
+  // |111|1 1|1000000|0000|1111110000000000|
+  // |5 3|2 1|0987654|3  0|5    0    5    0|
+  // |---|---|-------|----|----------------|
+  // |332|2 2|2222222|1111|1111110000000000|
+  // |1 9|8 7|6543210|9  6|5    0    5    0|
+  // |---|---|-------|----|----------------|
+  // |111|op1| op2   |    |                |
+  uint32_t op1 = (instr >> 27) & 3;
+  uint32_t op2 = (instr >> 20) & 0x7F;
+  os << StringPrintf("\t\t\t%p: ", instr_ptr);
+  switch (op1) {
+    case 0:
+      return DumpThumb16(os, instr_ptr);
+      break;
+    case 1:
+      switch (op2) {
+        case 0x00: case 0x01: case 0x02: case 0x03: case 0x08: case 0x09: case 0x0A: case 0x0B:
+        case 0x10: case 0x11: case 0x12: case 0x13: case 0x18: case 0x19: case 0x1A: case 0x1B: {
+          // |111|11|10|00|0|00|0000|1111110000000000|
+          // |5 3|21|09|87|6|54|3  0|5    0    5    0|
+          // |---|--|--|--|-|--|----|----------------|
+          // |332|22|22|22|2|22|1111|1111110000000000|
+          // |1 9|87|65|43|2|10|9  6|5    0    5    0|
+          // |---|--|--|--|-|--|----|----------------|
+          // |111|01|00|op|0|WL| Rn |                |
+          // |111|01| op2      |    |                |
+          // STM - 111 01 00-01-0-W0 nnnn rrrrrrrrrrrrrrrr
+          // LDM - 111 01 00-01-0-W1 nnnn rrrrrrrrrrrrrrrr
+          // PUSH- 111 01 00-01-0-10 1101 0M0rrrrrrrrrrrrr
+          // POP - 111 01 00-01-0-11 1101 PM0rrrrrrrrrrrrr
+          uint32_t op = (instr >> 23) & 3;
+          uint32_t W = (instr >> 21) & 1;
+          uint32_t L = (instr >> 20) & 1;
+          uint32_t Rn = (instr >> 16) & 0xF;
+          uint32_t reg_list = instr & 0xFFFF;
+          if (op == 1 || op == 2) {
+            if (op == 1) {
+              if (L == 0) {
+                os << "STM ";
+                DumpReg(os, Rn);
+                if (W == 0) {
+                  os << ", ";
+                } else {
+                  os << "!, ";
+                }
+              } else {
+                if (Rn != 13) {
+                  os << "LDM ";
+                  DumpReg(os, Rn);
+                  if (W == 0) {
+                    os << ", ";
+                  } else {
+                    os << "!, ";
+                  }
+                } else {
+                  os << "POP ";
+                }
+              }
+            } else {
+              if (L == 0) {
+                if (Rn != 13) {
+                  os << "STMDB ";
+                  DumpReg(os, Rn);
+                  if (W == 0) {
+                    os << ", ";
+                  } else {
+                    os << "!, ";
+                  }
+                } else {
+                  os << "PUSH ";
+                }
+              } else {
+                os << "LDMDB ";
+                DumpReg(os, Rn);
+                if (W == 0) {
+                  os << ", ";
+                } else {
+                  os << "!, ";
+                }
+              }
+            }
+            DumpRegList(os, reg_list);
+            os << "  // ";
+          }
+          break;
+        }
+        default:
+          break;
+      }
+      break;
+    case 3:
+      switch (op2) {
+        case 0x00: case 0x02: case 0x04: case 0x06:  // 000xxx0
+        case 0x08: case 0x0A: case 0x0C: case 0x0E: {
+          // Store single data item
+          uint32_t op3 = (instr >> 21) & 7;
+          //uint32_t op4 = (instr >> 6) & 0x3F;
+          switch (op3) {
+            case 0x2: case 0x6: {
+              // Load word
+              // |111|11|100|000|0|0000|1111|110000|000000|
+              // |5 3|21|098|765|4|3  0|5  2|10   6|5    0|
+              // |---|--|---|---|-|----|----|------|------|
+              // |332|22|222|222|2|1111|1111|110000|000000|
+              // |1 9|87|654|321|0|9  6|5  2|10   6|5    0|
+              // |---|--|---|---|-|----|----|------|------|
+              // |111|11|000|op3|0|    |    |  op4 |      |
+
+              // STR.W Rt, [Rn, #imm12] - 111 11 000 110 0 nnnn tttt iiiiiiiiiiii
+              // STR Rt, [Rn, #imm8]    - 111 11 000 010 0 nnnn tttt 1PUWiiiiiiii
+              uint32_t Rn = (instr >> 16) & 0xF;
+              uint32_t Rt = (instr >> 12) & 0xF;
+              if (op3 == 6) {
+                uint32_t imm12 = instr & 0xFFF;
+                os << "STR.W ";
+                DumpReg(os, Rt);
+                os << ", [";
+                DumpReg(os, Rn);
+                os << ", #" << imm12 << "]  // ";
+              }
+            }
+          }
+
+          break;
+        }
+        case 0x05: case 0x0D: case 0x15: case 0x1D: { // 00xx101
+          // Load word
+          // |111|11|10|0 0|00|0|0000|1111|110000|000000|
+          // |5 3|21|09|8 7|65|4|3  0|5  2|10   6|5    0|
+          // |---|--|--|---|--|-|----|----|------|------|
+          // |332|22|22|2 2|22|2|1111|1111|110000|000000|
+          // |1 9|87|65|4 3|21|0|9  6|5  2|10   6|5    0|
+          // |---|--|--|---|--|-|----|----|------|------|
+          // |111|11|00|op3|10|1| Rn | Rt | op4  |      |
+          // |111|11| op2       |    |    | imm12       |
+          uint32_t op3 = (instr >> 23) & 3;
+          uint32_t op4 = (instr >> 6) & 0x3F;
+          uint32_t Rn = (instr >> 16) & 0xF;
+          uint32_t Rt = (instr >> 12) & 0xF;
+          if (op3 == 1 || Rn == 15) {
+            // LDR.W Rt, [Rn, #imm12]          - 111 11 00 00 101 nnnn tttt iiiiiiiiiiii
+            // LDR.W Rt, [PC, #imm12]          - 111 11 00 0x 101 1111 tttt iiiiiiiiiiii
+            uint32_t imm12 = instr & 0xFFF;
+            os << "LDR.W ";
+            DumpReg(os, Rt);
+            os << ", [";
+            DumpReg(os, Rn);
+            os << ", #" << imm12 << "]  // ";
+          } else if (op4 == 0) {
+            // LDR.W Rt, [Rn, Rm{, LSL #imm2}] - 111 11 00 00 101 nnnn tttt 000000iimmmm
+            uint32_t imm2 = (instr >> 4) & 0xF;
+            uint32_t Rm = instr & 0xF;
+            os << "LDR.W ";
+            DumpReg(os, Rt);
+            os << ", [";
+            DumpReg(os, Rn);
+            os << ", ";
+            DumpReg(os, Rm);
+            if (imm2 != 0) {
+              os << ", LSL #" << imm2;
+            }
+            os << "]  // ";
+          } else {
+            // LDRT Rt, [Rn, #imm8]            - 111 11 00 00 101 nnnn tttt 1110iiiiiiii
+            uint32_t imm8 = instr & 0xFF;
+            os << "LDRT ";
+            DumpReg(os, Rt);
+            os << ", [";
+            DumpReg(os, Rn);
+            os << ", #" << imm8 << "]  // ";
+          }
+          break;
+        }
+      }
+    default:
+      break;
+  }
+  os << StringPrintf("%08x\n", instr);
+  return 4;
+}
+
+size_t DisassemblerArm::DumpThumb16(std::ostream& os, const uint8_t* instr_ptr) {
+  uint16_t instr = ReadU16(instr_ptr);
+  bool is_32bit = ((instr & 0xF000) == 0xF000) || ((instr & 0xF800) == 0xE800);
+  if (is_32bit) {
+    return DumpThumb32(os, instr_ptr);
+  } else {
+    os << StringPrintf("\t\t\t%p: ", instr_ptr);
+    uint16_t opcode1 = instr >> 10;
+    if (opcode1 < 0x10) {
+      // shift (immediate), add, subtract, move, and compare
+      uint16_t opcode2 = instr >> 9;
+      switch (opcode2) {
+        case 0x0: case 0x1: case 0x2: case 0x3: case 0x4: case 0x5: case 0x6: case 0x7:
+        case 0x8: case 0x9: case 0xA: case 0xB: {
+          // Logical shift left     - 00 000xx xxxxxxxxx
+          // Logical shift right    - 00 001xx xxxxxxxxx
+          // Arithmetic shift right - 00 010xx xxxxxxxxx
+          uint16_t imm5 = (instr >> 6) & 0x1F;
+          uint16_t Rm = (instr >> 3) & 7;
+          uint16_t Rd = instr & 7;
+          if (opcode2 <= 3) {
+            os << "LSLS ";
+          } else if (opcode2 <= 7) {
+            os << "LSRS ";
+          } else {
+            os << "ASRS ";
+          }
+          DumpReg(os, Rd);
+          os << ", ";
+          DumpReg(os, Rm);
+          os << ", #" << imm5 << "  // ";
+          break;
+        }
+        case 0xC: case 0xD: case 0xE: case 0xF: {
+          // Add register        - 00 01100 mmm nnn ddd
+          // Sub register        - 00 01101 mmm nnn ddd
+          // Add 3-bit immediate - 00 01110 iii nnn ddd
+          // Sub 3-bit immediate - 00 01111 iii nnn ddd
+          uint16_t imm3_or_Rm = (instr >> 6) & 7;
+          uint16_t Rn = (instr >> 3) & 7;
+          uint16_t Rd = instr & 7;
+          if ((opcode2 & 2) != 0 && imm3_or_Rm == 0) {
+            os << "MOV ";
+          } else {
+            if ((opcode2 & 1) == 0) {
+              os << "ADDS ";
+            } else {
+              os << "SUBS ";
+            }
+          }
+          DumpReg(os, Rd);
+          os << ", ";
+          DumpReg(os, Rn);
+          if ((opcode2 & 2) == 0) {
+            os << ", ";
+            DumpReg(os, imm3_or_Rm);
+          } else if (imm3_or_Rm != 0) {
+            os << ", #" << imm3_or_Rm;
+          }
+          os << "  // ";
+          break;
+        }
+        case 0x10: case 0x11: case 0x12: case 0x13:
+        case 0x14: case 0x15: case 0x16: case 0x17:
+        case 0x18: case 0x19: case 0x1A: case 0x1B:
+        case 0x1C: case 0x1D: case 0x1E: case 0x1F: {
+          // MOVS Rd, #imm8 - 00100 ddd iiiiiiii
+          // CMP  Rn, #imm8 - 00101 nnn iiiiiiii
+          // ADDS Rn, #imm8 - 00110 nnn iiiiiiii
+          // SUBS Rn, #imm8 - 00111 nnn iiiiiiii
+          uint16_t Rn = (instr >> 8) & 7;
+          uint16_t imm8 = instr & 0xFF;
+          switch (opcode2 >> 2) {
+            case 4: os << "MOVS "; break;
+            case 5: os << "CMP "; break;
+            case 6: os << "ADDS "; break;
+            case 7: os << "SUBS "; break;
+          }
+          DumpReg(os, Rn);
+          os << ", #" << imm8 << "  // ";
+          break;
+        }
+        default:
+          break;
+      }
+    } else if (opcode1 == 0x11) {
+      // Special data instructions and branch and exchange
+      uint16_t opcode2 = (instr >> 6) & 0x0F;
+      switch (opcode2) {
+        case 0x0: case 0x1: case 0x2: case 0x3: {
+          // Add low registers  - 010001 0000 xxxxxx
+          // Add high registers - 010001 0001/001x xxxxxx
+          uint16_t DN = (instr >> 7) & 1;
+          uint16_t Rm = (instr >> 3) & 0xF;
+          uint16_t Rdn = instr & 7;
+          uint16_t DN_Rdn = (DN << 3) | Rdn;
+          os << "ADD ";
+          DumpReg(os, DN_Rdn);
+          os << ", ";
+          DumpReg(os, Rm);
+          os << "  // ";
+          break;
+        }
+        case 0x8: case 0x9: case 0xA: case 0xB: {
+          // Move low registers  - 010001 1000 xxxxxx
+          // Move high registers - 010001 1001/101x xxxxxx
+          uint16_t DN = (instr >> 7) & 1;
+          uint16_t Rm = (instr >> 3) & 0xF;
+          uint16_t Rdn = instr & 7;
+          uint16_t DN_Rdn = (DN << 3) | Rdn;
+          os << "MOV ";
+          DumpReg(os, DN_Rdn);
+          os << ", ";
+          DumpReg(os, Rm);
+          os << "  // ";
+          break;
+        }
+        case 0x5: case 0x6: case 0x7: {
+          // Compare high registers - 010001 0101/011x xxxxxx
+          uint16_t N = (instr >> 7) & 1;
+          uint16_t Rm = (instr >> 3) & 0xF;
+          uint16_t Rn = instr & 7;
+          uint16_t N_Rn = (N << 3) | Rn;
+          os << "CMP ";
+          DumpReg(os, N_Rn);
+          os << ", ";
+          DumpReg(os, Rm);
+          os << "  // ";
+          break;
+        }
+        case 0xC: case 0xD: case 0xE: case 0xF: {
+          // Branch and exchange           - 010001 110x xxxxxx
+          // Branch with link and exchange - 010001 111x xxxxxx
+          uint16_t Rm = instr >> 3 & 0xF;
+          if ((opcode2 & 0x2) == 0) {
+            os << "BX ";
+          } else {
+            os << "BLX ";
+          }
+          DumpReg(os, Rm);
+          os << "  // ";
+          break;
+        }
+        default:
+          break;
+      }
+    } else if ((instr & 0xF000) == 0xB000) {
+      // Miscellaneous 16-bit instructions
+      uint16_t opcode2 = (instr >> 5) & 0x7F;
+      switch (opcode2) {
+        case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: case 0x06: case 0x07: {
+          // Add immediate to SP        - 1011 00000 ii iiiii
+          // Subtract immediate from SP - 1011 00001 ii iiiii
+          int imm7 = instr & 0x7F;
+          if ((opcode2 & 4) == 0) {
+            os << "ADD SP, SP, #";
+          } else {
+            os << "SUB SP, SP, #";
+          }
+          os << (imm7 << 2) << "  // ";
+          break;
+        }
+        default:
+          break;
+      }
+    } else if (((instr & 0xF000) == 0x5000) || ((instr & 0xE000) == 0x6000) ||
+        ((instr & 0xE000) == 0x8000)) {
+      // Load/store single data item
+      uint16_t opA = instr >> 12;
+      //uint16_t opB = (instr >> 9) & 7;
+      switch (opA) {
+        case 0x6: {
+          // STR Rt, Rn, #imm - 01100 iiiii nnn ttt
+          // LDR Rt, Rn, #imm - 01101 iiiii nnn ttt
+          uint16_t imm5 = (instr >> 6) & 0x1F;
+          uint16_t Rn = (instr >> 3) & 7;
+          uint16_t Rt = instr & 7;
+          if ((instr & 0x800) == 0) {
+            os << "STR ";
+          } else {
+            os << "LDR ";
+          }
+          DumpReg(os, Rt);
+          os << ", [";
+          DumpReg(os, Rn);
+          os << ", #" << (imm5 << 2) << "]  // ";
+          break;
+        }
+        case 0x9: {
+          // STR Rt, [SP, #imm] - 01100 ttt iiiiiiii
+          // LDR Rt, [SP, #imm] - 01101 ttt iiiiiiii
+          uint16_t imm8 = instr & 0xFF;
+          uint16_t Rt = (instr >> 8) & 7;
+          if ((instr & 0x800) == 0) {
+            os << "STR ";
+          } else {
+            os << "LDR ";
+          }
+          DumpReg(os, Rt);
+          os << ", [SP, #" << (imm8 << 2) << "]  // ";
+          break;
+        }
+        default:
+          break;
+      }
+    }
+    os << StringPrintf("%04x\n", instr);
+  }
+  return 2;
+}
+
+}  // namespace arm
+}  // namespace art
diff --git a/src/disassembler_arm.h b/src/disassembler_arm.h
new file mode 100644
index 0000000..ae6cc60
--- /dev/null
+++ b/src/disassembler_arm.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2012 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_SRC_DISASSEMBLER_ARM_H_
+#define ART_SRC_DISASSEMBLER_ARM_H_
+
+#include "disassembler.h"
+
+namespace art {
+namespace arm {
+
+class DisassemblerArm : public Disassembler {
+ public:
+  DisassemblerArm();
+
+  virtual void Dump(std::ostream& os, const uint8_t* begin, const uint8_t* end);
+ private:
+  void DumpArm(std::ostream& os, const uint8_t* instr);
+
+  // Returns the size of the instruction just decoded
+  size_t DumpThumb16(std::ostream& os, const uint8_t* instr);
+  size_t DumpThumb32(std::ostream& os, const uint8_t* instr_ptr);
+
+  void DumpReg(std::ostream& os, uint32_t reg);
+  void DumpRegList(std::ostream& os, uint32_t reg_list);
+};
+
+}  // namespace arm
+}  // namespace art
+
+#endif  // ART_SRC_DISASSEMBLER_ARM_H_
diff --git a/src/image.h b/src/image.h
index a709c1b..0a83c39 100644
--- a/src/image.h
+++ b/src/image.h
@@ -126,7 +126,7 @@
   uint32_t image_roots_;
 
   friend class ImageWriter;
-  friend class ImageDump;  // For GetImageRoots()
+  friend class ImageDumper;  // For GetImageRoots()
 };
 
 }  // namespace art
diff --git a/src/oatdump.cc b/src/oatdump.cc
index 0dde41f..cf4dc2d 100644
--- a/src/oatdump.cc
+++ b/src/oatdump.cc
@@ -24,7 +24,10 @@
 #include <vector>
 
 #include "class_linker.h"
+#include "context.h"
 #include "dex_instruction.h"
+#include "dex_verifier.h"
+#include "disassembler.h"
 #include "file.h"
 #include "image.h"
 #include "object_utils.h"
@@ -83,8 +86,14 @@
 
 class OatDumper {
  public:
-  void Dump(const std::string& oat_filename, std::ostream& os, const OatFile& oat_file) {
-    const OatHeader& oat_header = oat_file.GetOatHeader();
+  explicit OatDumper(const OatFile& oat_file) : oat_file_(oat_file),
+      oat_dex_files_(oat_file.GetOatDexFiles()), disassembler_(Disassembler::Create(kArm)) {
+    // TODO: the disassembler should find the oat file instruction set from the oat header
+    AddAllOffsets();
+  }
+
+  void Dump(std::ostream& os) {
+    const OatHeader& oat_header = oat_file_.GetOatHeader();
 
     os << "MAGIC:\n";
     os << oat_header.GetMagic() << "\n\n";
@@ -99,36 +108,68 @@
     os << StringPrintf("0x%08x\n\n", oat_header.GetExecutableOffset());
 
     os << "BEGIN:\n";
-    os << reinterpret_cast<const void*>(oat_file.Begin()) << "\n\n";
+    os << reinterpret_cast<const void*>(oat_file_.Begin()) << "\n\n";
 
     os << "END:\n";
-    os << reinterpret_cast<const void*>(oat_file.End()) << "\n\n";
+    os << reinterpret_cast<const void*>(oat_file_.End()) << "\n\n";
 
     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];
+    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);
-      DumpOatDexFile(os, oat_file, *oat_dex_file);
+      DumpOatDexFile(os, *oat_dex_file);
     }
   }
 
+  size_t ComputeSize(const void* oat_data) {
+    if (reinterpret_cast<const byte*>(oat_data) < oat_file_.Begin() ||
+        reinterpret_cast<const byte*>(oat_data) > oat_file_.End()) {
+      return 0;  // Address not in oat file
+    }
+    uint32_t begin_offset = reinterpret_cast<size_t>(oat_data) -
+                            reinterpret_cast<size_t>(oat_file_.Begin());
+    typedef std::set<uint32_t>::iterator It;
+    It it = offsets_.upper_bound(begin_offset);
+    CHECK(it != offsets_.end());
+    uint32_t end_offset = *it;
+    return end_offset - begin_offset;
+  }
+
+  const void* GetOatCode(Method* m) {
+    MethodHelper mh(m);
+    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) {
+        uint32_t class_def_index;
+        bool found = dex_file->FindClassDefIndex(mh.GetDeclaringClassDescriptor(), class_def_index);
+        if (found) {
+          const OatFile::OatClass* oat_class = oat_dex_file->GetOatClass(class_def_index);
+          CHECK(oat_class != NULL);
+          size_t method_index = m->GetMethodIndex();
+          return oat_class->GetOatMethod(method_index).GetCode();
+        }
+      }
+    }
+    return NULL;
+  }
+
  private:
-  void AddAllOffsets(const OatFile& oat_file, std::vector<const OatFile::OatDexFile*>& oat_dex_files) {
+  void AddAllOffsets() {
     // 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];
+    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;
+        continue;
       }
+      offsets_.insert(reinterpret_cast<uint32_t>(&dex_file->GetHeader()));
       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));
@@ -152,7 +193,7 @@
     // 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()));
+    offsets_.insert(static_cast<uint32_t>(oat_file_.End() - oat_file_.Begin()));
   }
 
   void AddOffsets(const OatFile::OatMethod& oat_method) {
@@ -163,8 +204,7 @@
     offsets_.insert(oat_method.GetInvokeStubOffset());
   }
 
-  void DumpOatDexFile(std::ostream& os, const OatFile& oat_file,
-                      const OatFile::OatDexFile& oat_dex_file) {
+  void DumpOatDexFile(std::ostream& os, 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: 0x%08x\n", oat_dex_file.GetDexFileLocationChecksum());
@@ -180,7 +220,7 @@
       CHECK(oat_class.get() != NULL);
       os << StringPrintf("%zd: %s (type_idx=%d) (", class_def_index, descriptor, class_def.class_idx_)
          << oat_class->GetStatus() << ")\n";
-      DumpOatClass(os, oat_file, *oat_class.get(), *(dex_file.get()), class_def);
+      DumpOatClass(os, *oat_class.get(), *(dex_file.get()), class_def);
     }
 
     os << std::flush;
@@ -195,8 +235,8 @@
     }
   }
 
-  void DumpOatClass(std::ostream& os, const OatFile& oat_file, const OatFile::OatClass& oat_class,
-      const DexFile& dex_file, const DexFile::ClassDef& class_def) {
+  void DumpOatClass(std::ostream& os, 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;
@@ -207,15 +247,15 @@
     uint32_t class_method_index = 0;
     while (it.HasNextDirectMethod()) {
       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());
+      DumpOatMethod(os, class_method_index, oat_method, dex_file,
+                    it.GetMemberIndex(), it.GetMethodCodeItem());
       class_method_index++;
       it.Next();
     }
     while (it.HasNextVirtualMethod()) {
       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());
+      DumpOatMethod(os, class_method_index, oat_method, dex_file,
+                    it.GetMemberIndex(), it.GetMethodCodeItem());
       class_method_index++;
       it.Next();
     }
@@ -223,7 +263,7 @@
     os << std::flush;
   }
 
-  void DumpOatMethod(std::ostream& os, uint32_t class_method_index, const OatFile& oat_file,
+  void DumpOatMethod(std::ostream& os, uint32_t class_method_index,
                      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);
@@ -235,24 +275,118 @@
                        oat_method.GetCode(), oat_method.GetCodeOffset());
     os << StringPrintf("\t\tframe_size_in_bytes: %zd\n",
                        oat_method.GetFrameSizeInBytes());
-    os << StringPrintf("\t\tcore_spill_mask: 0x%08x\n",
+    os << StringPrintf("\t\tcore_spill_mask: 0x%08x",
                        oat_method.GetCoreSpillMask());
-    os << StringPrintf("\t\tfp_spill_mask: 0x%08x\n",
+    DumpSpillMask(os, oat_method.GetCoreSpillMask(), false);
+    os << StringPrintf("\n\t\tfp_spill_mask: 0x%08x",
                        oat_method.GetFpSpillMask());
-    os << StringPrintf("\t\tmapping_table: %p (offset=0x%08x)\n",
+    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_file, oat_method, dex_file, code_item);
+    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.GetGcMap(), oat_method.GetGcMapOffset());
+    DumpGcMap(os, oat_method.GetGcMap());
     os << StringPrintf("\t\tinvoke_stub: %p (offset=0x%08x)\n",
                        oat_method.GetInvokeStub(), oat_method.GetInvokeStubOffset());
+    os << "\t\tCODE:\n";
+    DumpCode(os, oat_method.GetCode(), oat_method.GetMappingTable(), dex_file, code_item);
+    os << "\t\tINVOKE STUB:\n";
+    DumpCode(os, reinterpret_cast<const void*>(oat_method.GetInvokeStub()), NULL, dex_file, NULL);
   }
 
-  void DumpMappingTable(std::ostream& os,
-      const OatFile& oat_file, const OatFile::OatMethod& oat_method,
-      const DexFile& dex_file, const DexFile::CodeItem* code_item) {
+  void DumpSpillMask(std::ostream& os, uint32_t spill_mask, bool is_float) {
+    if (spill_mask == 0) {
+      return;
+    }
+    os << " (";
+    for (size_t i = 0; i < 32; i++) {
+      if ((spill_mask & (1 << i)) != 0) {
+        if (is_float) {
+          os << "fr" << i;
+        } else {
+          os << "r" << i;
+        }
+        spill_mask ^= 1 << i;  // clear bit
+        if (spill_mask != 0) {
+          os << ", ";
+        } else {
+          break;
+        }
+      }
+    }
+    os << ")";
+  }
+
+  void DumpVmap(std::ostream& os, const uint16_t* raw_table, uint32_t core_spill_mask,
+                uint32_t fp_spill_mask) {
+    if (raw_table == NULL) {
+      return;
+    }
+    const VmapTable vmap_table(raw_table);
+    bool first = true;
+    os << "\t\t\t";
+    for (size_t i = 0; i < vmap_table.size(); i++) {
+      uint16_t dex_reg = vmap_table[i];
+      size_t matches = 0;
+      size_t spill_shifts = 0;
+      uint32_t spill_mask = core_spill_mask;
+      bool processing_fp = false;
+      while (matches != (i + 1)) {
+        if (spill_mask == 0) {
+          CHECK(!processing_fp);
+          spill_mask = fp_spill_mask;
+          processing_fp = true;
+        }
+        matches += spill_mask & 1;  // Add 1 if the low bit is set
+        spill_mask >>= 1;
+        spill_shifts++;
+      }
+      size_t arm_reg = spill_shifts - 1;  // wind back one as we want the last match
+      os << (first ? "v" : ", v")  << dex_reg;
+      if (arm_reg < 16) {
+        os << "/r" << arm_reg;
+      } else {
+        os << "/fr" << (arm_reg - 16);
+      }
+      if (first) {
+        first = false;
+      }
+    }
+    os << std::endl;
+  }
+
+  void DumpGcMap(std::ostream& os, const uint8_t* gc_map_raw) {
+    if (gc_map_raw == NULL) {
+      return;
+    }
+    uint32_t gc_map_length = (gc_map_raw[0] << 24) | (gc_map_raw[1] << 16) |
+                             (gc_map_raw[2] << 8) | (gc_map_raw[3] << 0);
+    verifier::PcToReferenceMap map(gc_map_raw + sizeof(uint32_t), gc_map_length);
+    for (size_t entry = 0; entry < map.NumEntries(); entry++) {
+      os << StringPrintf("\t\t\t0x%04x", map.GetPC(entry));
+      size_t num_regs = map.RegWidth() * 8;
+      const uint8_t* reg_bitmap = map.GetBitMap(entry);
+      bool first = true;
+      for (size_t reg = 0; reg < num_regs; reg++) {
+        if (((reg_bitmap[reg / 8] >> (reg % 8)) & 0x01) != 0) {
+          if (first) {
+            os << "  v" << reg;
+            first = false;
+          } else {
+            os << ", v" << reg;
+          }
+        }
+      }
+      os << std::endl;
+    }
+  }
+
+  void DumpMappingTable(std::ostream& os, const OatFile::OatMethod& oat_method) {
     const uint32_t* raw_table = oat_method.GetMappingTable();
     const void* code = oat_method.GetCode();
     if (raw_table == NULL || code == NULL) {
@@ -262,17 +396,54 @@
     uint32_t length = *raw_table;
     ++raw_table;
 
+    os << "\t\t{";
     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 < length) {
+        os << ", ";
+      }
+    }
+    os << "}" << std::endl << std::flush;
+  }
+
+  void DumpCode(std::ostream& os, const void* code, const uint32_t* raw_mapping_table,
+                const DexFile& dex_file, const DexFile::CodeItem* code_item) {
+    if (code == NULL) {
+      return;
+    }
+
+    if (raw_mapping_table == NULL) {
+      // code but no mapping table is most likely caused by code created by the JNI compiler
+      const uint8_t* native_pc = reinterpret_cast<const uint8_t*>(code);
+      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_.upper_bound(last_offset);
+      CHECK(it != offsets_.end());
+      const uint8_t* end_native_pc = reinterpret_cast<const uint8_t*>(oat_begin) + *it;
+      CHECK(native_pc < end_native_pc);
+
+      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* native_pc = reinterpret_cast<const uint8_t*>(code) + raw_table[i];
+      const uint8_t* native_pc = reinterpret_cast<const uint8_t*>(code) + raw_mapping_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];
+        end_native_pc = reinterpret_cast<const uint8_t*>(code) + raw_mapping_table[i + 2];
       } else {
-        const uint8_t* oat_begin = reinterpret_cast<const uint8_t*>(oat_file.Begin());
+        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;
@@ -280,50 +451,50 @@
         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);
-      os << StringPrintf("\t\t\t%p:", native_pc);
-      for (; native_pc < end_native_pc; ++native_pc) {
-        os << StringPrintf(" 0x%02x", *native_pc);
-      }
-      os << "\n";
+      disassembler_->Dump(os, native_pc, end_native_pc);
     }
   }
 
+  const OatFile& oat_file_;
+  std::vector<const OatFile::OatDexFile*> oat_dex_files_;
   std::set<uint32_t> offsets_;
+  UniquePtr<Disassembler> disassembler_;
 };
 
-class ImageDump {
+class ImageDumper {
  public:
-  static void Dump(const std::string& image_filename,
-                   const std::string& host_prefix,
-                   std::ostream& os,
-                   Space& image_space,
-                   const ImageHeader& image_header) {
-    os << "MAGIC:\n";
-    os << image_header.GetMagic() << "\n\n";
+  explicit ImageDumper(std::ostream& os, const std::string& image_filename,
+                       const std::string& host_prefix, Space& image_space,
+                       const ImageHeader& image_header) : os_(os),
+                       image_filename_(image_filename), host_prefix_(host_prefix),
+                       image_space_(image_space), image_header_(image_header) {
+  }
 
-    os << "IMAGE BEGIN:\n";
-    os << reinterpret_cast<void*>(image_header.GetImageBegin()) << "\n\n";
+  void Dump() {
+    os_ << "MAGIC:\n";
+    os_ << image_header_.GetMagic() << "\n\n";
 
-    os << "OAT CHECKSUM:\n";
-    os << StringPrintf("0x%08x\n\n", image_header.GetOatChecksum());
+    os_ << "IMAGE BEGIN:\n";
+    os_ << reinterpret_cast<void*>(image_header_.GetImageBegin()) << "\n\n";
 
-    os << "OAT BEGIN:\n";
-    os << reinterpret_cast<void*>(image_header.GetOatBegin()) << "\n\n";
+    os_ << "OAT CHECKSUM:\n";
+    os_ << StringPrintf("0x%08x\n\n", image_header_.GetOatChecksum());
 
-    os << "OAT END:\n";
-    os << reinterpret_cast<void*>(image_header.GetOatEnd()) << "\n\n";
+    os_ << "OAT BEGIN:\n";
+    os_ << reinterpret_cast<void*>(image_header_.GetOatBegin()) << "\n\n";
 
-    os << "ROOTS:\n";
-    os << reinterpret_cast<void*>(image_header.GetImageRoots()) << "\n";
+    os_ << "OAT END:\n";
+    os_ << reinterpret_cast<void*>(image_header_.GetOatEnd()) << "\n\n";
+
+    os_ << "ROOTS:\n";
+    os_ << reinterpret_cast<void*>(image_header_.GetImageRoots()) << "\n";
     CHECK_EQ(arraysize(image_roots_descriptions_), size_t(ImageHeader::kImageRootsMax));
     for (int i = 0; i < ImageHeader::kImageRootsMax; i++) {
       ImageHeader::ImageRoot image_root = static_cast<ImageHeader::ImageRoot>(i);
       const char* image_root_description = image_roots_descriptions_[i];
-      Object* image_root_object = image_header.GetImageRoot(image_root);
-      os << StringPrintf("%s: %p\n", image_root_description, image_root_object);
+      Object* image_root_object = image_header_.GetImageRoot(image_root);
+      os_ << StringPrintf("%s: %p\n", image_root_description, image_root_object);
       if (image_root_object->IsObjectArray()) {
         // TODO: replace down_cast with AsObjectArray (g++ currently has a problem with this)
         ObjectArray<Object>* image_root_object_array
@@ -332,66 +503,61 @@
         for (int i = 0; i < image_root_object_array->GetLength(); i++) {
           Object* value = image_root_object_array->Get(i);
           if (value != NULL) {
-            os << "\t" << i << ": ";
+            os_ << "\t" << i << ": ";
             std::string summary;
             PrettyObjectValue(summary, value->GetClass(), value);
-            os << summary;
+            os_ << summary;
           } else {
-            os << StringPrintf("\t%d: null\n", i);
+            os_ << StringPrintf("\t%d: null\n", i);
           }
         }
       }
     }
-    os << "\n";
+    os_ << "\n";
 
-    os << "OBJECTS:\n" << std::flush;
-    ImageDump state(image_space, os);
-    HeapBitmap* heap_bitmap = Heap::GetLiveBits();
-    DCHECK(heap_bitmap != NULL);
-    heap_bitmap->Walk(ImageDump::Callback, &state);
-    os << "\n";
-
-    os << "STATS:\n" << std::flush;
-    UniquePtr<File> file(OS::OpenFile(image_filename.c_str(), false));
-    state.stats_.file_bytes = file->Length();
-    size_t header_bytes = sizeof(ImageHeader);
-    state.stats_.header_bytes = header_bytes;
-    size_t alignment_bytes = RoundUp(header_bytes, kObjectAlignment) - header_bytes;
-    state.stats_.alignment_bytes += alignment_bytes;
-    state.stats_.Dump(os);
-    os << "\n";
-
-    os << std::flush;
-
-    os << "OAT LOCATION:\n" << std::flush;
-    Object* oat_location_object = image_header.GetImageRoot(ImageHeader::kOatLocation);
-    std::string oat_location(oat_location_object->AsString()->ToModifiedUtf8());
     ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-    os << oat_location;
-    if (!host_prefix.empty()) {
-      oat_location = host_prefix + oat_location;
-      os << " (" << oat_location << ")";
+    Object* oat_location_object = image_header_.GetImageRoot(ImageHeader::kOatLocation);
+    std::string oat_location(oat_location_object->AsString()->ToModifiedUtf8());
+    if (!host_prefix_.empty()) {
+      oat_location = host_prefix_ + oat_location;
+      os_ << " (" << oat_location << ")";
     }
-    os << "\n";
     const OatFile* oat_file = class_linker->FindOatFileFromOatLocation(oat_location);
     if (oat_file == NULL) {
-      os << "NOT FOUND\n";
-      os << std::flush;
+      os_ << "OAT FILE NOT FOUND: " << oat_location << std::endl << std::flush;
       return;
     }
-    os << "\n";
-    os << std::flush;
 
-    OatDumper oat_dumper;
-    oat_dumper.Dump(oat_location, os, *oat_file);
+    stats_.oat_file_bytes = oat_file->Size();
+
+    oat_dumper_.reset(new OatDumper(*oat_file));
+
+    os_ << "OBJECTS:\n" << std::flush;
+    HeapBitmap* heap_bitmap = Heap::GetLiveBits();
+    DCHECK(heap_bitmap != NULL);
+    heap_bitmap->Walk(ImageDumper::Callback, this);
+    os_ << "\n";
+
+    os_ << "STATS:\n" << std::flush;
+    UniquePtr<File> file(OS::OpenFile(image_filename_.c_str(), false));
+    stats_.file_bytes = file->Length();
+    size_t header_bytes = sizeof(ImageHeader);
+    stats_.header_bytes = header_bytes;
+    size_t alignment_bytes = RoundUp(header_bytes, kObjectAlignment) - header_bytes;
+    stats_.alignment_bytes += alignment_bytes;
+    stats_.Dump(os_);
+    os_ << "\n";
+
+    os_ << std::flush;
+
+    os_ << "OAT LOCATION:\n" << std::flush;
+    os_ << oat_location;
+    os_ << "\n";
+    oat_dumper_->Dump(os_);
   }
 
  private:
 
-  ImageDump(const Space& dump_space, std::ostream& os) : dump_space_(dump_space), os_(os) {}
-
-  ~ImageDump() {}
-
   static void PrettyObjectValue(std::string& summary, Class* type, Object* value) {
     CHECK(type != NULL);
     if (value == NULL) {
@@ -445,10 +611,24 @@
     }
   }
 
+  bool InDumpSpace(const Object* object) {
+    return image_space_.Contains(object);
+ }
+
+  const void* GetOatCode(Method* m) {
+    Runtime* runtime = Runtime::Current();
+    const void* code = m->GetCode();
+    if (code == runtime->GetResolutionStubArray(Runtime::kStaticMethod)->GetData() ||
+        code == runtime->GetResolutionStubArray(Runtime::kInstanceMethod)->GetData()) {
+      code = oat_dumper_->GetOatCode(m);
+    }
+    return code;
+  }
+
   static void Callback(Object* obj, void* arg) {
     DCHECK(obj != NULL);
     DCHECK(arg != NULL);
-    ImageDump* state = reinterpret_cast<ImageDump*>(arg);
+    ImageDumper* state = reinterpret_cast<ImageDumper*>(arg);
     if (!state->InDumpSpace(obj)) {
       return;
     }
@@ -520,6 +700,20 @@
         DCHECK(method->GetGcMap() == NULL) << PrettyMethod(method);
         DCHECK_EQ(0U, method->GetGcMapLength()) << PrettyMethod(method);
         DCHECK(method->GetMappingTable() == NULL) << PrettyMethod(method);
+        bool first_occurrence;
+        size_t invoke_stub_size = state->ComputeOatSize(
+            reinterpret_cast<const void*>(method->GetInvokeStub()), &first_occurrence);
+        if (first_occurrence) {
+          state->stats_.managed_to_native_code_bytes += invoke_stub_size;
+        }
+        const void* oat_code = state->GetOatCode(method);
+        size_t code_size = state->ComputeOatSize(oat_code, &first_occurrence);
+        if (first_occurrence) {
+          state->stats_.native_to_managed_code_bytes += code_size;
+        }
+        if (oat_code != method->GetCode()) {
+          StringAppendF(&summary, "\t\tOAT CODE: %p\n", oat_code);
+        }
       } else if (method->IsAbstract() || method->IsCalleeSaveMethod() ||
           method->IsResolutionMethod()) {
         DCHECK(method->GetGcMap() == NULL) << PrettyMethod(method);
@@ -529,18 +723,52 @@
         DCHECK(method->GetGcMap() != NULL) << PrettyMethod(method);
         DCHECK_NE(0U, method->GetGcMapLength()) << PrettyMethod(method);
 
-        size_t register_map_bytes = method->GetGcMapLength();
-        state->stats_.register_map_bytes += register_map_bytes;
-
-        size_t pc_mapping_table_bytes = method->GetMappingTableLength();
-        state->stats_.pc_mapping_table_bytes += pc_mapping_table_bytes;
-
         const DexFile::CodeItem* code_item = MethodHelper(method).GetCodeItem();
         size_t dex_instruction_bytes = code_item->insns_size_in_code_units_ * 2;
         state->stats_.dex_instruction_bytes += dex_instruction_bytes;
 
+        bool first_occurance;
+        size_t gc_map_bytes = state->ComputeOatSize(method->GetGcMapRaw(), &first_occurance);
+        if (first_occurance) {
+          state->stats_.gc_map_bytes += gc_map_bytes;
+        }
+
+        size_t pc_mapping_table_bytes =
+            state->ComputeOatSize(method->GetMappingTableRaw(), &first_occurance);
+        if (first_occurance) {
+          state->stats_.pc_mapping_table_bytes += pc_mapping_table_bytes;
+        }
+
+        size_t vmap_table_bytes =
+            state->ComputeOatSize(method->GetVmapTableRaw(), &first_occurance);
+        if (first_occurance) {
+          state->stats_.vmap_table_bytes += vmap_table_bytes;
+        }
+
+        size_t invoke_stub_size = state->ComputeOatSize(
+            reinterpret_cast<const void*>(method->GetInvokeStub()), &first_occurance);
+        if (first_occurance) {
+          state->stats_.native_to_managed_code_bytes += invoke_stub_size;
+        }
+        const void* oat_code = state->GetOatCode(method);
+        size_t code_size = state->ComputeOatSize(oat_code, &first_occurance);
+        if (first_occurance) {
+          state->stats_.managed_code_bytes += code_size;
+        }
+        state->stats_.managed_code_bytes_ignoring_deduplication += code_size;
+
+        if (oat_code != method->GetCode()) {
+          StringAppendF(&summary, "\t\tOAT CODE: %p\n", oat_code);
+        }
         StringAppendF(&summary, "\t\tSIZE: Dex Instructions=%zd GC=%zd Mapping=%zd\n",
-                      dex_instruction_bytes, register_map_bytes, pc_mapping_table_bytes);
+                      dex_instruction_bytes, gc_map_bytes, pc_mapping_table_bytes);
+
+        size_t total_size = dex_instruction_bytes + gc_map_bytes + pc_mapping_table_bytes +
+            vmap_table_bytes + invoke_stub_size + code_size + object_bytes;
+
+        double expansion =
+            static_cast<double>(code_size) / static_cast<double>(dex_instruction_bytes);
+        state->stats_.ComputeOutliers(total_size, expansion, method);
       }
     }
     std::string descriptor(ClassHelper(obj_class).GetDescriptor());
@@ -550,12 +778,22 @@
     state->os_ << summary << std::flush;
   }
 
-  bool InDumpSpace(const Object* object) {
-    return dump_space_.Contains(object);
+  std::set<const void*> already_seen_;
+  // Compute the size of the given data within the oat file and whether this is the first time
+  // this data has been requested
+  size_t ComputeOatSize(const void* oat_data, bool* first_occurance) {
+    if (already_seen_.count(oat_data) == 0) {
+      *first_occurance = true;
+      already_seen_.insert(oat_data);
+    } else {
+      *first_occurance = false;
+    }
+    return oat_dumper_->ComputeSize(oat_data);
   }
 
  public:
   struct Stats {
+    size_t oat_file_bytes;
     size_t file_bytes;
 
     size_t header_bytes;
@@ -563,24 +801,33 @@
     size_t alignment_bytes;
 
     size_t managed_code_bytes;
+    size_t managed_code_bytes_ignoring_deduplication;
     size_t managed_to_native_code_bytes;
     size_t native_to_managed_code_bytes;
 
-    size_t register_map_bytes;
+    size_t gc_map_bytes;
     size_t pc_mapping_table_bytes;
+    size_t vmap_table_bytes;
 
     size_t dex_instruction_bytes;
 
-    Stats()
-        : file_bytes(0),
+    std::vector<Method*> method_outlier;
+    std::vector<size_t> method_outlier_size;
+    std::vector<double> method_outlier_expansion;
+
+    explicit Stats()
+        : oat_file_bytes(0),
+          file_bytes(0),
           header_bytes(0),
           object_bytes(0),
           alignment_bytes(0),
           managed_code_bytes(0),
+          managed_code_bytes_ignoring_deduplication(0),
           managed_to_native_code_bytes(0),
           native_to_managed_code_bytes(0),
-          register_map_bytes(0),
+          gc_map_bytes(0),
           pc_mapping_table_bytes(0),
+          vmap_table_bytes(0),
           dex_instruction_bytes(0) {}
 
     typedef std::map<std::string, size_t> TableBytes;
@@ -589,6 +836,10 @@
     typedef std::map<std::string, size_t> TableCount;
     TableCount descriptor_to_count;
 
+    double PercentOfOatBytes(size_t size) {
+      return (static_cast<double>(size) / static_cast<double>(oat_file_bytes)) * 100;
+    }
+
     double PercentOfFileBytes(size_t size) {
       return (static_cast<double>(size) / static_cast<double>(file_bytes)) * 100;
     }
@@ -597,19 +848,124 @@
       return (static_cast<double>(size) / static_cast<double>(object_bytes)) * 100;
     }
 
-    void Dump(std::ostream& os) {
-      os << StringPrintf("\tfile_bytes = %zd\n", file_bytes);
-      os << "\n";
+    void ComputeOutliers(size_t total_size, double expansion, Method* method) {
+      method_outlier_size.push_back(total_size);
+      method_outlier_expansion.push_back(expansion);
+      method_outlier.push_back(method);
+    }
 
-      os << "\tfile_bytes = header_bytes + object_bytes + alignment_bytes\n";
-      os << StringPrintf("\theader_bytes    = %10zd (%2.0f%% of file_bytes)\n",
-                         header_bytes, PercentOfFileBytes(header_bytes));
-      os << StringPrintf("\tobject_bytes    = %10zd (%2.0f%% of file_bytes)\n",
-                         object_bytes, PercentOfFileBytes(object_bytes));
-      os << StringPrintf("\talignment_bytes = %10zd (%2.0f%% of file_bytes)\n",
-                         alignment_bytes, PercentOfFileBytes(alignment_bytes));
-      os << "\n";
-      os << std::flush;
+    void DumpOutliers(std::ostream& os) {
+      size_t sum_of_sizes = 0;
+      size_t sum_of_sizes_squared = 0;
+      size_t sum_of_expansion = 0;
+      size_t sum_of_expansion_squared = 0;
+      size_t n = method_outlier_size.size();
+      for (size_t i = 0; i < n; i++) {
+        size_t cur_size = method_outlier_size[i];
+        sum_of_sizes += cur_size;
+        sum_of_sizes_squared += cur_size * cur_size;
+        double cur_expansion = method_outlier_expansion[i];
+        sum_of_expansion += cur_expansion;
+        sum_of_expansion_squared += cur_expansion * cur_expansion;
+      }
+      size_t size_mean = sum_of_sizes / n;
+      size_t size_variance = (sum_of_sizes_squared - sum_of_sizes * size_mean) / (n - 1);
+      double expansion_mean = sum_of_expansion / n;
+      double expansion_variance =
+          (sum_of_expansion_squared - sum_of_expansion * expansion_mean) / (n - 1);
+
+      // Dump methods whose size is a certain number of standard deviations from the mean
+      size_t dumped_values = 0;
+      size_t skipped_values = 0;
+      for (size_t i = 100; i > 0; i--) {  // i is the current number of standard deviations
+        size_t cur_size_variance = i * i * size_variance;
+        bool first = true;
+        for (size_t j = 0; j < n; j++) {
+          size_t cur_size = method_outlier_size[j];
+          if (cur_size > size_mean) {
+            size_t cur_var = cur_size - size_mean;
+            cur_var = cur_var * cur_var;
+            if (cur_var > cur_size_variance) {
+              if (dumped_values > 20) {
+                if (i == 1) {
+                  skipped_values++;
+                } else {
+                  i = 2;  // jump to counting for 1 standard deviation
+                  break;
+                }
+              } else {
+                if (first) {
+                  os << "\nBig methods (size > " << i << " standard deviations the norm):"
+                     << std::endl;
+                  first = false;
+                }
+                os << "\t" << PrettyMethod(method_outlier[j]) << " requires storage of "
+                    << PrettySize(cur_size) << std::endl;
+                method_outlier_size[j] = 0;  // don't consider this method again
+                dumped_values++;
+              }
+            }
+          }
+        }
+      }
+      if (skipped_values > 0) {
+        os << "\t... skipped " << skipped_values
+           << " methods with size > 1 standard deviation from the norm" << std::endl;
+      }
+      os << std::endl << std::flush;
+
+      // Dump methods whose expansion is a certain number of standard deviations from the mean
+      dumped_values = 0;
+      skipped_values = 0;
+      for (size_t i = 10; i > 0; i--) {  // i is the current number of standard deviations
+        double cur_expansion_variance = i * i * expansion_variance;
+        bool first = true;
+        for (size_t j = 0; j < n; j++) {
+          double cur_expansion = method_outlier_expansion[j];
+          if (cur_expansion > expansion_mean) {
+            size_t cur_var = cur_expansion - expansion_mean;
+            cur_var = cur_var * cur_var;
+            if (cur_var > cur_expansion_variance) {
+              if (dumped_values > 20) {
+                if (i == 1) {
+                  skipped_values++;
+                } else {
+                  i = 2;  // jump to counting for 1 standard deviation
+                  break;
+                }
+              } else {
+                if (first) {
+                  os << "\nLarge expansion methods (size > " << i
+                      << " standard deviations the norm):" << std::endl;
+                  first = false;
+                }
+                os << "\t" << PrettyMethod(method_outlier[j]) << " expanded code by "
+                    << cur_expansion << std::endl;
+                method_outlier_expansion[j] = 0.0;  // don't consider this method again
+                dumped_values++;
+              }
+            }
+          }
+        }
+      }
+      if (skipped_values > 0) {
+        os << "\t... skipped " << skipped_values
+           << " methods with expansion > 1 standard deviation from the norm" << std::endl;
+      }
+      os << std::endl << std::flush;
+    }
+
+    void Dump(std::ostream& os) {
+      os << "\tart_file_bytes = " << PrettySize(file_bytes) << std::endl << std::endl
+         << "\tart_file_bytes = header_bytes + object_bytes + alignment_bytes" << std::endl
+         << StringPrintf("\theader_bytes    =  %8zd (%2.0f%% of art file bytes)\n"
+                         "\tobject_bytes    =  %8zd (%2.0f%% of art file bytes)\n"
+                         "\talignment_bytes =  %8zd (%2.0f%% of art file bytes)\n",
+                         header_bytes, PercentOfFileBytes(header_bytes),
+                         object_bytes, PercentOfFileBytes(object_bytes),
+                         alignment_bytes, PercentOfFileBytes(alignment_bytes))
+         << std::endl << std::flush;
+
       CHECK_EQ(file_bytes, header_bytes + object_bytes + alignment_bytes);
 
       os << "\tobject_bytes = sum of descriptor_to_bytes values below:\n";
@@ -628,42 +984,45 @@
 
         object_bytes_total += bytes;
       }
-      os << "\n";
-      os << std::flush;
+      os << std::endl << std::flush;
       CHECK_EQ(object_bytes, object_bytes_total);
 
-      os << StringPrintf("\tmanaged_code_bytes           = %8zd (%2.0f%% of object_bytes)\n",
-                         managed_code_bytes, PercentOfObjectBytes(managed_code_bytes));
-      os << StringPrintf("\tmanaged_to_native_code_bytes = %8zd (%2.0f%% of object_bytes)\n",
-                         managed_to_native_code_bytes,
-                         PercentOfObjectBytes(managed_to_native_code_bytes));
-      os << StringPrintf("\tnative_to_managed_code_bytes = %8zd (%2.0f%% of object_bytes)\n",
-                         native_to_managed_code_bytes,
-                         PercentOfObjectBytes(native_to_managed_code_bytes));
-      os << "\n";
-      os << std::flush;
+      os << StringPrintf("\tmanaged_code_bytes           = %8zd (%2.0f%% of oat file bytes)\n"
+                         "\tmanaged_to_native_code_bytes = %8zd (%2.0f%% of oat file bytes)\n"
+                         "\tnative_to_managed_code_bytes = %8zd (%2.0f%% of oat file bytes)\n",
+                         managed_code_bytes, PercentOfOatBytes(managed_code_bytes),
+                         managed_to_native_code_bytes, PercentOfOatBytes(managed_to_native_code_bytes),
+                         native_to_managed_code_bytes, PercentOfOatBytes(native_to_managed_code_bytes))
+         << std::endl << std::flush;
 
-      os << StringPrintf("\tregister_map_bytes     = %7zd (%2.0f%% of object_bytes)\n",
-                         register_map_bytes, PercentOfObjectBytes(register_map_bytes));
-      os << StringPrintf("\tpc_mapping_table_bytes = %7zd (%2.0f%% of object_bytes)\n",
-                         pc_mapping_table_bytes, PercentOfObjectBytes(pc_mapping_table_bytes));
-      os << "\n";
-      os << std::flush;
+      os << StringPrintf("\tgc_map_bytes           = %7zd (%2.0f%% of oat file_bytes)\n"
+                         "\tpc_mapping_table_bytes = %7zd (%2.0f%% of oat file_bytes)\n"
+                         "\tvmap_table_bytes       = %7zd (%2.0f%% of oat file_bytes)\n",
+                         gc_map_bytes, PercentOfOatBytes(gc_map_bytes),
+                         pc_mapping_table_bytes, PercentOfOatBytes(pc_mapping_table_bytes),
+                         vmap_table_bytes, PercentOfOatBytes(vmap_table_bytes))
+         << std::endl << std::flush;
 
       os << StringPrintf("\tdex_instruction_bytes = %zd\n", dex_instruction_bytes);
-      os << StringPrintf("\tmanaged_code_bytes expansion = %.2f\n",
-                         static_cast<double>(managed_code_bytes)
-                         / static_cast<double>(dex_instruction_bytes));
-      os << "\n";
-      os << std::flush;
+      os << StringPrintf("\tmanaged_code_bytes expansion = %.2f (ignoring deduplication %.2f)\n",
+          static_cast<double>(managed_code_bytes) / static_cast<double>(dex_instruction_bytes),
+          static_cast<double>(managed_code_bytes_ignoring_deduplication) /
+            static_cast<double>(dex_instruction_bytes));
+      os << std::endl << std::flush;
+
+      DumpOutliers(os);
     }
   } stats_;
 
  private:
-  const Space& dump_space_;
+  UniquePtr<OatDumper> oat_dumper_;
   std::ostream& os_;
+  const std::string image_filename_;
+  const std::string host_prefix_;
+  Space& image_space_;
+  const ImageHeader& image_header_;
 
-  DISALLOW_COPY_AND_ASSIGN(ImageDump);
+  DISALLOW_COPY_AND_ASSIGN(ImageDumper);
 };
 
 int oatdump(int argc, char** argv) {
@@ -718,13 +1077,13 @@
   }
 
   if (oat_filename != NULL) {
-    const OatFile* oat_file = OatFile::Open(oat_filename, oat_filename, NULL);
+    OatFile* oat_file = OatFile::Open(oat_filename, oat_filename, NULL);
     if (oat_file == NULL) {
       fprintf(stderr, "Failed to open oat file from %s\n", oat_filename);
       return EXIT_FAILURE;
     }
-    OatDumper oat_dumper;
-    oat_dumper.Dump(oat_filename, *os, *oat_file);
+    OatDumper oat_dumper(*oat_file);
+    oat_dumper.Dump(*os);
     return EXIT_SUCCESS;
   }
 
@@ -767,7 +1126,8 @@
     fprintf(stderr, "Invalid image header %s\n", image_filename);
     return EXIT_FAILURE;
   }
-  ImageDump::Dump(image_filename, host_prefix, *os, *image_space, image_header);
+  ImageDumper image_dumper(*os, image_filename, host_prefix, *image_space, image_header);
+  image_dumper.Dump();
   return EXIT_SUCCESS;
 }