深度解析KGDB调试Linux模块和内核

转载文章请注明作者和二维码及全文信息。

不会编程的程序员,不是好的架构师,编程和内核调试也是出色架构师的必修课。谈起编程人员的数量,基于Linux平台的软件工程师肯定是最多的,没有之一。那今天我们就以Linux为例,深入讲一下内核模块和内核的调试技术和调试工具KGDB。

1 KGDB背景

KGDB是在内核2.6.26版本中正式支持的,对应发行版即SLES11及以上、RHEL6及以上,在此之前的内核版本由Linsyssoft Technologies公司提供补丁以支持KGDB,但并不是所有内核版本都有补丁可用,同时打补丁操作也比较繁琐且问题多多,因此可用性不高。

2 调试环境搭建

注:以下称 “被调试的主机”为目标机,运行gdb进行调试的主机为开发机

2.1 目标机配置

2.1.1 配置串口

物理机串口根据实际环境要求配置,虚拟机按如下方式配置,pipe名字可以修改,但要保证和开发机一致:

2.1.2 更新内核以支持kgdb

注:本文以SLES11SP1作为目标机为例,内核源码直接安装RPM包就可以使用,RHEL要稍微麻烦一些,需要下载源码包,进行编译后进行安装。

更新内核前准备

加入调试信息后内核及各个ko的体积会增大数倍,因此编译内核前一定要确认磁盘有7G以上剩余空间(保险起见建议预留10G),执行make后源码目录空间占用超过5G。

执行make modules_install后/lib/modules目录还要占用1.4G

SLES系列默认内核源码目录是/usr/src/linux-xxx/,但由于试验用的虚拟机创建时磁盘选择默认大小只有8G,因此额外创建了一块20G的磁盘挂载到/home目录作为内核编译目录,可直接将目录usr/src/linux-xxx/拷贝到/home/linux-xxx/不影响编译。

更新内核步骤

1、执行uname –r确认当前运行内核的类型,拷贝/boot/目录下对应内核类型的config文件到内核源码目录并重命名为.config;大多数情况下编译内核后启动失败都是因为内核配置不当,因此最好在系统原有配置文件基础上修改。

2、在内核源码目录执行make menuconfig进行内核配置;

  • 进入Kernel hacking子选项,确认激活以下项目:

[*]Compile the kernel with debug info

[*]Compile the kernel with frame pointers

[*]KGDB: kernel debugging with remote gdb

  • 清除 Write protect kernel read-only data structures选项;此项默认是激活的,会导致后续使用gdb调试时无法加断点;

在SLES11SP1上去掉Write protect kernel read-only data structures后编译会出错,原因是函数mark_rodata_ro在init/main.c和cacheflush.h中重复定义了

解决办法是注掉main.c中的定义:

3、执行make all编译内核;(耗时约1小时,可使用make –j x all加快编译速度,x表示线程数)

4、安装模块,编译完成后,新生成的模块ko还在源码目录,并未更新到/lib/module/对应目录:

  • 注意,在安装模块前强烈建议备份原模块目录,以便调试完成后或新编译模块有问题时恢复环境,如下。

  • 执行make modules_install(注意:不是make modules install)将拷贝ko到/lib/module/

5、创建启动内核及initrd

  • 注:依然强烈建议先备份/boot/目录下的原vmlinuz和initrd文件,因为虽然内核install脚本会自动备份,但如果install执行两次或以上,则之前的备份会被新备份覆盖。
  • 设置/etc/modprobe.d/unsupported-modules中allow_unsupported_modules为1,否则新编译生成的模块ko可能无法加载:

  • 执行make install,将会拷贝源码目录下的vmlinux到/boot/目录并压缩为vmlinuz,并创建initrd:

6、为KGDB内核创建新的启动项

  • 注:继续强烈建议先备份原始启动项,将原始启动项使用的内核和initrd文件指定为之前备份的文件:

  • 新增的KGDB启动项,与原始启动项相比只增加了一个参数:kgdboc=ttyS0,115200

  • 如果需要目标机一启动就断住(比如要调试启动阶段的代码),则再增加一个参数kgdbwait

7、重启目标机,以KGDB选项启动

2.2 开发机配置

开发机不需要和目标机硬件或内核相同,只要上面装的gdb版本满足kgdb的要求就可以。本文使用一个SLES10SP4的32位虚拟机作为开发机。

2.2.1 配置串口

物理机串口根据实际环境要求配置,虚拟机按如下方式配置:

检查参数,确认串口配置正确:

  • 1、 在目标机执行cat /dev/ttyS0;
  • 2、 在开发机执行echo test > /dev/ttyS0
  • 3、 观察目标机是否打印test字样;

2.2.2 准备调试代码和目标二进制文件

调试代码

由于gdb调试需要源码文件,因此需要把内核源码拷贝到开发机。建议在目标机编译前把整个源码目录拷贝到开发机,否则编译后整个源码目录体积太大。

目标二进制文件

目标二进制文件就是要调试的文件,如vmlinux或xxx.ko,直接把目标机上编译好的文件拷贝到开发机,建议放在内核源码目录下。

3 调试步骤

3.1调试内核vmlinux

以调试函数block层的函数get_request_wait为例

1、 在目标机执行echo g > /proc/sysrq-trigger,会触发目标机挂起以等待开发机输入;

2、 在开发机启动gdb:

3、 设置启动远程调试

在gdb界面输入以下两条命令,成功的话会显示断在kgdb_breakpoint函数:

  • set remotebaud 115200
  • target remote /dev/ttyS0

4、 输入b get_request_wait为我们想调试的函数设置断点(b表示breakpoint),然后执行c(continue)让目标机继续运行直到断点;

5、 查看调用栈(bt)和单步调试(n)都是比较有用的手段;

查看函数get_request_wait的调用栈:

单步调试:

  • 下图例子中代码执行到rq = get_request(q, rw_flags, bio, GFP_NOIO);这行前;
  • 执行p rq打印指针变量rq的地址显示value optimized out表示为空;
  • 执行p *rq打印指针变量rq的内容显示无法访问0x0地址;
  • 执行n让rq = get_request(q, rw_flags, bio, GFP_NOIO);执行完;
  • 再次执行p rq成功打印出指针变量rq的地址;
  • 执行p *rq成功打印出指针变量rq的内容;

6、 调试完成后清除断点让目标机恢复正常运行;

  • 执行info b查看当前断点;
  • 执行d breakpoint 1清除断点1;
  • 执行c让目标机恢复运行;

目标机之前挂起后网络就中断了,此时恢复后又可重新登录:

3.2 调试模块KO

以调试模块scsi_mod.ko为例:

1、先在目标机上查看模块在内核中的偏移地址,然后挂起目标机:

2、在开发机启动gdb,并执行add-symbol-file [模块ko] [内核地址]加载模块ko文件:

之后的步骤同调试内核vmlinux一样:启动远程调试、设置断点…

4 总结

使用KGDB,一方面可以帮助阅读内核代码,实际观察代码执行的流程;另一方面可以帮助非自研模块相关流程的问题定位,不需要反复添加打印重编内核,提高问题定位效率。本文重点描述了KGDB环境搭建及启动调试的步骤,更多gdb调试技巧请参考gdb手册。

环境搭建重点在于更新内核,这块也是整个过程中最耗时和容易出错的,项目组可以组织分工进行各个版本、类型内核的KGDB更新(如SLES11 32位/64位、RHEL等等)并保存,后续使用时可以直接拷贝。请搜索“ICT_Architect”加入微信公众号“架构师技术联盟”获取更多精彩内容。

时间: 2025-01-07 05:45:58

深度解析KGDB调试Linux模块和内核的相关文章

用 kGDB 调试 Linux 内核

简介 这个文档记录了用kGDB调试Linux内核的全过程,都是在前人工作基础上的一些总结.以下操作都是基于特定板子来进行,但是大部分都能应用于其他平台. 要使用KGDB来调试内核,首先需要修改config配置文件,打开相应的配置,配置内核启动参数,甚至修改串口驱动添加poll支持,然后才能通过串口远程调试内核. 配置内核 基本配置 在内核配置文件:.config中,需要打开如下选项 CONFIG_KGDB 加入KGDB支持 CONFIG_KGDB_SERIAL_CONSOLE 使KGDB通过串口

VELT-0.1.5开发:使用kgdb调试Linux内核

VELT的全称是Visual EmbedLinuxTools,它是一个与visual gdb类似的visual studio插件,用以辅助完成Linux开发.利用这个插件,将可以在visual studio的IDE中进行Linux应用程序的开发(包括编译和调试),也可以进行uboot和linux内核的编译,并根据编译时的错误信息正确定位到源码.目前的版本是0.1.4,仅支持vs2013.此插件可以在CSDN下载频道下载(http://download.csdn.net/detail/lights

用KGdb和VMware调试Linux内核,System Call

Linux的内核和System Call不好调试,参考这里: http://stackoverflow.com/questions/5999205/cannot-step-into-system-call-source-code 简单来说,如果想在本机调试system call,那么当你进入system call时,系统已经在挂起状态了,那么它又怎样能响应用户的输入? 所以,有一个UML(http://user-mode-linux.sourceforge.net/)的方式,把内核当成一个进程启

一张图深度解析Linux共享内存的内核实现

一张图深度解析Linux共享内存的内核实现 Sailor_forever  sailing_9806#163.com http://blog.csdn.net/sailor_8318/article/details/39484747 (本原创文章发表于Sailor_forever 的个人blog,未经本人许可,不得用于商业用途.任何个人.媒体.其他网站不得私自抄袭:网络媒体转载请注明出处,增加原文链接,否则属于侵权行为.如有任何问题,请留言或者发邮件给sailing_9806#163.com)

Unity加载模块深度解析(网格篇)

在上一篇 加载模块深度解析(一)中,我们重点讨论了纹理资源的加载性能.这次,我们再来为你揭开其他主流资源的加载效率. 这是侑虎科技第53篇原创文章,欢迎转发分享,未经作者授权请勿转载.同时如果您有任何独到的见解或者发现也欢迎联系我们,一起探讨.(QQ群465082844) 资源加载性能测试代码 与上篇所提出的测试代码一样,我们对于其他资源的加载性能分析同样使用该测试代码.我们将每种资源均制作成一定大小的AssetBundle文件,并逐一通过以下代码在不同设备上进行加载,以期得到不同硬件设备上的资

Unity加载模块深度解析(Shader)

作者:张鑫链接:https://zhuanlan.zhihu.com/p/21949663来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出处. 接上一篇 加载模块深度解析(二),我们重点讨论了网格资源的加载性能.今天,我们再来为你揭开Shader资源的加载效率. 这是侑虎科技第59篇原创文章,欢迎转发分享,未经作者授权请勿转载.同时如果您有任何独到的见解或者发现也欢迎联系我们,一起探讨.(QQ群465082844) 资源加载性能测试代码 与上篇所提出的测试代码一样,我们

大杀招之使用QEMU调试Linux内核代码

Linux内核代码的调试非常麻烦,一般都是加printk, 或者用JTAG调试. 这里的方法是用QEMU来调试Linux内核.因为QEMU自己实现了一个gdb server, 所以可以非常方便的使用gdb来调内核. 这对内核的学习也非常有帮助. 为了尽量不多花时间在QEMU设置上,这里直接使用以下的内核image: http://free-electrons.com/community/demos/qemu-arm-directfb/ 1. QEMU的安装 这个可以自己去QEMU的官网下载编译,

[WebKit内核] JavaScript引擎深度解析--基础篇(一)字节码生成及语法树的构建详情分析

[WebKit内核] JavaScript引擎深度解析--基础篇(一)字节码生成及语法树的构建详情分析 标签: webkit内核JavaScriptCore 2015-03-26 23:26 2285人阅读 评论(1) 收藏 举报  分类: Webkit(34)  JavascriptCore/JIT(3)  版权声明:本文为博主原创文章,未经博主允许不得转载. 看到HorkeyChen写的文章<[WebKit] JavaScriptCore解析--基础篇(三)从脚本代码到JIT编译的代码实现>

跟踪调试Linux内核的启动过程

跟踪调试Linux内核的启动过程---使用gdb 符钰婧 原创作品转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 ” 本次的实验是使用gdb跟踪调试内核从start_kernel到init进程启动,并分析启动的过程. 1.首先是在实验楼虚拟机上进行调试跟踪的过程. (1) 先构造一个简单的Linux系统 (2) 接下来使用gdb跟踪调试内核 启动(窗口被冻结) 另开一个shell窗口(水平分割)