You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
243 lines
10 KiB
243 lines
10 KiB
<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>
|
|
|