| <html> | |
| <head> | |
| <title>Dalvik Debugger Support</title> | |
| </head> | |
| <body> | |
| <h1>Dalvik Debugger Support</h1> | |
| <p> | |
| The Dalvik virtual machine supports source-level debugging with many popular | |
| development environments. Any tool that allows remote debugging over JDWP | |
| (the | |
| <a href="http://java.sun.com/javase/6/docs/technotes/guides/jpda/jdwp-spec.html"> | |
| Java Debug Wire Protocol</a>) is expected work. Supported debuggers | |
| include jdb, Eclipse, IntelliJ, and JSwat. | |
| </p><p> | |
| The VM does not support tools based on JVMTI (Java Virtual | |
| Machine Tool Interface). This is a relatively intrusive approach that | |
| relies on bytecode insertion, something the Dalvik VM does not currently | |
| support. | |
| </p><p> | |
| Dalvik's implementation of JDWP also includes hooks for supporting | |
| DDM (Dalvik Debug Monitor) features, notably as implemented by DDMS | |
| (Dalvik Debug Monitor Server) and the Eclipse ADT plugin. The protocol | |
| and VM interaction is described in some detail | |
| <a href="debugmon.html">here</a>. | |
| </p><p> | |
| All of the debugger support in the VM lives in the <code>dalvik/vm/jdwp</code> | |
| directory, and is almost entirely isolated from the rest of the VM sources. | |
| <code>dalvik/vm/Debugger.c</code> bridges the gap. The goal in doing so | |
| was to make it easier to re-use the JDWP code in other projects. | |
| </p><p> | |
| <h2>Implementation</h2> | |
| <p> | |
| Every VM that has debugging enabled starts a "JDWP" thread. The thread | |
| typically sits idle until DDMS or a debugger connects. The thread is | |
| only responsible for handling requests from the debugger; VM-initated | |
| communication, such as notifying the debugger when the VM has stopped at | |
| a breakpoint, are sent from the affected thread. | |
| </p><p> | |
| When the VM is started from the Android app framework, debugging is enabled | |
| for all applications when the system property <code>ro.debuggable</code> | |
| is set to </code>1</code> (use <code>adb shell getprop ro.debuggable</code> | |
| to check it). If it's zero, debugging can be enabled via the application's | |
| manifest, which must include <code>android:debuggable="true"</code> in the | |
| <code><application></code> element. | |
| </p><p> | |
| The VM recognizes the difference between a connection from DDMS and a | |
| connection from a debugger (either directly or in concert with DDMS). | |
| A connection from DDMS alone doesn't result in a change in VM behavior, | |
| but when the VM sees debugger packets it allocates additional data | |
| structures and may switch to a different implementation of the interpreter. | |
| </p><p> | |
| Pre-Froyo implementations of the Dalvik VM used read-only memory mappings | |
| for all bytecode, which made it necessary to scan for breakpoints by | |
| comparing the program counter to a set of addresses. In Froyo this was | |
| changed to allow insertion of breakpoint opcodes. This allows the VM | |
| to execute code more quickly, and does away with the hardcoded limit | |
| of 20 breakpoints. Even with this change, however, the debug-enabled | |
| interpreter is much slower than the regular interpreter (perhaps 5x). | |
| </p><p> | |
| The JDWP protocol is stateless, so the VM handles individual debugger | |
| requests as they arrive, and posts events to the debugger as they happen. | |
| </p><p> | |
| <h2>Debug Data</h2> | |
| <p> Source code debug data, which includes mappings of source code to | |
| bytecode and lists describing which registers are used to hold method | |
| arguments and local variables, are optionally emitted by the Java compiler. | |
| When <code>dx</code> converts Java bytecode to Dalvik bytecode, it must | |
| also convert this debug data. | |
| </p><p> | |
| <code>dx</code> must also ensure that it doesn't perform operations | |
| that confuse the debugger. For example, re-using registers that hold | |
| method arguments and the "<code>this</code>" pointer is allowed in | |
| Dalvik bytecode if the values are never used or no longer needed. | |
| This can be very confusing for the debugger (and the programmer) | |
| since the values have method scope and aren't expected to disappear. For | |
| this reason, <code>dx</code> generates sub-optimal code in some situations | |
| when debugging support is enabled. | |
| </p><p> | |
| Some of the debug data is used for other purposes; in particular, having | |
| filename and line number data is necessary for generating useful exception | |
| stack traces. This data can be omitted by <code>dx</code> to make the DEX | |
| file smaller. | |
| </p><p> | |
| <h2>Usage</h2> | |
| <p> | |
| The Dalvik VM supports many of the same command-line flags that other popular | |
| desktop VMs do. To start a VM with debugging enabled, you add a command-line | |
| flag with some basic options. The basic incantation looks something | |
| like this: | |
| <pre>-Xrunjdwp:transport=dt_socket,address=8000,server=y,suspend=y</pre> | |
| or | |
| <pre>-agentlib:jdwp=transport=dt_socket,address=8000,server=y,suspend=y</pre> | |
| </p><p> | |
| After the initial prefix, options are provided as name=value pairs. The | |
| options currently supported by the Dalvik VM are: | |
| <dl> | |
| <dt>transport (no default)</dt> | |
| <dd>Communication transport mechanism to use. Dalvik supports | |
| TCP/IP sockets (<code>dt_socket</code>) and connection over USB | |
| through ADB (<code>dt_android_adb</code>). | |
| </dd> | |
| <dt>server (default='n')</dt> | |
| <dd>Determines whether the VM acts as a client or a server. When | |
| acting as a server, the VM waits for a debugger to connect to it. | |
| When acting as a client, the VM attempts to connect to a waiting | |
| debugger. | |
| </dd> | |
| <dt>suspend (default='n')</dt> | |
| <dd>If set to 'y', the VM will wait for a debugger connection | |
| before executing application code. When the debugger connects (or | |
| when the VM finishes connecting to the debugger), the VM tells the | |
| debugger that it has suspended, and will not proceed until told | |
| to resume. If set to 'n', the VM just plows ahead. | |
| </dd> | |
| <dt>address (default="")</dt> | |
| <dd>This must be <code>hostname:port</code> when <code>server=n</code>, | |
| but can be just <code>port</code> when <code>server=y</code>. This | |
| specifies the IP address and port number to connect or listen to. | |
| <br> | |
| Listening on port 0 has a special meaning: try to | |
| listen on port 8000; if that fails, try 8001, 8002, and so on. (This | |
| behavior is non-standard and may be removed from a future release.) | |
| <br>This option has no meaning for <code>transport=dt_android_adb</code>. | |
| </dd> | |
| <dt>help (no arguments)</dt> | |
| <dd>If this is the only option, a brief usage message is displayed. | |
| </dd> | |
| <dt>launch, onthrow, oncaught, timeout</dt> | |
| <dd>These options are accepted but ignored. | |
| </dd> | |
| </dl> | |
| </p><p> | |
| To debug a program on an Android device using DDMS over USB, you could | |
| use a command like this: | |
| <pre>% dalvikvm -agentlib:jdwp=transport=dt_android_adb,suspend=y,server=y -cp /data/foo.jar Foo</pre> | |
| This tells the Dalvik VM to run the program with debugging enabled, listening | |
| for a connection from DDMS, and waiting for a debugger. The program will show | |
| up with an app name of "?" in the process list, because it wasn't started | |
| from the Android application framework. From here you would connect your | |
| debugger to the appropriate DDMS listen port (e.g. | |
| <code>jdb -attach localhost:8700</code> after selecting it in the app list). | |
| </p><p> | |
| To debug a program on an Android device using TCP/IP bridged across ADB, | |
| you would first need to set up forwarding: | |
| <pre>% adb forward tcp:8000 tcp:8000 | |
| % adb shell dalvikvm -agentlib:jdwp=transport=dt_socket,address=8000,suspend=y,server=y -cp /data/foo.jar Foo</pre> | |
| and then <code>jdb -attach localhost:8000</code>. | |
| </p><p> | |
| (In the above examples, the VM will be suspended when you attach. In jdb, | |
| type <code>cont</code> to continue.) | |
| </p><p> | |
| The DDMS integration makes the <code>dt_android_adb</code> transport much | |
| more convenient when debugging on an Android device, but when working with | |
| Dalvik on the desktop it makes sense to use the TCP/IP transport. | |
| </p><p> | |
| <h2>Known Issues and Limitations</h2> | |
| </p><p> | |
| Most of the optional features JDWP allows are not implemented. These | |
| include field access watchpoints and better tracking of monitors. | |
| </p><p> | |
| Not all JDWP requests are implemented. In particular, anything that | |
| never gets emitted by the debuggers we've used is not supported and will | |
| result in error messages being logged. Support will be added when a | |
| use case is uncovered. | |
| </p><p> | |
| | |
| </p><p> | |
| The debugger and garbage collector are somewhat loosely | |
| integrated at present. The VM currently guarantees that any object the | |
| debugger is aware of will not be garbage collected until after the | |
| debugger disconnects. This can result in a build-up over time while the | |
| debugger is connected. For example, if the debugger sees a running | |
| thread, the associated Thread object will not be collected, even after | |
| the thread terminates. | |
| </p><p> | |
| The only way to "unlock" the references is to detach and reattach the | |
| debugger. | |
| </p><p> | |
| | |
| </p><p> | |
| The translation from Java bytecode to Dalvik bytecode may result in | |
| identical sequences of instructions being combined. This can make it | |
| look like the wrong bit of code is being executed. For example: | |
| <pre> int test(int i) { | |
| if (i == 1) { | |
| return 0; | |
| } | |
| return 1; | |
| }</pre> | |
| The Dalvik bytecode uses a common <code>return</code> instruction for both | |
| <code>return</code> statements, so when <code>i</code> is 1 the debugger | |
| will single-step through <code>return 0</code> and then <code>return 1</code>. | |
| </p><p> | |
| | |
| </p><p> | |
| Dalvik handles synchronized methods differently from other VMs. | |
| Instead of marking a method as <code>synchronized</code> and expecting | |
| the VM to handle the locks, <code>dx</code> inserts a "lock" | |
| instruction at the top of the method and an "unlock" instruction in a | |
| synthetic <code>finally</code> block. As a result, when single-stepping | |
| a <code>return</code> statement, the "current line" cursor may jump to | |
| the last line in the method. | |
| </p><p> | |
| This can also affect the way the debugger processes exceptions. The | |
| debugger may decide to break on an | |
| exception based on whether that exception is "caught" or "uncaught". To | |
| be considered uncaught, there must be no matching <code>catch</code> block | |
| or <code>finally</code> clause between the current point of execution and | |
| the top of the thread. An exception thrown within or below a synchronized | |
| method will always be considered "caught", so the debugger won't stop | |
| until the exception is re-thrown from the synthetic <code>finally</code> block. | |
| </p><p> | |
| <address>Copyright © 2009 The Android Open Source Project</address> | |
| </p> | |
| </body> | |
| </html> |