使用 JDB 调试 Android 应用程序

By Huang Tao of TinyLab.org

2015/04/13

前言

自从有了各种 IDE 工具,程序猿调试工作轻松了不少,只要在 IDE 上面点击两下按钮,各种程序运行时的信息全部都显示在屏幕上面,很美好的一件事情,我们都要感谢开发这些 IDE 工具的前辈,是他们让我们的工作变得这么“轻松简单”,但是对于我个人来说,不是很喜欢这些 IDE 工具:

  • 第一是因为这类 IDE 工具实在是变化太快,我们要花费很大的时间成本来学习这一类工具,然而当你好不容易熟悉了一种工具之后,别人又出了一种更牛 B 的工具,谷歌从 ADT 切换到 Android Studio 就是如此。
  • 第二是因为使用这类工具过程中一旦遇到问题,或者想要增加一种功能往往会使人们不知所措,给人的感觉不够灵活。

所以相比使用 IDE 工具来说,我比较倾向于使用命令行工具,虽然原始了一点,但是从里面我们可以学到很多东西,使用起来也更加灵活,今天我们要讲的 JDB 就是一种这样的命令行工具,目前大多数程序员在调试 Android 应用程序的时候,大多选择的是 ADT 和 Android Studio,这两个 IDE 已经为我们集成了很多调试的功能,像打断点、单步调试、dump 虚拟机的堆栈信息等,这些工具很强大,是我们开发过程中不可缺少的,但是有没有想过他们是怎么做到的呢?其实他们也是利用了类似 JDB 的功能,然后以可视化界面显示在人们面前。

JDWP 协议介绍

首先让我们认识一下什么是 JDWP(Java调试线协议),说白了就是 JVM 或者类 JVM 的虚拟机都支持一种协议,通过该协议,Debugger 端可以和目标 VM 通信,可以获取目标 VM 的包括类、对象、线程等信息,在调试 Android 应用程序这一场景中 Debugger 一般是指你的 develop machine 的某一支持 JDWP 协议的工具例如 Android Studio 或者 JDB,而 Target JVM 是指运行在你 mobile 设备当中的各个 App(因为它们都是一个个虚拟机 Dalvik 或者 ART),JDWP Agent一般负责监听某一个端口,当有 Debugger 向这一个端口发起请求的时候,Agent 就转发该请求给 Target JVM 并最终由该 JVM 来处理请求,并把 reply 信息返回给 Debugger 端。

上面这个图是借用别人说明 JVM 的,针对 Android 来说可能不是特别准确,我们来看一下 Android 上面是什么情况,调试的时候我们一般通过 ADB 来连接移动设备,所以上面的 JDWP Agent 在 Android 手机上应该是指 adbd 进程,接着上图:

上图说明了使用 DDMS 来跟 App VMs 通信的流程,关于 adb 的使用说明,这里就不详细展开了,可以参见 Google 官方文档。

再来唠叨一下 JDWP 协议的报文格式,JDWP 协议中主要有两种报文:Command packet 和 Reply packet,command packet 就是我们上面所说的请求报文,reply 自然就是对 command 的回答。

JDWP Packet 分为包头(header)和数据(data)两部分组成。包头部分的结构和长度是固定,而数据部分的长度是可变的,具体内容视 packet 的内容而定。Command packet 和 reply packet 的包头长度相同,都是 11 个 bytes.

  1. Command packet 的 header 的结构

    • Length

      是整个 packet 的长度,包括 length 部分。因为包头的长度是固定的 11 bytes,所以如果一个 command packet 没有数据部分,则 length 的值就是 11。

    • Id

      是一个唯一值,用来标记和识别 reply 所属的 command。Reply packet 与它所回复的 command packet 具有相同的 Id,异步的消息就是通过 Id 来配对识别的。

    • Flags

      目前对于 command packet 值始终是 0。

    • Command Set

    相当于一个 command 的分组,一些功能相近的 command 被分在同一个 Command Set 中。Command Set 的值被划分为 3 个部分:

    • 0-63: 从 debugger 发往 target Java 虚拟机的命令
    • 64 – 127: 从 target Java 虚拟机发往 debugger 的命令
    • 128 – 256: 预留的自定义和扩展命令
  2. Reply packet 的 header 的结构

    • Length、Id 作用与 command packet 中的一样。
    • Flags

      目前对于 reply packet 值始终是 0x80。我们可以通过 Flags 的值来判断接收到的 packet 是 command 还是 reply。

    • Error Code

      用来表示被回复的命令是否被正确执行了。零表示正确,非零表示执行错误。

Data 的内容和结构依据不同的 command 和 reply 都有所不同。比如请求一个对象成员变量值的 command,它的 data 中就包含该对象的 id 和成员变量的 id。而 reply 中则包含该成员变量的值。

JDB 的使用方式

上面说了这么多,其实都是为了讲 JDB 的使用原理做的铺垫,JDB 其实是 JDWP 协议中所讲的 Debugger,它运行在 develop machine 上面,它和移动设备上面的 App VMs 通过 JDWP 协议来通信,JDB 一般位于你的 JDK 安装目录下面,可以直接运行,因为 JDB 和移动设备必须通过 ADB 来沟通,所以在 Android 上面使用 JDB 之前必须做一些配置:

  1. 通过 adb jwdp 列出移动设备上面可以执行 JDWP 协议的进程 ID。
  2. 通过 adb forward tcp:123456 jdwp:pid (第一步所得到的 PID )设置使用 123456 端口来和移动设备上面的App VMs(其实是 adbd)来通信。
  3. 执行 jdb -attach localhost:123456 将 jdb attach 到本机的 123456 端口。

这样一个 JDB 到移动设备 App VMs 的连接就成了,可以使用 JDB 提供的各种命令来和 App VMs 交互。

JDB 的使用示例

  1. 使用 adb shell ps | grep com.android.settings 来得到 settings 进程的 pid 号为 3107
  2. 执行 adb forward tcp:12345 jdwp:3107
  3. 执行 jdb -attach localhost:12345

    执行完上面三步之后,jdb 与设置 App 之间的连接就建立好了。

  4. 执行 jdb 命令 classes,得到设置 App 当中所有的类列表。

  5. 我们感兴趣的是 com.android.setting.Settings 这个类,所以我们继续使用 jdb 命令 methods 来查看这个类拥有哪一些方法。

  6. 假设我们想在 com.android.setting.Settings 这个类的 onCreate 这个方法中添加断点,那么我们执行 stop in com.android.setting.Settings.onCreate(android.os.Bundle) 在这个方法中设置断点,然后我们打开设置 app,jdb 会提示我们断点命中,同时告知我们哪个线程,具体的方法、哪一行等信息。
  7. 执行 next 命令,使代码执行到下一行。
  8. 执行 step 命令,使代码单步执行。
  9. 执行 run 命令,使程序跳过断点继续执行。

JDB 的命令列表

* 命令列表 *

connectors – 列出此 VM 中可用的连接器和传输

run [class [args]] – 开始执行应用程序的主类

threads [threadgroup] – 列出线程

thread – 设置默认线程

suspend [thread id(s)] – 挂起线程 (默认值: all)

resume [thread id(s)] – 恢复线程 (默认值: all)

where [ | all] – 转储线程的堆栈

wherei [ | all]– 转储线程的堆栈, 以及 pc 信息

up [n frames] – 上移线程的堆栈

down [n frames] – 下移线程的堆栈

kill – 终止具有给定的异常错误对象的线程

interrupt – 中断线程

print – 输出表达式的值

dump – 输出所有对象信息

eval – 对表达式求值 (与 print 相同)

set = – 向字段/变量/数组元素分配新值

locals – 输出当前堆栈帧中的所有本地变量

classes – 列出当前已知的类

class – 显示已命名类的详细资料

methods – 列出类的方法

fields – 列出类的字段

threadgroups – 列出线程组

threadgroup – 设置当前线程组

stop in .[(argument_type,…)] – 在方法中设置断点

stop at : – 在行中设置断点

clear .[(argument_type,…)] – 清除方法中的断点

clear : – 清除行中的断点

clear – 列出断点

catch [uncaught|caught|all] | – 出现指定的异常错误时中断

ignore [uncaught|caught|all] | – 对于指定的异常错误, 取消 ‘catch’

watch [access|all] . – 监视对字段的访问/修改

unwatch [access|all] . – 停止监视对字段的访问/修改

trace [go] methods [thread] – 跟踪方法进入和退出。 – 除非指定 ‘go’, 否则挂起所有线程

trace [go] method exit | exits [thread] – 跟踪当前方法的退出, 或者所有方法的退出 – 除非指定 ‘go’, 否则挂起所有线程

untrace [methods] – 停止跟踪方法进入和/或退出

step – 执行当前行

step up – 一直执行, 直到当前方法返回到其调用方

stepi – 执行当前指令

下一步 – 步进一行 (步过调用)

cont – 从断点处继续执行

list [line number|method] – 输出源代码

use (或 sourcepath) [source file path] – 显示或更改源路径

exclude [, … | “none”] – 对于指定的类, 不报告步骤或方法事件

classpath – 从目标 VM 输出类路径信息

monitor – 每次程序停止时执行命令

monitor – 列出监视器

unmonitor

参考资料

时间: 2024-10-13 00:08:36

使用 JDB 调试 Android 应用程序的相关文章

Eclipse替代ndk-gdb断点调试Android NDK程序

一 概述: Eclipse调试NDK程序的操作并不难,难的是环境的搭建.光是安装Eclipse就有不少问题,真是"大错不犯,小过不断"不胜期烦.在网上,已经有先行者记录下了Eclipse断点调试Android NDK程序的方法: <Eclipse + ADT(包括NDK Plugin) + CDT 搭建Android NDK开发环境>网址:http://blog.sina.com.cn/s/blog_48ed03c80101nhei.html 我遇到的情况与上文有些不同,上

Eclipse+CDT+GDB调试android NDK程序(转)

Eclipse+CDT+gdb调试android ndk程序 先介绍一下开发环境,在这个环境下,up主保证是没有问题的. ubuntu 11.10 eclipse 3.7(indego) for java jdk 6 android sdk 2.2 andrid ndk r7 当然,在windows环境下通过cygwin等工具也是可以实现gdb调试的,我也确实实现过.但是性能实在太低,卡的根本没法用.Linux下直接用gdb调试本地方法是很流畅的. 再确定安装并配置好开发环境之后,就可以开始了.

gdb 远程调试 android native 程序

先看一张原理图: 我是 Linux 和 Android 双料 0 基础,目前对 gdb 了解的很浅显.(注意:在 Android 上执行的命令,需要在 adb shell 下执行.) ①      打开命令终端(Ctrl + Alt + T),输入 adb shell.在 Android 的 data 目录下新建一个目录,如 dog_ld. ②      将 gdbserver 和要调试的程序从 Linux 拷贝到 Android 的 /data/dog_ld 目录. ③      进入 And

如何在真机上调试Android应用程序(图文详解)(zz)

http://www.cnblogs.com/lanxuezaipiao/archive/2013/03/11/2953564.html 1.首先将手机设置为调试模式 方法:设置——应用程序——开发——USB调试,打上√即可 2.用数据线连接至电脑,在电脑上安装豌豆荚,此时豌豆荚会帮你安装驱动,安装好后豌豆荚就可以连接上手机了 3.用adb命令测试是否有装置已连接 命令:adb devices 看到已经有一个装置了,即为我们连接的真机 注意:有的人可能提示找不到这个adb命令,这是因为你没有将其

如何在真机上调试Android应用程序(图文详解)

1.首先将手机设置为调试模式 方法:设置——应用程序——开发——USB调试,打上√即可 2.用数据线连接至电脑,在电脑上安装豌豆荚,此时豌豆荚会帮你安装驱动,安装好后豌豆荚就可以连接上手机了 3.用adb命令测试是否有装置已连接 命令:adb devices 看到已经有一个装置了,即为我们连接的真机 注意:有的人可能提示找不到这个adb命令,这是因为你没有将其加入到path环境变量中,或者你进入sdk下的tools目录在运行此命令就不会报错,或者将tools路径加入到环境变量中,当然推荐第二种方

用GDB远程调试android native程序

上次写了几个native程序,想着如何调试,经过一阵子搜索和测试,终于完成了.有几个关键点: 1 gdb和gdbserver 因为这两个需要配套,建议使用同一个ndk下面的gdb和gdbserver gdbserver的是在 android-ndk-r9\prebuilt\android-arm\gdbserver 这个目录下 gdb 是在 android-ndk-r9\toolchains\arm-linux-androideabi-4.8\prebuilt\windows-x86_64\bi

Android应用程序用真机调试步骤

仅供参考: 1.开启调试模式 2.安装 Adb.exe 将platform-tools文件夹里面adb.exe AdbWinApi.dll AdbWinUsbApi.dll拷贝到tools 在tools 文件目录下执行安装命令:adb install 3.查看设备是否安装,用adb devices命令 C:\Windows\system32>adb devices List of devices attached 000e91eb75d68f  device device标识链接手机成功. *前

调试 Android* x86 应用程序的方法以及要使用的工具

作者:Xiaodong Wang 1.简介 众所周知,Android* 开发人员头顶许多称呼:设计员.程序猿等,并且通常会不可避免地被称为故障检修工.代码中的错误无法避免,因此无论您是否一开始就造成错误,了解调试工具以及如何迅速而有效地跟踪并解决错误都很重要.鉴于此,如今的 Android 开发人员必须掌握有效的调试技巧.本文提供了 Android 应用程序调试工具的简单教学,用于帮助 Android SDK 以及相关工具的新手迅速入门,并在 Android x86 平台上更有效地解决故障. 2

真机在wifi下调试android程序

大家好,最近在学习android程序由于手机接口问题,调试程序的时候老是接触不良而不能正常调试,因此感到相当苦恼,于是在网上查找无线调试android的方法.经过研究和尝试现已成功无线调试程序,方法分享如下: 1. 手机是否具备条件 1)手机一定要root才可以哦,如果没有root请不要往下走. 2)手机和开发电脑在同一局域网内. 2. 手机上安装调试必备软件 1)在手机软件市场找到AdbWireless软件并安装. 2)安装之后如下图: 3)点击开启开关如下图: 3. 电脑上连接手机 1)在c