先写一个个人比较喜欢的调试技巧.
1. menuconfig中打开CONFIG_DEBUG_KERNEL
2. objdump -d -S(大写) *.o > file 可以得到混合C和汇编的代码
或者 make *.lst 也能得到
3. addr2line -f -e vmlinux address(0xcxxxxxxxx) 能得到address对应的函数名和所在的文件中的行数
4. 根据OOPS信息, 查看R13(SP), R14(LR), R15(PC)寄存器的值, ARM架构
?printk
?内核中的调试支持
?通过监视调试
?调试系统故障
– oops 消息
– 系统挂起
?调试器和相关工具
– 使用 gdb
– kdb 内核调试器
– kgdb 补丁
– 用户模式的linux虚拟机
– Linux 追踪工具包
– 动态探测
1. printk (最基础常用的)
?printk()函数的健壮性
– 任何时候,任何地方都能调用。(中断,进程,持有锁时,多处理器)
– 脆弱之处:终端初始化之前,不能使用。这时,可能的方法是使用early_printk()或直接进行串口操作。
– 如果没有特别指定等级,默认DEFAULT_MESSAGE_LOGLEVEL,默认是KERN_WARNING。修改可以 "echo n > /proc/sys/kernel/printk"
2. 内核中的调试支持
内核开发者已经在内核自身中构建了多个调试特性.
下面列出开发内核一般使用到的配置选项,一般出现在内核配置工具“kernel hacking”菜单.
?CONFIG_DEBUG_KERNEL #这个选项相当于总开关
?CONFIG_DEBUG_SLAB #这个重要的选项打开了内核内存分配函数的检查
?CONFIG_DEBUG_PAGEALLOC #页在释放时被从内核地址空间去除.
?CONFIG_DEBUG_SPINLOCK #内核捕捉对未初始化的自旋锁的操作, 以及各种其他自旋锁的错误
?CONFIG_DEBUG_SPINLOCK_SLEEP #激活对持有自旋锁时进入睡眠的检查.
?CONFIG_INIT_DEBUG #用__init (或者 __initdata) 标志的项在系统初始化或者模块加载后都被丢弃. 可以用来检查初始化完之后对于初始化的内存空间的访问企图.
?CONFIG_DEBUG_INFO #使得内核在建立时包含完整的调试信息. 如果你想使用 gdb 调试内核, 你将需要这些信息. 如果打算使用gdb, 还要激活CONFIG_FRAME_POINTER.
?CONFIG_MAGIC_SYSRQ #激活"魔术 SysRq"键.
?CONFIG_DEBUG_STACKOVERFLOW
?CONFIG_DEBUG_STACK_USAGE #能帮助跟踪内核堆栈溢出.
第一个选项给内核增加了明确的溢出检查; 第 2 个使得内核监测堆栈使用并作一些统计, 这些统计可以用魔术 SysRq 键得到.
?CONFIG_KALLSYMS #使得内核中包含符号信息; 符号选项用在调试上下文中; 没有它, 一个 oops 列表只能以 16 进制格式给你一个内核回溯.
?CONFIG_IKCONFIG
?CONFIG_IKCONFIG_PROC #使得完整的内核配置状态被建立到内核中, 可以通过 /proc 来访问.一般自己开发的内核是不需要的.
?CONFIG_ACPI_DEBUG #打开详细的 ACPI调试信息.
?CONFIG_DEBUG_DRIVER #打开了驱动核心的调试信息, 可用以追踪低层支持代码的问题.
?CONFIG_SCSI_CONSTANTS #建立详细的 SCSI 错误消息的信息.
?CONFIG_INPUT_EVBUG #打开输入事件的详细日志.
?CONFIG_PROFILING #通常用在系统性能调整, 但是在追踪一些内核挂起和相关问题上也有用.
3. 通过监视调试
? Strace命令是一个功能非常强大的工具,可以显示有用户空间所发出的所有系统调用。
– 不仅显示调用, 还以符号形式显示调用的参数和返回值. 当一个系统调用失败, 错误的符号值(例如, ENOMEM)和对应的字串(Out of memory) 都显示.
– strace 有很多命令行选项; 其中最有用的是
? -t 来显示每个调用执行的时间,
? -T 来显示调用中花费的时间,
? -e 来限制被跟踪调用的类型,
? -o 来重定向输出到一个文件. 缺省地, strace 打印调用信息到 stderr.
4. 调试系统故障
?oops消息
– 大部分 bug 是引用 NULL 指针或者使用其他不正确指针值. 此类 bug 通常的输出是一个 oops 消息.
– 一个 oops 显示了出错时的处理器状态, 包括CPU 寄存器内容和其他看来不可理解的信息.
– 只在你的内核是打开 CONFIG_KALLSYMS 选项而编译时可以看到符号的调用堆栈. 否则, 如果见到一个裸的 16进制列表, 除非以别的方式对其解码.
?系统挂起
– 尽管内核代码的大部分 bug 以 oops 消息结束, 有时候它们可能完全挂起系统. 如果系统挂起, 没有任何消息可以打印出来,系统不响应任何动作,包括Ctrl-Alt-Del
– 对上述情况一个必不可少的工具是“魔术 sysrq 键”, 在大部分体系上都可用. 魔术键 sysrq 是 PC 键盘上alt 和 sysrq 键组合来发出的
– 开启SysRq功能:
? echo 0 > /proc/sys/kernel/sysrq
5. 调试器和相关工具
?使用gdb
–gdb 对于看系统内部是非常有用. 在这个层次上,精通调试器的使用要求掌握gdb 命令, 需要理解目标平台的汇编代码, 以及对应源码和优化的目标代码匹配的能力.
–典型调用:
?gdb /usr/src/linux/vmlinux /proc/kcore
–可以使用gdb命令来获取信息,如
?p global_variable
?print *(address)
–编译内核-g可以提供更多信息,如打印结构体中的成员和跟踪一个指针
?Kdb内核调试器
–为什么内核没有建立更多高级的调试特性在里面.是Linus不相信交互式的调试器. 他担心它们会导致不良的修改, 这些修改给问题打了补丁而不是找到问题的真正原因.
–kdb 内嵌式内核调试器来自 oss.sgi.com 的一个非官方补丁. 要使用kdb, 你必须获得这个补丁应用它, 重编译并安装内核.仅x86可用
–启动kdb:一种是在启动时加入“kdb=on”,另一种方式是在proc文件系统加载后,输入如下命令:
#echo 1”>/proc/sys/kernel/kdb。
然后就可以按“pause”键进入调试环境了。
–kdb提供丰富的命令实现运行控制、内存操纵、寄存器操纵、断点设置、堆栈跟踪等许多功能
?Kgdb补丁
–将运行调试内核的系统和运行调试器的系统隔离开来工作。两个系统使用串口线缆连接。
–可以让完整功能的gdb针对内核运行。需要对gdb进行一些设置和安装。
–支持x86,SuperH,ia64,x96_64,SPAR和32位的PPC架构
–在Documentation/i386/kgdb下有详细描述
?用户模式的linux虚拟机
–UML 使用 Linux 内核来运行, 作为一个Linux 系统上的独立的用户模式的进程
–好处:
?内核错误不会破坏“真正”的系统
?很容易用gdb等调试器对用户模式的linux进行处理
–缺点:
?用户模式的内核无法访问主机系统的硬件,无法调试真正和硬件打交道的驱动程序
–http://user-mode-linux.sf.net/ 有关于 UML 的更多信息
?Linux 追踪工具包
–Linux Trace Toolkit (LTT) 是一个内核补丁以及一套相关工具, 允许追踪内核中的事件.追踪内容包括时间信息, 可以创建一个给定时间段内所发生事情的完整描述.
因此, 它不仅可以用来调试,也可以追踪性能问题.
–LTT文档一起, 可以在http://www.opersys.com/LTT 找到
?动态探测
–Dynamic Probes ( DProbes ) 是由 IBM 发行的(在 GPL 之下)为 IA-32 体系的 Linux 的调试工具. 它允许安放一个“探针”在几乎系统中任何地方, 用户空间和内核空间都可以.
–探针由一些代码组成( 有一个特殊的,面向堆栈的语言写成), 当控制命中给定的点时执行代码. 代码可以报告信息给用户空间,改变寄存器, 或者做其他很多事情.
–探针可以插入在一个运行中的系统的任何地方, 不用内核重建或者重启. DProbes 可以协助 LTT在任意位置插入新的跟踪事件.
– DProbes 工具可以从 IBM 的开放源码网站:http://oss.sof-ware.ibm.com 下载.