Move dex CFG dumping out of utils.cc

Move CFG dumping to dexdump, the only client.

Bug: 22322814
Test: test-art-host
Change-Id: I0f39f1d5dfc446419d26d709b78d04e45616f42c
diff --git a/dexdump/Android.bp b/dexdump/Android.bp
index 3e589f7..60ce363 100644
--- a/dexdump/Android.bp
+++ b/dexdump/Android.bp
@@ -18,6 +18,7 @@
     name: "dexdump2",
     host_supported: true,
     srcs: [
+        "dexdump_cfg.cc",
         "dexdump_main.cc",
         "dexdump.cc",
     ],
diff --git a/dexdump/dexdump.cc b/dexdump/dexdump.cc
index b241c8b..15b6e17 100644
--- a/dexdump/dexdump.cc
+++ b/dexdump/dexdump.cc
@@ -43,9 +43,9 @@
 #include <vector>
 
 #include "base/stringprintf.h"
+#include "dexdump_cfg.h"
 #include "dex_file-inl.h"
 #include "dex_instruction-inl.h"
-#include "utils.h"
 
 namespace art {
 
@@ -1358,7 +1358,7 @@
   if (code_item != nullptr) {
     std::ostringstream oss;
     DumpMethodCFG(dex_file, dex_method_idx, oss);
-    fprintf(gOutFile, "%s", oss.str().c_str());
+    fputs(oss.str().c_str(), gOutFile);
   }
 }
 
diff --git a/dexdump/dexdump_cfg.cc b/dexdump/dexdump_cfg.cc
new file mode 100644
index 0000000..9e58128
--- /dev/null
+++ b/dexdump/dexdump_cfg.cc
@@ -0,0 +1,395 @@
+/*
+ * Copyright (C) 2016 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.
+ *
+ * Implementation file for control flow graph dumping for the dexdump utility.
+ */
+
+#include "dexdump_cfg.h"
+
+#include <inttypes.h>
+#include <ostream>
+#include <map>
+#include <set>
+
+#include "dex_file-inl.h"
+#include "dex_instruction-inl.h"
+
+namespace art {
+
+static void dumpMethodCFGImpl(const DexFile* dex_file,
+                              uint32_t dex_method_idx,
+                              const DexFile::CodeItem* code_item,
+                              std::ostream& os) {
+  os << "digraph {\n";
+  os << "  # /* " << dex_file->PrettyMethod(dex_method_idx, true) << " */\n";
+
+  std::set<uint32_t> dex_pc_is_branch_target;
+  {
+    // Go and populate.
+    const Instruction* inst = Instruction::At(code_item->insns_);
+    for (uint32_t dex_pc = 0;
+         dex_pc < code_item->insns_size_in_code_units_;
+         dex_pc += inst->SizeInCodeUnits(), inst = inst->Next()) {
+      if (inst->IsBranch()) {
+        dex_pc_is_branch_target.insert(dex_pc + inst->GetTargetOffset());
+      } else if (inst->IsSwitch()) {
+        const uint16_t* insns = code_item->insns_ + dex_pc;
+        int32_t switch_offset = insns[1] | (static_cast<int32_t>(insns[2]) << 16);
+        const uint16_t* switch_insns = insns + switch_offset;
+        uint32_t switch_count = switch_insns[1];
+        int32_t targets_offset;
+        if ((*insns & 0xff) == Instruction::PACKED_SWITCH) {
+          /* 0=sig, 1=count, 2/3=firstKey */
+          targets_offset = 4;
+        } else {
+          /* 0=sig, 1=count, 2..count*2 = keys */
+          targets_offset = 2 + 2 * switch_count;
+        }
+        for (uint32_t targ = 0; targ < switch_count; targ++) {
+          int32_t offset =
+              static_cast<int32_t>(switch_insns[targets_offset + targ * 2]) |
+              static_cast<int32_t>(switch_insns[targets_offset + targ * 2 + 1] << 16);
+          dex_pc_is_branch_target.insert(dex_pc + offset);
+        }
+      }
+    }
+  }
+
+  // Create nodes for "basic blocks."
+  std::map<uint32_t, uint32_t> dex_pc_to_node_id;  // This only has entries for block starts.
+  std::map<uint32_t, uint32_t> dex_pc_to_incl_id;  // This has entries for all dex pcs.
+
+  {
+    const Instruction* inst = Instruction::At(code_item->insns_);
+    bool first_in_block = true;
+    bool force_new_block = false;
+    for (uint32_t dex_pc = 0;
+         dex_pc < code_item->insns_size_in_code_units_;
+         dex_pc += inst->SizeInCodeUnits(), inst = inst->Next()) {
+      if (dex_pc == 0 ||
+          (dex_pc_is_branch_target.find(dex_pc) != dex_pc_is_branch_target.end()) ||
+          force_new_block) {
+        uint32_t id = dex_pc_to_node_id.size();
+        if (id > 0) {
+          // End last node.
+          os << "}\"];\n";
+        }
+        // Start next node.
+        os << "  node" << id << " [shape=record,label=\"{";
+        dex_pc_to_node_id.insert(std::make_pair(dex_pc, id));
+        first_in_block = true;
+        force_new_block = false;
+      }
+
+      // Register instruction.
+      dex_pc_to_incl_id.insert(std::make_pair(dex_pc, dex_pc_to_node_id.size() - 1));
+
+      // Print instruction.
+      if (!first_in_block) {
+        os << " | ";
+      } else {
+        first_in_block = false;
+      }
+
+      // Dump the instruction. Need to escape '"', '<', '>', '{' and '}'.
+      os << "<" << "p" << dex_pc << ">";
+      os << " 0x" << std::hex << dex_pc << std::dec << ": ";
+      std::string inst_str = inst->DumpString(dex_file);
+      size_t cur_start = 0;  // It's OK to start at zero, instruction dumps don't start with chars
+                             // we need to escape.
+      while (cur_start != std::string::npos) {
+        size_t next_escape = inst_str.find_first_of("\"{}<>", cur_start + 1);
+        if (next_escape == std::string::npos) {
+          os << inst_str.substr(cur_start, inst_str.size() - cur_start);
+          break;
+        } else {
+          os << inst_str.substr(cur_start, next_escape - cur_start);
+          // Escape all necessary characters.
+          while (next_escape < inst_str.size()) {
+            char c = inst_str.at(next_escape);
+            if (c == '"' || c == '{' || c == '}' || c == '<' || c == '>') {
+              os << '\\' << c;
+            } else {
+              break;
+            }
+            next_escape++;
+          }
+          if (next_escape >= inst_str.size()) {
+            next_escape = std::string::npos;
+          }
+          cur_start = next_escape;
+        }
+      }
+
+      // Force a new block for some fall-throughs and some instructions that terminate the "local"
+      // control flow.
+      force_new_block = inst->IsSwitch() || inst->IsBasicBlockEnd();
+    }
+    // Close last node.
+    if (dex_pc_to_node_id.size() > 0) {
+      os << "}\"];\n";
+    }
+  }
+
+  // Create edges between them.
+  {
+    std::ostringstream regular_edges;
+    std::ostringstream taken_edges;
+    std::ostringstream exception_edges;
+
+    // Common set of exception edges.
+    std::set<uint32_t> exception_targets;
+
+    // These blocks (given by the first dex pc) need exception per dex-pc handling in a second
+    // pass. In the first pass we try and see whether we can use a common set of edges.
+    std::set<uint32_t> blocks_with_detailed_exceptions;
+
+    {
+      uint32_t last_node_id = std::numeric_limits<uint32_t>::max();
+      uint32_t old_dex_pc = 0;
+      uint32_t block_start_dex_pc = std::numeric_limits<uint32_t>::max();
+      const Instruction* inst = Instruction::At(code_item->insns_);
+      for (uint32_t dex_pc = 0;
+          dex_pc < code_item->insns_size_in_code_units_;
+          old_dex_pc = dex_pc, dex_pc += inst->SizeInCodeUnits(), inst = inst->Next()) {
+        {
+          auto it = dex_pc_to_node_id.find(dex_pc);
+          if (it != dex_pc_to_node_id.end()) {
+            if (!exception_targets.empty()) {
+              // It seems the last block had common exception handlers. Add the exception edges now.
+              uint32_t node_id = dex_pc_to_node_id.find(block_start_dex_pc)->second;
+              for (uint32_t handler_pc : exception_targets) {
+                auto node_id_it = dex_pc_to_incl_id.find(handler_pc);
+                if (node_id_it != dex_pc_to_incl_id.end()) {
+                  exception_edges << "  node" << node_id
+                      << " -> node" << node_id_it->second << ":p" << handler_pc
+                      << ";\n";
+                }
+              }
+              exception_targets.clear();
+            }
+
+            block_start_dex_pc = dex_pc;
+
+            // Seems to be a fall-through, connect to last_node_id. May be spurious edges for things
+            // like switch data.
+            uint32_t old_last = last_node_id;
+            last_node_id = it->second;
+            if (old_last != std::numeric_limits<uint32_t>::max()) {
+              regular_edges << "  node" << old_last << ":p" << old_dex_pc
+                  << " -> node" << last_node_id << ":p" << dex_pc
+                  << ";\n";
+            }
+          }
+
+          // Look at the exceptions of the first entry.
+          CatchHandlerIterator catch_it(*code_item, dex_pc);
+          for (; catch_it.HasNext(); catch_it.Next()) {
+            exception_targets.insert(catch_it.GetHandlerAddress());
+          }
+        }
+
+        // Handle instruction.
+
+        // Branch: something with at most two targets.
+        if (inst->IsBranch()) {
+          const int32_t offset = inst->GetTargetOffset();
+          const bool conditional = !inst->IsUnconditional();
+
+          auto target_it = dex_pc_to_node_id.find(dex_pc + offset);
+          if (target_it != dex_pc_to_node_id.end()) {
+            taken_edges << "  node" << last_node_id << ":p" << dex_pc
+                << " -> node" << target_it->second << ":p" << (dex_pc + offset)
+                << ";\n";
+          }
+          if (!conditional) {
+            // No fall-through.
+            last_node_id = std::numeric_limits<uint32_t>::max();
+          }
+        } else if (inst->IsSwitch()) {
+          // TODO: Iterate through all switch targets.
+          const uint16_t* insns = code_item->insns_ + dex_pc;
+          /* make sure the start of the switch is in range */
+          int32_t switch_offset = insns[1] | (static_cast<int32_t>(insns[2]) << 16);
+          /* offset to switch table is a relative branch-style offset */
+          const uint16_t* switch_insns = insns + switch_offset;
+          uint32_t switch_count = switch_insns[1];
+          int32_t targets_offset;
+          if ((*insns & 0xff) == Instruction::PACKED_SWITCH) {
+            /* 0=sig, 1=count, 2/3=firstKey */
+            targets_offset = 4;
+          } else {
+            /* 0=sig, 1=count, 2..count*2 = keys */
+            targets_offset = 2 + 2 * switch_count;
+          }
+          /* make sure the end of the switch is in range */
+          /* verify each switch target */
+          for (uint32_t targ = 0; targ < switch_count; targ++) {
+            int32_t offset =
+                static_cast<int32_t>(switch_insns[targets_offset + targ * 2]) |
+                static_cast<int32_t>(switch_insns[targets_offset + targ * 2 + 1] << 16);
+            int32_t abs_offset = dex_pc + offset;
+            auto target_it = dex_pc_to_node_id.find(abs_offset);
+            if (target_it != dex_pc_to_node_id.end()) {
+              // TODO: value label.
+              taken_edges << "  node" << last_node_id << ":p" << dex_pc
+                  << " -> node" << target_it->second << ":p" << (abs_offset)
+                  << ";\n";
+            }
+          }
+        }
+
+        // Exception edges. If this is not the first instruction in the block
+        if (block_start_dex_pc != dex_pc) {
+          std::set<uint32_t> current_handler_pcs;
+          CatchHandlerIterator catch_it(*code_item, dex_pc);
+          for (; catch_it.HasNext(); catch_it.Next()) {
+            current_handler_pcs.insert(catch_it.GetHandlerAddress());
+          }
+          if (current_handler_pcs != exception_targets) {
+            exception_targets.clear();  // Clear so we don't do something at the end.
+            blocks_with_detailed_exceptions.insert(block_start_dex_pc);
+          }
+        }
+
+        if (inst->IsReturn() ||
+            (inst->Opcode() == Instruction::THROW) ||
+            (inst->IsBranch() && inst->IsUnconditional())) {
+          // No fall-through.
+          last_node_id = std::numeric_limits<uint32_t>::max();
+        }
+      }
+      // Finish up the last block, if it had common exceptions.
+      if (!exception_targets.empty()) {
+        // It seems the last block had common exception handlers. Add the exception edges now.
+        uint32_t node_id = dex_pc_to_node_id.find(block_start_dex_pc)->second;
+        for (uint32_t handler_pc : exception_targets) {
+          auto node_id_it = dex_pc_to_incl_id.find(handler_pc);
+          if (node_id_it != dex_pc_to_incl_id.end()) {
+            exception_edges << "  node" << node_id
+                << " -> node" << node_id_it->second << ":p" << handler_pc
+                << ";\n";
+          }
+        }
+        exception_targets.clear();
+      }
+    }
+
+    // Second pass for detailed exception blocks.
+    // TODO
+    // Exception edges. If this is not the first instruction in the block
+    for (uint32_t dex_pc : blocks_with_detailed_exceptions) {
+      const Instruction* inst = Instruction::At(&code_item->insns_[dex_pc]);
+      uint32_t this_node_id = dex_pc_to_incl_id.find(dex_pc)->second;
+      while (true) {
+        CatchHandlerIterator catch_it(*code_item, dex_pc);
+        if (catch_it.HasNext()) {
+          std::set<uint32_t> handled_targets;
+          for (; catch_it.HasNext(); catch_it.Next()) {
+            uint32_t handler_pc = catch_it.GetHandlerAddress();
+            auto it = handled_targets.find(handler_pc);
+            if (it == handled_targets.end()) {
+              auto node_id_it = dex_pc_to_incl_id.find(handler_pc);
+              if (node_id_it != dex_pc_to_incl_id.end()) {
+                exception_edges << "  node" << this_node_id << ":p" << dex_pc
+                    << " -> node" << node_id_it->second << ":p" << handler_pc
+                    << ";\n";
+              }
+
+              // Mark as done.
+              handled_targets.insert(handler_pc);
+            }
+          }
+        }
+        if (inst->IsBasicBlockEnd()) {
+          break;
+        }
+
+        // Loop update. Have a break-out if the next instruction is a branch target and thus in
+        // another block.
+        dex_pc += inst->SizeInCodeUnits();
+        if (dex_pc >= code_item->insns_size_in_code_units_) {
+          break;
+        }
+        if (dex_pc_to_node_id.find(dex_pc) != dex_pc_to_node_id.end()) {
+          break;
+        }
+        inst = inst->Next();
+      }
+    }
+
+    // Write out the sub-graphs to make edges styled.
+    os << "\n";
+    os << "  subgraph regular_edges {\n";
+    os << "    edge [color=\"#000000\",weight=.3,len=3];\n\n";
+    os << "    " << regular_edges.str() << "\n";
+    os << "  }\n\n";
+
+    os << "  subgraph taken_edges {\n";
+    os << "    edge [color=\"#00FF00\",weight=.3,len=3];\n\n";
+    os << "    " << taken_edges.str() << "\n";
+    os << "  }\n\n";
+
+    os << "  subgraph exception_edges {\n";
+    os << "    edge [color=\"#FF0000\",weight=.3,len=3];\n\n";
+    os << "    " << exception_edges.str() << "\n";
+    os << "  }\n\n";
+  }
+
+  os << "}\n";
+}
+
+void DumpMethodCFG(const DexFile* dex_file, uint32_t dex_method_idx, std::ostream& os) {
+  // This is painful, we need to find the code item. That means finding the class, and then
+  // iterating the table.
+  if (dex_method_idx >= dex_file->NumMethodIds()) {
+    os << "Could not find method-idx.";
+    return;
+  }
+  const DexFile::MethodId& method_id = dex_file->GetMethodId(dex_method_idx);
+
+  const DexFile::ClassDef* class_def = dex_file->FindClassDef(method_id.class_idx_);
+  if (class_def == nullptr) {
+    os << "Could not find class-def.";
+    return;
+  }
+
+  const uint8_t* class_data = dex_file->GetClassData(*class_def);
+  if (class_data == nullptr) {
+    os << "No class data.";
+    return;
+  }
+
+  ClassDataItemIterator it(*dex_file, class_data);
+  // Skip fields
+  while (it.HasNextStaticField() || it.HasNextInstanceField()) {
+    it.Next();
+  }
+
+  // Find method, and dump it.
+  while (it.HasNextDirectMethod() || it.HasNextVirtualMethod()) {
+    uint32_t method_idx = it.GetMemberIndex();
+    if (method_idx == dex_method_idx) {
+      dumpMethodCFGImpl(dex_file, dex_method_idx, it.GetMethodCodeItem(), os);
+      return;
+    }
+    it.Next();
+  }
+
+  // Otherwise complain.
+  os << "Something went wrong, didn't find the method in the class data.";
+}
+
+}  // namespace art
diff --git a/dexdump/dexdump_cfg.h b/dexdump/dexdump_cfg.h
new file mode 100644
index 0000000..64e5f9a
--- /dev/null
+++ b/dexdump/dexdump_cfg.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2016 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_DEXDUMP_DEXDUMP_CFG_H_
+#define ART_DEXDUMP_DEXDUMP_CFG_H_
+
+#include <inttypes.h>
+#include <ostream>
+
+namespace art {
+
+class DexFile;
+
+void DumpMethodCFG(const DexFile* dex_file, uint32_t dex_method_idx, std::ostream& os);
+
+}  // namespace art
+
+#endif  // ART_DEXDUMP_DEXDUMP_CFG_H_
diff --git a/dexlayout/dexlayout.cc b/dexlayout/dexlayout.cc
index 1071880..2b30a1b 100644
--- a/dexlayout/dexlayout.cc
+++ b/dexlayout/dexlayout.cc
@@ -1286,49 +1286,6 @@
 }
 
 /*
- * Dumping a CFG. Note that this will do duplicate work. utils.h doesn't expose the code-item
- * version, so the DumpMethodCFG code will have to iterate again to find it. But dexdump is a
- * tool, so this is not performance-critical.
- */
-
-static void DumpCFG(const DexFile* dex_file,
-                    uint32_t dex_method_idx,
-                    const DexFile::CodeItem* code) {
-  if (code != nullptr) {
-    std::ostringstream oss;
-    DumpMethodCFG(dex_file, dex_method_idx, oss);
-    fprintf(out_file_, "%s", oss.str().c_str());
-  }
-}
-
-static void DumpCFG(const DexFile* dex_file, int idx) {
-  const DexFile::ClassDef& class_def = dex_file->GetClassDef(idx);
-  const uint8_t* class_data = dex_file->GetClassData(class_def);
-  if (class_data == nullptr) {  // empty class such as a marker interface?
-    return;
-  }
-  ClassDataItemIterator it(*dex_file, class_data);
-  while (it.HasNextStaticField()) {
-    it.Next();
-  }
-  while (it.HasNextInstanceField()) {
-    it.Next();
-  }
-  while (it.HasNextDirectMethod()) {
-    DumpCFG(dex_file,
-            it.GetMemberIndex(),
-            it.GetMethodCodeItem());
-    it.Next();
-  }
-  while (it.HasNextVirtualMethod()) {
-    DumpCFG(dex_file,
-            it.GetMemberIndex(),
-            it.GetMethodCodeItem());
-    it.Next();
-  }
-}
-
-/*
  * Dumps the class.
  *
  * Note "idx" is a DexClassDef index, not a DexTypeId index.
@@ -1336,10 +1293,7 @@
  * If "*last_package" is nullptr or does not match the current class' package,
  * the value will be replaced with a newly-allocated string.
  */
-static void DumpClass(const DexFile* dex_file,
-                      dex_ir::Header* header,
-                      int idx,
-                      char** last_package) {
+static void DumpClass(dex_ir::Header* header, int idx, char** last_package) {
   dex_ir::ClassDef* class_def = header->GetCollections().GetClassDef(idx);
   // Omitting non-public class.
   if (options_.exports_only_ && (class_def->GetAccessFlags() & kAccPublic) == 0) {
@@ -1354,11 +1308,6 @@
     DumpClassAnnotations(header, idx);
   }
 
-  if (options_.show_cfg_) {
-    DumpCFG(dex_file, idx);
-    return;
-  }
-
   // For the XML output, show the package name.  Ideally we'd gather
   // up the classes, sort them, and dump them alphabetically so the
   // package name wouldn't jump around, but that's not a great plan
@@ -1561,7 +1510,7 @@
   char* package = nullptr;
   const uint32_t class_defs_size = header->GetCollections().ClassDefsSize();
   for (uint32_t i = 0; i < class_defs_size; i++) {
-    DumpClass(dex_file, header.get(), i, &package);
+    DumpClass(header.get(), i, &package);
   }  // for
 
   // Free the last package allocated.
diff --git a/dexlayout/dexlayout.h b/dexlayout/dexlayout.h
index c01eb79..a5bd992 100644
--- a/dexlayout/dexlayout.h
+++ b/dexlayout/dexlayout.h
@@ -44,7 +44,6 @@
   bool exports_only_;
   bool ignore_bad_checksum_;
   bool show_annotations_;
-  bool show_cfg_;
   bool show_file_headers_;
   bool show_section_headers_;
   bool verbose_;
diff --git a/dexlayout/dexlayout_main.cc b/dexlayout/dexlayout_main.cc
index 2203fba..825dd50 100644
--- a/dexlayout/dexlayout_main.cc
+++ b/dexlayout/dexlayout_main.cc
@@ -51,7 +51,6 @@
   fprintf(stderr, " -d : disassemble code sections\n");
   fprintf(stderr, " -e : display exported items only\n");
   fprintf(stderr, " -f : display summary information from file header\n");
-  fprintf(stderr, " -g : display CFG for dex\n");
   fprintf(stderr, " -h : display file header details\n");
   fprintf(stderr, " -i : ignore checksum failures\n");
   fprintf(stderr, " -l : output layout, either 'plain' or 'xml'\n");
@@ -99,9 +98,6 @@
       case 'f':  // display outer file header
         options_.show_file_headers_ = true;
         break;
-      case 'g':  // display cfg
-        options_.show_cfg_ = true;
-        break;
       case 'h':  // display section headers, i.e. all meta-data
         options_.show_section_headers_ = true;
         break;
diff --git a/runtime/utils.cc b/runtime/utils.cc
index 5557d5f..6ed54f7 100644
--- a/runtime/utils.cc
+++ b/runtime/utils.cc
@@ -1124,370 +1124,6 @@
   return PrettyDescriptor(Primitive::Descriptor(type));
 }
 
-static void DumpMethodCFGImpl(const DexFile* dex_file,
-                              uint32_t dex_method_idx,
-                              const DexFile::CodeItem* code_item,
-                              std::ostream& os) {
-  os << "digraph {\n";
-  os << "  # /* " << dex_file->PrettyMethod(dex_method_idx, true) << " */\n";
-
-  std::set<uint32_t> dex_pc_is_branch_target;
-  {
-    // Go and populate.
-    const Instruction* inst = Instruction::At(code_item->insns_);
-    for (uint32_t dex_pc = 0;
-         dex_pc < code_item->insns_size_in_code_units_;
-         dex_pc += inst->SizeInCodeUnits(), inst = inst->Next()) {
-      if (inst->IsBranch()) {
-        dex_pc_is_branch_target.insert(dex_pc + inst->GetTargetOffset());
-      } else if (inst->IsSwitch()) {
-        const uint16_t* insns = code_item->insns_ + dex_pc;
-        int32_t switch_offset = insns[1] | (static_cast<int32_t>(insns[2]) << 16);
-        const uint16_t* switch_insns = insns + switch_offset;
-        uint32_t switch_count = switch_insns[1];
-        int32_t targets_offset;
-        if ((*insns & 0xff) == Instruction::PACKED_SWITCH) {
-          /* 0=sig, 1=count, 2/3=firstKey */
-          targets_offset = 4;
-        } else {
-          /* 0=sig, 1=count, 2..count*2 = keys */
-          targets_offset = 2 + 2 * switch_count;
-        }
-        for (uint32_t targ = 0; targ < switch_count; targ++) {
-          int32_t offset =
-              static_cast<int32_t>(switch_insns[targets_offset + targ * 2]) |
-              static_cast<int32_t>(switch_insns[targets_offset + targ * 2 + 1] << 16);
-          dex_pc_is_branch_target.insert(dex_pc + offset);
-        }
-      }
-    }
-  }
-
-  // Create nodes for "basic blocks."
-  std::map<uint32_t, uint32_t> dex_pc_to_node_id;  // This only has entries for block starts.
-  std::map<uint32_t, uint32_t> dex_pc_to_incl_id;  // This has entries for all dex pcs.
-
-  {
-    const Instruction* inst = Instruction::At(code_item->insns_);
-    bool first_in_block = true;
-    bool force_new_block = false;
-    for (uint32_t dex_pc = 0;
-         dex_pc < code_item->insns_size_in_code_units_;
-         dex_pc += inst->SizeInCodeUnits(), inst = inst->Next()) {
-      if (dex_pc == 0 ||
-          (dex_pc_is_branch_target.find(dex_pc) != dex_pc_is_branch_target.end()) ||
-          force_new_block) {
-        uint32_t id = dex_pc_to_node_id.size();
-        if (id > 0) {
-          // End last node.
-          os << "}\"];\n";
-        }
-        // Start next node.
-        os << "  node" << id << " [shape=record,label=\"{";
-        dex_pc_to_node_id.insert(std::make_pair(dex_pc, id));
-        first_in_block = true;
-        force_new_block = false;
-      }
-
-      // Register instruction.
-      dex_pc_to_incl_id.insert(std::make_pair(dex_pc, dex_pc_to_node_id.size() - 1));
-
-      // Print instruction.
-      if (!first_in_block) {
-        os << " | ";
-      } else {
-        first_in_block = false;
-      }
-
-      // Dump the instruction. Need to escape '"', '<', '>', '{' and '}'.
-      os << "<" << "p" << dex_pc << ">";
-      os << " 0x" << std::hex << dex_pc << std::dec << ": ";
-      std::string inst_str = inst->DumpString(dex_file);
-      size_t cur_start = 0;  // It's OK to start at zero, instruction dumps don't start with chars
-                             // we need to escape.
-      while (cur_start != std::string::npos) {
-        size_t next_escape = inst_str.find_first_of("\"{}<>", cur_start + 1);
-        if (next_escape == std::string::npos) {
-          os << inst_str.substr(cur_start, inst_str.size() - cur_start);
-          break;
-        } else {
-          os << inst_str.substr(cur_start, next_escape - cur_start);
-          // Escape all necessary characters.
-          while (next_escape < inst_str.size()) {
-            char c = inst_str.at(next_escape);
-            if (c == '"' || c == '{' || c == '}' || c == '<' || c == '>') {
-              os << '\\' << c;
-            } else {
-              break;
-            }
-            next_escape++;
-          }
-          if (next_escape >= inst_str.size()) {
-            next_escape = std::string::npos;
-          }
-          cur_start = next_escape;
-        }
-      }
-
-      // Force a new block for some fall-throughs and some instructions that terminate the "local"
-      // control flow.
-      force_new_block = inst->IsSwitch() || inst->IsBasicBlockEnd();
-    }
-    // Close last node.
-    if (dex_pc_to_node_id.size() > 0) {
-      os << "}\"];\n";
-    }
-  }
-
-  // Create edges between them.
-  {
-    std::ostringstream regular_edges;
-    std::ostringstream taken_edges;
-    std::ostringstream exception_edges;
-
-    // Common set of exception edges.
-    std::set<uint32_t> exception_targets;
-
-    // These blocks (given by the first dex pc) need exception per dex-pc handling in a second
-    // pass. In the first pass we try and see whether we can use a common set of edges.
-    std::set<uint32_t> blocks_with_detailed_exceptions;
-
-    {
-      uint32_t last_node_id = std::numeric_limits<uint32_t>::max();
-      uint32_t old_dex_pc = 0;
-      uint32_t block_start_dex_pc = std::numeric_limits<uint32_t>::max();
-      const Instruction* inst = Instruction::At(code_item->insns_);
-      for (uint32_t dex_pc = 0;
-          dex_pc < code_item->insns_size_in_code_units_;
-          old_dex_pc = dex_pc, dex_pc += inst->SizeInCodeUnits(), inst = inst->Next()) {
-        {
-          auto it = dex_pc_to_node_id.find(dex_pc);
-          if (it != dex_pc_to_node_id.end()) {
-            if (!exception_targets.empty()) {
-              // It seems the last block had common exception handlers. Add the exception edges now.
-              uint32_t node_id = dex_pc_to_node_id.find(block_start_dex_pc)->second;
-              for (uint32_t handler_pc : exception_targets) {
-                auto node_id_it = dex_pc_to_incl_id.find(handler_pc);
-                if (node_id_it != dex_pc_to_incl_id.end()) {
-                  exception_edges << "  node" << node_id
-                      << " -> node" << node_id_it->second << ":p" << handler_pc
-                      << ";\n";
-                }
-              }
-              exception_targets.clear();
-            }
-
-            block_start_dex_pc = dex_pc;
-
-            // Seems to be a fall-through, connect to last_node_id. May be spurious edges for things
-            // like switch data.
-            uint32_t old_last = last_node_id;
-            last_node_id = it->second;
-            if (old_last != std::numeric_limits<uint32_t>::max()) {
-              regular_edges << "  node" << old_last << ":p" << old_dex_pc
-                  << " -> node" << last_node_id << ":p" << dex_pc
-                  << ";\n";
-            }
-          }
-
-          // Look at the exceptions of the first entry.
-          CatchHandlerIterator catch_it(*code_item, dex_pc);
-          for (; catch_it.HasNext(); catch_it.Next()) {
-            exception_targets.insert(catch_it.GetHandlerAddress());
-          }
-        }
-
-        // Handle instruction.
-
-        // Branch: something with at most two targets.
-        if (inst->IsBranch()) {
-          const int32_t offset = inst->GetTargetOffset();
-          const bool conditional = !inst->IsUnconditional();
-
-          auto target_it = dex_pc_to_node_id.find(dex_pc + offset);
-          if (target_it != dex_pc_to_node_id.end()) {
-            taken_edges << "  node" << last_node_id << ":p" << dex_pc
-                << " -> node" << target_it->second << ":p" << (dex_pc + offset)
-                << ";\n";
-          }
-          if (!conditional) {
-            // No fall-through.
-            last_node_id = std::numeric_limits<uint32_t>::max();
-          }
-        } else if (inst->IsSwitch()) {
-          // TODO: Iterate through all switch targets.
-          const uint16_t* insns = code_item->insns_ + dex_pc;
-          /* make sure the start of the switch is in range */
-          int32_t switch_offset = insns[1] | (static_cast<int32_t>(insns[2]) << 16);
-          /* offset to switch table is a relative branch-style offset */
-          const uint16_t* switch_insns = insns + switch_offset;
-          uint32_t switch_count = switch_insns[1];
-          int32_t targets_offset;
-          if ((*insns & 0xff) == Instruction::PACKED_SWITCH) {
-            /* 0=sig, 1=count, 2/3=firstKey */
-            targets_offset = 4;
-          } else {
-            /* 0=sig, 1=count, 2..count*2 = keys */
-            targets_offset = 2 + 2 * switch_count;
-          }
-          /* make sure the end of the switch is in range */
-          /* verify each switch target */
-          for (uint32_t targ = 0; targ < switch_count; targ++) {
-            int32_t offset =
-                static_cast<int32_t>(switch_insns[targets_offset + targ * 2]) |
-                static_cast<int32_t>(switch_insns[targets_offset + targ * 2 + 1] << 16);
-            int32_t abs_offset = dex_pc + offset;
-            auto target_it = dex_pc_to_node_id.find(abs_offset);
-            if (target_it != dex_pc_to_node_id.end()) {
-              // TODO: value label.
-              taken_edges << "  node" << last_node_id << ":p" << dex_pc
-                  << " -> node" << target_it->second << ":p" << (abs_offset)
-                  << ";\n";
-            }
-          }
-        }
-
-        // Exception edges. If this is not the first instruction in the block
-        if (block_start_dex_pc != dex_pc) {
-          std::set<uint32_t> current_handler_pcs;
-          CatchHandlerIterator catch_it(*code_item, dex_pc);
-          for (; catch_it.HasNext(); catch_it.Next()) {
-            current_handler_pcs.insert(catch_it.GetHandlerAddress());
-          }
-          if (current_handler_pcs != exception_targets) {
-            exception_targets.clear();  // Clear so we don't do something at the end.
-            blocks_with_detailed_exceptions.insert(block_start_dex_pc);
-          }
-        }
-
-        if (inst->IsReturn() ||
-            (inst->Opcode() == Instruction::THROW) ||
-            (inst->IsBranch() && inst->IsUnconditional())) {
-          // No fall-through.
-          last_node_id = std::numeric_limits<uint32_t>::max();
-        }
-      }
-      // Finish up the last block, if it had common exceptions.
-      if (!exception_targets.empty()) {
-        // It seems the last block had common exception handlers. Add the exception edges now.
-        uint32_t node_id = dex_pc_to_node_id.find(block_start_dex_pc)->second;
-        for (uint32_t handler_pc : exception_targets) {
-          auto node_id_it = dex_pc_to_incl_id.find(handler_pc);
-          if (node_id_it != dex_pc_to_incl_id.end()) {
-            exception_edges << "  node" << node_id
-                << " -> node" << node_id_it->second << ":p" << handler_pc
-                << ";\n";
-          }
-        }
-        exception_targets.clear();
-      }
-    }
-
-    // Second pass for detailed exception blocks.
-    // TODO
-    // Exception edges. If this is not the first instruction in the block
-    for (uint32_t dex_pc : blocks_with_detailed_exceptions) {
-      const Instruction* inst = Instruction::At(&code_item->insns_[dex_pc]);
-      uint32_t this_node_id = dex_pc_to_incl_id.find(dex_pc)->second;
-      while (true) {
-        CatchHandlerIterator catch_it(*code_item, dex_pc);
-        if (catch_it.HasNext()) {
-          std::set<uint32_t> handled_targets;
-          for (; catch_it.HasNext(); catch_it.Next()) {
-            uint32_t handler_pc = catch_it.GetHandlerAddress();
-            auto it = handled_targets.find(handler_pc);
-            if (it == handled_targets.end()) {
-              auto node_id_it = dex_pc_to_incl_id.find(handler_pc);
-              if (node_id_it != dex_pc_to_incl_id.end()) {
-                exception_edges << "  node" << this_node_id << ":p" << dex_pc
-                    << " -> node" << node_id_it->second << ":p" << handler_pc
-                    << ";\n";
-              }
-
-              // Mark as done.
-              handled_targets.insert(handler_pc);
-            }
-          }
-        }
-        if (inst->IsBasicBlockEnd()) {
-          break;
-        }
-
-        // Loop update. Have a break-out if the next instruction is a branch target and thus in
-        // another block.
-        dex_pc += inst->SizeInCodeUnits();
-        if (dex_pc >= code_item->insns_size_in_code_units_) {
-          break;
-        }
-        if (dex_pc_to_node_id.find(dex_pc) != dex_pc_to_node_id.end()) {
-          break;
-        }
-        inst = inst->Next();
-      }
-    }
-
-    // Write out the sub-graphs to make edges styled.
-    os << "\n";
-    os << "  subgraph regular_edges {\n";
-    os << "    edge [color=\"#000000\",weight=.3,len=3];\n\n";
-    os << "    " << regular_edges.str() << "\n";
-    os << "  }\n\n";
-
-    os << "  subgraph taken_edges {\n";
-    os << "    edge [color=\"#00FF00\",weight=.3,len=3];\n\n";
-    os << "    " << taken_edges.str() << "\n";
-    os << "  }\n\n";
-
-    os << "  subgraph exception_edges {\n";
-    os << "    edge [color=\"#FF0000\",weight=.3,len=3];\n\n";
-    os << "    " << exception_edges.str() << "\n";
-    os << "  }\n\n";
-  }
-
-  os << "}\n";
-}
-
-void DumpMethodCFG(const DexFile* dex_file, uint32_t dex_method_idx, std::ostream& os) {
-  // This is painful, we need to find the code item. That means finding the class, and then
-  // iterating the table.
-  if (dex_method_idx >= dex_file->NumMethodIds()) {
-    os << "Could not find method-idx.";
-    return;
-  }
-  const DexFile::MethodId& method_id = dex_file->GetMethodId(dex_method_idx);
-
-  const DexFile::ClassDef* class_def = dex_file->FindClassDef(method_id.class_idx_);
-  if (class_def == nullptr) {
-    os << "Could not find class-def.";
-    return;
-  }
-
-  const uint8_t* class_data = dex_file->GetClassData(*class_def);
-  if (class_data == nullptr) {
-    os << "No class data.";
-    return;
-  }
-
-  ClassDataItemIterator it(*dex_file, class_data);
-  // Skip fields
-  while (it.HasNextStaticField() || it.HasNextInstanceField()) {
-    it.Next();
-  }
-
-  // Find method, and dump it.
-  while (it.HasNextDirectMethod() || it.HasNextVirtualMethod()) {
-    uint32_t method_idx = it.GetMemberIndex();
-    if (method_idx == dex_method_idx) {
-      DumpMethodCFGImpl(dex_file, dex_method_idx, it.GetMethodCodeItem(), os);
-      return;
-    }
-    it.Next();
-  }
-
-  // Otherwise complain.
-  os << "Something went wrong, didn't find the method in the class data.";
-}
-
 static void ParseStringAfterChar(const std::string& s,
                                  char c,
                                  std::string* parsed_value,
diff --git a/runtime/utils.h b/runtime/utils.h
index f96ddd7..94738d2 100644
--- a/runtime/utils.h
+++ b/runtime/utils.h
@@ -36,12 +36,8 @@
 #include "obj_ptr.h"
 #include "primitive.h"
 
-class BacktraceMap;
-
 namespace art {
 
-class DexFile;
-
 template <typename T>
 bool ParseUint(const char *in, T* out) {
   char* end;
@@ -274,8 +270,6 @@
   return pointer_size == 4 || pointer_size == 8;
 }
 
-void DumpMethodCFG(const DexFile* dex_file, uint32_t dex_method_idx, std::ostream& os);
-
 static inline const void* EntryPointToCodePointer(const void* entry_point) {
   uintptr_t code = reinterpret_cast<uintptr_t>(entry_point);
   // TODO: Make this Thumb2 specific. It is benign on other architectures as code is always at