Debug.java revision d52d8dee7768e04b936b8df86028823bdc5b1dc6
1/* 2 * Copyright (C) 2007 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 17package android.os; 18 19import com.android.internal.util.TypedProperties; 20 21import android.util.Config; 22import android.util.Log; 23 24import java.io.FileNotFoundException; 25import java.io.FileOutputStream; 26import java.io.FileReader; 27import java.io.IOException; 28import java.io.OutputStreamWriter; 29import java.io.PrintWriter; 30import java.io.Reader; 31import java.lang.reflect.Field; 32import java.lang.reflect.Modifier; 33 34import org.apache.harmony.dalvik.ddmc.Chunk; 35import org.apache.harmony.dalvik.ddmc.ChunkHandler; 36import org.apache.harmony.dalvik.ddmc.DdmServer; 37 38import dalvik.bytecode.Opcodes; 39import dalvik.system.VMDebug; 40 41 42/** 43 * Provides various debugging functions for Android applications, including 44 * tracing and allocation counts. 45 * <p><strong>Logging Trace Files</strong></p> 46 * <p>Debug can create log files that give details about an application, such as 47 * a call stack and start/stop times for any running methods. See <a 48href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Log Viewer</a> for 49 * information about reading trace files. To start logging trace files, call one 50 * of the startMethodTracing() methods. To stop tracing, call 51 * {@link #stopMethodTracing()}. 52 */ 53public final class Debug 54{ 55 /** 56 * Flags for startMethodTracing(). These can be ORed together. 57 * 58 * TRACE_COUNT_ALLOCS adds the results from startAllocCounting to the 59 * trace key file. 60 */ 61 public static final int TRACE_COUNT_ALLOCS = VMDebug.TRACE_COUNT_ALLOCS; 62 63 /** 64 * Flags for printLoadedClasses(). Default behavior is to only show 65 * the class name. 66 */ 67 public static final int SHOW_FULL_DETAIL = 1; 68 public static final int SHOW_CLASSLOADER = (1 << 1); 69 public static final int SHOW_INITIALIZED = (1 << 2); 70 71 // set/cleared by waitForDebugger() 72 private static volatile boolean mWaiting = false; 73 74 private Debug() {} 75 76 /* 77 * How long to wait for the debugger to finish sending requests. I've 78 * seen this hit 800msec on the device while waiting for a response 79 * to travel over USB and get processed, so we take that and add 80 * half a second. 81 */ 82 private static final int MIN_DEBUGGER_IDLE = 1300; // msec 83 84 /* how long to sleep when polling for activity */ 85 private static final int SPIN_DELAY = 200; // msec 86 87 /** 88 * Default trace file path and file 89 */ 90 private static final String DEFAULT_TRACE_PATH_PREFIX = "/sdcard/"; 91 private static final String DEFAULT_TRACE_BODY = "dmtrace"; 92 private static final String DEFAULT_TRACE_EXTENSION = ".trace"; 93 private static final String DEFAULT_TRACE_FILE_PATH = 94 DEFAULT_TRACE_PATH_PREFIX + DEFAULT_TRACE_BODY 95 + DEFAULT_TRACE_EXTENSION; 96 97 98 /** 99 * This class is used to retrieved various statistics about the memory mappings for this 100 * process. The returns info broken down by dalvik, native, and other. All results are in kB. 101 */ 102 public static class MemoryInfo { 103 /** The proportional set size for dalvik. */ 104 public int dalvikPss; 105 /** The private dirty pages used by dalvik. */ 106 public int dalvikPrivateDirty; 107 /** The shared dirty pages used by dalvik. */ 108 public int dalvikSharedDirty; 109 110 /** The proportional set size for the native heap. */ 111 public int nativePss; 112 /** The private dirty pages used by the native heap. */ 113 public int nativePrivateDirty; 114 /** The shared dirty pages used by the native heap. */ 115 public int nativeSharedDirty; 116 117 /** The proportional set size for everything else. */ 118 public int otherPss; 119 /** The private dirty pages used by everything else. */ 120 public int otherPrivateDirty; 121 /** The shared dirty pages used by everything else. */ 122 public int otherSharedDirty; 123 } 124 125 126 /** 127 * Wait until a debugger attaches. As soon as the debugger attaches, 128 * this returns, so you will need to place a breakpoint after the 129 * waitForDebugger() call if you want to start tracing immediately. 130 */ 131 public static void waitForDebugger() { 132 if (!VMDebug.isDebuggingEnabled()) { 133 //System.out.println("debugging not enabled, not waiting"); 134 return; 135 } 136 if (isDebuggerConnected()) 137 return; 138 139 // if DDMS is listening, inform them of our plight 140 System.out.println("Sending WAIT chunk"); 141 byte[] data = new byte[] { 0 }; // 0 == "waiting for debugger" 142 Chunk waitChunk = new Chunk(ChunkHandler.type("WAIT"), data, 0, 1); 143 DdmServer.sendChunk(waitChunk); 144 145 mWaiting = true; 146 while (!isDebuggerConnected()) { 147 try { Thread.sleep(SPIN_DELAY); } 148 catch (InterruptedException ie) {} 149 } 150 mWaiting = false; 151 152 System.out.println("Debugger has connected"); 153 154 /* 155 * There is no "ready to go" signal from the debugger, and we're 156 * not allowed to suspend ourselves -- the debugger expects us to 157 * be running happily, and gets confused if we aren't. We need to 158 * allow the debugger a chance to set breakpoints before we start 159 * running again. 160 * 161 * Sit and spin until the debugger has been idle for a short while. 162 */ 163 while (true) { 164 long delta = VMDebug.lastDebuggerActivity(); 165 if (delta < 0) { 166 System.out.println("debugger detached?"); 167 break; 168 } 169 170 if (delta < MIN_DEBUGGER_IDLE) { 171 System.out.println("waiting for debugger to settle..."); 172 try { Thread.sleep(SPIN_DELAY); } 173 catch (InterruptedException ie) {} 174 } else { 175 System.out.println("debugger has settled (" + delta + ")"); 176 break; 177 } 178 } 179 } 180 181 /** 182 * Returns "true" if one or more threads is waiting for a debugger 183 * to attach. 184 */ 185 public static boolean waitingForDebugger() { 186 return mWaiting; 187 } 188 189 /** 190 * Determine if a debugger is currently attached. 191 */ 192 public static boolean isDebuggerConnected() { 193 return VMDebug.isDebuggerConnected(); 194 } 195 196 /** 197 * Change the JDWP port. 198 * 199 * @deprecated no longer needed or useful 200 */ 201 @Deprecated 202 public static void changeDebugPort(int port) {} 203 204 /** 205 * This is the pathname to the sysfs file that enables and disables 206 * tracing on the qemu emulator. 207 */ 208 private static final String SYSFS_QEMU_TRACE_STATE = "/sys/qemu_trace/state"; 209 210 /** 211 * Enable qemu tracing. For this to work requires running everything inside 212 * the qemu emulator; otherwise, this method will have no effect. The trace 213 * file is specified on the command line when the emulator is started. For 214 * example, the following command line <br /> 215 * <code>emulator -trace foo</code><br /> 216 * will start running the emulator and create a trace file named "foo". This 217 * method simply enables writing the trace records to the trace file. 218 * 219 * <p> 220 * The main differences between this and {@link #startMethodTracing()} are 221 * that tracing in the qemu emulator traces every cpu instruction of every 222 * process, including kernel code, so we have more complete information, 223 * including all context switches. We can also get more detailed information 224 * such as cache misses. The sequence of calls is determined by 225 * post-processing the instruction trace. The qemu tracing is also done 226 * without modifying the application or perturbing the timing of calls 227 * because no instrumentation is added to the application being traced. 228 * </p> 229 * 230 * <p> 231 * One limitation of using this method compared to using 232 * {@link #startMethodTracing()} on the real device is that the emulator 233 * does not model all of the real hardware effects such as memory and 234 * bus contention. The emulator also has a simple cache model and cannot 235 * capture all the complexities of a real cache. 236 * </p> 237 */ 238 public static void startNativeTracing() { 239 // Open the sysfs file for writing and write "1" to it. 240 PrintWriter outStream = null; 241 try { 242 FileOutputStream fos = new FileOutputStream(SYSFS_QEMU_TRACE_STATE); 243 outStream = new PrintWriter(new OutputStreamWriter(fos)); 244 outStream.println("1"); 245 } catch (Exception e) { 246 } finally { 247 if (outStream != null) 248 outStream.close(); 249 } 250 251 VMDebug.startEmulatorTracing(); 252 } 253 254 /** 255 * Stop qemu tracing. See {@link #startNativeTracing()} to start tracing. 256 * 257 * <p>Tracing can be started and stopped as many times as desired. When 258 * the qemu emulator itself is stopped then the buffered trace records 259 * are flushed and written to the trace file. In fact, it is not necessary 260 * to call this method at all; simply killing qemu is sufficient. But 261 * starting and stopping a trace is useful for examining a specific 262 * region of code.</p> 263 */ 264 public static void stopNativeTracing() { 265 VMDebug.stopEmulatorTracing(); 266 267 // Open the sysfs file for writing and write "0" to it. 268 PrintWriter outStream = null; 269 try { 270 FileOutputStream fos = new FileOutputStream(SYSFS_QEMU_TRACE_STATE); 271 outStream = new PrintWriter(new OutputStreamWriter(fos)); 272 outStream.println("0"); 273 } catch (Exception e) { 274 // We could print an error message here but we probably want 275 // to quietly ignore errors if we are not running in the emulator. 276 } finally { 277 if (outStream != null) 278 outStream.close(); 279 } 280 } 281 282 /** 283 * Enable "emulator traces", in which information about the current 284 * method is made available to the "emulator -trace" feature. There 285 * is no corresponding "disable" call -- this is intended for use by 286 * the framework when tracing should be turned on and left that way, so 287 * that traces captured with F9/F10 will include the necessary data. 288 * 289 * This puts the VM into "profile" mode, which has performance 290 * consequences. 291 * 292 * To temporarily enable tracing, use {@link #startNativeTracing()}. 293 */ 294 public static void enableEmulatorTraceOutput() { 295 VMDebug.startEmulatorTracing(); 296 } 297 298 /** 299 * Start method tracing with default log name and buffer size. See <a 300href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Log Viewer</a> for 301 * information about reading these files. Call stopMethodTracing() to stop 302 * tracing. 303 */ 304 public static void startMethodTracing() { 305 VMDebug.startMethodTracing(DEFAULT_TRACE_FILE_PATH, 0, 0); 306 } 307 308 /** 309 * Start method tracing, specifying the trace log file name. The trace 310 * file will be put under "/sdcard" unless an absolute path is given. 311 * See <a 312 href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Log Viewer</a> for 313 * information about reading trace files. 314 * 315 * @param traceName Name for the trace log file to create. 316 * If no name argument is given, this value defaults to "/sdcard/dmtrace.trace". 317 * If the files already exist, they will be truncated. 318 * If the trace file given does not end in ".trace", it will be appended for you. 319 */ 320 public static void startMethodTracing(String traceName) { 321 startMethodTracing(traceName, 0, 0); 322 } 323 324 /** 325 * Start method tracing, specifying the trace log file name and the 326 * buffer size. The trace files will be put under "/sdcard" unless an 327 * absolute path is given. See <a 328 href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Log Viewer</a> for 329 * information about reading trace files. 330 * @param traceName Name for the trace log file to create. 331 * If no name argument is given, this value defaults to "/sdcard/dmtrace.trace". 332 * If the files already exist, they will be truncated. 333 * If the trace file given does not end in ".trace", it will be appended for you. 334 * 335 * @param bufferSize The maximum amount of trace data we gather. If not given, it defaults to 8MB. 336 */ 337 public static void startMethodTracing(String traceName, int bufferSize) { 338 startMethodTracing(traceName, bufferSize, 0); 339 } 340 341 /** 342 * Start method tracing, specifying the trace log file name and the 343 * buffer size. The trace files will be put under "/sdcard" unless an 344 * absolute path is given. See <a 345 href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Log Viewer</a> for 346 * information about reading trace files. 347 * 348 * <p> 349 * When method tracing is enabled, the VM will run more slowly than 350 * usual, so the timings from the trace files should only be considered 351 * in relative terms (e.g. was run #1 faster than run #2). The times 352 * for native methods will not change, so don't try to use this to 353 * compare the performance of interpreted and native implementations of the 354 * same method. As an alternative, consider using "native" tracing 355 * in the emulator via {@link #startNativeTracing()}. 356 * </p> 357 * 358 * @param traceName Name for the trace log file to create. 359 * If no name argument is given, this value defaults to "/sdcard/dmtrace.trace". 360 * If the files already exist, they will be truncated. 361 * If the trace file given does not end in ".trace", it will be appended for you. 362 * @param bufferSize The maximum amount of trace data we gather. If not given, it defaults to 8MB. 363 */ 364 public static void startMethodTracing(String traceName, int bufferSize, 365 int flags) { 366 367 String pathName = traceName; 368 if (pathName.charAt(0) != '/') 369 pathName = DEFAULT_TRACE_PATH_PREFIX + pathName; 370 if (!pathName.endsWith(DEFAULT_TRACE_EXTENSION)) 371 pathName = pathName + DEFAULT_TRACE_EXTENSION; 372 373 VMDebug.startMethodTracing(pathName, bufferSize, flags); 374 } 375 376 /** 377 * Determine whether method tracing is currently active. 378 * @hide 379 */ 380 public static boolean isMethodTracingActive() { 381 return VMDebug.isMethodTracingActive(); 382 } 383 384 /** 385 * Stop method tracing. 386 */ 387 public static void stopMethodTracing() { 388 VMDebug.stopMethodTracing(); 389 } 390 391 /** 392 * Get an indication of thread CPU usage. The value returned 393 * indicates the amount of time that the current thread has spent 394 * executing code or waiting for certain types of I/O. 395 * 396 * The time is expressed in nanoseconds, and is only meaningful 397 * when compared to the result from an earlier call. Note that 398 * nanosecond resolution does not imply nanosecond accuracy. 399 * 400 * On system which don't support this operation, the call returns -1. 401 */ 402 public static long threadCpuTimeNanos() { 403 return VMDebug.threadCpuTimeNanos(); 404 } 405 406 /** 407 * Count the number and aggregate size of memory allocations between 408 * two points. 409 * 410 * The "start" function resets the counts and enables counting. The 411 * "stop" function disables the counting so that the analysis code 412 * doesn't cause additional allocations. The "get" function returns 413 * the specified value. 414 * 415 * Counts are kept for the system as a whole and for each thread. 416 * The per-thread counts for threads other than the current thread 417 * are not cleared by the "reset" or "start" calls. 418 */ 419 public static void startAllocCounting() { 420 VMDebug.startAllocCounting(); 421 } 422 public static void stopAllocCounting() { 423 VMDebug.stopAllocCounting(); 424 } 425 426 public static int getGlobalAllocCount() { 427 return VMDebug.getAllocCount(VMDebug.KIND_GLOBAL_ALLOCATED_OBJECTS); 428 } 429 public static int getGlobalAllocSize() { 430 return VMDebug.getAllocCount(VMDebug.KIND_GLOBAL_ALLOCATED_BYTES); 431 } 432 public static int getGlobalFreedCount() { 433 return VMDebug.getAllocCount(VMDebug.KIND_GLOBAL_FREED_OBJECTS); 434 } 435 public static int getGlobalFreedSize() { 436 return VMDebug.getAllocCount(VMDebug.KIND_GLOBAL_FREED_BYTES); 437 } 438 public static int getGlobalExternalAllocCount() { 439 return VMDebug.getAllocCount(VMDebug.KIND_GLOBAL_EXT_ALLOCATED_OBJECTS); 440 } 441 public static int getGlobalExternalAllocSize() { 442 return VMDebug.getAllocCount(VMDebug.KIND_GLOBAL_EXT_ALLOCATED_BYTES); 443 } 444 public static int getGlobalExternalFreedCount() { 445 return VMDebug.getAllocCount(VMDebug.KIND_GLOBAL_EXT_FREED_OBJECTS); 446 } 447 public static int getGlobalExternalFreedSize() { 448 return VMDebug.getAllocCount(VMDebug.KIND_GLOBAL_EXT_FREED_BYTES); 449 } 450 public static int getGlobalGcInvocationCount() { 451 return VMDebug.getAllocCount(VMDebug.KIND_GLOBAL_GC_INVOCATIONS); 452 } 453 public static int getThreadAllocCount() { 454 return VMDebug.getAllocCount(VMDebug.KIND_THREAD_ALLOCATED_OBJECTS); 455 } 456 public static int getThreadAllocSize() { 457 return VMDebug.getAllocCount(VMDebug.KIND_THREAD_ALLOCATED_BYTES); 458 } 459 public static int getThreadExternalAllocCount() { 460 return VMDebug.getAllocCount(VMDebug.KIND_THREAD_EXT_ALLOCATED_OBJECTS); 461 } 462 public static int getThreadExternalAllocSize() { 463 return VMDebug.getAllocCount(VMDebug.KIND_THREAD_EXT_ALLOCATED_BYTES); 464 } 465 public static int getThreadGcInvocationCount() { 466 return VMDebug.getAllocCount(VMDebug.KIND_THREAD_GC_INVOCATIONS); 467 } 468 469 public static void resetGlobalAllocCount() { 470 VMDebug.resetAllocCount(VMDebug.KIND_GLOBAL_ALLOCATED_OBJECTS); 471 } 472 public static void resetGlobalAllocSize() { 473 VMDebug.resetAllocCount(VMDebug.KIND_GLOBAL_ALLOCATED_BYTES); 474 } 475 public static void resetGlobalFreedCount() { 476 VMDebug.resetAllocCount(VMDebug.KIND_GLOBAL_FREED_OBJECTS); 477 } 478 public static void resetGlobalFreedSize() { 479 VMDebug.resetAllocCount(VMDebug.KIND_GLOBAL_FREED_BYTES); 480 } 481 public static void resetGlobalExternalAllocCount() { 482 VMDebug.resetAllocCount(VMDebug.KIND_GLOBAL_EXT_ALLOCATED_OBJECTS); 483 } 484 public static void resetGlobalExternalAllocSize() { 485 VMDebug.resetAllocCount(VMDebug.KIND_GLOBAL_EXT_ALLOCATED_BYTES); 486 } 487 public static void resetGlobalExternalFreedCount() { 488 VMDebug.resetAllocCount(VMDebug.KIND_GLOBAL_EXT_FREED_OBJECTS); 489 } 490 public static void resetGlobalExternalFreedSize() { 491 VMDebug.resetAllocCount(VMDebug.KIND_GLOBAL_EXT_FREED_BYTES); 492 } 493 public static void resetGlobalGcInvocationCount() { 494 VMDebug.resetAllocCount(VMDebug.KIND_GLOBAL_GC_INVOCATIONS); 495 } 496 public static void resetThreadAllocCount() { 497 VMDebug.resetAllocCount(VMDebug.KIND_THREAD_ALLOCATED_OBJECTS); 498 } 499 public static void resetThreadAllocSize() { 500 VMDebug.resetAllocCount(VMDebug.KIND_THREAD_ALLOCATED_BYTES); 501 } 502 public static void resetThreadExternalAllocCount() { 503 VMDebug.resetAllocCount(VMDebug.KIND_THREAD_EXT_ALLOCATED_OBJECTS); 504 } 505 public static void resetThreadExternalAllocSize() { 506 VMDebug.resetAllocCount(VMDebug.KIND_THREAD_EXT_ALLOCATED_BYTES); 507 } 508 public static void resetThreadGcInvocationCount() { 509 VMDebug.resetAllocCount(VMDebug.KIND_THREAD_GC_INVOCATIONS); 510 } 511 public static void resetAllCounts() { 512 VMDebug.resetAllocCount(VMDebug.KIND_ALL_COUNTS); 513 } 514 515 /** 516 * Returns the size of the native heap. 517 * @return The size of the native heap in bytes. 518 */ 519 public static native long getNativeHeapSize(); 520 521 /** 522 * Returns the amount of allocated memory in the native heap. 523 * @return The allocated size in bytes. 524 */ 525 public static native long getNativeHeapAllocatedSize(); 526 527 /** 528 * Returns the amount of free memory in the native heap. 529 * @return The freed size in bytes. 530 */ 531 public static native long getNativeHeapFreeSize(); 532 533 /** 534 * Retrieves information about this processes memory usages. This information is broken down by 535 * how much is in use by dalivk, the native heap, and everything else. 536 */ 537 public static native void getMemoryInfo(MemoryInfo memoryInfo); 538 539 /** 540 * Establish an object allocation limit in the current thread. Useful 541 * for catching regressions in code that is expected to operate 542 * without causing any allocations. 543 * 544 * Pass in the maximum number of allowed allocations. Use -1 to disable 545 * the limit. Returns the previous limit. 546 * 547 * The preferred way to use this is: 548 * 549 * int prevLimit = -1; 550 * try { 551 * prevLimit = Debug.setAllocationLimit(0); 552 * ... do stuff that's not expected to allocate memory ... 553 * } finally { 554 * Debug.setAllocationLimit(prevLimit); 555 * } 556 * 557 * This allows limits to be nested. The try/finally ensures that the 558 * limit is reset if something fails. 559 * 560 * Exceeding the limit causes a dalvik.system.AllocationLimitError to 561 * be thrown from a memory allocation call. The limit is reset to -1 562 * when this happens. 563 * 564 * The feature may be disabled in the VM configuration. If so, this 565 * call has no effect, and always returns -1. 566 */ 567 public static int setAllocationLimit(int limit) { 568 return VMDebug.setAllocationLimit(limit); 569 } 570 571 /** 572 * Establish a global object allocation limit. This is similar to 573 * {@link #setAllocationLimit(int)} but applies to all threads in 574 * the VM. It will coexist peacefully with per-thread limits. 575 * 576 * [ The value of "limit" is currently restricted to 0 (no allocations 577 * allowed) or -1 (no global limit). This may be changed in a future 578 * release. ] 579 */ 580 public static int setGlobalAllocationLimit(int limit) { 581 if (limit != 0 && limit != -1) 582 throw new IllegalArgumentException("limit must be 0 or -1"); 583 return VMDebug.setGlobalAllocationLimit(limit); 584 } 585 586 /** 587 * Dump a list of all currently loaded class to the log file. 588 * 589 * @param flags See constants above. 590 */ 591 public static void printLoadedClasses(int flags) { 592 VMDebug.printLoadedClasses(flags); 593 } 594 595 /** 596 * Get the number of loaded classes. 597 * @return the number of loaded classes. 598 */ 599 public static int getLoadedClassCount() { 600 return VMDebug.getLoadedClassCount(); 601 } 602 603 /** 604 * Dump "hprof" data to the specified file. This will cause a GC. 605 * 606 * @param fileName Full pathname of output file (e.g. "/sdcard/dump.hprof"). 607 * @throws UnsupportedOperationException if the VM was built without 608 * HPROF support. 609 * @throws IOException if an error occurs while opening or writing files. 610 */ 611 public static void dumpHprofData(String fileName) throws IOException { 612 VMDebug.dumpHprofData(fileName); 613 } 614 615 /** 616 * Returns the number of sent transactions from this process. 617 * @return The number of sent transactions or -1 if it could not read t. 618 */ 619 public static native int getBinderSentTransactions(); 620 621 /** 622 * Returns the number of received transactions from the binder driver. 623 * @return The number of received transactions or -1 if it could not read the stats. 624 */ 625 public static native int getBinderReceivedTransactions(); 626 627 /** 628 * Returns the number of active local Binder objects that exist in the 629 * current process. 630 */ 631 public static final native int getBinderLocalObjectCount(); 632 633 /** 634 * Returns the number of references to remote proxy Binder objects that 635 * exist in the current process. 636 */ 637 public static final native int getBinderProxyObjectCount(); 638 639 /** 640 * Returns the number of death notification links to Binder objects that 641 * exist in the current process. 642 */ 643 public static final native int getBinderDeathObjectCount(); 644 645 /** 646 * Primes the register map cache. 647 * 648 * Only works for classes in the bootstrap class loader. Does not 649 * cause classes to be loaded if they're not already present. 650 * 651 * The classAndMethodDesc argument is a concatentation of the VM-internal 652 * class descriptor, method name, and method descriptor. Examples: 653 * Landroid/os/Looper;.loop:()V 654 * Landroid/app/ActivityThread;.main:([Ljava/lang/String;)V 655 * 656 * @param classAndMethodDesc the method to prepare 657 * 658 * @hide 659 */ 660 public static final boolean cacheRegisterMap(String classAndMethodDesc) { 661 return VMDebug.cacheRegisterMap(classAndMethodDesc); 662 } 663 664 /** 665 * API for gathering and querying instruction counts. 666 * 667 * Example usage: 668 * Debug.InstructionCount icount = new Debug.InstructionCount(); 669 * icount.resetAndStart(); 670 * [... do lots of stuff ...] 671 * if (icount.collect()) { 672 * System.out.println("Total instructions executed: " 673 * + icount.globalTotal()); 674 * System.out.println("Method invocations: " 675 * + icount.globalMethodInvocations()); 676 * } 677 */ 678 public static class InstructionCount { 679 private static final int NUM_INSTR = 256; 680 681 private int[] mCounts; 682 683 public InstructionCount() { 684 mCounts = new int[NUM_INSTR]; 685 } 686 687 /** 688 * Reset counters and ensure counts are running. Counts may 689 * have already been running. 690 * 691 * @return true if counting was started 692 */ 693 public boolean resetAndStart() { 694 try { 695 VMDebug.startInstructionCounting(); 696 VMDebug.resetInstructionCount(); 697 } catch (UnsupportedOperationException uoe) { 698 return false; 699 } 700 return true; 701 } 702 703 /** 704 * Collect instruction counts. May or may not stop the 705 * counting process. 706 */ 707 public boolean collect() { 708 try { 709 VMDebug.stopInstructionCounting(); 710 VMDebug.getInstructionCount(mCounts); 711 } catch (UnsupportedOperationException uoe) { 712 return false; 713 } 714 return true; 715 } 716 717 /** 718 * Return the total number of instructions executed globally (i.e. in 719 * all threads). 720 */ 721 public int globalTotal() { 722 int count = 0; 723 for (int i = 0; i < NUM_INSTR; i++) 724 count += mCounts[i]; 725 return count; 726 } 727 728 /** 729 * Return the total number of method-invocation instructions 730 * executed globally. 731 */ 732 public int globalMethodInvocations() { 733 int count = 0; 734 735 //count += mCounts[Opcodes.OP_EXECUTE_INLINE]; 736 count += mCounts[Opcodes.OP_INVOKE_VIRTUAL]; 737 count += mCounts[Opcodes.OP_INVOKE_SUPER]; 738 count += mCounts[Opcodes.OP_INVOKE_DIRECT]; 739 count += mCounts[Opcodes.OP_INVOKE_STATIC]; 740 count += mCounts[Opcodes.OP_INVOKE_INTERFACE]; 741 count += mCounts[Opcodes.OP_INVOKE_VIRTUAL_RANGE]; 742 count += mCounts[Opcodes.OP_INVOKE_SUPER_RANGE]; 743 count += mCounts[Opcodes.OP_INVOKE_DIRECT_RANGE]; 744 count += mCounts[Opcodes.OP_INVOKE_STATIC_RANGE]; 745 count += mCounts[Opcodes.OP_INVOKE_INTERFACE_RANGE]; 746 //count += mCounts[Opcodes.OP_INVOKE_DIRECT_EMPTY]; 747 count += mCounts[Opcodes.OP_INVOKE_VIRTUAL_QUICK]; 748 count += mCounts[Opcodes.OP_INVOKE_VIRTUAL_QUICK_RANGE]; 749 count += mCounts[Opcodes.OP_INVOKE_SUPER_QUICK]; 750 count += mCounts[Opcodes.OP_INVOKE_SUPER_QUICK_RANGE]; 751 return count; 752 } 753 } 754 755 756 /** 757 * A Map of typed debug properties. 758 */ 759 private static final TypedProperties debugProperties; 760 761 /* 762 * Load the debug properties from the standard files into debugProperties. 763 */ 764 static { 765 if (Config.DEBUG) { 766 final String TAG = "DebugProperties"; 767 final String[] files = { "/system/debug.prop", "/debug.prop", "/data/debug.prop" }; 768 final TypedProperties tp = new TypedProperties(); 769 770 // Read the properties from each of the files, if present. 771 for (String file : files) { 772 Reader r; 773 try { 774 r = new FileReader(file); 775 } catch (FileNotFoundException ex) { 776 // It's ok if a file is missing. 777 continue; 778 } 779 780 try { 781 tp.load(r); 782 } catch (Exception ex) { 783 throw new RuntimeException("Problem loading " + file, ex); 784 } finally { 785 try { 786 r.close(); 787 } catch (IOException ex) { 788 // Ignore this error. 789 } 790 } 791 } 792 793 debugProperties = tp.isEmpty() ? null : tp; 794 } else { 795 debugProperties = null; 796 } 797 } 798 799 800 /** 801 * Returns true if the type of the field matches the specified class. 802 * Handles the case where the class is, e.g., java.lang.Boolean, but 803 * the field is of the primitive "boolean" type. Also handles all of 804 * the java.lang.Number subclasses. 805 */ 806 private static boolean fieldTypeMatches(Field field, Class<?> cl) { 807 Class<?> fieldClass = field.getType(); 808 if (fieldClass == cl) { 809 return true; 810 } 811 Field primitiveTypeField; 812 try { 813 /* All of the classes we care about (Boolean, Integer, etc.) 814 * have a Class field called "TYPE" that points to the corresponding 815 * primitive class. 816 */ 817 primitiveTypeField = cl.getField("TYPE"); 818 } catch (NoSuchFieldException ex) { 819 return false; 820 } 821 try { 822 return fieldClass == (Class<?>) primitiveTypeField.get(null); 823 } catch (IllegalAccessException ex) { 824 return false; 825 } 826 } 827 828 829 /** 830 * Looks up the property that corresponds to the field, and sets the field's value 831 * if the types match. 832 */ 833 private static void modifyFieldIfSet(final Field field, final TypedProperties properties, 834 final String propertyName) { 835 if (field.getType() == java.lang.String.class) { 836 int stringInfo = properties.getStringInfo(propertyName); 837 switch (stringInfo) { 838 case TypedProperties.STRING_SET: 839 // Handle as usual below. 840 break; 841 case TypedProperties.STRING_NULL: 842 try { 843 field.set(null, null); // null object for static fields; null string 844 } catch (IllegalAccessException ex) { 845 throw new IllegalArgumentException( 846 "Cannot set field for " + propertyName, ex); 847 } 848 return; 849 case TypedProperties.STRING_NOT_SET: 850 return; 851 case TypedProperties.STRING_TYPE_MISMATCH: 852 throw new IllegalArgumentException( 853 "Type of " + propertyName + " " + 854 " does not match field type (" + field.getType() + ")"); 855 default: 856 throw new IllegalStateException( 857 "Unexpected getStringInfo(" + propertyName + ") return value " + 858 stringInfo); 859 } 860 } 861 Object value = properties.get(propertyName); 862 if (value != null) { 863 if (!fieldTypeMatches(field, value.getClass())) { 864 throw new IllegalArgumentException( 865 "Type of " + propertyName + " (" + value.getClass() + ") " + 866 " does not match field type (" + field.getType() + ")"); 867 } 868 try { 869 field.set(null, value); // null object for static fields 870 } catch (IllegalAccessException ex) { 871 throw new IllegalArgumentException( 872 "Cannot set field for " + propertyName, ex); 873 } 874 } 875 } 876 877 878 /** 879 * Reflectively sets static fields of a class based on internal debugging 880 * properties. This method is a no-op if android.util.Config.DEBUG is 881 * false. 882 * <p> 883 * <strong>NOTE TO APPLICATION DEVELOPERS</strong>: Config.DEBUG will 884 * always be false in release builds. This API is typically only useful 885 * for platform developers. 886 * </p> 887 * Class setup: define a class whose only fields are non-final, static 888 * primitive types (except for "char") or Strings. In a static block 889 * after the field definitions/initializations, pass the class to 890 * this method, Debug.setFieldsOn(). Example: 891 * <pre> 892 * package com.example; 893 * 894 * import android.os.Debug; 895 * 896 * public class MyDebugVars { 897 * public static String s = "a string"; 898 * public static String s2 = "second string"; 899 * public static String ns = null; 900 * public static boolean b = false; 901 * public static int i = 5; 902 * public static float f = 0.1f; 903 * public static double d = 0.5d; 904 * 905 * // This MUST appear AFTER all fields are defined and initialized! 906 * static { 907 * Debug.setFieldsOn(MyDebugVars.class); 908 * } 909 * } 910 * </pre> 911 * setFieldsOn() may override the value of any field in the class based 912 * on internal properties that are fixed at boot time. 913 * <p> 914 * These properties are only set during platform debugging, and are not 915 * meant to be used as a general-purpose properties store. 916 * 917 * {@hide} 918 * 919 * @param cl The class to (possibly) modify 920 * @throws IllegalArgumentException if any fields are final or non-static, 921 * or if the type of the field does not match the type of 922 * the internal debugging property value. 923 */ 924 public static void setFieldsOn(Class<?> cl) { 925 if (Config.DEBUG) { 926 if (debugProperties != null) { 927 /* Only look for fields declared directly by the class, 928 * so we don't mysteriously change static fields in superclasses. 929 */ 930 for (Field field : cl.getDeclaredFields()) { 931 final String propertyName = cl.getName() + "." + field.getName(); 932 boolean isStatic = Modifier.isStatic(field.getModifiers()); 933 boolean isFinal = Modifier.isFinal(field.getModifiers()); 934 if (!isStatic || isFinal) { 935 throw new IllegalArgumentException(propertyName + 936 " must be static and non-final"); 937 } 938 modifyFieldIfSet(field, debugProperties, propertyName); 939 } 940 } 941 } else { 942 Log.w("android.os.Debug", 943 "setFieldsOn(" + (cl == null ? "null" : cl.getName()) + 944 ") called in non-DEBUG build"); 945 } 946 } 947} 948