当前位置: 首页 > 工具软件 > KDB+ > 使用案例 >

KDB内核调试方法

燕英奕
2023-12-01

1 入门

  • KDB 项目是由 Silicon Graphics 维护的,您需要从它的 FTP 站点下载与内 核版本有关的补丁。(在编写本文时)可用的新 KDB 版本是 4.2。您将需要下 载并应用两个补丁。
  • 一个是“公共的”补丁,包含了对通用内核代码的更改,另一个是特定于体系结构的补丁。补丁可以 bz2 文件获取。例如,在运行 2.4.20 内核的 x86 机器 上,您会需要 kdb-v4.2-2.4.20-common-1.bz2 和 kdb-v4.2-2.4.20-i386-1.bz2。
  • 这里所提供的所有示例都是针对 i386 体系结构和 2.4.20 内核的。您将需要 根据您的机器和内核版本进行适当的更改。您还需要拥有 root 许可权以执行这 些操作。

将文件复制到/usr/src/linux 目录中并用 bzip2 解压缩补丁文件:
#bzip2 -d kdb-v4.2-2.4.20-common-1.bz2
#bzip2 -d kdb-v4.2-2.4.20-i386-1.bz2

  • 您将获得 kdb-v4.2-2.4.20-common-1 和 kdb-v4.2-2.4-i386-1 文件。现在,应 用这些补丁:

#patch -p1 <kdb-v4.2-2.4.20-common-1
#patch -p1 <kdb-v4.2-2.4.20-i386-1

  • 这些补丁应该干净利落地加以应用。查找任何以.rej 结尾的文件。这个扩展 名表明这些是失败的补丁。如果内核树没问题,那么补丁的应用就不会有任何问 题。
  • 接下来,需要构建内核以支持 KDB。第一步是设置 CONFIG_KDB 选项。使用您 喜欢的配置机制(xconfig 和 menuconfig 等)来完成这一步。转到结尾处的 “Kernel hacking”部分并选择“Built-in Kernel Debugger support”选项。
    您还可以根据自己的偏好选择其它两个选项。选择“Compile the kernel with frame pointers”选项(如果有的话)则设置 CONFIG_FRAME_POINTER 标志。这 将产生更好的堆栈回溯,因为帧指针寄存器被用作帧指针而不是通用寄存器。
  • 您还可以选择“KDB off by default”选项。这将设置 CONFIG_KDB_OFF 标 志,并且在缺省情况下将关闭 KDB。我们将在后面一节中对此进行详细介绍。
  • 保存配置,然后退出重新编译内核。建议在构建内核之前执行“make clean”。 用常用方式安装内核并引导它。

1.1 初始化并设置环境变量

  • 您可以定义将在 KDB 初始化期间执行的 KDB 命令。需要在纯文本文件 kdb_cmds 中定义这些命令,该文件位于 Linux 源代码树(当然是在打了补丁之 后)的 KDB 目录中。该文件还可以用来定义设置显示和打印选项的环境变量。文 件开头的注释提供了编辑文件方面的帮助。使用这个文件的缺点是,在您更改了 文件之后需要重新构建并重新安装内核。

1.2 激活 KDB

  • 如果编译期间没有选中 CONFIG_KDB_OFF,那么在缺省情况下 KDB 是活动的。 否则,您需要显式地激活它,通过在引导期间将 kdb=on 标志传递给内核或者通过在挂装了 /proc 之后执行该工作:

#echo “1” >/proc/sys/kernel/kdb

  • 倒过来执行上述步骤则会取消激活 KDB。也就是说,如果缺省情况下 KDB 是 打开的,那么将 kdb=off 标志传递给内核或者执行下面这个操作将会取消激活 KDB:

#echo “0” >/proc/sys/kernel/kdb

  • 在引导期间还可以将另一个标志传递给内核。kdb=early 标志将导致在引导 过程的初始阶段就把控制权传递给 KDB。如果您需要在引导过程初始阶段进行调 试,那么这将有所帮助。
  • 调用 KDB 的方式有很多。如果 KDB 处于打开状态,那么只要内核中有紧急情 况就自动调用它。按下键盘上的 PAUSE 键将手工调用 KDB。调用 KDB 的另一种方 式是通过串行控制台。
  • 当然,要做到这一点,需要设置串行控制台(请参阅参考资料以获取这方面 的帮助)并且需要一个从串行控制台进行读取的程序。按键序列 Ctrl-A 将从串 行控制台调用 KDB。

2 KDB 命令

  • KDB 是一个功能非常强大的工具,它允许进行几个操作,比如内存和寄存器 修改、应用断点和堆栈跟踪。根据这些,可以将 KDB 命令分成几个类别。下面是 有关每一类中常用命令的详细信息。

2.1 内核断点

  • 常用的断点命令有 bp、bc、bd、be 和 bl。 bp 命令以一个地址/符号作为参数,它在地址处应用断点。当遇到该断点时则停止执行并将控制权交予 KDB。该命令有几个有用的变体。

bp xxx:设置本CPU的软件断点,参数可以是地址、符号。
bph xxx:设置本CPU的硬件断点(命令强制在支持硬件寄存器的系统上使用它)。
bpa xxx:设置所有CPU的硬件断点。
bpha xxx:设置所有CPU的软件断点。
bd num:禁用第num断点。
be num:启用第num断点。
bl: 列出当前的断点集。它包含了启用的和禁用的断点。
bc num:命令从断点表中除去断点。它以具体的断点号或*作为参数,在后一种情 况下它将除去所有断点。

命令举例:
[0]kdb> bp ARP_Input
Instruction(i) BP #1 at 0xfbd1ab03 ([system]ARP_Input)
    is enabled globally adjust 1        addr at 00000000fbd1ab03, hardtype=0, forcehw=0, installed=0, hard=c0833b54

[0]kdb> bl
Instruction(i) BP #0 at 0xfbcc742f ([system]ARP_Entry_ProcTrapEnanle)
    is enabled globally adjust 1        addr at 00000000fbcc742f, hardtype=0, forcehw=0, installed=0, hard=c0833ab8

Instruction(i) BP #1 at 0xfbd1ab03 ([system]ARP_Input)
    is enabled globally adjust 1        addr at 00000000fbd1ab03, hardtype=0, forcehw=0, installed=0, hard=c0833b54

[0]kdb> bc 0
Breakpoint 0 at 0xfbcc742f cleared

[0]kdb> bl
Instruction(i) BP #1 at 0xfbd1ab03 ([system]ARP_Input)
    is enabled globally adjust 1        addr at 00000000fbd1ab03, hardtype=0, forcehw=0, installed=0, hard=c0833b54

2.2 单步执行

ss:执行一条指令后,将控制权交给KDB
ssb:逐条指令执行,直到遇到分支转移指令时,将控制权交给KDB

2.3 内存显示

这一类别中常用的命令是 md、mdr、mm 和 mmW。 注意设备的大小端序

1、 md addr/symbal line-count:显示从该地址开始的line-count 行的内存。如果没有指定 line-count,那么就使用环境变量所指定的缺省值。 如果没有指定地址,那么 md 就从上一次打印的地址继续。地址打印在开头,字 符转换打印在结尾。
2、md:按缺省宽度显示,32位环境4字节,64位环境8字节。
3、md1:按1字节显示。
4、md2:按2字节显示。
5、md4:按4字节显示。
6、md8:按8字节显示,仅在64位系统上支持。

[0]kdb> md g_auiARPRatelimitIf
0xfdad6780 00000012 00000013 00000015 00000044   ............D...
0xfdad6790 00000045 0000002c 00000038 00000032   E...,...8...2...
0xfdad67a0 00000058 00000016 00000017 00000019   X...............
0xfdad67b0 00000046 00000047 00000059 00000043   F...G...Y...C...
0xfdad67c0 00000079 0000007a 0000007b 0000007c   y...z...{...|...
0xfdad67d0 0000008f 00000094 0000009c 00000090   ................
0xfdad67e0 00000095 0000009d 000000b6 fd34225c   ............\"4.
0xfdad67f0 00000400 00000400 00000400 00001000   ................

7、mdWcN addr/symbal line-count:以W字节显示N个从该地址开始的line-count 行的内存。

[0]kdb> md4c2 g_auiARPRatelimitIf
0xfdad6780 00000012 00000013                     ........
[0]kdb> md4 g_auiARPRatelimitIf 1
0xfdad6780 00000012 00000013 00000015 00000044   ............D...
[0]kdb> md4c2 g_auiARPRatelimitIf 1
0xfdad6780 00000012 00000013 00000015 00000044   ............D...

2.4 内存修改

1、mm addr/symbol new-content:修改内存内容。它以地址/符号和新内容作为参数,用 new-contents 替换地址处的内容
2、mmW addr/symbol new-content:修改从地址开始的 W 个字节内存内容。它以地址/符号和新内容作为参数,用 new-contents 替换地址处的内容

[0]kdb> md g_auiARPRatelimitIf
0xfdad6780 00000012 00000013 00000015 00000044   ............D...
0xfdad6790 00000045 0000002c 00000038 00000032   E...,...8...2...
0xfdad67a0 00000058 00000016 00000017 00000019   X...............
0xfdad67b0 00000046 00000047 00000059 00000043   F...G...Y...C...
0xfdad67c0 00000079 0000007a 0000007b 0000007c   y...z...{...|...
0xfdad67d0 0000008f 00000094 0000009c 00000090   ................
0xfdad67e0 00000095 0000009d 000000b6 fd34225c   ............\"4.
0xfdad67f0 00000400 00000400 00000400 00001000   ................
[0]kdb> mm g_auiARPRatelimitIf 0
0xfdad6780 = 0x0
[0]kdb> md g_auiARPRatelimitIf
0xfdad6780 00000000 00000013 00000015 00000044   ............D...
0xfdad6790 00000045 0000002c 00000038 00000032   E...,...8...2...
0xfdad67a0 00000058 00000016 00000017 00000019   X...............
0xfdad67b0 00000046 00000047 00000059 00000043   F...G...Y...C...
0xfdad67c0 00000079 0000007a 0000007b 0000007c   y...z...{...|...
0xfdad67d0 0000008f 00000094 0000009c 00000090   ................
0xfdad67e0 00000095 0000009d 000000b6 fd34225c   ............\"4.
0xfdad67f0 00000400 00000400 00000400 00001000   ................
[0]kdb> mm4 g_auiARPRatelimitIf 0x11
0xfdad6780 = 0x11
[0]kdb> md g_auiARPRatelimitIf
0xfdad6780 00000011 00000013 00000015 00000044   ............D...
0xfdad6790 00000045 0000002c 00000038 00000032   E...,...8...2...
0xfdad67a0 00000058 00000016 00000017 00000019   X...............
0xfdad67b0 00000046 00000047 00000059 00000043   F...G...Y...C...
0xfdad67c0 00000079 0000007a 0000007b 0000007c   y...z...{...|...
0xfdad67d0 0000008f 00000094 0000009c 00000090   ................
0xfdad67e0 00000095 0000009d 000000b6 fd34225c   ............\"4.
0xfdad67f0 00000400 00000400 00000400 00001000   ................
[0]kdb> mm1 g_auiARPRatelimitIf 0x11
0xfdad6780 = 0x11
[0]kdb> md g_auiARPRatelimitIf
0xfdad6780 00000011 00000013 00000015 00000044   ............D...
0xfdad6790 00000045 0000002c 00000038 00000032   E...,...8...2...
0xfdad67a0 00000058 00000016 00000017 00000019   X...............
0xfdad67b0 00000046 00000047 00000059 00000043   F...G...Y...C...
0xfdad67c0 00000079 0000007a 0000007b 0000007c   y...z...{...|...
0xfdad67d0 0000008f 00000094 0000009c 00000090   ................
0xfdad67e0 00000095 0000009d 000000b6 fd34225c   ............\"4.
0xfdad67f0 00000400 00000400 00000400 00001000   ................
[0]kdb> mm8 g_auiARPRatelimitIf 0x11
kdb_putword: bad width 8
diag: -3: Illegal value for BYTESPERWORD use 1, 2, 4 or 8, 8 is only allowed on 64 bit systems
[0]kdb> mm2 g_auiARPRatelimitIf 0x11
0xfdad6780 = 0x11
[0]kdb> md2 g_auiARPRatelimitIf
0xfdad6780 0011 0000 0013 0000 0015 0000 0044 0000   ............D...
0xfdad6790 0045 0000 002c 0000 0038 0000 0032 0000   E...,...8...2...
0xfdad67a0 0058 0000 0016 0000 0017 0000 0019 0000   X...............
0xfdad67b0 0046 0000 0047 0000 0059 0000 0043 0000   F...G...Y...C...
0xfdad67c0 0079 0000 007a 0000 007b 0000 007c 0000   y...z...{...|...
0xfdad67d0 008f 0000 0094 0000 009c 0000 0090 0000   ................
0xfdad67e0 0095 0000 009d 0000 00b6 0000 225c fd34   ............\"4.
0xfdad67f0 0400 0000 0400 0000 0400 0000 1000 0000   ................

2.5 寄存器显示和修改

这一类别中的命令有 rd、rm 和 ef。注意:不同的处理器都有自己一套独立的寄存器,举例仅供参考

1、rd [ c | d | u ]:显示处理器寄存器的内容。

c:显示处理器的控制寄存器
d :显示调试寄存器
u:显示上一次进入内核的当前任务的寄存器组

[0]kdb> rd
ax = 0xc04034ba bx = 0xc0403510 cx = 0x00000000 dx = 0x00004b7b
si = 0x00000000 di = 0xc07bc800 sp = 0xc075ffb8 ip = 0xc0403546
bp = 0xc075ffb8 ss = 0xc04034ba cs = 0x00000060 flags = 0x00000246
ds = 0xc044007b es = 0xc040007b origax = 0xfffffffc &regs = 0xc075ff80

[0]kdb> rd c
cr0 = 0x8005003b  cr1 = 0x00000000  cr2 = 0xb0ea6000  cr3 = 0x1d552000
cr4 = 0x000006f0

[0]kdb> rd d
dr0 = 0x00000000  dr1 = 0x00000000  dr2 = 0x00000000  dr3 = 0x00000000
dr6 = 0xffff0ff0  dr7 = 0x00000400

[0]kdb> rd u
ax = 0xc078491c bx = 0x00000020 cx = 0x0008f800 dx = 0xc074b000
si = 0xc075ffd4 di = 0xc0620a7e sp = 0x010b4007 ip = 0x00020800
bp = 0xc075fff0 ss = 0x00000000 cs = 0xc075fff8 flags = 0xc0784008
ds = 0xc0688495 es = 0xc07843ef origax = 0xc07a5c20 &regs = 0xc075ffc0

2、rm reg new-contents:修改名为reg的寄存内容为new-contents,寄存器名称与特定的体系结构有关。目前,不能修改控制寄存器。
3、ef addr:显示指定地址处的异常帧

[0]kdb> ef 0xfdad6780
ax = 0x00000038 bx = 0x00000011 cx = 0x00000013 dx = 0x00000015
si = 0x00000044 di = 0x00000045 sp = 0x00000059 ip = 0x00000019
bp = 0x0000002c ss = 0x00000043 cs = 0x00000046 flags = 0x00000047
ds = 0x00000032 es = 0x00000058 origax = 0x00000017 &regs = 0xfdad6780

2.6 堆栈跟踪

主要的堆栈跟踪命令有 bt、btp、btc 和 bta。

bt :设法提供有关当前线程的堆栈的信息,它可以有选择地将堆栈帧地址 作为参数。如果没有提供地址,那么它采用当前寄存器来回溯堆栈。否则,它假 定所提供的地址是有效的堆栈帧起始地址并设法进行回溯。如果内核编译期间设 置了 CONFIG_FRAME_POINTER 选项,那么就用帧指针寄存器来维护堆栈,从而就 可以正确地执行堆栈回溯。如果没有设置 CONFIG_FRAME_POINTER,那么 bt 命令 可能会产生错误的结果。

[0]kdb> bp ARP_Entry_ProcTrapEnanle
Instruction(i) BP #0 at 0xfbcc742f ([system]ARP_Entry_ProcTrapEnanle)
    is enabled globally adjust 1        addr at 00000000fbcc742f, hardtype=0, forcehw=0, installed=0, hard=c0833ab8

[0]kdb> go
Instruction(i) breakpoint #0 at 0xfbcc742f (adjusted)
0xfbcc742f ARP_Entry_ProcTrapEnanle:         int3

Entering kdb (current=0xc9e24000, pid 261) on processor 0 due to Breakpoint @ 0xfbcc742f
[0]kdb> bt
Stack traceback for pid 261
0xc9e24000    261      2  1   0  R  0xc9e24328      0K *[karp/1]
sp        ip        Function (args)
0xcb957d40 0xfbcc742f [system]ARP_Entry_ProcTrapEnanle (0x0)
0xcb957d4c 0xfbd34ade [system]arp_TrapProcMsg+0x80 (0xf5e2b7a8, 0xc763df86, 0x18)
0xcb957d78 0xfc2aa5b4 [system]arp_ProcCliSynMsg+0x56 (0xf5e2b7a8, 0xc763df86, 0xc7630018, 0x1)
0xcb957d9c 0xfbca10a4 [system]ADJ_SYNC_DispatchMsg+0x1e6 (0xf5e2b7a8, 0xf3335c00, 0xfdb3be80, 0x4b)
0xcb957e0c 0xfbcec14b [system]arp_DispatchLipcMsg+0x23 (0xf5e2b7a8, 0xf3335c00)
0xcb957e20 0xfbcec195 [system]arp_RecvMsgFromUsr+0x48 (0xf5e2b7a8, 0xf3335c00)
0xcb957e34 0xfb6e9a67 [system]sync_UC_DataProc+0xe1 (0x1, 0xf5e2b7a8)
0xcb957e80 0xfb6e9238 [system]sync_EpollProc+0xae (0x1, 0xf5e2c5a0)
0xcb957eb8 0xfbca4fea [system]arp_Thread+0xed (0x0)
0xcb957fa8 0xc0440244 kthread+0xd4 (0xc4b27e38)
0xcb957fe4 0xc0401daf kernel_thread_helper+0x7 (invalid, invalid)
[0]kdb>

2、btc :对每个活动 CPU 上正在运行的进程执行堆栈回溯

[0]kdb> btc
btc: cpu status: Currently on cpu 0
Available cpus: 0, 1(I)
Stack traceback for pid 261
0xc9e24000    261      2  1   0  R  0xc9e24328      0K *[karp/1]
sp        ip        Function (args)
0xcb957d40 0xfbcc742f [system]ARP_Entry_ProcTrapEnanle (0x0)
0xcb957d4c 0xfbd34ade [system]arp_TrapProcMsg+0x80 (0xf5e2b7a8, 0xc763df86, 0x18)
0xcb957d78 0xfc2aa5b4 [system]arp_ProcCliSynMsg+0x56 (0xf5e2b7a8, 0xc763df86, 0xc7630018, 0x1)
0xcb957d9c 0xfbca10a4 [system]ADJ_SYNC_DispatchMsg+0x1e6 (0xf5e2b7a8, 0xf3335c00, 0xfdb3be80, 0x4b)
0xcb957e0c 0xfbcec14b [system]arp_DispatchLipcMsg+0x23 (0xf5e2b7a8, 0xf3335c00)
0xcb957e20 0xfbcec195 [system]arp_RecvMsgFromUsr+0x48 (0xf5e2b7a8, 0xf3335c00)
0xcb957e34 0xfb6e9a67 [system]sync_UC_DataProc+0xe1 (0x1, 0xf5e2b7a8)
0xcb957e80 0xfb6e9238 [system]sync_EpollProc+0xae (0x1, 0xf5e2c5a0)
0xcb957eb8 0xfbca4fea [system]arp_Thread+0xed (0x0)
0xcb957fa8 0xc0440244 kthread+0xd4 (0xc4b27e38)
0xcb957fe4 0xc0401daf kernel_thread_helper+0x7 (invalid, invalid)
Stack traceback for pid 0
0xf003e900      0      1  1   1  I  0xf003ec28      0K  [swapper]
sp        ip        Function (args)
0xf008ff78 0xc0403546 default_idle+0x36
0xf008ff88 0xc04034ba cpu_idle+0x6a
[0]kdb>

3、btp pid-num :对指定进程号为pid-num的进程执行回溯。
4、bta :对处于某种特定状态的所有进程执行回溯。若不带任何参数,它就 对所有进程执行回溯。可以有选择地将各种参数传递给该命令。将根据参数处理 处于特定状态的进程。
选项以及相应的状态如下:

D:不可中断状态
R:正运行
S:可中断休眠
T:已跟踪或已停止
Z:僵死
U:不可运行
其他有关bt命令的介绍参考手册介绍

2.7 反汇编

1、id addr/symbal:从指定地址/符号位置开始反汇编,如果没指定参数,则从当前位置开始。
2、set IDCOUNT num:设置一次反汇指令函数为num(行环境变量 IDCOUNT 确定要显示多少行输出)

2.8 其它常用命令

1、go addr:转到指定符号或地址继续执行,若没有参数,则从当前位置继续执行。指定地址需要谨慎,容易造成设备崩溃
2、cpu num:切换到指定的cpu上下文。
3、dmesg:显示缓存记录的信息。
4、bpa addr dataw 4:4字节写入指定地址时断下
5、bpa addr datar 8:8字节读取指定地址时断下

3 技巧和诀窍

调试一个问题涉及到:使用调试器(或任何其它工具)找到问题的根源以及 使用源代码来跟踪导致问题的根源。单单使用源代码来确定问题是极其困难的, 只有老练的内核黑客才有可能做得到。相反,大多数的新手往往要过多地依靠调 试器来修正错误。
这种方法可能会产生不正确的问题解决方案。我们担心的是这种方法只会修 正表面症状而不能解决真正的问题。此类错误的典型示例是添加错误处理代码以 处理 NULL 指针或错误的引用,却没有查出无效引用的真正原因。
结合研究代码和使用调试工具这两种方法是识别和修正问题的佳方案。
调试器的主要用途是找到错误的位置、确认症状(在某些情况下还有起因)、 确定变量的值,以及确定程序是如何出现这种情况的(即,建立调用堆栈)。有 经验的黑客会知道对于某种特定的问题应使用哪一个调试器,并且能迅速地根据 调试获取必要的信息,然后继续分析代码以识别起因。
因此,这里为您介绍了一些技巧,以便您能使用 KDB 快速地取得上述结果。 当然,要记住,调试的速度和精确度来自经验、实践和良好的系统知识(硬件和 内核内部机理等)。

3.1 技巧 1

在 KDB 中,在提示处输入地址将返回与之为匹配的符号。这在堆栈分析以 及确定全局数据的地址/值和函数地址方面极其有用。同样,输入符号名则返回 其虚拟地址。

 示例 表明函数 sys_read 从地址 0xc013db4c 开始: 
 [0]kdb> 0xc013db4c 
 [0]kdb> 0xc013db4c = 0xc013db4c (sys_read) 
 同样,表明 sys_write 位于地址 0xc013dcc8:  
 [0]kdb> sys_write 
 [0]kdb> sys_write = 0xc013dcc8 (sys_write) 
 这些有助于在分析堆栈时找到全局数据和函数地址。 

3.2 技巧 2

在编译带 KDB 的内核时,只要 CONFIG_FRAME_POINTER 选项出现就使用该选 项。为此,需要在配置内核时选择“Kernel hacking”部分下面的“Compile the kernel with frame pointers”选项。
这确保了帧指针寄存器将被用作帧指针,从而产生正确的回溯。实际上,您 可以手工转储帧指针寄存器的内容并跟踪整个堆栈。例如,在 i386 机器上,%ebp 寄存器可以用来回溯整个堆栈。

例如,在函数 rmqueue()上执行第一个指令后,堆栈看上去类似于下面这样: 
[0]kdb> md %ebp 
0xc74c9f38 c74c9f60 c0136c40 000001f0 00000000 
0xc74c9f48 08053328 c0425238 c04253a8 00000000 
0xc74c9f58 000001f0 00000246 c74c9f6c c0136a25 
0xc74c9f68 c74c8000 c74c9f74 c0136d6d c74c9fbc 
0xc74c9f78 c014fe45 c74c8000 00000000 08053328 

[0]kdb> 0xc0136c40 
[0]kdb> 0xc0136c40 = 0xc0136c40 (__alloc_pages +0x44) 

[0]kdb> 0xc0136a25 
0xc0136a25 = 0xc0136a25 (_alloc_pages +0x19) 
 
[0]kdb> 0xc0136d6d 
0xc0136d6d = 0xc0136d6d (__get_free_pages +0xd) 
我们可看到rmqueue()被__alloc_pages调用,后者接下来又被_alloc_pages 调用,以此类推。 每一帧的第一个双字(double word)指向下一帧,这后面紧跟着调用函数 的地址。因此,跟踪堆栈就变成一件轻松的工作了。 

3.3 技巧 3

go 命令可以有选择地以一个地址作为参数。如果您想在某个特定地址处继续 执行,则可以提供该地址作为参数。另一个办法是使用 rm 命令修改指令指针寄 存器,然后只要输入 go。如果您想跳过似乎会引起问题的某个特定指令或一组 指令,这就会很有用。但是,请注意,该指令使用不慎会造成严重的问题,系统 可能会严重崩溃。

3.4 技巧 4

您可以利用一个名为 defcmd 的有用命令来定义自己的命令集。例如,每当 遇到断点时,您可能希望能同时检查某个特殊变量、检查某些寄存器的内容并转 储堆栈。通常,您必须要输入一系列命令,以便能同时执行所有这些工作。
defcmd 允许您定义自己的命令,该命令可以包含一个或多个预定义的 KDB 命令。然后只需要用一个命令就可以完成所有这三项工作。

其语法如下: 
[0]kdb> defcmd name "usage" "help" 
[0]kdb> [defcmd] type the commands here 
[0]kdb> [defcmd] endefcmd 

例如,可以定义一个(简单的)新命令 sjj,它显示从地址0xc0430b15开始 的2行内存、显示寄存器的内容并转储堆栈:

[0]kdb> defcmd sjj "" "no arguments needed"
[0]kdb> [defcmd]md 0xc0430b15 2
[0]kdb> [defcmd]rd
[0]kdb> [defcmd]md %cs 1
[0]kdb> [defcmd]endefcmd
[0]kdb> sjj
[sjj]kdb> md 0xc0430b15 2

0xc0430b14 00768dff 9090d3eb 90909090 57e58955   ..v.........U..W
0xc0430b24 c3895356 b804ec83 00000001 00158b64   VS..........d...
[sjj]kdb> rd

ax = 0xc0430b15 bx = 0x00000002 cx = 0x00000100 dx = 0x01ed8000
si = 0x00000000 di = 0x00000000 sp = 0xc075ff18 ip = 0xc04309ad
bp = 0xc075ff40 ss = 0xc075ff28 cs = 0x00000060 flags = 0x00000246
ds = 0x00c3007b es = 0xc043007b origax = 0xfffffffc &regs = 0xc075fee0
[sjj]kdb> md %cs 1

kdb_getarea: Bad address 0x60
diag: -21: Invalid address
[0]kdb>

3.5 技巧 5

可以使用 bph 和 bpha 命令(假如体系结构支持使用硬件寄存器)来应用读 写断点。这意味着每当从某个特定地址读取数据或将数据写入该地址时,我们都可以对此进行控制。当调试数据/内存毁坏问题时这可能会极其方便,在这种情 况中您可以用它来识别毁坏的代码/进程。

示例 
每当将四个字节写入地址 0xc0204060 时就进入内核调试器:
[0]kdb> bph 0xc0204060 dataw 4 
在读取从 0xc000000 开始的至少两个字节的数据时进入内核调试器:
[0]kdb> bph 0xc000000 datar 2 

4 结束语

对于执行内核调试,KDB 是一个方便的且功能强大的工具。它提供了各种选 项,并且使我们能够分析内存内容和数据结构。妙的是,它不需要用另一台机 器来执行调试。

 类似资料: