1 /* 2 * Copyright (C) 2009 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 import java.io.File; 18 import java.lang.ref.WeakReference; 19 import java.lang.reflect.Constructor; 20 import java.lang.reflect.Method; 21 import java.lang.reflect.InvocationTargetException; 22 23 public class Main { 24 private static final int TEST_LENGTH = 100; 25 26 private static boolean makeArray(int i) { 27 return i % 10 == 0; 28 } 29 30 private static void fillArray(Object global[], Object local[], int i) { 31 // Very stupid linking. 32 local[0] = global; 33 for (int j = 1; j < local.length; j++) { 34 local[j] = global[j]; 35 } 36 } 37 38 private static Object allocInDifferentLoader() throws Exception { 39 final String DEX_FILE = System.getenv("DEX_LOCATION") + "/130-hprof-ex.jar"; 40 Class<?> pathClassLoader = Class.forName("dalvik.system.PathClassLoader"); 41 if (pathClassLoader == null) { 42 throw new AssertionError("Couldn't find path class loader class"); 43 } 44 Constructor<?> constructor = 45 pathClassLoader.getDeclaredConstructor(String.class, ClassLoader.class); 46 ClassLoader loader = (ClassLoader)constructor.newInstance( 47 DEX_FILE, ClassLoader.getSystemClassLoader()); 48 Class<?> allocator = loader.loadClass("Allocator"); 49 return allocator.getDeclaredMethod("allocObject", null).invoke(null); 50 } 51 52 private static void createDumpAndConv() throws RuntimeException { 53 File dumpFile = null; 54 File convFile = null; 55 56 try { 57 // Now dump the heap. 58 dumpFile = createDump(); 59 60 // Run hprof-conv on it. 61 convFile = getConvFile(); 62 63 File hprof_conv = getHprofConf(); 64 try { 65 ProcessBuilder pb = new ProcessBuilder( 66 hprof_conv.getAbsoluteFile().toString(), 67 dumpFile.getAbsoluteFile().toString(), 68 convFile.getAbsoluteFile().toString()); 69 pb.redirectErrorStream(true); 70 Process process = pb.start(); 71 int ret = process.waitFor(); 72 if (ret != 0) { 73 throw new RuntimeException("Exited abnormally with " + ret); 74 } 75 } catch (Exception exc) { 76 throw new RuntimeException(exc); 77 } 78 } finally { 79 // Delete the files. 80 if (dumpFile != null) { 81 dumpFile.delete(); 82 } 83 if (convFile != null) { 84 convFile.delete(); 85 } 86 } 87 } 88 89 public static void main(String[] args) throws Exception { 90 testBasicDump(); 91 testAllocationTrackingAndClassUnloading(); 92 testGcAndDump(); 93 } 94 95 private static void testBasicDump() throws Exception { 96 // Create some data. 97 Object data[] = new Object[TEST_LENGTH]; 98 for (int i = 0; i < data.length; i++) { 99 if (makeArray(i)) { 100 data[i] = new Object[TEST_LENGTH]; 101 } else { 102 data[i] = String.valueOf(i); 103 } 104 } 105 for (int i = 0; i < data.length; i++) { 106 if (makeArray(i)) { 107 Object data2[] = (Object[]) data[i]; 108 fillArray(data, data2, i); 109 } 110 } 111 System.out.println("Generated data."); 112 createDumpAndConv(); 113 } 114 115 private static void testAllocationTrackingAndClassUnloading() throws Exception { 116 Class<?> klass = Class.forName("org.apache.harmony.dalvik.ddmc.DdmVmInternal"); 117 if (klass == null) { 118 throw new AssertionError("Couldn't find path class loader class"); 119 } 120 Method enableMethod = klass.getDeclaredMethod("enableRecentAllocations", 121 Boolean.TYPE); 122 if (enableMethod == null) { 123 throw new AssertionError("Couldn't find path class loader class"); 124 } 125 enableMethod.invoke(null, true); 126 Object o = allocInDifferentLoader(); 127 // Run GC to cause class unloading. 128 Runtime.getRuntime().gc(); 129 createDumpAndConv(); 130 // TODO: Somehow check contents of hprof file. 131 enableMethod.invoke(null, false); 132 } 133 134 private static void testGcAndDump() throws Exception { 135 Allocator allocator = new Allocator(); 136 Dumper dumper = new Dumper(allocator); 137 allocator.start(); 138 dumper.start(); 139 try { 140 allocator.join(); 141 dumper.join(); 142 } catch (InterruptedException e) { 143 System.out.println("join interrupted"); 144 } 145 } 146 147 private static class Allocator extends Thread { 148 private static int ARRAY_SIZE = 1024; 149 public volatile boolean running = true; 150 public void run() { 151 Object[] array = new Object[ARRAY_SIZE]; 152 int i = 0; 153 while (running) { 154 array[i] = new byte[1024]; 155 if (i % ARRAY_SIZE == 0) { 156 Main.sleep(100L); 157 } 158 i = (i + 1) % ARRAY_SIZE; 159 } 160 } 161 } 162 163 private static class Dumper extends Thread { 164 Dumper(Allocator allocator) { 165 this.allocator = allocator; 166 } 167 Allocator allocator; 168 public void run() { 169 for (int i = 0; i < 5; ++i) { 170 Main.sleep(1000L); 171 createDumpAndConv(); 172 } 173 allocator.running = false; 174 } 175 } 176 177 public static void sleep(long ms) { 178 try { 179 Thread.sleep(ms); 180 } catch (InterruptedException e) { 181 System.out.println("sleep interrupted"); 182 } 183 } 184 185 private static File getHprofConf() { 186 // Use the java.library.path. It points to the lib directory. 187 File libDir = new File(System.getProperty("java.library.path").split(":")[0]); 188 return new File(new File(libDir.getParentFile(), "bin"), "hprof-conv"); 189 } 190 191 private static File createDump() { 192 java.lang.reflect.Method dumpHprofDataMethod = getDumpHprofDataMethod(); 193 if (dumpHprofDataMethod != null) { 194 File f = getDumpFile(); 195 try { 196 dumpHprofDataMethod.invoke(null, f.getAbsoluteFile().toString()); 197 return f; 198 } catch (Exception exc) { 199 exc.printStackTrace(System.out); 200 } 201 } else { 202 System.out.println("Could not find dump method!"); 203 } 204 return null; 205 } 206 207 /** 208 * Finds VMDebug.dumpHprofData() through reflection. In the reference 209 * implementation this will not be available. 210 * 211 * @return the reflection object, or null if the method can't be found 212 */ 213 private static Method getDumpHprofDataMethod() { 214 ClassLoader myLoader = Main.class.getClassLoader(); 215 Class<?> vmdClass; 216 try { 217 vmdClass = myLoader.loadClass("dalvik.system.VMDebug"); 218 } catch (ClassNotFoundException cnfe) { 219 return null; 220 } 221 222 Method meth; 223 try { 224 meth = vmdClass.getMethod("dumpHprofData", String.class); 225 } catch (NoSuchMethodException nsme) { 226 System.out.println("Found VMDebug but not dumpHprofData method"); 227 return null; 228 } 229 230 return meth; 231 } 232 233 private static File getDumpFile() { 234 try { 235 return File.createTempFile("test-130-hprof", "dump"); 236 } catch (Exception exc) { 237 return null; 238 } 239 } 240 241 private static File getConvFile() { 242 try { 243 return File.createTempFile("test-130-hprof", "conv"); 244 } catch (Exception exc) { 245 return null; 246 } 247 } 248 } 249