zoukankan      html  css  js  c++  java
  • 转载:Linux内核调试方法

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

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

    转自:http://blog.csdn.net/swingwang/article/details/72331196

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

    1 KGDB背景

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

    2 调试环境搭建

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

    2.1 目标机配置

    2.1.1 配置串口

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

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

    2.1.2 更新内核以支持kgdb

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

    更新内核前准备

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

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

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

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

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

    更新内核步骤

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

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

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

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

    • 进入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调试时无法加断点;

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

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

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

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

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

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

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

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

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

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

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

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

    5、创建启动内核及initrd

    • 注:依然强烈建议先备份/boot/目录下的原vmlinuz和initrd文件,因为虽然内核install脚本会自动备份,但如果install执行两次或以上,则之前的备份会被新备份覆盖。

    • 设置/etc/modprobe.d/unsupported-modules中allow_unsupported_modules为1,否则新编译生成的模块ko可能无法加载:

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

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

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

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

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

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

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

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

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

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

    2.2 开发机配置

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

    2.2.1 配置串口

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

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

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

    • 1、 在目标机执行cat /dev/ttyS0;

    • 2、 在开发机执行echo test > /dev/ttyS0

    • 3、 观察目标机是否打印test字样;

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

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

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

    调试代码

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

    目标二进制文件

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

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

    3 调试步骤

    3.1调试内核vmlinux

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

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

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

    2、 在开发机启动gdb:

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

    3、 设置启动远程调试

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

    • set remotebaud 115200

    • target remote /dev/ttyS0

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

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

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

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

    查看函数get_request_wait的调用栈:

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

    单步调试:

    • 下图例子中代码执行到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的内容;

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

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

    • 执行info b查看当前断点;

    • 执行d breakpoint 1清除断点1;

    • 执行c让目标机恢复运行;

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

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

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

    3.2 调试模块KO

    以调试模块scsi_mod.ko为例:

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

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

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

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

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

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

    4 总结

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

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

  • 相关阅读:
    How to build Linux system from kernel to UI layer
    Writing USB driver for Android
    Xposed Framework for Android 8.x Oreo is released (in beta)
    Linux Smartphone Operating Systems You Can Install Today
    Librem 5 Leads New Wave of Open Source Mobile Linux Contenders
    GUADEC: porting GNOME to Android
    Librem 5 – A Security and Privacy Focused Phone
    GNOME and KDE Join Librem 5 Linux Smartphone Party
    Purism计划推出安全开源的Linux Librem 5智能手机
    国产系统之殇:你知道的这些系统都是国外的
  • 原文地址:https://www.cnblogs.com/ck1020/p/7766782.html
Copyright © 2011-2022 走看看