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