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 testRuntimeStat(); 37 testRuntimeStats(); 38 testGetAllocCount(); 39 testGetVmFeatureList(); 40 testDebuggerDetails(); 41 } 42 createTempFile()43 private static File createTempFile() throws Exception { 44 try { 45 return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX); 46 } catch (IOException e) { 47 System.setProperty("java.io.tmpdir", "/data/local/tmp"); 48 try { 49 return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX); 50 } catch (IOException e2) { 51 System.setProperty("java.io.tmpdir", "/sdcard"); 52 return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX); 53 } 54 } 55 } 56 testMethodTracing()57 private static void testMethodTracing() throws Exception { 58 File tempFile = null; 59 try { 60 tempFile = createTempFile(); 61 testMethodTracingToFile(tempFile); 62 } finally { 63 if (tempFile != null) { 64 tempFile.delete(); 65 } 66 } 67 } 68 testMethodTracingToFile(File tempFile)69 private static void testMethodTracingToFile(File tempFile) throws Exception { 70 String tempFileName = tempFile.getPath(); 71 72 if (VMDebug.getMethodTracingMode() != 0) { 73 VMDebug.stopMethodTracing(); 74 } 75 76 System.out.println("Confirm enable/disable"); 77 System.out.println("status=" + VMDebug.getMethodTracingMode()); 78 VMDebug.startMethodTracing(tempFileName, 0, 0, false, 0); 79 System.out.println("status=" + VMDebug.getMethodTracingMode()); 80 VMDebug.stopMethodTracing(); 81 System.out.println("status=" + VMDebug.getMethodTracingMode()); 82 if (tempFile.length() == 0) { 83 System.out.println("ERROR: tracing output file is empty"); 84 } 85 86 System.out.println("Confirm sampling"); 87 VMDebug.startMethodTracing(tempFileName, 0, 0, true, 1000); 88 System.out.println("status=" + VMDebug.getMethodTracingMode()); 89 VMDebug.stopMethodTracing(); 90 System.out.println("status=" + VMDebug.getMethodTracingMode()); 91 if (tempFile.length() == 0) { 92 System.out.println("ERROR: sample tracing output file is empty"); 93 } 94 95 System.out.println("Test starting when already started"); 96 VMDebug.startMethodTracing(tempFileName, 0, 0, false, 0); 97 System.out.println("status=" + VMDebug.getMethodTracingMode()); 98 VMDebug.startMethodTracing(tempFileName, 0, 0, false, 0); 99 System.out.println("status=" + VMDebug.getMethodTracingMode()); 100 101 System.out.println("Test stopping when already stopped"); 102 VMDebug.stopMethodTracing(); 103 System.out.println("status=" + VMDebug.getMethodTracingMode()); 104 VMDebug.stopMethodTracing(); 105 System.out.println("status=" + VMDebug.getMethodTracingMode()); 106 107 System.out.println("Test tracing with empty filename"); 108 try { 109 VMDebug.startMethodTracing("", 0, 0, false, 0); 110 System.out.println("Should have thrown an exception"); 111 } catch (Exception e) { 112 System.out.println("Got expected exception"); 113 } 114 115 System.out.println("Test tracing with bogus (< 1024 && != 0) filesize"); 116 try { 117 VMDebug.startMethodTracing(tempFileName, 1000, 0, false, 0); 118 System.out.println("Should have thrown an exception"); 119 } catch (Exception e) { 120 System.out.println("Got expected exception"); 121 } 122 123 try { 124 VMDebug.startMethodTracingDdms(1000, 0, false, 0); 125 System.out.println("Should have thrown an exception"); 126 } catch (Exception e) { 127 System.out.println("Got expected exception"); 128 } 129 130 System.out.println("Test sampling with bogus (<= 0) interval"); 131 try { 132 VMDebug.startMethodTracing(tempFileName, 0, 0, true, 0); 133 System.out.println("Should have thrown an exception"); 134 } catch (Exception e) { 135 System.out.println("Got expected exception"); 136 } 137 try { 138 VMDebug.startMethodTracingDdms(0, 0, true, 0); 139 System.out.println("Should have thrown an exception"); 140 } catch (Exception e) { 141 System.out.println("Got expected exception"); 142 } 143 144 tempFile.delete(); 145 } 146 checkNumber(String s)147 private static void checkNumber(String s) throws Exception { 148 if (s == null) { 149 System.out.println("Got null string"); 150 return; 151 } 152 long n = Long.parseLong(s); 153 if (n < 0) { 154 System.out.println("Got negative number " + n); 155 } 156 } 157 checkBiggerThanZero(int i)158 private static void checkBiggerThanZero(int i) throws Exception { 159 if (i <= 0) { 160 System.out.println("Got zero or smaller " + i); 161 } 162 } 163 checkZero(int i)164 private static void checkZero(int i) throws Exception { 165 if (i != 0) { 166 System.out.println("Got non-zero result after reset " + i); 167 } 168 } 169 checkHistogram(String s)170 private static void checkHistogram(String s) throws Exception { 171 if (s == null || s.length() == 0) { 172 System.out.println("Got null or empty string"); 173 return; 174 } 175 String[] buckets = s.split(","); 176 long last_key = 0; 177 for (int i = 0; i < buckets.length; ++i) { 178 String bucket = buckets[i]; 179 if (bucket.length() == 0) { 180 System.out.println("Got empty bucket"); 181 continue; 182 } 183 String[] kv = bucket.split(":"); 184 if (kv.length != 2 || kv[0].length() == 0 || kv[1].length() == 0) { 185 System.out.println("Got bad bucket " + bucket); 186 continue; 187 } 188 long key = Long.parseLong(kv[0]); 189 long value = Long.parseLong(kv[1]); 190 if (key < 0 || value < 0) { 191 System.out.println("Got negative key or value " + bucket); 192 continue; 193 } 194 if (key < last_key) { 195 System.out.println("Got decreasing key " + bucket); 196 continue; 197 } 198 last_key = key; 199 } 200 } 201 testRuntimeStat()202 private static void testRuntimeStat() throws Exception { 203 // Invoke at least one GC and wait for 20 seconds or so so we get at 204 // least one bucket in the histograms. 205 for (int i = 0; i < 20; ++i) { 206 Runtime.getRuntime().gc(); 207 Thread.sleep(1000L); 208 } 209 String gc_count = VMDebug.getRuntimeStat("art.gc.gc-count"); 210 String gc_time = VMDebug.getRuntimeStat("art.gc.gc-time"); 211 String bytes_allocated = VMDebug.getRuntimeStat("art.gc.bytes-allocated"); 212 String bytes_freed = VMDebug.getRuntimeStat("art.gc.bytes-freed"); 213 String blocking_gc_count = VMDebug.getRuntimeStat("art.gc.blocking-gc-count"); 214 String blocking_gc_time = VMDebug.getRuntimeStat("art.gc.blocking-gc-time"); 215 String gc_count_rate_histogram = VMDebug.getRuntimeStat("art.gc.gc-count-rate-histogram"); 216 String blocking_gc_count_rate_histogram = 217 VMDebug.getRuntimeStat("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 testRuntimeStats()228 private static void testRuntimeStats() throws Exception { 229 // Invoke at least one GC and wait for 20 seconds or so so we get at 230 // least one bucket in the histograms. 231 for (int i = 0; i < 20; ++i) { 232 Runtime.getRuntime().gc(); 233 Thread.sleep(1000L); 234 } 235 Map<String, String> map = VMDebug.getRuntimeStats(); 236 String gc_count = map.get("art.gc.gc-count"); 237 String gc_time = map.get("art.gc.gc-time"); 238 String bytes_allocated = map.get("art.gc.bytes-allocated"); 239 String bytes_freed = map.get("art.gc.bytes-freed"); 240 String blocking_gc_count = map.get("art.gc.blocking-gc-count"); 241 String blocking_gc_time = map.get("art.gc.blocking-gc-time"); 242 String gc_count_rate_histogram = map.get("art.gc.gc-count-rate-histogram"); 243 String blocking_gc_count_rate_histogram = 244 map.get("art.gc.blocking-gc-count-rate-histogram"); 245 checkNumber(gc_count); 246 checkNumber(gc_time); 247 checkNumber(bytes_allocated); 248 checkNumber(bytes_freed); 249 checkNumber(blocking_gc_count); 250 checkNumber(blocking_gc_time); 251 checkHistogram(gc_count_rate_histogram); 252 checkHistogram(blocking_gc_count_rate_histogram); 253 } 254 255 /* constants for getAllocCount */ 256 private static final int KIND_ALLOCATED_OBJECTS = 1<<0; 257 private static final int KIND_ALLOCATED_BYTES = 1<<1; 258 private static final int KIND_FREED_OBJECTS = 1<<2; 259 private static final int KIND_FREED_BYTES = 1<<3; 260 private static final int RESET_ALL = 0xffffffff; 261 testGetAllocCount()262 private static void testGetAllocCount() throws Exception { 263 VMDebug.startAllocCounting(); 264 265 ClassA a1 = new ClassA(); 266 Object obj1 = new Object(); 267 Runtime.getRuntime().gc(); 268 269 int alloc_objects = VMDebug.getAllocCount(KIND_ALLOCATED_OBJECTS); 270 int alloc_bytes = VMDebug.getAllocCount(KIND_ALLOCATED_BYTES); 271 int freed_objects = VMDebug.getAllocCount(KIND_FREED_OBJECTS); 272 int freed_bytes = VMDebug.getAllocCount(KIND_FREED_BYTES); 273 checkBiggerThanZero(alloc_objects); 274 checkBiggerThanZero(alloc_bytes); 275 checkBiggerThanZero(freed_objects); 276 checkBiggerThanZero(freed_bytes); 277 278 VMDebug.stopAllocCounting(); 279 VMDebug.resetAllocCount(RESET_ALL); 280 checkZero(VMDebug.getAllocCount(KIND_ALLOCATED_OBJECTS)); 281 checkZero(VMDebug.getAllocCount(KIND_ALLOCATED_BYTES)); 282 checkZero(VMDebug.getAllocCount(KIND_FREED_OBJECTS)); 283 checkZero(VMDebug.getAllocCount(KIND_FREED_BYTES)); 284 285 // Even if we create new classes the count should remain 0. 286 ClassA a2 = new ClassA(); 287 Object obj2 = new Object(); 288 289 checkZero(VMDebug.getAllocCount(KIND_ALLOCATED_OBJECTS)); 290 } 291 testGetVmFeatureList()292 private static void testGetVmFeatureList() throws Exception { 293 String[] feature_list = VMDebug.getVmFeatureList(); 294 if (feature_list.length == 0) { 295 System.out.println("Got empty feature list"); 296 } 297 } 298 testDebuggerDetails()299 private static void testDebuggerDetails() throws Exception { 300 boolean debugger_connected = VMDebug.isDebuggerConnected(); 301 boolean debugging_enabled = VMDebug.isDebuggingEnabled(); 302 long last_activity = VMDebug.lastDebuggerActivity(); 303 if (debugger_connected && last_activity < 0) { 304 System.out.println("Last debugging activity expected but not found"); 305 } 306 if (!debugger_connected && last_activity != -1) { 307 System.out.println("Found unexpected last activity"); 308 } 309 if (VMDebug.threadCpuTimeNanos() <= 0) { 310 System.out.println("Could not get CPU thread time"); 311 } 312 VMDebug.dumpHprofDataDdms(); 313 VMDebug.dumpReferenceTables(); 314 } 315 316 static class ClassA { } 317 static class ClassB { } 318 static class ClassC extends ClassA { } 319 testCountInstances()320 private static void testCountInstances() throws Exception { 321 ArrayList<Object> l = new ArrayList<Object>(); 322 l.add(new ClassA()); 323 l.add(new ClassB()); 324 l.add(new ClassA()); 325 l.add(new ClassC()); 326 Runtime.getRuntime().gc(); 327 System.out.println("Instances of ClassA " + 328 VMDebug.countInstancesofClass(ClassA.class, false)); 329 System.out.println("Instances of ClassB " + 330 VMDebug.countInstancesofClass(ClassB.class, false)); 331 System.out.println("Instances of null " + VMDebug.countInstancesofClass(null, false)); 332 System.out.println("Instances of ClassA assignable " + 333 VMDebug.countInstancesofClass(ClassA.class, true)); 334 Class<?>[] classes = new Class<?>[] {ClassA.class, ClassB.class, null}; 335 long[] counts = VMDebug.countInstancesofClasses(classes, false); 336 System.out.println("Array counts " + Arrays.toString(counts)); 337 counts = VMDebug.countInstancesofClasses(classes, true); 338 System.out.println("Array counts assignable " + Arrays.toString(counts)); 339 int class_count = VMDebug.getLoadedClassCount(); 340 checkBiggerThanZero(class_count); 341 } 342 343 static class ClassD { 344 public int mask; 345 ClassD(int mask)346 public ClassD(int mask) { 347 this.mask = mask; 348 } 349 } 350 351 static class ClassE extends ClassD { ClassE(int mask)352 public ClassE(int mask) { 353 super(mask); 354 } 355 } 356 357 private static class VMDebug { 358 private static final Method startMethodTracingMethod; 359 private static final Method startMethodTracingDdmsMethod; 360 private static final Method stopMethodTracingMethod; 361 private static final Method getMethodTracingModeMethod; 362 private static final Method getRuntimeStatMethod; 363 private static final Method getRuntimeStatsMethod; 364 private static final Method countInstancesOfClassMethod; 365 private static final Method countInstancesOfClassesMethod; 366 private static final Method getAllocCountMethod; 367 private static final Method startAllocCountingMethod; 368 private static final Method stopAllocCountingMethod; 369 private static final Method setAllocTrackerStackDepthMethod; 370 private static final Method resetAllocCountMethod; 371 private static final Method getLoadedClassCountMethod; 372 private static final Method getVmFeatureListMethod; 373 private static final Method isDebuggerConnectedMethod; 374 private static final Method isDebuggingEnabledMethod; 375 private static final Method lastDebuggerActivityMethod; 376 private static final Method threadCpuTimeNanosMethod; 377 private static final Method dumpHprofDataDdmsMethod; 378 private static final Method dumpReferenceTablesMethod; 379 static { 380 try { 381 Class<?> c = Class.forName("dalvik.system.VMDebug"); 382 startMethodTracingMethod = c.getDeclaredMethod("startMethodTracing", String.class, 383 Integer.TYPE, Integer.TYPE, Boolean.TYPE, Integer.TYPE); 384 startMethodTracingDdmsMethod = c.getDeclaredMethod("startMethodTracingDdms", 385 Integer.TYPE, Integer.TYPE, Boolean.TYPE, Integer.TYPE); 386 stopMethodTracingMethod = c.getDeclaredMethod("stopMethodTracing"); 387 getMethodTracingModeMethod = c.getDeclaredMethod("getMethodTracingMode"); 388 getRuntimeStatMethod = c.getDeclaredMethod("getRuntimeStat", String.class); 389 getRuntimeStatsMethod = c.getDeclaredMethod("getRuntimeStats"); 390 countInstancesOfClassMethod = c.getDeclaredMethod("countInstancesOfClass", 391 Class.class, Boolean.TYPE); 392 countInstancesOfClassesMethod = c.getDeclaredMethod("countInstancesOfClasses", 393 Class[].class, Boolean.TYPE); 394 getAllocCountMethod = c.getDeclaredMethod("getAllocCount", 395 Integer.TYPE); 396 startAllocCountingMethod = c.getDeclaredMethod("startAllocCounting"); 397 stopAllocCountingMethod = c.getDeclaredMethod("stopAllocCounting"); 398 setAllocTrackerStackDepthMethod = c.getDeclaredMethod("setAllocTrackerStackDepth", 399 Integer.TYPE); 400 resetAllocCountMethod = c.getDeclaredMethod("resetAllocCount", 401 Integer.TYPE); 402 getLoadedClassCountMethod = c.getDeclaredMethod("getLoadedClassCount"); 403 getVmFeatureListMethod = c.getDeclaredMethod("getVmFeatureList"); 404 isDebuggerConnectedMethod = c.getDeclaredMethod("isDebuggerConnected"); 405 isDebuggingEnabledMethod = c.getDeclaredMethod("isDebuggingEnabled"); 406 lastDebuggerActivityMethod = c.getDeclaredMethod("lastDebuggerActivity"); 407 threadCpuTimeNanosMethod = c.getDeclaredMethod("threadCpuTimeNanos"); 408 dumpHprofDataDdmsMethod = c.getDeclaredMethod("dumpHprofDataDdms"); 409 dumpReferenceTablesMethod = c.getDeclaredMethod("dumpReferenceTables"); 410 } catch (Exception e) { 411 throw new RuntimeException(e); 412 } 413 } 414 startMethodTracing(String filename, int bufferSize, int flags, boolean samplingEnabled, int intervalUs)415 public static void startMethodTracing(String filename, int bufferSize, int flags, 416 boolean samplingEnabled, int intervalUs) throws Exception { 417 startMethodTracingMethod.invoke(null, filename, bufferSize, flags, samplingEnabled, 418 intervalUs); 419 } startMethodTracingDdms(int bufferSize, int flags, boolean samplingEnabled, int intervalUs)420 public static void startMethodTracingDdms(int bufferSize, int flags, 421 boolean samplingEnabled, int intervalUs) throws Exception { 422 startMethodTracingDdmsMethod.invoke(null, bufferSize, flags, samplingEnabled, 423 intervalUs); 424 } stopMethodTracing()425 public static void stopMethodTracing() throws Exception { 426 stopMethodTracingMethod.invoke(null); 427 } getMethodTracingMode()428 public static int getMethodTracingMode() throws Exception { 429 return (int) getMethodTracingModeMethod.invoke(null); 430 } getRuntimeStat(String statName)431 public static String getRuntimeStat(String statName) throws Exception { 432 return (String) getRuntimeStatMethod.invoke(null, statName); 433 } getRuntimeStats()434 public static Map<String, String> getRuntimeStats() throws Exception { 435 return (Map<String, String>) getRuntimeStatsMethod.invoke(null); 436 } countInstancesofClass(Class<?> c, boolean assignable)437 public static long countInstancesofClass(Class<?> c, boolean assignable) throws Exception { 438 return (long) countInstancesOfClassMethod.invoke(null, new Object[]{c, assignable}); 439 } countInstancesofClasses(Class<?>[] classes, boolean assignable)440 public static long[] countInstancesofClasses(Class<?>[] classes, boolean assignable) 441 throws Exception { 442 return (long[]) countInstancesOfClassesMethod.invoke( 443 null, new Object[]{classes, assignable}); 444 } getAllocCount(Integer kind)445 public static int getAllocCount(Integer kind) throws Exception { 446 return (int) getAllocCountMethod.invoke(null, kind); 447 } startAllocCounting()448 public static void startAllocCounting() throws Exception { 449 startAllocCountingMethod.invoke(null); 450 } stopAllocCounting()451 public static void stopAllocCounting() throws Exception { 452 stopAllocCountingMethod.invoke(null); 453 } setAllocTrackerStackDepth(Integer stackDepth)454 public static void setAllocTrackerStackDepth(Integer stackDepth) throws Exception { 455 setAllocTrackerStackDepthMethod.invoke(null, stackDepth); 456 } resetAllocCount(Integer kind)457 public static void resetAllocCount(Integer kind) throws Exception { 458 resetAllocCountMethod.invoke(null, kind); 459 } getLoadedClassCount()460 public static int getLoadedClassCount() throws Exception { 461 return (int) getLoadedClassCountMethod.invoke(null); 462 } getVmFeatureList()463 public static String[] getVmFeatureList() throws Exception { 464 return (String[]) getVmFeatureListMethod.invoke(null); 465 } isDebuggerConnected()466 public static boolean isDebuggerConnected() throws Exception { 467 return (boolean) isDebuggerConnectedMethod.invoke(null); 468 } isDebuggingEnabled()469 public static boolean isDebuggingEnabled() throws Exception { 470 return (boolean) isDebuggingEnabledMethod.invoke(null); 471 } lastDebuggerActivity()472 public static long lastDebuggerActivity() throws Exception { 473 return (long) lastDebuggerActivityMethod.invoke(null); 474 } threadCpuTimeNanos()475 public static long threadCpuTimeNanos() throws Exception { 476 return (long) threadCpuTimeNanosMethod.invoke(null); 477 } dumpHprofDataDdms()478 public static void dumpHprofDataDdms() throws Exception { 479 dumpHprofDataDdmsMethod.invoke(null); 480 } dumpReferenceTables()481 public static void dumpReferenceTables() throws Exception { 482 dumpReferenceTablesMethod.invoke(null); 483 } 484 } 485 } 486