android (17)
The Dalvik virtual machine supports source-level debugging with many popular development environments.
支持源码级调试
Any tool that allows remote debugging over JDWP (the Java Debug Wire Protocol) is expected work.
远程调试需要JDWP
Supported debuggers include jdb, Eclipse, IntelliJ, and JSwat.
The VM does not support tools based on JVMTI (Java Virtual Machine Tool Interface).
dalvik 不支持JVMTI接口
This is a relatively intrusive approach that relies on bytecode insertion, something the Dalvik VM does not currently support.
不支持字节码插入
Dalvik's implementation of JDWP also includes hooks for supporting DDM (Dalvik Debug Monitor) features,
为DDM 提供了支持
notably as implemented by DDMS (Dalvik Debug Monitor Server) and the Eclipse ADT plugin.
The protocol and VM interaction is described in some detail here.
All of the debugger support in the VM lives in the dalvik/vm/jdwp directory,
所有的实现都在JDWP目录
and is almost entirely isolated from the rest of the VM sources.
dalvik/vm/Debugger.c bridges the gap. The goal in doing so was to make it easier to re-use the JDWP code in other projects.
Implementation
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.
通知调试器目标线程已经断点了
When the VM is started from the Android app framework, debugging is enabled for all applications when the system property ro.debuggable is set to 1
如果环境变量ro.debuggable 被设置成1 ,调试生效
(use adb shell getprop ro.debuggable to check it).
可以用adb shell 来检测
If it's zero, debugging can be enabled via the application's manifest, which must include android:debuggable="true" in the <application> element.
application's manifest 同样可以启动调试
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.
改变vm的行为?
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).
The JDWP protocol is stateless, so the VM handles individual debugger requests as they arrive, and posts events to the debugger as they happen.
无状态
Debug Data
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 dx converts Java bytecode to Dalvik bytecode, it must also convert this debug data.
dx 必须转化 调试数据
dx must also ensure that it doesn't perform operations that confuse the debugger.
For example, re-using registers that hold method arguments and the "this" 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, dx generates sub-optimal code in some situations when debugging support is enabled.
不能和优化冲突
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 dx to make the DEX file smaller.
文件名和行号
Usage
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:
-Xrunjdwp:transport=dt_socket,address=8000,server=y,suspend=y
or
-agentlib:jdwp=transport=dt_socket,address=8000,server=y,suspend=y
After the initial prefix, options are provided as name=value pairs. The options currently supported by the Dalvik VM are:
transport (no default)
Communication transport mechanism to use. Dalvik supports TCP/IP sockets (dt_socket) and connection over USB through ADB (dt_android_adb).
传输机制 通过TCP和ADB
server (default='n')
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.
server决定VM是否主动连接
suspend (default='n')
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.
是否等待
address (default="")
This must be hostname:port when server=n, but can be just port when server=y.
This specifies the IP address and port number to connect or listen to.
address 指定IP和port
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.)
This option has no meaning for transport=dt_android_adb.
help (no arguments)
If this is the only option, a brief usage message is displayed.
launch, onthrow, oncaught, timeout
These options are accepted but ignored.
以上均忽略
To debug a program on an Android device using DDMS over USB, you could use a command like this:
% dalvikvm -agentlib:jdwp=transport=dt_android_adb,suspend=y,server=y -cp /data/foo.jar Foo
dalvikvm -agentlib:jdwp=transport=dt_android_adb,suspend=y,server=y -cp /sdcard/foo.jar Foo
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. jdb -attach localhost:8700 after selecting it in the app list).
To debug a program on an Android device using TCP/IP bridged across ADB, you would first need to set up forwarding:
% 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
and then jdb -attach localhost:8000.
(In the above examples, the VM will be suspended when you attach. In jdb, type cont to continue.)
The DDMS integration makes the dt_android_adb 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.
Known Issues and Limitations
Most of the optional features JDWP allows are not implemented. These include field access watchpoints and better tracking of monitors.
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.
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.
垃圾回收
The only way to "unlock" the references is to detach and reattach the debugger.
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:
int test(int i) {
if (i == 1)
{
return 0;
}
return 1;
}
The Dalvik bytecode uses a common return instruction for both return statements, so when i is 1 the debugger will single-step through return 0 and then return 1.
调试的时候有区别?
Dalvik handles synchronized methods differently from other VMs.
Instead of marking a method as synchronized and expecting the VM to handle the locks, dx inserts a "lock" instruction at the top of the method and
an "unlock" instruction in a synthetic finally block. As a result, when single-stepping a return statement, the "current line" cursor may jump to the last line
in the method.
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 catch block or finally 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 finally block.