Dalvik虚拟机调试监视器


CR:

    做测试调研的时候感觉不是很熟,就找了点资料来学习下DDMS、JDWP和调试器的东西。翻译了点资料。
DDM:dalvik debug monitor,调试监视器,很生硬的名字,但是这样可以表明它的作用,同时区别于Debugger调试器。它只是一个监视器,提供了UI,连接VM和Debugger,是server;
Debugger:调试器,这才是调试用的东西;
JDWP:传输协议,允许vm-DDM-debugger的数据传输模式。
vm:dalvikvm,是client,调试其实就是client-server模式

1、简介
    能实时观察虚拟机的状态时非常有用的,对于Android,我们需要能监视通过USB或者无线连接的设备上的多个虚拟机(CR:每个app创建一个vm实例)。本文档描述了一个连接到多个vm的调试监视器终端(CR:下面都叫DDM,dalvik debug monitor server)以及一个API,虚拟机和应用可以通过API向监视器传递信息。
    通过Dalvik调试监视器(DDM)可以监视的东西:
(1)线程状态:追踪线程创建、退出、忙/空闲状态;
(2)所有的堆的状态:对于heap bitmap display和片段分析很有用。
    亦可以用除了VM之外的东西来作为DDM的客户端,但是这是次要目的。例子中包含了“logcat”信息和对虚拟内存使用及LOAD平均的系统监视。(CR:这句话不太明白)
    通过将信息展现在设备的UI上,DDM也可以在设备上运行。但是,它最初的目的是一个利用桌面工具和屏幕资源来提供显示的工具。
    DDM用JAVA语言写成,从而可移植,它通过桌面UI工具(SWT)来实现接口。

2、协议
    为了利用已有的接口,DDM协议建立在JDWP协议之上(CR:piggy-back类似于在卡车后面挂车的意思)(java debug wire protocol,通常运行在vm和调试器之间)。这样,对于一个无DDM的客户端来说,DDM作用其实就像一个调试器。
    JDWP协议和我们想要实现的功能很像,特别的:
(1)它允许vender-defined包,因此没有必要改变JDWP细则;
(2)VM可能在任何时间传出事件,这些事件不会导致调试器输出一个响应,这意味着客户端可以传出数据并且立刻继续工作而不需要等待事件响应;
(3)基本的协议是无状态、异步的,调试器端的请求包包含了一串数字,而vm则包含在了响应包中,这就实现了同时同步会话,意味着DDM通信和调试器通信可以交错进行。
    下面是我们使用JDWP的一些情形:
(1)VM只等待和调试器的一个连接,因此你不能同时连接监视器和调试器。为此,就要将调试器和监视器连起来然后传输数据(我们目前可以通过jdwpspy传输数据,尽管还需要管理我们需要的ID)。连接了设备之后,通过这种方式比现在的方法(猜测传输端口号)更加方便;
(2)当调试器连接后,VM运行会不一样。它会更慢,任何传向监视器或者调试器的对象都不会产生GC;对此,我们可以通过直到监测到无DDM通信再激活慢路径(CR:slow path,不懂)。另外,我们还希望能产生“调试器已经断开/连接”的信息,从而使VM释放调试相关资源并且不断开连接;
(3)无DDM的vm在DDM连接后不应该出错,事实上三方vm并没有对此保证(例如某种主流vm会立刻崩溃),但是老式的JanVM会拒绝“hello”包。

2.1、建立连接
    可以通过两种基本方法:DDM连接vm,vm连接DDM。前者没有后者“精确”,因为你必须扫描客户端,但是它也有自己的优点。
    有三种有趣的情形:
(1)DDM开启,然后boot USB连接的设备或者启动模拟器;(DDM,设备)
(2)设备或模拟器在运行DDM之前已经在运行;(设备,DDM)
(3)DDM已经在运行了,当一个已经启动的设备通过USB连接的时候。(DDM,USB连接运行中的设备)
    如果设备连接上了已经启动的DDM,我们只要处理情形1;如果DDM在它开启的时候扫描VM,我们只要处理情形2;没有处理情形3,而它可能是随着设备的运行最重要的一支。
    因此可以设计一个下拉菜单,它有两个选项:扫描服务器、扫描设备。前者使得DDM扫描“本地主机”(localhost)上的vm,后者使得它查询adb连接另一边的vm。DDM会每隔几秒就扫描vm,或者检测已知vm端口序列(例如8000——8040),或者和设备上的某些数据库交互。改变模式会导致已有的连接中断。
    当DDM第一次启动时,它会尝试运行“adb usb”来确保adb可以运行(注意:确保DDM从可以在该路径中运行adb的shell中启动很重要)。如果失败,和设备的交互很可能会等到adb守护进程运行才开始。

2.2、连接调试器
    当DDM在所有vm的JWDP端口运行时,通过DDM连接调试器很重要。每个要调试的vm会有一个等待DDM监听的端口,这使得你可以将一个调试器和多个vm同时相连。
    更常见的是,程序员只想将一个调试器和一个vm相连。一个端口会被DDM监听(例如8700),然后任何和它相连的东西会被“当前vm”连接(从UI中选择)。这使得程序员可以关注单个应用,而不需要每次重启设备时调整IDE设置以适应不同端口号,如果不是这样,序号就会不停变动。

2.3、包格式
    信息以块(chunk)的形式传递,每个块开头如下:
U4 type
U4 length
    然后包含了不同数量的特定类型(type)的数据。不识别的类型会导致客户端的空响应,这在DDM会被安静地忽略。(一般是返回ERROR,需要一个ERROR块类型及DDM端的handler)
    同样的块类型可能在传递方向不同时有不同的含义,例如,同样的数据类型可能被用在询问和对应的响应上,为了清晰明了,特定的类型必须用在对应的功能上。
    上面的包格式对于JDWP框架有些冗余,JDWP框架有4字节的length和2字节的类型码(“命令集”和“命令”,一组命令集用于“vendor-defined命令和扩展”)。
    使用块的形式允许潜在传递(underlying transport)的独立性,避免了和JDWP客户端码元的插入集成(intrusive integration),提供了在单个传输单元中发送多个块的方法。(包含多个块的包是可以设计的,这里不讨论)
    因为我们可能通过慢的USB连接发送数据,块会被压缩。压缩块也是块类型,但是指明了被压缩,压缩后的长度紧跟其后,最后是原来的块类型和未压缩的长度。例如,zlib的压缩算法而言,块类型是ZLIB。
    基于JDWP模型,DDM到客户端的包都会被确认,但从客户端到DDM的包却不会,JDWP错误码码域总是被设为“no error”,因此特定请求未响应的情形一定要被编码到DDM信息中。
    所谓的U4,是一个无符号32bit的值,U1是无符号的8bit的值。数据是大印第安序列形式以匹配JDWP。===================================CUT==========================

Dalvik VM
Debug Monitor

Status:Draft (as of March 6, 2007)

[authors] Modified: Sat Apr 11 2009

Introduction

It's extremely useful to be able to monitor the live state of the VM. For Android, we need to monitor multiple VMs running on a device connected through USB or a wireless network connection. This document describes a debug monitor server that interacts with multiple VMs, and an API that VMs and applications can use to provide information to the monitor.

Some things we can monitor with the Dalvik Debug Monitor ("DDM"):

  • Thread states. Track thread creation/exit, busy/idle status.
  • Overall heap status, useful for a heap bitmap display or fragmentation analysis.

It is possible for something other than a VM to act as a DDM client, but that is a secondary goal. Examples include "logcat" log extraction and system monitors for virtual memory usage and load average.

It's also possible for the DDM server to be run on the device, with the information presented through the device UI. However, the initial goal is to provide a display tool that takes advantage of desktop tools and screen real estate.

This work is necessary because we are unable to use standard JVMTI-based tools with Dalvik. JVMTI relies on bytecode insertion, which is not currently possible because Dalvik doesn't support Java bytecode.

The DDM server is written in the Java programming language for portability. It uses a desktop UI toolkit (SWT) for its interface.

Protocol

To take advantage of existing infrastructure we are piggy-backing the DDM protocol on top of JDWP (the Java Debug Wire Protocol, normally spoken between a VM and a debugger). To a non-DDM client, the DDM server just looks like a debugger.

The JDWP protocol is very close to what we want to use. In particular:

  • It explicitly allows for vendor-defined packets, so there is no need to "bend" the JDWP spec.
  • Events may be posted from the VM at arbitrary points. Such events do not elicit a response from the debugger, meaning the client can post data and immediately resume work without worrying about the eventual response.
  • The basic protocol is stateless and asynchronous. Request packets from the debugger side include a serial number, which the VM includes in the response packet. This allows multiple simultaneous conversations, which means the DDM traffic can be interleaved with debugger traffic.

There are a few issues with using JDWP for our purposes:

  • The VM only expects one connection from a debugger, so you couldn't attach the monitor and a debugger at the same time. This will be worked around by connecting the debugger to the monitor and passing the traffic through. (We're already doing the pass-through with "jdwpspy"; requires some management of our request IDs though.) This should be more convenient than the current "guess the port number" system when we're attached to a device.
  • The VM behaves differently when a debugger is attached. It will run more slowly, and any objects passed to the monitor or debugger are immune to GC. We can work around this by not enabling the slow path until non-DDM traffic is observed. We also want to have a "debugger has connected/disconnected" message that allows the VM to release debugger-related resources without dropping the net connection.
  • Non-DDM VMs should not freak out when DDM connects. There are no guarantees here for 3rd-party VMs (e.g. a certain mainstream VM, which crashes instantly), but our older JamVM can be configured to reject the "hello" packet.

Connection Establishment

There are two basic approaches: have the server contact the VMs, and have the VMs contact the server. The former is less "precise" than the latter, because you have to scan for the clients, but it has some advantages.

There are three interesting scenarios:

  1. The DDM server is started, then the USB-attached device is booted or the simulator is launched.
  2. The device or simulator is already running when the DDM server is started.
  3. The DDM server is running when an already-started device is attached to USB.

If we have the VMs connect to the DDM server on startup, we only handle case #1. If the DDM server scans for VMs when it starts, we only handle case #2. Neither handles case #3, which is probably the most important of the bunch as the device matures.

The plan is to have a drop-down menu with two entries, "scan workstation" and "scan device". The former causes the DDM server to search for VMs on "localhost", the latter causes it to search for VMs on the other side of an ADB connection. The DDM server will scan for VMs every few seconds, either checking a range of known VM ports (e.g. 8000-8040) or interacting with some sort of process database on the device. Changing modes causes all existing connections to be dropped.

When the DDM server first starts, it will try to execute "adb usb" to ensure that the ADB server is running. (Note it will be necessary to launch the DDM server from a shell with "adb" in the path.) If this fails, talking to the device will still be possible so long as the ADB daemon is already running.

Connecting a Debugger

With the DDM server sitting on the JDWP port of all VMs, it will be necessary to connect the debugger through the DDM server. Each VM being debugged will have a separate port being listened to by the DDM server, allowing you to connect a debugger to one or more VMs simultaneously.

In the common case, however, the developer will only want to debug a single VM. One port (say 8700) will be listened to by the DDM server, and anything connecting to it will be connected to the "current VM" (selected in the UI). This should allow developers to focus on a single application, which may otherwise shift around in the ordering, without having to adjust their IDE settings to a different port every time they restart the device.

Packet Format

Information is sent in chunks. Each chunk starts with:

u4   type
u4   length
and contains a variable amount of type-specific data. Unrecognized types cause an empty response from the client and are quietly ignored by the server. [Should probably return an error; need an "error" chunk type and a handler on the server side.]

The same chunk type may have different meanings when sent in different directions. For example, the same type may be used for both a query and a response to the query. For sanity the type must always be used in related transactions.

This is somewhat redundant with the JDWP framing, which includes a 4-byte length and a two-byte type code ("command set" and "command"; a range of command set values is designated for "vendor-defined commands and extensions"). Using the chunk format allows us to remain independent of the underlying transport, avoids intrusive integration with JDWP client code, and provides a way to send multiple chunks in a single transmission unit. [I'm taking the multi-chunk packets into account in the design, but do not plan to implement them unless the need arises.]

Because we may be sending data over a slow USB link, the chunks may be compressed. Compressed chunks are written as a chunk type that indicates the compression, followed by the compressed length, followed by the original chunk type and the uncompressed length. For zlib's deflate algorithm, the chunk type is "ZLIB".

Following the JDWP model, packets sent from the server to the client are always acknowledged, but packets sent from client to server never are. The JDWP error code field is always set to "no error"; failure responses from specific requests must be encoded into the DDM messages.

In what follows "u4" is an unsigned 32-bit value and "u1" is an unsigned 8-bit value. Values are written in big-endian order to match JDWP.

  • 1
  • 2
  • 下一页

相关内容