244 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			HTML
		
	
	
	
			
		
		
	
	
			244 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			HTML
		
	
	
	
<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>
 |