diff options
author | 2015-09-10 16:45:54 -0700 | |
---|---|---|
committer | 2015-10-01 10:24:03 -0700 | |
commit | 3524472e8f064dbd42e1a35d511a1efe36d2cbf2 (patch) | |
tree | 720ff24091021c6c3c6befc3b631d644df5144e9 | |
parent | d0d11f20811f260453f6dfe2e26d7dbd6ed55f01 (diff) |
Generate an hprof file to test ahat.
This change sets up the infrastructure to write test cases for ahat
that make use of an hprof file automatically generated from a sample
program.
Change-Id: Id11f656afb69c96a26655cc4caeb745ad844f431
-rw-r--r-- | tools/ahat/Android.mk | 44 | ||||
-rw-r--r-- | tools/ahat/src/AhatSnapshot.java | 27 | ||||
-rw-r--r-- | tools/ahat/src/InstanceUtils.java | 4 | ||||
-rw-r--r-- | tools/ahat/src/Main.java | 15 | ||||
-rw-r--r-- | tools/ahat/test-dump/Main.java | 52 | ||||
-rw-r--r-- | tools/ahat/test/InstanceUtilsTest.java | 48 | ||||
-rw-r--r-- | tools/ahat/test/TestDump.java | 89 | ||||
-rw-r--r-- | tools/ahat/test/Tests.java | 1 |
8 files changed, 261 insertions, 19 deletions
diff --git a/tools/ahat/Android.mk b/tools/ahat/Android.mk index 3c1522c3d8..71366c1313 100644 --- a/tools/ahat/Android.mk +++ b/tools/ahat/Android.mk @@ -16,6 +16,8 @@ LOCAL_PATH := $(call my-dir) +include art/build/Android.common_test.mk + # --- ahat.jar ---------------- include $(CLEAR_VARS) LOCAL_SRC_FILES := $(call all-java-files-under, src) @@ -44,7 +46,7 @@ $(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/ahat $(ACP) ahat: $(LOCAL_BUILT_MODULE) -# --- ahat-test.jar -------------- +# --- ahat-tests.jar -------------- include $(CLEAR_VARS) LOCAL_SRC_FILES := $(call all-java-files-under, test) LOCAL_JAR_MANIFEST := test/manifest.txt @@ -53,6 +55,42 @@ LOCAL_IS_HOST_MODULE := true LOCAL_MODULE_TAGS := tests LOCAL_MODULE := ahat-tests include $(BUILD_HOST_JAVA_LIBRARY) +AHAT_TEST_JAR := $(LOCAL_BUILT_MODULE) + +# --- 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) +include $(BUILD_HOST_DALVIK_JAVA_LIBRARY) + +# Determine the location of the test-dump.jar and test-dump.hprof files. +# These use variables set implicitly by the include of +# BUILD_HOST_DALVIK_JAVA_LIBRARY above. +AHAT_TEST_DUMP_JAR := $(LOCAL_BUILT_MODULE) +AHAT_TEST_DUMP_HPROF := $(intermediates.COMMON)/test-dump.hprof + +# Run ahat-test-dump.jar to generate test-dump.hprof +AHAT_TEST_DUMP_DEPENDENCIES := \ + $(ART_HOST_EXECUTABLES) \ + $(HOST_OUT_EXECUTABLES)/art \ + $(HOST_CORE_IMG_OUT_BASE)$(CORE_IMG_SUFFIX) + +$(AHAT_TEST_DUMP_HPROF): PRIVATE_AHAT_TEST_ART := $(HOST_OUT_EXECUTABLES)/art +$(AHAT_TEST_DUMP_HPROF): PRIVATE_AHAT_TEST_DUMP_JAR := $(AHAT_TEST_DUMP_JAR) +$(AHAT_TEST_DUMP_HPROF): PRIVATE_AHAT_TEST_DUMP_DEPENDENCIES := $(AHAT_TEST_DUMP_DEPENDENCIES) +$(AHAT_TEST_DUMP_HPROF): $(AHAT_TEST_DUMP_JAR) $(AHAT_TEST_DUMP_DEPENDENCIES) + $(PRIVATE_AHAT_TEST_ART) -cp $(PRIVATE_AHAT_TEST_DUMP_JAR) Main $@ + +.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: $(AHAT_TEST_JAR) $(AHAT_TEST_DUMP_HPROF) + java -Dahat.test.dump.hprof=$(PRIVATE_AHAT_TEST_DUMP_HPROF) -jar $(PRIVATE_AHAT_TEST_JAR) + +# Clean up local variables. +AHAT_TEST_DUMP_DEPENDENCIES := +AHAT_TEST_DUMP_HPROF := +AHAT_TEST_DUMP_JAR := +AHAT_TEST_JAR := -ahat-test: $(LOCAL_BUILT_MODULE) - java -jar $< diff --git a/tools/ahat/src/AhatSnapshot.java b/tools/ahat/src/AhatSnapshot.java index 2437d0388c..3035ef75c9 100644 --- a/tools/ahat/src/AhatSnapshot.java +++ b/tools/ahat/src/AhatSnapshot.java @@ -18,13 +18,18 @@ package com.android.ahat; import com.android.tools.perflib.heap.ClassObj; import com.android.tools.perflib.heap.Heap; +import com.android.tools.perflib.heap.HprofParser; import com.android.tools.perflib.heap.Instance; import com.android.tools.perflib.heap.RootObj; import com.android.tools.perflib.heap.Snapshot; import com.android.tools.perflib.heap.StackFrame; import com.android.tools.perflib.heap.StackTrace; +import com.android.tools.perflib.heap.io.HprofBuffer; +import com.android.tools.perflib.heap.io.MemoryMappedFileBuffer; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; +import java.io.File; +import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -47,7 +52,22 @@ class AhatSnapshot { private Site mRootSite; private Map<Heap, Long> mHeapSizes; - public AhatSnapshot(Snapshot snapshot) { + /** + * Create an AhatSnapshot from an hprof file. + */ + public static AhatSnapshot fromHprof(File hprof) throws IOException { + HprofBuffer buffer = new MemoryMappedFileBuffer(hprof); + Snapshot snapshot = (new HprofParser(buffer)).parse(); + snapshot.computeDominators(); + return new AhatSnapshot(snapshot); + } + + /** + * Construct an AhatSnapshot for the given perflib snapshot. + * Ther user is responsible for calling snapshot.computeDominators before + * calling this AhatSnapshot constructor. + */ + private AhatSnapshot(Snapshot snapshot) { mSnapshot = snapshot; mHeaps = new ArrayList<Heap>(mSnapshot.getHeaps()); mDominated = new HashMap<Instance, List<Instance>>(); @@ -92,6 +112,11 @@ class AhatSnapshot { } } + // Note: This method is exposed for testing purposes. + public ClassObj findClass(String name) { + return mSnapshot.findClass(name); + } + public Instance findInstance(long id) { return mSnapshot.findInstance(id); } diff --git a/tools/ahat/src/InstanceUtils.java b/tools/ahat/src/InstanceUtils.java index 7ee3ff24ed..a6ac3b8765 100644 --- a/tools/ahat/src/InstanceUtils.java +++ b/tools/ahat/src/InstanceUtils.java @@ -32,7 +32,7 @@ class InstanceUtils { * given name. */ public static boolean isInstanceOfClass(Instance inst, String className) { - ClassObj cls = inst.getClassObj(); + ClassObj cls = (inst == null) ? null : inst.getClassObj(); return (cls != null && className.equals(cls.getClassName())); } @@ -132,7 +132,7 @@ class InstanceUtils { * Read a field of an instance. * Returns null if the field value is null or if the field couldn't be read. */ - private static Object getField(Instance inst, String fieldName) { + public static Object getField(Instance inst, String fieldName) { if (!(inst instanceof ClassInstance)) { return null; } diff --git a/tools/ahat/src/Main.java b/tools/ahat/src/Main.java index 2e2ddd299f..1563aa0262 100644 --- a/tools/ahat/src/Main.java +++ b/tools/ahat/src/Main.java @@ -16,10 +16,6 @@ package com.android.ahat; -import com.android.tools.perflib.heap.HprofParser; -import com.android.tools.perflib.heap.Snapshot; -import com.android.tools.perflib.heap.io.HprofBuffer; -import com.android.tools.perflib.heap.io.MemoryMappedFileBuffer; import com.sun.net.httpserver.HttpServer; import java.io.File; import java.io.IOException; @@ -71,15 +67,8 @@ public class Main { return; } - System.out.println("Reading hprof file..."); - HprofBuffer buffer = new MemoryMappedFileBuffer(hprof); - Snapshot snapshot = (new HprofParser(buffer)).parse(); - - System.out.println("Computing Dominators..."); - snapshot.computeDominators(); - - System.out.println("Processing snapshot for ahat..."); - AhatSnapshot ahat = new AhatSnapshot(snapshot); + System.out.println("Processing hprof file..."); + AhatSnapshot ahat = AhatSnapshot.fromHprof(hprof); InetAddress loopback = InetAddress.getLoopbackAddress(); InetSocketAddress addr = new InetSocketAddress(loopback, port); diff --git a/tools/ahat/test-dump/Main.java b/tools/ahat/test-dump/Main.java new file mode 100644 index 0000000000..cea1dc179e --- /dev/null +++ b/tools/ahat/test-dump/Main.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2015 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. + */ + +import dalvik.system.VMDebug; +import java.io.IOException; + +/** + * Program used to create a heap dump for test purposes. + */ +public class Main { + // Keep a reference to the DumpedStuff instance so that it is not garbage + // collected before we take the heap dump. + public static DumpedStuff stuff; + + // We will take a heap dump that includes a single instance of this + // DumpedStuff class. Objects stored as fields in this class can be easily + // found in the hprof dump by searching for the instance of the DumpedStuff + // class and reading the desired field. + public static class DumpedStuff { + public String basicString = "hello, world"; + public String nullString = null; + public Object anObject = new Object(); + } + + public static void main(String[] args) throws IOException { + if (args.length < 1) { + System.err.println("no output file specified"); + return; + } + String file = args[0]; + + // Allocate the instance of DumpedStuff. + stuff = new DumpedStuff(); + + // Take a heap dump that will include that instance of DumpedStuff. + System.err.println("Dumping hprof data to " + file); + VMDebug.dumpHprofData(file); + } +} diff --git a/tools/ahat/test/InstanceUtilsTest.java b/tools/ahat/test/InstanceUtilsTest.java new file mode 100644 index 0000000000..7613df4994 --- /dev/null +++ b/tools/ahat/test/InstanceUtilsTest.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2015 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. + */ + +package com.android.ahat; + +import com.android.tools.perflib.heap.Instance; +import java.io.IOException; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import org.junit.Test; + +public class InstanceUtilsTest { + @Test + public void basicString() throws IOException { + TestDump dump = TestDump.getTestDump(); + Instance str = (Instance)dump.getDumpedThing("basicString"); + assertEquals("hello, world", InstanceUtils.asString(str)); + } + + @Test + public void nullString() throws IOException { + TestDump dump = TestDump.getTestDump(); + Instance obj = (Instance)dump.getDumpedThing("nullString"); + assertNull(InstanceUtils.asString(obj)); + } + + @Test + public void notString() throws IOException { + TestDump dump = TestDump.getTestDump(); + Instance obj = (Instance)dump.getDumpedThing("anObject"); + assertNotNull(obj); + assertNull(InstanceUtils.asString(obj)); + } +} diff --git a/tools/ahat/test/TestDump.java b/tools/ahat/test/TestDump.java new file mode 100644 index 0000000000..c3a76e4f18 --- /dev/null +++ b/tools/ahat/test/TestDump.java @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2015 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. + */ + +package com.android.ahat; + +import com.android.tools.perflib.heap.ClassObj; +import com.android.tools.perflib.heap.Field; +import com.android.tools.perflib.heap.Instance; +import java.io.File; +import java.io.IOException; +import java.util.Map; + +/** + * The TestDump class is used to get an AhatSnapshot for the test-dump + * program. + */ +public class TestDump { + // It can take on the order of a second to parse and process the test-dump + // hprof. To avoid repeating this overhead for each test case, we cache the + // loaded instance of TestDump and reuse it when possible. In theory the + // test cases should not be able to modify the cached snapshot in a way that + // is visible to other test cases. + private static TestDump mCachedTestDump = null; + + private AhatSnapshot mSnapshot = null; + + /** + * Load the test-dump.hprof file. + * The location of the file is read from the system property + * "ahat.test.dump.hprof", which is expected to be set on the command line. + * 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. + */ + private TestDump() throws IOException { + String hprof = System.getProperty("ahat.test.dump.hprof"); + mSnapshot = AhatSnapshot.fromHprof(new File(hprof)); + } + + /** + * Get the AhatSnapshot for the test dump program. + */ + public AhatSnapshot getAhatSnapshot() { + return mSnapshot; + } + + /** + * Return the value of a field in the DumpedStuff instance in the + * snapshot for the test-dump program. + */ + public Object getDumpedThing(String name) { + ClassObj main = mSnapshot.findClass("Main"); + Instance stuff = null; + for (Map.Entry<Field, Object> fields : main.getStaticFieldValues().entrySet()) { + if ("stuff".equals(fields.getKey().getName())) { + stuff = (Instance) fields.getValue(); + } + } + return InstanceUtils.getField(stuff, name); + } + + /** + * Get the test dump. + * An IOException is thrown if there is an error reading the test dump hprof + * file. + * To improve performance, this returns a cached instance of the TestDump + * when possible. + */ + public static synchronized TestDump getTestDump() throws IOException { + if (mCachedTestDump == null) { + mCachedTestDump = new TestDump(); + } + return mCachedTestDump; + } +} diff --git a/tools/ahat/test/Tests.java b/tools/ahat/test/Tests.java index fb53d90801..bab712199c 100644 --- a/tools/ahat/test/Tests.java +++ b/tools/ahat/test/Tests.java @@ -22,6 +22,7 @@ public class Tests { public static void main(String[] args) { if (args.length == 0) { args = new String[]{ + "com.android.ahat.InstanceUtilsTest", "com.android.ahat.QueryTest", "com.android.ahat.SortTest" }; |