1 /*
2  * Copyright (C) 2014 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.io.IOException;
19 import java.lang.reflect.Method;
20 import java.util.Arrays;
21 import java.util.ArrayList;
22 import java.util.Map;
23 
24 public class Main {
25     private static final String TEMP_FILE_NAME_PREFIX = "test";
26     private static final String TEMP_FILE_NAME_SUFFIX = ".trace";
27 
main(String[] args)28     public static void main(String[] args) throws Exception {
29         String name = System.getProperty("java.vm.name");
30         if (!"Dalvik".equals(name)) {
31             System.out.println("This test is not supported on " + name);
32             return;
33         }
34         testMethodTracing();
35         testCountInstances();
36         testGetInstances();
37         testRuntimeStat();
38         testRuntimeStats();
39     }
40 
createTempFile()41     private static File createTempFile() throws Exception {
42         try {
43             return  File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX);
44         } catch (IOException e) {
45             System.setProperty("java.io.tmpdir", "/data/local/tmp");
46             try {
47                 return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX);
48             } catch (IOException e2) {
49                 System.setProperty("java.io.tmpdir", "/sdcard");
50                 return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX);
51             }
52         }
53     }
54 
testMethodTracing()55     private static void testMethodTracing() throws Exception {
56         File tempFile = null;
57         try {
58             tempFile = createTempFile();
59             testMethodTracingToFile(tempFile);
60         } finally {
61             if (tempFile != null) {
62                 tempFile.delete();
63             }
64         }
65     }
66 
testMethodTracingToFile(File tempFile)67     private static void testMethodTracingToFile(File tempFile) throws Exception {
68         String tempFileName = tempFile.getPath();
69 
70         if (VMDebug.getMethodTracingMode() != 0) {
71             VMDebug.stopMethodTracing();
72         }
73 
74         System.out.println("Confirm enable/disable");
75         System.out.println("status=" + VMDebug.getMethodTracingMode());
76         VMDebug.startMethodTracing(tempFileName, 0, 0, false, 0);
77         System.out.println("status=" + VMDebug.getMethodTracingMode());
78         VMDebug.stopMethodTracing();
79         System.out.println("status=" + VMDebug.getMethodTracingMode());
80         if (tempFile.length() == 0) {
81             System.out.println("ERROR: tracing output file is empty");
82         }
83 
84         System.out.println("Confirm sampling");
85         VMDebug.startMethodTracing(tempFileName, 0, 0, true, 1000);
86         System.out.println("status=" + VMDebug.getMethodTracingMode());
87         VMDebug.stopMethodTracing();
88         System.out.println("status=" + VMDebug.getMethodTracingMode());
89         if (tempFile.length() == 0) {
90             System.out.println("ERROR: sample tracing output file is empty");
91         }
92 
93         System.out.println("Test starting when already started");
94         VMDebug.startMethodTracing(tempFileName, 0, 0, false, 0);
95         System.out.println("status=" + VMDebug.getMethodTracingMode());
96         VMDebug.startMethodTracing(tempFileName, 0, 0, false, 0);
97         System.out.println("status=" + VMDebug.getMethodTracingMode());
98 
99         System.out.println("Test stopping when already stopped");
100         VMDebug.stopMethodTracing();
101         System.out.println("status=" + VMDebug.getMethodTracingMode());
102         VMDebug.stopMethodTracing();
103         System.out.println("status=" + VMDebug.getMethodTracingMode());
104 
105         System.out.println("Test tracing with empty filename");
106         try {
107             VMDebug.startMethodTracing("", 0, 0, false, 0);
108             System.out.println("Should have thrown an exception");
109         } catch (Exception e) {
110             System.out.println("Got expected exception");
111         }
112 
113         System.out.println("Test tracing with bogus (< 1024 && != 0) filesize");
114         try {
115             VMDebug.startMethodTracing(tempFileName, 1000, 0, false, 0);
116             System.out.println("Should have thrown an exception");
117         } catch (Exception e) {
118             System.out.println("Got expected exception");
119         }
120 
121         System.out.println("Test sampling with bogus (<= 0) interval");
122         try {
123             VMDebug.startMethodTracing(tempFileName, 0, 0, true, 0);
124             System.out.println("Should have thrown an exception");
125         } catch (Exception e) {
126             System.out.println("Got expected exception");
127         }
128 
129         tempFile.delete();
130     }
131 
checkNumber(String s)132     private static void checkNumber(String s) throws Exception {
133         if (s == null) {
134             System.out.println("Got null string");
135             return;
136         }
137         long n = Long.parseLong(s);
138         if (n < 0) {
139             System.out.println("Got negative number " + n);
140         }
141     }
142 
checkHistogram(String s)143     private static void checkHistogram(String s) throws Exception {
144         if (s == null || s.length() == 0) {
145             System.out.println("Got null or empty string");
146             return;
147         }
148         String[] buckets = s.split(",");
149         long last_key = 0;
150         for (int i = 0; i < buckets.length; ++i) {
151             String bucket = buckets[i];
152             if (bucket.length() == 0) {
153                 System.out.println("Got empty bucket");
154                 continue;
155             }
156             String[] kv = bucket.split(":");
157             if (kv.length != 2 || kv[0].length() == 0 || kv[1].length() == 0) {
158                 System.out.println("Got bad bucket " + bucket);
159                 continue;
160             }
161             long key = Long.parseLong(kv[0]);
162             long value = Long.parseLong(kv[1]);
163             if (key < 0 || value < 0) {
164                 System.out.println("Got negative key or value " + bucket);
165                 continue;
166             }
167             if (key < last_key) {
168                 System.out.println("Got decreasing key " + bucket);
169                 continue;
170             }
171             last_key = key;
172         }
173     }
174 
testRuntimeStat()175     private static void testRuntimeStat() throws Exception {
176         // Invoke at least one GC and wait for 20 seconds or so so we get at
177         // least one bucket in the histograms.
178         for (int i = 0; i < 20; ++i) {
179           Runtime.getRuntime().gc();
180           Thread.sleep(1000L);
181         }
182         String gc_count = VMDebug.getRuntimeStat("art.gc.gc-count");
183         String gc_time = VMDebug.getRuntimeStat("art.gc.gc-time");
184         String bytes_allocated = VMDebug.getRuntimeStat("art.gc.bytes-allocated");
185         String bytes_freed = VMDebug.getRuntimeStat("art.gc.bytes-freed");
186         String blocking_gc_count = VMDebug.getRuntimeStat("art.gc.blocking-gc-count");
187         String blocking_gc_time = VMDebug.getRuntimeStat("art.gc.blocking-gc-time");
188         String gc_count_rate_histogram = VMDebug.getRuntimeStat("art.gc.gc-count-rate-histogram");
189         String blocking_gc_count_rate_histogram =
190             VMDebug.getRuntimeStat("art.gc.blocking-gc-count-rate-histogram");
191         checkNumber(gc_count);
192         checkNumber(gc_time);
193         checkNumber(bytes_allocated);
194         checkNumber(bytes_freed);
195         checkNumber(blocking_gc_count);
196         checkNumber(blocking_gc_time);
197         checkHistogram(gc_count_rate_histogram);
198         checkHistogram(blocking_gc_count_rate_histogram);
199     }
200 
testRuntimeStats()201     private static void testRuntimeStats() throws Exception {
202         // Invoke at least one GC and wait for 20 seconds or so so we get at
203         // least one bucket in the histograms.
204         for (int i = 0; i < 20; ++i) {
205           Runtime.getRuntime().gc();
206           Thread.sleep(1000L);
207         }
208         Map<String, String> map = VMDebug.getRuntimeStats();
209         String gc_count = map.get("art.gc.gc-count");
210         String gc_time = map.get("art.gc.gc-time");
211         String bytes_allocated = map.get("art.gc.bytes-allocated");
212         String bytes_freed = map.get("art.gc.bytes-freed");
213         String blocking_gc_count = map.get("art.gc.blocking-gc-count");
214         String blocking_gc_time = map.get("art.gc.blocking-gc-time");
215         String gc_count_rate_histogram = map.get("art.gc.gc-count-rate-histogram");
216         String blocking_gc_count_rate_histogram =
217             map.get("art.gc.blocking-gc-count-rate-histogram");
218         checkNumber(gc_count);
219         checkNumber(gc_time);
220         checkNumber(bytes_allocated);
221         checkNumber(bytes_freed);
222         checkNumber(blocking_gc_count);
223         checkNumber(blocking_gc_time);
224         checkHistogram(gc_count_rate_histogram);
225         checkHistogram(blocking_gc_count_rate_histogram);
226     }
227 
228     static class ClassA { }
229     static class ClassB { }
230     static class ClassC extends ClassA { }
231 
testCountInstances()232     private static void testCountInstances() throws Exception {
233         ArrayList<Object> l = new ArrayList<Object>();
234         l.add(new ClassA());
235         l.add(new ClassB());
236         l.add(new ClassA());
237         l.add(new ClassC());
238         Runtime.getRuntime().gc();
239         System.out.println("Instances of ClassA " +
240                 VMDebug.countInstancesofClass(ClassA.class, false));
241         System.out.println("Instances of ClassB " +
242                 VMDebug.countInstancesofClass(ClassB.class, false));
243         System.out.println("Instances of null " + VMDebug.countInstancesofClass(null, false));
244         System.out.println("Instances of ClassA assignable " +
245                 VMDebug.countInstancesofClass(ClassA.class, true));
246         Class<?>[] classes = new Class<?>[] {ClassA.class, ClassB.class, null};
247         long[] counts = VMDebug.countInstancesofClasses(classes, false);
248         System.out.println("Array counts " + Arrays.toString(counts));
249         counts = VMDebug.countInstancesofClasses(classes, true);
250         System.out.println("Array counts assignable " + Arrays.toString(counts));
251     }
252 
253     static class ClassD {
254         public int mask;
255 
ClassD(int mask)256         public ClassD(int mask) {
257             this.mask = mask;
258         }
259     }
260 
261     static class ClassE extends ClassD {
ClassE(int mask)262         public ClassE(int mask) {
263             super(mask);
264         }
265     }
266 
testGetInstances()267     private static void testGetInstances() throws Exception {
268         ArrayList<Object> l = new ArrayList<Object>();
269         l.add(new ClassD(0x01));
270         l.add(new ClassE(0x02));
271         l.add(new ClassD(0x04));
272         l.add(new ClassD(0x08));
273         l.add(new ClassE(0x10));
274         Runtime.getRuntime().gc();
275         Class<?>[] classes = new Class<?>[] {ClassD.class, ClassE.class, null};
276         Object[][] instances = VMDebug.getInstancesOfClasses(classes, false);
277 
278         int mask = 0;
279         for (Object instance : instances[0]) {
280             mask |= ((ClassD)instance).mask;
281         }
282         System.out.println("ClassD got " + instances[0].length + ", combined mask: " + mask);
283 
284         mask = 0;
285         for (Object instance : instances[1]) {
286             mask |= ((ClassD)instance).mask;
287         }
288         System.out.println("ClassE got " + instances[1].length + ", combined mask: " + mask);
289         System.out.println("null got " + instances[2].length);
290 
291         instances = VMDebug.getInstancesOfClasses(classes, true);
292         mask = 0;
293         for (Object instance : instances[0]) {
294             mask |= ((ClassD)instance).mask;
295         }
296         System.out.println("ClassD assignable got " + instances[0].length + ", combined mask: " + mask);
297 
298         mask = 0;
299         for (Object instance : instances[1]) {
300             mask |= ((ClassD)instance).mask;
301         }
302         System.out.println("ClassE assignable got " + instances[1].length + ", combined mask: " + mask);
303         System.out.println("null assignable got " + instances[2].length);
304     }
305 
306     private static class VMDebug {
307         private static final Method startMethodTracingMethod;
308         private static final Method stopMethodTracingMethod;
309         private static final Method getMethodTracingModeMethod;
310         private static final Method getRuntimeStatMethod;
311         private static final Method getRuntimeStatsMethod;
312         private static final Method countInstancesOfClassMethod;
313         private static final Method countInstancesOfClassesMethod;
314         private static final Method getInstancesOfClassesMethod;
315         static {
316             try {
317                 Class<?> c = Class.forName("dalvik.system.VMDebug");
318                 startMethodTracingMethod = c.getDeclaredMethod("startMethodTracing", String.class,
319                         Integer.TYPE, Integer.TYPE, Boolean.TYPE, Integer.TYPE);
320                 stopMethodTracingMethod = c.getDeclaredMethod("stopMethodTracing");
321                 getMethodTracingModeMethod = c.getDeclaredMethod("getMethodTracingMode");
322                 getRuntimeStatMethod = c.getDeclaredMethod("getRuntimeStat", String.class);
323                 getRuntimeStatsMethod = c.getDeclaredMethod("getRuntimeStats");
324                 countInstancesOfClassMethod = c.getDeclaredMethod("countInstancesOfClass",
325                         Class.class, Boolean.TYPE);
326                 countInstancesOfClassesMethod = c.getDeclaredMethod("countInstancesOfClasses",
327                         Class[].class, Boolean.TYPE);
328                 getInstancesOfClassesMethod = c.getDeclaredMethod("getInstancesOfClasses",
329                         Class[].class, Boolean.TYPE);
330             } catch (Exception e) {
331                 throw new RuntimeException(e);
332             }
333         }
334 
startMethodTracing(String filename, int bufferSize, int flags, boolean samplingEnabled, int intervalUs)335         public static void startMethodTracing(String filename, int bufferSize, int flags,
336                 boolean samplingEnabled, int intervalUs) throws Exception {
337             startMethodTracingMethod.invoke(null, filename, bufferSize, flags, samplingEnabled,
338                     intervalUs);
339         }
stopMethodTracing()340         public static void stopMethodTracing() throws Exception {
341             stopMethodTracingMethod.invoke(null);
342         }
getMethodTracingMode()343         public static int getMethodTracingMode() throws Exception {
344             return (int) getMethodTracingModeMethod.invoke(null);
345         }
getRuntimeStat(String statName)346         public static String getRuntimeStat(String statName) throws Exception {
347             return (String) getRuntimeStatMethod.invoke(null, statName);
348         }
getRuntimeStats()349         public static Map<String, String> getRuntimeStats() throws Exception {
350             return (Map<String, String>) getRuntimeStatsMethod.invoke(null);
351         }
countInstancesofClass(Class<?> c, boolean assignable)352         public static long countInstancesofClass(Class<?> c, boolean assignable) throws Exception {
353             return (long) countInstancesOfClassMethod.invoke(null, new Object[]{c, assignable});
354         }
countInstancesofClasses(Class<?>[] classes, boolean assignable)355         public static long[] countInstancesofClasses(Class<?>[] classes, boolean assignable)
356                 throws Exception {
357             return (long[]) countInstancesOfClassesMethod.invoke(
358                     null, new Object[]{classes, assignable});
359         }
getInstancesOfClasses(Class<?>[] classes, boolean assignable)360         public static Object[][] getInstancesOfClasses(Class<?>[] classes, boolean assignable) throws Exception {
361             return (Object[][]) getInstancesOfClassesMethod.invoke(
362                     null, new Object[]{classes, assignable});
363         }
364     }
365 }
366