|  | <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> |