linux内核调试方法总结

所需积分/C币:33 2018-08-02 11:54:44 8.98MB PDF
4
收藏 收藏
举报

一 调试前的准备 二 内核中的bug 三 内核调试配置选项 1 内核配置 2 调试原子操作 四 引发bug并打印信息 1 BUG()和BUG_ON() 2 dump_stack() 五 printk() 1 printk函数的健壮性 2 printk函数脆弱之处 3 LOG等级 4 记录缓冲区 5 syslogd/klogd 6 dmesg 7 注意 8 内核printk和日志系统的总体结构 9 动态调试 六 内存调试工具 1 MEMWATCH 2 YAMD 3 Electric Fence 七 strace 八 OOPS 1 ksymoops
1 printk函数的健壮性 健壮性是prnk最容易被接受的一个特质,几乎在任何地方,任何时侯内核都可以调用它(中断上下文、进程 上下文、持有锁时、多处理器处理时等)。 2 prints函数脆弱之处 在系统启动过程中,终端初始化之前,在某些地方是不能调用的。如果真的需要调试系统启动过程最开始的地 方,有以下方法可以使用 使用串口调试,将调试信息输出到其他终端设备。 使用eary_ printk(),该函数在系统启动初期就有打印能力。但它只支持部分硬件体系 3LOG等级 printk和 printf-一个主要的区别就是前者可以指定一个LOG等级。内核根据这个等级来判断是否在终端上打印消 息。内核把比指定等级高的所有消息显示在终端。 可以使用下面的方式指定一个LOG级别 printk (KERN CRIT Hello, world! \n); 注意,第一个参数并不一个真正的参数,因为其中没有用于分隔级别( KERN CRIT)和格式字符的逗号()。 KERN CRIT本身只是一个普通的字符串(事实上,它表示的是字符串"<2>";表1列出了完整的日志级别清 单)。作为预处理程序的部分,C会自动地使用一个名为字符串串联的功能将这两个字符串组合在一起。组合 的结果是将日志级别和用户指定的格式字符串包含在一个字符串中, 内核使用这个指定LOG级别与当前终端LOG等级 console loglevel来决定是不是向终端打印。 下面是可使用的LOG等级 #detine KERN EMERG<>"/* system is unusable #definc KERN alert action must be taken immediately #define Kern cri <2>" /* critical conditions #define KERn Err <3>"/* error conditions #define KERN WARNING <4> / warning conditions #define KERN NOTIce /* normal but significant condition #define Kern info <6>"/ informational #define KERN DEBUG <7>/ debug-level messag #define KERn default d>"/* Usc the default ke logIcal 注意,如果调用者未将日志级别提供给 printk,那么系统就会使用默认值κ RN WARNING"<4>"(表示只有 KERN WARNING级别以上的日志消息会被记录)。由于默认值存在变化,所以在使用时最好指定LOG级别。有 LoG级别的一个好处就是我们可以选择性的输岀LG。比如平时我们只需要打印 KERN WARNING级别以上的关 键性LOG,但是调试的时候,我们可以选择打印 KERN DEBUG等以上的详细LOG。而这些都不需要我们修改代 码,只需要通过命令修改默认日志输出级别 mtj@ubuntu s cat /proc/sys/kernel/printk 4417 mtj@ubuntu ms cat /proc/sys/kernel/printk delay mtj@ubuntu : s cat /proc/sys/kernel/printk ratelimit 5 mtj@ubuntu :s cat /proc/ sys/kernel/printk ratelimit burst 第一项定义了 printk AP当前使用的日志级别。这些日志级别表示了控制台的日志级别、默认消息日志级别、最小 控制台日志级别和默认控制台日志级别。 printk_ delay值表示的是 printk消息之间的延迟毫秒数(用于提高某些场 景的可读性)。注意,这里它的值为0,而它是不可以通过/proc设置的。 printk ratelimit定义了消息之间允许的 最小时间间隔(当前定义为每5秒内的某个内核消息数)。消息数量是由 printk ratelimit burst定义的(当前定义 为10)。如果您拥有一个非正式内核而又使用有带宽限制的控制台设备(如通过串口),那么这非常有用。注 意,在內核中,速度限制是由调用者控制的,而不是在prnk中实现的。如果一个 printk用户要求进行速度限制 那么该用户就需要调用 printk rate limit函数 4记录缓冲区 内核消息都被保存在一个 LOG BUF LEN大小的环形队列中 关于 LOG BUF LEN定义 #define LOG BUF LEN (1 < CONFIG LOG BUF SHIFT) ※变量 CONFIG LOG BUF SHIFT在内核编译时由配置文件定义,对于i386平台,其值定义如下(在 linux26/arch/i386/defconfigF) CONFIG LOG BUF SHIFT=18 记录缓冲区操作 ①消息被读出到用户空间时,此消息就会从环形队列中删除 ②当消息缓冲区满时,如果再有 printk(调用时,新消息将覆盖队列中的老消息 ③在读写环形队列时,同步问顾很容易得到解决 ※这个纪录缓冲区之所以称为环形,是因为它的读写都是按照环形队列的方式进行操作的。 5 syslogd/klogd 在标准的Liux系统上,用户空间的守护进程kogd从纪录绶冲区中获取内核消息,再通过 syslogd守护进程把这些 消息保存在系统日志文件中。kogd进程既可以从/ρ proc/msg文件中,也可以通过 syslog()系统调用读取这些消息 默认情况下,它选择读取/ρro方式实现。kogd守护进程在消息缓冲区有新的消息之前,一直处于阻塞状态。一旦 有新的内核消息,kg被唤醒,读出內核消息并进行处理。默认情况下,处理例程就是把內核消息传给 syslogd守 护进程。 syslogd守护进程般把接收到的消息写入 var/log/messages文件中。不过,还是可以通 过/ etc/syslog cont文件来进行配置,可以选择其他的输出文件。 Applications (rsyslog, dmesg, etc glibc (syslog) Loa files Systcm call interface /orc ernel users do syslog printk u ①cm 员需 priN log buf (Ring buffer) 6 dmesg dmesg命令也可用于打印和控制内核环缓冲区。这个命令使用kogc系统用来读取内核环缓冲区,并将它转发 到标准输岀( stdout)。这个命令也可以用来清賒内核环缓冲区(使用选项),设置控制台日志级别(ηn选 项),以及定义用于读取内核日志消息的缀冲区大小(-选项)。注意,如粜没有指定缓冲区大小,那么 dmesg 会使用 klogctl的 SYSLOG_ ACTION SIZE BUFFER操作确定缓冲区大小。 7注意 a)虽然 printk很健壮,但是看了源码你就知道,这个函数的效率很低:做字符拷贝时一次只拷贝一个字节,且去调 用 console输出可能还产生中断。所以如果你的驱动在功能调试完成以后做性能测试或者发布的时候干万记得尽量 减少prin输出,做到仅在出错时输出少量信息。否则往 console输出无用信息影响性能。 b)pink的临时缓存 printk buf只有1K,所有一次prin函数只能记录<1k的信息到 og buffer,并且pink使用 的" ringbuffer 8内核pnk和日志系统的总体结构 kernel_space kernel/printk. 放入 og buffer 输出到控削台 on start.和 log end间的效握 S系统调闻 %月 user_space ictl ogd 统日芯工具 可靠的和扩展的 syslogd 作为/3g的替代工具}是一个sogd的多我程增通版 dmesg syslogd 简甲川户空门命令行工具 nux呆统中善不月的 syslog护进程 不同的发行版中可能使用其中的一种实现 busybox也是类似实 Ch naun博 9动态调试 动态调试是通过动态的开启和禁止某些内核代码来获取额外的内核信息 首先内核选项CNFG_ DYNAMIC DEBUG应该被设置。所有通过 pr debug(/dev debug)打印的信息都可以动态 的显示或不显示。 可以通过简单的查询语句来筛选需要显示的信息。 源文件名 函数名 行号(包括指定范围的行号) 模块名 格式化字符串 将要打印信息的格式写入< debugfs>/ dynamic debug/contro中 nullarbor: w echo ' file svcsock c line 1603 +p> <debugfs>/dynamic debu introl 参考 1内核日志及prnk结构浅析- Tekkaman Ninja 2内核日志:AP及实现 3 printk实现分析 4 dynamic-debug-howto txt 六内存调试工具 1 MEMWATCH MEMWATCH由 Johan lindh编写,是一个开放源代码C语言内存错误检测工具,您可以自己下载它。只要在代 码中添加一个头文件并在gc语句中定义了 MEMWATCH之后,您就可以跟踪程序中的内存泄漏和错误了。 MEMWATCH支持ANsC,它提供结果日志纪录,能检测双重释放( double-free)、错误释放( erroneous free)、没有释放的内存( unfreedmemory)、溢出和下溢等等。 清单1.内存样本(test1c) #include <stdlib. h> #inc⊥ude< stdio.h> #inc⊥ude" memwatch.h int main(void) char ptr1; ptr1 malloc(512); ptr2- malloc(512); ptr2= ptr1 free(ptr2); free(ptr1) 清单1中的代码将分配两个512字节的内存块,然后指向第一个内存块的指针被设定为指向第二个内存块。结 果,第二个内存块的地址丢失,从而产生了内存泄漏。 现在我们编译清单1的 memwatch.c。下面是一个 makefile示例: gcc -DMEMWATCH-DMw STDIo testl.c memwatch 当您运行test1程序后,它会生成一个关于泄漏的内存的报告。清单2展示了示例 memwatch log输出文件。 清单2. test1 memwatch log文件 MEMWATCH 2.67 Copyright (C) 1992-1999 Johan Lindh double-free: <4> testl. c(15), 0x80517b4 was freed from test1. c(14) unfreed: <2> test1. c(11), 512 bytes at 0x80519e4 IFE FE FE FEFE FE FEFEFEFEFE FE Memory usage statistics (global) N)umber of allocations made: 2 Largest memory usage :1024 Dotal of all alloc()calls: 1024 U)treed bytes totals 512 MEMWVATCH为您显示真正导致问题的行。如果您释放一个已经释放过的指针,它会告诉您。对于没有释放的内存也一样。日芯结尾部 分显示统计信息,包括泄漏了多少内存,使用了多少内存,以及总共分配了多少内存。 2 YAMD YAMD软件包由 Nate Eldredge编写,可以查找C和C++中动态的、与内存分配有关的问题。在撰写本文时 YAMD的最新版本为0.32。请下载yamd-.32 tar.gz。执行make命令来构建程序;然后执行 make instal命令安 装程序并设置工具 一旦您下载了YAMD之后,请在test1.c上使用它。请删除# nclude memwatch h并对 makefile进行如下小小的 修改 使用YAMD的test1 清单3展示了来自test上的YAMD的输出 清单3.使用YAMD的test1输出 Executable: /usr/src/test/yamd-0 32/test1 INFO: Normal allocation of this block Address 0x40025e00, size 512 INFO: Normal allocation of this block INFO: Normal deallocation of this block Addr ERROR: Multiple freeing At frec of pointer alrcady frccd Address 0x40025e00, size 512 WARNING: Memory leak dress 0x40028e00, size 512 WARNING: Total memory leaks 1 unfreed allocations totaling 512 bytes 半* Finished at tue 18:g7:152082 Allocated a grand total of 1024 bytes 2 allocations Average of 512 bytes per allocat 4 k alloted internally 12 k mapped now /8K max Virtual program size is 1416 K YAMD显示我们已经释放了内存,而且存在內存泄漏。让我们在清单4中另一个样本程序上试试YAMD 清单4.内存代码(test2c) #inc lude <stdlib. h> #include <stdio. h> int main(void) char * ptr2; har*chptr ptr1 malloc(512); tr2= malloc(512) chptr -(char *)malloc(512); ptr2= ptr1 free(ptr1); free(chptr); 您可以使用下面的命令来启动YAMD 清单5显示了在样本程序test2上使用YAMD得到的输出。YAMD告诉我们在for循环中有域界( out-of- bounds)"的情况 清单5.使用YAMD的test2输出 Running /usr/src/test/test2/test2 Temp output to /tmp/yamd-out, 1243 /run-yamd: line 101: 1248 Segmentation fault (core dumped) Starting run: /usr/src/test/test2/test2 Executable: /usr/src/test/test2/test2 ize is 1380 K INFO: Normal allocation of this block Address 0x40025e00, size 512 INFO: Normal allocation of this block Address 0x40028e00, size 512 INFo: Normal allocation of this block Address 0x4002be00, size 512 ERROR: Crash ried to write address 0x4002c300 Seems to be part of this block Address 0x4002be00, size 512 Address in question is at offset 512 (out of bounds) ill dump core after checking heap MEMWATCH和YAMD都是很有用的调试工具,它们的使用方法有所不同。对于 MEMWATCH,您需要添加包含 文件 memwatch h并打开两个编译时间标记。对于链接(Iink)语句,YAMD只需要q选项。 3 Electric Fence 多数 Linux分发版包含一个 Electric Fence包,不过您也可以选择下载它。 Electric Fence是一个由 Bruce perens 编写的maoc调试库。它就在您分配内存后分配受俣护的内存。如果存在 fencepost错误(超过数组未尾运 行),程序就会产生保护错误,并立即结束。通过结合 Electric Fence和gdb,您可以精确地跟踪到哪-行试图访 问受保护内存。 ElectricFence的另一个功能就是能够检测内存泄漏 七 strace strace命令是一种强大的工具,它能够显示所有由用户空闫程序发岀的系统调用。 strace显示这些调用的参数并 返回符号形式的值。 strace从内核接收信息,而且不需要以任何特殊的方式来构建内核。将跟踪信息发送到应用程 序及内核开发者都很有用。在清单6中,分区的一种格式有错误,清单显示了 strace的开头部分,内容是关于调 出创建文件系统操作(mks)的。 strace确定哪个调用导致问题出现 清单6.mkfs上 strace的开头部分 execve("/sbin/mkfs ifs",[mkfs. jfs","-f","/dev/ test1"l,& open("/dev/test1"0 RDWR O LARGEFILE)=4 stat64("/dev/testI", (st_ mode=&, st rdev=makedev (63, 255),.,1)=e ioctl(4, 0x40041271, Oxbttfe128)--1 EINVAL(Invalid argument) write(2,"kfs. ifs: warning- cannot setb".., 98mkfs ifs: warning cannot set blocksize on block device dev/test1: Invalid argument stat 64("/dev/test1",(st mode=&, st rdev=makedev(63, 255),..3)=e open ("/dev/test1",0 RDONLY O LARGEFILE)=5 ioctl(5, 0X80041272, 0xbfffe124)=-1 EINVAL(Invalid argument) write(2,"mkts. its: can\'t determine device",,,,. exit(1 清单6显示ioct调用导致用来格式化分区的mks程序失败。 ioctl BLKGETSIZE64失败。( BLKGET-S|zE64 在调用ioct的源代码中定义。) BLKGETSIZE64ioct将被添加到Lnux中所有的设备,而在这里,逻辑卷管理器 还不支持它。因此,如果 BLKGETSIZE64ioct调用失败,mks代码将改为调用较早的ioct调用;这使得mks 适用于逻辑卷管理器。 参考 http://www.ibm.com/developerworks/cn/linux/sdk/-debug/index.htmlfresources 八 oOPs(也称 Panic)消息包含系统错误的细节,如CPU寄存器的内容等。是内核告知用户有不幸发生的最常用 的方式 内核只能发布ooPS,这个过程包括向终端上翰岀错误消息,输出寄存器保存的信息,并翰岀可供跟踪的回溯线 索。通常,发送完OOPS之后,内核会处于一种不稳定的状态。 OOPS的产生有很多可能原因,其中包括内存访问越界或非法的指令等。 ※作为内核的开发者,必定将会经常处理ooPs ※OOPS中包含的重要信息,对所有体系结构的机器都是完全相同的:寄存器上下文和回溯线索(回溯线索显示了导致错误发生的函数凋 用链)。 symoops 在Linα中,调试系统崩溃的专统方法是分析在发生崩溃时发送到系统控制台的σσυρs消息。一旦您掌握了细节 就可以将消息发送到 ksymoops实用程序,它将试图将代码转换为指令并将堆栈值映射到内核符号。 ※如:回溯线索中的地址,会通过 ksymoops转化成名称可见的函数名 ksymoops需要几项内容:oops消息输出、来自正在运行的内核的 System.map文件,还有/proc/ syms vmlinux和 comodules。 关于如何使用 ksymoops,内核源代码/ sr/src/linux/ Documentation/ oops-tracing.tt中或 ksymoops手册页上有 完整的说明可以参考。 Ksymoops反汇编代码部分,指岀发生错误的指令,并显示一个跟踪部分表明代码如何被调 用 首先,将Oops消息保存在一个文件中以使通过 ksymoops实用程序运行它。清单7显示了由安装J「s文件系统 的 mount命令创建的oops消息。 清单7. ksymoops处理后的oops消息 ksymoops 2.4.0 on 1686 2.. Options used 15: 59: 37 sfb1 kernel: Urable to handle kernel NULL pointer dereference at virtual address 0000000 15:59:3 15: 59: 37 sfb1 kernel: * pde =0000000 15: 59: 37 sfb1 kernel: Oops: 0000 15: 59: 37 sfb1 kernel: CpU: 0 15: 59: 37 sfb1 kernel: EIP: 0010: [ifs mount+50/704 15: 59: 37 sfb1 kernel: Call Trace: [ifs read super +287/688 [get sb bdev+563/736] [do kern mount+189/336] [do add mount+35/208] [do page fault+0/12641 15: 59: 37 sfb1 kernel: Call Trace: [<c0155d4f>] 15:59:37sfb1 kerne1:[<c1e6e84 15:59:37s+b1 kernel:code:8b2d980390g955 >>EIP; c01588fc <ifs mount+3c/2c0>< Trace; co106cf3 <system call+33/40> Code: c01588fc <ifs mount+3c/2c0> Code: c01588Fc <jfs mount+3C/2C0><===== 00 moy 0x0. %eb Codc ce158902 <ifs mount+42/2ce 6: 55 push %ebp 接下来,您要确定js_ mount中的哪一行代码引起了这个问题。oops消息告诉我们问题是由位于偏移地址3c的 指令引起的。做这件事的办法之一是对 jfs mount.o文件使用 objdum实用程序,然后查看偏移地址3c。 Objdump用来反汇编模块函数,看看您的C源代码会产生什么汇编指令。清单8显示了使用 bedumρ后您将看 到的内容,按着,我们查看js_ mount的C代码,可以看到空值是第109行引起的。偏移地址3c之所以很重要, 是因为oops消息将该处标识为引起问题的位置。 清单8.fs_ mount的汇编程序清单 109 printk( %d\n","ptr); objdump jfs mount.o jfs mount. o: file format elf32-1386 Disassembly of section text 00000000<ifs mount> 2c: e8 cf 03 0000 call 400 <chkSuper> ‰eax 34:85db test ebx.%ebx 36:8f8555926808jne291< fs mount+Bx291 3c: 8b 2d 00000000 mov 6x0, %ebp << problem line above push %ebp 开发版2.5内核引入了 kallsyms特性,它可以通过定义 CONFIG_ KALLSYMS编译选项启用。该选项可以载入内核镜 像所对应的内存地址的符号名称(即函数名),所以内核可以打印解码之后的跟踪线索。相应,解码OOPS也不再 需要 System map和 ksymoops工具了。另外 这样做,会使内核变大些,因为地址对应符号名称必须始终驻留在内核所在内存上。 #cat / proc/kallsyms c0100240 stext 0100240 t run init process c91024 stext 0100269 t init 3 Kdump 3.1 Kdump的基本概念 3.1.1什么是 kexec? Kexec是实现 kdump机制的关键,它包括2个组成部分:-是内核空间的系统调用 kexec load,负责在生产內 核( production kerne或 first kernel)启动时将捕获内核( capture kernel或 sencond kernel)加载到指定地址 二是用户空间的工具 kexec-tols,他将捕获内核的地址传递给生产內核,从而在系统崩溃的时候能够找到捕获內 核的地址并运行。没有 kexec就没有 kdump。先有 kexec实现了在一个内核中可以启动另一个内核,才让 kdump 有了用武之地。 kexec原来的目的是为了节省 kerne开发人员重启系统的时间,谁能想到这个“偷懒"的技术却孕育 了最成功的内存转存机制呢? 3.1.2什么是 kdump? Kdump的概念出现在2005左右,是迄今为止最可靠的内核转存机制,已经被主要的 linux TM厂商选用。 kdump是 种先进的基于 kexec的内核崩溃转储机制。当系统崩溃时, kdump使用 kexec启动到第二个內核。第二个内核 通常叫做捕获内核,以很小内存启动以捕获转储镜像。第—个内核保留了内存的-部分给第二内核启动用。由于 kdump利用 kexec启动捕获内核,绕过了Bos,所以第一个内核的内存得以保留。这是内核崩溃转储的本质。 kdump需要两个不同目的的内核,生产內核和捕获内核。生产内核是捕获内核服务的对像。捕获内核会在生产内 核崩溃时启动起来,与相应的 ramdisk一起组建一个微环境,用以对生产内核下的内存进行收集和转存。 313如何使用 kdump 构建系统和 dump-capture内核,此操作有2种方式可选 1)构建一个单独的自定义转储捕获内核以捕获內核转储; 2)或者将系统内核本身作为转储捕获内核,这就不需要构建—个单独的转储捕获内核。 方法(2)只能用于可支持可重定位内核的体系结构上;目前i386,x86_64,ppc64和a64体系结构支持可重定 位内核。构建一个可重定位内核使得不需要构建第二个内核就可以捕获转储。但是可能有时想构建一个自定乂转储 捕获内核以满足特定要求 314如何访问捕获内存 在内核崩溃之前所有关于核心映像的必要信息都用ELF格式編码并存储在保留的内存区域中。ELF头所在的物理 地址被作为命令行参数( fcorehdr=)传递给新启动的转储内核。 在386体系结构上,启动的时候需要使用物理内存开始的640κ,而不管操作系统内核转载在何处。因此,这个 640K的区域在重新启动第二个内核的时候由 kexec备份 在第二个内核中,前一个系统的内存”可以通过两种方式访问 1)通过 devoldmem这个设备接口。

...展开详情
试读 31P linux内核调试方法总结
立即下载 低至0.43元/次 身份认证VIP会员低至7折
抢沙发
一个资源只可评论一次,评论内容不能少于5个字
上传资源赚积分or赚钱
    最新推荐
    linux内核调试方法总结 33积分/C币 立即下载
    1/31
    linux内核调试方法总结第1页
    linux内核调试方法总结第2页
    linux内核调试方法总结第3页
    linux内核调试方法总结第4页
    linux内核调试方法总结第5页
    linux内核调试方法总结第6页
    linux内核调试方法总结第7页

    试读结束, 可继续读3页

    33积分/C币 立即下载 >