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 
makeArray(int i)26     private static boolean makeArray(int i) {
27         return i % 10 == 0;
28     }
29 
fillArray(Object global[], Object local[], int i)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 
allocInDifferentLoader()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 
createDumpAndConv()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 
main(String[] args)89     public static void main(String[] args) throws Exception {
90         testBasicDump();
91         testAllocationTrackingAndClassUnloading();
92         testGcAndDump();
93     }
94 
testBasicDump()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 
testAllocationTrackingAndClassUnloading()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 
testGcAndDump()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;
run()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 {
Dumper(Allocator allocator)164         Dumper(Allocator allocator) {
165             this.allocator = allocator;
166         }
167         Allocator allocator;
run()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 
sleep(long ms)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 
getHprofConf()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 
createDump()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      */
getDumpHprofDataMethod()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 
getDumpFile()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 
getConvFile()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