/* * Copyright (C) 2009 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 java.io.File; import java.lang.ref.WeakReference; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.lang.reflect.InvocationTargetException; public class Main { private static final int TEST_LENGTH = 100; private static boolean makeArray(int i) { return i % 10 == 0; } private static void fillArray(Object global[], Object local[], int i) { // Very stupid linking. local[0] = global; for (int j = 1; j < local.length; j++) { local[j] = global[j]; } } private static Object allocInDifferentLoader() throws Exception { final String DEX_FILE = System.getenv("DEX_LOCATION") + "/130-hprof-ex.jar"; Class<?> pathClassLoader = Class.forName("dalvik.system.PathClassLoader"); if (pathClassLoader == null) { throw new AssertionError("Couldn't find path class loader class"); } Constructor<?> constructor = pathClassLoader.getDeclaredConstructor(String.class, ClassLoader.class); ClassLoader loader = (ClassLoader)constructor.newInstance( DEX_FILE, ClassLoader.getSystemClassLoader()); Class<?> allocator = loader.loadClass("Allocator"); return allocator.getDeclaredMethod("allocObject", null).invoke(null); } private static void createDumpAndConv() throws RuntimeException { File dumpFile = null; File convFile = null; try { // Now dump the heap. dumpFile = createDump(); // Run hprof-conv on it. convFile = getConvFile(); File hprof_conv = getHprofConf(); try { ProcessBuilder pb = new ProcessBuilder( hprof_conv.getAbsoluteFile().toString(), dumpFile.getAbsoluteFile().toString(), convFile.getAbsoluteFile().toString()); pb.redirectErrorStream(true); Process process = pb.start(); int ret = process.waitFor(); if (ret != 0) { throw new RuntimeException("Exited abnormally with " + ret); } } catch (Exception exc) { throw new RuntimeException(exc); } } finally { // Delete the files. if (dumpFile != null) { dumpFile.delete(); } if (convFile != null) { convFile.delete(); } } } public static void main(String[] args) throws Exception { testBasicDump(); testAllocationTrackingAndClassUnloading(); testGcAndDump(); } private static void testBasicDump() throws Exception { // Create some data. Object data[] = new Object[TEST_LENGTH]; for (int i = 0; i < data.length; i++) { if (makeArray(i)) { data[i] = new Object[TEST_LENGTH]; } else { data[i] = String.valueOf(i); } } for (int i = 0; i < data.length; i++) { if (makeArray(i)) { Object data2[] = (Object[]) data[i]; fillArray(data, data2, i); } } System.out.println("Generated data."); createDumpAndConv(); } private static void testAllocationTrackingAndClassUnloading() throws Exception { Class<?> klass = Class.forName("org.apache.harmony.dalvik.ddmc.DdmVmInternal"); if (klass == null) { throw new AssertionError("Couldn't find path class loader class"); } Method enableMethod = klass.getDeclaredMethod("enableRecentAllocations", Boolean.TYPE); if (enableMethod == null) { throw new AssertionError("Couldn't find path class loader class"); } enableMethod.invoke(null, true); Object o = allocInDifferentLoader(); // Run GC to cause class unloading. Runtime.getRuntime().gc(); createDumpAndConv(); // TODO: Somehow check contents of hprof file. enableMethod.invoke(null, false); } private static void testGcAndDump() throws Exception { Allocator allocator = new Allocator(); Dumper dumper = new Dumper(allocator); allocator.start(); dumper.start(); try { allocator.join(); dumper.join(); } catch (InterruptedException e) { System.err.println("join interrupted"); } } private static class Allocator extends Thread { private static int ARRAY_SIZE = 1024; public volatile boolean running = true; public void run() { Object[] array = new Object[ARRAY_SIZE]; int i = 0; while (running) { array[i] = new byte[1024]; if (i % ARRAY_SIZE == 0) { Main.sleep(100L); } i = (i + 1) % ARRAY_SIZE; } } } private static class Dumper extends Thread { Dumper(Allocator allocator) { this.allocator = allocator; } Allocator allocator; public void run() { for (int i = 0; i < 5; ++i) { Main.sleep(1000L); createDumpAndConv(); } allocator.running = false; } } public static void sleep(long ms) { try { Thread.sleep(ms); } catch (InterruptedException e) { System.err.println("sleep interrupted"); } } private static File getHprofConf() { // Use the java.library.path. It points to the lib directory. File libDir = new File(System.getProperty("java.library.path").split(":")[0]); return new File(new File(libDir.getParentFile(), "bin"), "hprof-conv"); } private static File createDump() { java.lang.reflect.Method dumpHprofDataMethod = getDumpHprofDataMethod(); if (dumpHprofDataMethod != null) { File f = getDumpFile(); try { dumpHprofDataMethod.invoke(null, f.getAbsoluteFile().toString()); return f; } catch (Exception exc) { exc.printStackTrace(System.out); } } else { System.out.println("Could not find dump method!"); } return null; } /** * Finds VMDebug.dumpHprofData() through reflection. In the reference * implementation this will not be available. * * @return the reflection object, or null if the method can't be found */ private static Method getDumpHprofDataMethod() { ClassLoader myLoader = Main.class.getClassLoader(); Class<?> vmdClass; try { vmdClass = myLoader.loadClass("dalvik.system.VMDebug"); } catch (ClassNotFoundException cnfe) { return null; } Method meth; try { meth = vmdClass.getMethod("dumpHprofData", String.class); } catch (NoSuchMethodException nsme) { System.err.println("Found VMDebug but not dumpHprofData method"); return null; } return meth; } private static File getDumpFile() { try { return File.createTempFile("test-130-hprof", "dump"); } catch (Exception exc) { return null; } } private static File getConvFile() { try { return File.createTempFile("test-130-hprof", "conv"); } catch (Exception exc) { return null; } } }