linux内核单步调试,linux内核调试之KDB(1)

说到linux调试人们第一想到的就是GDB,GDB是GNU开源组织发布的一个强大的UNIX下的程序调试工具,想必大家都比较熟悉,但是在调试模块时缺少一些至关重要的功能,它可用来查看内核的运行情况,包括反汇编内核函数,可是我们一般调试内核问题不会用GDB,因为还有更多更适合调试内核的工具等着我们。

KDB:很好的内核调试工具,优点是不需要两台机器进行调试,缺点是只能在汇编代码级进行调试,看不到C源代码,需要一定的汇编基础。但我要说的是这点小障碍难得住我们?

(PS:KDB项目是由Silicon Graphics维护的,您需要从它的ftp站点下载与内核版本有关的补丁。据我所知目前支持x86和mips等体系结构。需要指出的是官方linux内核不自带kdb,kdb相当于内核补丁,是需要我们自己添加安装,这里不是重点,度娘上面很多了)

KGDB:优点是能很方便的在源码级对内核进行调试,缺点是需要两台机器,只能进行远程调试,它需要一根串口线及两台机器来调试内核(也可以是在同一台主机上用vmware软件运行两个操作系统来调试)。

Kprobes:提供了一个强行进入任何内核例程,并从中断处理器无干扰地收集信息的接口。使用 Kprobes 可以轻松地收集处理器寄存器和全局数据结构等调试信息,而无需对Linux内核频繁编译和启动。这个调试工具没用过,不是很清楚。

当然printk,内核转储也是不可缺少的内核调试技能。

这里着重介绍kdb的使用,kdb调试需要一定的汇编知识,这里以mips体系结构为例,列出mips汇编指令(详细了解mips汇编可参考《see mips run linux》)

76ccd52aab22b2ed164f9578362c3133.png

当然我们还要了解一下kdb的调试命令

内存显示和修改这一类别中最常用的命令是md、mdr、mm和mmW。常用的是md和mm

md命令以一个地址/符号和行计数为参数,显示从该地址开始的line-count行的内存。如果没有指定line-count,那么就使用环境变量所指定的缺省值。如果没有指定地址,那么md就从上一次打印的地址继续。地址打印在开头,字符转换打印在结尾。

mdr命令带有地址/符号以及字节计数,显示从指定的地址开始的byte-count字节数的初始内存内容。它本质上和md一样,但是它不显示起始地址并且不在结尾显示字符转换。mdr命令较少使用。

mm命令修改内存内容。它以地址/符号和新内容作为参数,用new-contents替换地址处的内容。

mmW命令更改从地址开始的W个字节。请注意,mm更改一个机器字。

示例

显示从0xc000000开始的15行内存:[0]kdb> md 0xc000000 15

将内存位置为0xc000000上的内容更改为0x10:[0]kdb> mm 0xc000000 0x10

寄存器显示和修改这一类别中的命令有rd、rm和ef。常用的是rd。

rd命令(不带任何参数)显示处理器寄存器的内容。它可以有选择地带三个参数。如果传递了c参数,则rd显示处理器的控制寄存器;如果带有d参数,那么它就显示调试寄存器;如果带有u参数,则显示上一次进入内核的当前任务的寄存器组。

rm命令修改寄存器的内容。它以寄存器名称和new-contents作为参数,用new-contents修改寄存器。寄存器名称与特定的体系结构有关。目前,不能修改控制寄存器。

ef命令以一个地址作为参数,它显示指定地址处的异常帧。

示例

显示通用寄存器组:[0]kdb> rd

将寄存器ebx的内容设置成0x25:[0]kdb> rm %ebx 0x25

断点常用的断点命令有bp、bc、bd、be和bl。

bp命令以一个地址/符号作为参数,它在地址处应用断点。当遇到该断点时则停止执行并将控制权交予KDB。该命令有几个有用的变体。bpa命令对SMP系统中的所有处理器应用断点。bph命令强制在支持硬件寄存器的系统上使用它。bpha命令类似于bpa命令,差别在于它强制使用硬件寄存器。

bd命令禁用特殊断点。它接收断点号作为参数。该命令不是从断点表中除去断点,而只是禁用它。断点号从0开始,根据可用性顺序分配给断点。

be命令启用断点。该命令的参数也是断点号。

bl命令列出当前的断点集。它包含了启用的和禁用的断点。

bc命令从断点表中除去断点。它以具体的断点号或*作为参数,在后一种情况下它将除去所有断点。

示例

对函数sys_write()设置断点:[0]kdb> bp sys_write

列出断点表中的所有断点:[0]kdb> bl

清除断点号1:[0]kdb> bc 1

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

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

btp命令将进程标识作为参数,并对这个特定进程进行堆栈回溯。

btc命令对每个活动CPU上正在运行的进程执行堆栈回溯。它从第一个活动CPU开始执行bt,然后切换到下一个活动CPU,以此类推。

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

·D:不可中断状态

·R:正运行

·S:可中断休眠

·T:已跟踪或已停止

·Z:僵死

·U:不可运行

这类命令中的每一个都会打印出一大堆信息。请查阅下面的参考资料以获取这些字段的详细文档。

示例

跟踪当前活动线程的堆栈:[0]kdb> bt

跟踪标识为575的进程的堆栈:[0]kdb> btp 575

其它命令下面是在内核调试过程中非常有用的其它几个KDB命令。

id命令以一个地址/符号作为参数,它对从该地址开始的指令进行反汇编。环境变量IDCOUNT确定要显示多少行输出。

ss命令单步执行指令然后将控制返回给KDB。该指令的一个变体是ssb,它执行从当前指令指针地址开始的指令(在屏幕上打印指令),直到它遇到将引起分支转移的指令为止。分支转移指令的典型示例有call、return和jump。

go命令让系统继续正常执行。一直执行到遇到断点为止(如果已应用了一个断点的话)。

reboot命令立刻重新引导系统。它并没有彻底关闭系统,因此结果是不可预测的。

ll命令以地址、偏移量和另一个KDB命令作为参数。它对链表中的每个元素反复执行作为参数的这个命令。所执行的命令以列表中当前元素的地址作为参数。

示例

反汇编从例程schedule开始的指令。所显示的行数取决于环境变量IDCOUNT:[0]kdb> id schedule

执行指令直到它遇到分支转移条件(在本例中为指令jne)为止:[0]kdb> ssb0xc0105355 default_idle+0x25: cli0xc0105356 default_idle+0x26: mov 0x14(%edx),%eax0xc0105359 default_idle+0x29: test %eax, %eax0xc010535b default_idle+0x2b: jne 0xc0105361 default_idle+0x31

了解完基本的相关知识后,就需要实际应用啦,下面我们就需要将自己手中的C源代码和kdb调试中的汇编代码对应上,这样才能在kdb中看汇编游刃有余。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值