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