Merge "Add support for proguard deobfuscation."
diff --git a/tools/ahat/Android.mk b/tools/ahat/Android.mk
index ebf087d..27c2054 100644
--- a/tools/ahat/Android.mk
+++ b/tools/ahat/Android.mk
@@ -55,11 +55,23 @@
 include $(BUILD_HOST_JAVA_LIBRARY)
 AHAT_TEST_JAR := $(LOCAL_BUILT_MODULE)
 
+# Rule to generate the proguard configuration for the test-dump program.
+# We copy the configuration to the intermediates directory because jack will
+# output the proguard map in that same directory.
+AHAT_TEST_DUMP_PROGUARD_CONFIG := $(intermediates.COMMON)/config.pro
+AHAT_TEST_DUMP_PROGUARD_MAP := $(intermediates.COMMON)/proguard.map
+$(AHAT_TEST_DUMP_PROGUARD_CONFIG): PRIVATE_AHAT_PROGUARD_CONFIG_IN := $(LOCAL_PATH)/test-dump/config.pro
+$(AHAT_TEST_DUMP_PROGUARD_CONFIG): PRIVATE_AHAT_PROGUARD_CONFIG := $(AHAT_TEST_DUMP_PROGUARD_CONFIG)
+$(AHAT_TEST_DUMP_PROGUARD_CONFIG): $(LOCAL_PATH)/test-dump/config.pro
+	cp $(PRIVATE_AHAT_PROGUARD_CONFIG_IN) $(PRIVATE_AHAT_PROGUARD_CONFIG)
+
 # --- ahat-test-dump.jar --------------
 include $(CLEAR_VARS)
 LOCAL_MODULE := ahat-test-dump
 LOCAL_MODULE_TAGS := tests
 LOCAL_SRC_FILES := $(call all-java-files-under, test-dump)
+LOCAL_ADDITIONAL_DEPENDENCIES := $(AHAT_TEST_DUMP_PROGUARD_CONFIG)
+LOCAL_JACK_FLAGS := --config-proguard $(AHAT_TEST_DUMP_PROGUARD_CONFIG)
 include $(BUILD_HOST_DALVIK_JAVA_LIBRARY)
 
 # Determine the location of the test-dump.jar and test-dump.hprof files.
@@ -84,12 +96,15 @@
 .PHONY: ahat-test
 ahat-test: PRIVATE_AHAT_TEST_DUMP_HPROF := $(AHAT_TEST_DUMP_HPROF)
 ahat-test: PRIVATE_AHAT_TEST_JAR := $(AHAT_TEST_JAR)
+ahat-test: PRIVATE_AHAT_PROGUARD_MAP := $(AHAT_TEST_DUMP_PROGUARD_MAP)
 ahat-test: $(AHAT_TEST_JAR) $(AHAT_TEST_DUMP_HPROF)
-	java -Dahat.test.dump.hprof=$(PRIVATE_AHAT_TEST_DUMP_HPROF) -jar $(PRIVATE_AHAT_TEST_JAR)
+	java -Dahat.test.dump.hprof=$(PRIVATE_AHAT_TEST_DUMP_HPROF) -Dahat.test.dump.map=$(PRIVATE_AHAT_PROGUARD_MAP) -jar $(PRIVATE_AHAT_TEST_JAR)
 
 # Clean up local variables.
 AHAT_TEST_DUMP_DEPENDENCIES :=
 AHAT_TEST_DUMP_HPROF :=
 AHAT_TEST_DUMP_JAR :=
+AHAT_TEST_DUMP_PROGUARD_CONFIG :=
+AHAT_TEST_DUMP_PROGUARD_MAP :=
 AHAT_TEST_JAR :=
 
diff --git a/tools/ahat/README.txt b/tools/ahat/README.txt
index dbc1102..8dfb4ab 100644
--- a/tools/ahat/README.txt
+++ b/tools/ahat/README.txt
@@ -1,12 +1,14 @@
 AHAT - Android Heap Analysis Tool
 
 Usage:
-  java -jar ahat.jar [-p port] FILE
+  java -jar ahat.jar [-p port] [--proguard-map FILE] FILE
     Launch an http server for viewing the given Android heap-dump FILE.
 
   Options:
     -p <port>
        Serve pages on the given port. Defaults to 7100.
+    --proguard-map FILE
+       Use the proguard map FILE to deobfuscate the heap dump.
 
 TODO:
  * Have a way to diff two heap dumps.
@@ -84,7 +86,7 @@
    Target Java 1.7.
 
  0.6 Jun 21, 2016
-   Add support for proguard deobfuscation (pending AOSP push of perflib)
+   Add support for proguard deobfuscation.
 
  0.5 Apr 19, 2016
    Update perflib to perflib-25.0.0 to improve processing performance.
diff --git a/tools/ahat/src/AhatSnapshot.java b/tools/ahat/src/AhatSnapshot.java
index a8205c7..ba8243f 100644
--- a/tools/ahat/src/AhatSnapshot.java
+++ b/tools/ahat/src/AhatSnapshot.java
@@ -20,6 +20,7 @@
 import com.android.tools.perflib.heap.ClassObj;
 import com.android.tools.perflib.heap.Heap;
 import com.android.tools.perflib.heap.Instance;
+import com.android.tools.perflib.heap.ProguardMap;
 import com.android.tools.perflib.heap.RootObj;
 import com.android.tools.perflib.heap.RootType;
 import com.android.tools.perflib.heap.Snapshot;
@@ -71,8 +72,8 @@
   /**
    * Create an AhatSnapshot from an hprof file.
    */
-  public static AhatSnapshot fromHprof(File hprof) throws IOException {
-    Snapshot snapshot = Snapshot.createSnapshot(new MemoryMappedFileBuffer(hprof));
+  public static AhatSnapshot fromHprof(File hprof, ProguardMap map) throws IOException {
+    Snapshot snapshot = Snapshot.createSnapshot(new MemoryMappedFileBuffer(hprof), map);
     snapshot.computeDominators();
     return new AhatSnapshot(snapshot);
   }
diff --git a/tools/ahat/src/Main.java b/tools/ahat/src/Main.java
index fdc5a86..c79b578 100644
--- a/tools/ahat/src/Main.java
+++ b/tools/ahat/src/Main.java
@@ -16,24 +16,28 @@
 
 package com.android.ahat;
 
+import com.android.tools.perflib.heap.ProguardMap;
 import com.sun.net.httpserver.HttpServer;
 import java.io.File;
 import java.io.IOException;
 import java.io.PrintStream;
 import java.net.InetAddress;
 import java.net.InetSocketAddress;
+import java.text.ParseException;
 import java.util.concurrent.Executors;
 
 public class Main {
 
   public static void help(PrintStream out) {
-    out.println("java -jar ahat.jar [-p port] FILE");
+    out.println("java -jar ahat.jar [-p port] [--proguard-map FILE] FILE");
     out.println("  Launch an http server for viewing "
         + "the given Android heap-dump FILE.");
     out.println("");
     out.println("Options:");
     out.println("  -p <port>");
     out.println("     Serve pages on the given port. Defaults to 7100.");
+    out.println("  --proguard-map FILE");
+    out.println("     Use the proguard map FILE to deobfuscate the heap dump.");
     out.println("");
   }
 
@@ -47,10 +51,19 @@
     }
 
     File hprof = null;
+    ProguardMap map = new ProguardMap();
     for (int i = 0; i < args.length; i++) {
       if ("-p".equals(args[i]) && i + 1 < args.length) {
         i++;
         port = Integer.parseInt(args[i]);
+      } else if ("--proguard-map".equals(args[i]) && i + 1 < args.length) {
+        i++;
+        try {
+          map.readFromFile(new File(args[i]));
+        } catch (IOException|ParseException ex) {
+          System.out.println("Unable to read proguard map: " + ex);
+          System.out.println("The proguard map will not be used.");
+        }
       } else {
         if (hprof != null) {
           System.err.println("multiple input files.");
@@ -74,7 +87,7 @@
     HttpServer server = HttpServer.create(addr, 0);
 
     System.out.println("Processing hprof file...");
-    AhatSnapshot ahat = AhatSnapshot.fromHprof(hprof);
+    AhatSnapshot ahat = AhatSnapshot.fromHprof(hprof, map);
     server.createContext("/", new AhatHttpHandler(new OverviewHandler(ahat, hprof)));
     server.createContext("/rooted", new AhatHttpHandler(new RootedHandler(ahat)));
     server.createContext("/object", new AhatHttpHandler(new ObjectHandler(ahat)));
diff --git a/tools/ahat/test-dump/config.pro b/tools/ahat/test-dump/config.pro
new file mode 100644
index 0000000..0cf7a87
--- /dev/null
+++ b/tools/ahat/test-dump/config.pro
@@ -0,0 +1,15 @@
+# The goal of this proguard configuration is to obfuscate the test-dump
+# program so that the heap dump it generates is an obfuscated heap dump.
+# This allows us to test that deobfuscation of the generated heap dump is
+# working properly.
+
+# All we care about is obfuscation. Don't do any other optimizations.
+-dontpreverify
+-dontoptimize
+-dontshrink
+
+-keep public class Main {
+  public static void main(java.lang.String[]);
+}
+
+-printmapping proguard.map
diff --git a/tools/ahat/test/TestDump.java b/tools/ahat/test/TestDump.java
index c3a76e4..ebce61c 100644
--- a/tools/ahat/test/TestDump.java
+++ b/tools/ahat/test/TestDump.java
@@ -19,8 +19,10 @@
 import com.android.tools.perflib.heap.ClassObj;
 import com.android.tools.perflib.heap.Field;
 import com.android.tools.perflib.heap.Instance;
+import com.android.tools.perflib.heap.ProguardMap;
 import java.io.File;
 import java.io.IOException;
+import java.text.ParseException;
 import java.util.Map;
 
 /**
@@ -44,11 +46,21 @@
    * For example:
    *   java -Dahat.test.dump.hprof=test-dump.hprof -jar ahat-tests.jar
    *
-   * An IOException is thrown if there is a failure reading the hprof file.
+   * An IOException is thrown if there is a failure reading the hprof file or
+   * the proguard map.
    */
   private TestDump() throws IOException {
       String hprof = System.getProperty("ahat.test.dump.hprof");
-      mSnapshot = AhatSnapshot.fromHprof(new File(hprof));
+
+      String mapfile = System.getProperty("ahat.test.dump.map");
+      ProguardMap map = new ProguardMap();
+      try {
+        map.readFromFile(new File(mapfile));
+      } catch (ParseException e) {
+        throw new IOException("Unable to load proguard map", e);
+      }
+
+      mSnapshot = AhatSnapshot.fromHprof(new File(hprof), map);
   }
 
   /**