ART: Add utility function to dump dex CFG

Add a utility function in utils.h to dump the dex CFG of
a method.

Add an option ("-g") to dump CFGs of a dex file in dexdump2.

Change-Id: I380082f0abe8ed7eeb6a9186364a99425f69f55c
diff --git a/dexdump/dexdump.cc b/dexdump/dexdump.cc
index 84c465f..282db5d 100644
--- a/dexdump/dexdump.cc
+++ b/dexdump/dexdump.cc
@@ -38,11 +38,14 @@
 #include <inttypes.h>
 #include <stdio.h>
 
+#include <iostream>
 #include <memory>
+#include <sstream>
 #include <vector>
 
 #include "dex_file-inl.h"
 #include "dex_instruction-inl.h"
+#include "utils.h"
 
 namespace art {
 
@@ -1046,6 +1049,49 @@
 }
 
 /*
+ * 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_item) {
+  if (code_item != nullptr) {
+    std::ostringstream oss;
+    DumpMethodCFG(dex_file, dex_method_idx, oss);
+    fprintf(gOutFile, "%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.
@@ -1061,6 +1107,11 @@
     return;
   }
 
+  if (gOptions.cfg) {
+    dumpCfg(pDexFile, 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
diff --git a/dexdump/dexdump.h b/dexdump/dexdump.h
index f2cd16a..50280a9 100644
--- a/dexdump/dexdump.h
+++ b/dexdump/dexdump.h
@@ -45,6 +45,7 @@
   bool showFileHeaders;
   bool showSectionHeaders;
   bool verbose;
+  bool cfg;
   OutputFormat outputFormat;
   const char* outputFileName;
   const char* tempFileName;
diff --git a/dexdump/dexdump_main.cc b/dexdump/dexdump_main.cc
index 9be0922..2466f33 100644
--- a/dexdump/dexdump_main.cc
+++ b/dexdump/dexdump_main.cc
@@ -46,6 +46,7 @@
   fprintf(stderr, " -c : verify checksum and exit\n");
   fprintf(stderr, " -d : disassemble code sections\n");
   fprintf(stderr, " -f : display summary information from file header\n");
+  fprintf(stderr, " -g : dump 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");
@@ -68,7 +69,7 @@
 
   // Parse all arguments.
   while (1) {
-    const int ic = getopt(argc, argv, "cdfhil:t:o:");
+    const int ic = getopt(argc, argv, "cdfghil:t:o:");
     if (ic < 0) {
       break;  // done
     }
@@ -82,6 +83,9 @@
       case 'f':  // dump outer file header
         gOptions.showFileHeaders = true;
         break;
+      case 'g':  // dump cfg
+        gOptions.cfg = true;
+        break;
       case 'h':  // dump section headers, i.e. all meta-data
         gOptions.showSectionHeaders = true;
         break;