标题:
内核调试的方法
[打印本页]
作者:
yuyang911220
时间:
2017-5-15 11:12
标题:
内核调试的方法
驱动程序的调试
一. 打印: prink, 自制proc文件
UBOOT传入console=ttySAC0 console=tty1
1. 内核处理UBOOT传入的参数
console_setup
add_preferred_console // 我想用名为"ttySAC0"的控制台,先记录下来
2. 硬件驱动的入口函数里:
drivers/serial/s3c2410.c
register_console(&s3c24xx_serial_console);
3. printk
vprintk
/* Emit the output into the temporary buffer */
// 先把输出信息放入临时BUFFER
vscnprintf
// Copy the output into log_buf.
// 把临时BUFFER里的数据稍作处理,再写入log_buf
// 比如printk("abc")会得到"<4>abc", 再写入log_buf
// 可以用dmesg命令把log_buf里的数据打印出来重现内核的输出信息
// 调用硬件的write函数输出
release_console_sem();
call_console_drivers(_con_start, _log_end);
// 从log_buf得到数据,算出打印级别
_call_console_drivers(start_print, cur_index, msg_level);
// 如果可以级别够格打印
if ((msg_log_level < console_loglevel
__call_console_drivers
con->write(con, &LOG_BUF(start), end - start);
二. 根据内核打印的段错误信息分析
oops信息 : 单词oops的含义是“惊讶”,当内核出错时(比如访问非法地址),打印出来的信息被称为oops信息。
a). 作为模块:
1,根据PC值,找到导致错误的指令。
pc = 0x00000000 它属于什么的地址?是内核的地址,还是通过insmod加载的驱动程序的地址?
先判断是否属于内核的地址 : 看 内核编译makefile目录下的 System.map(编译完内核都会发现在内核根目录下面多出来一个System.map文件)
确定内核的函数的地址范围 : c0004000~c03faa94。
所以可以确定 : 导致错误的指令不在内核的地址范围,则它属于insmod加载的驱动程序的地址范围。
2,假设它的加载的驱动程序引入的错误。那又怎么确定是哪一个驱动程序?
有时候 Modules linked in: 会指明是哪个驱动程序,但是很多时候加载的驱动程序很多,是不会指明具体是哪个。
所以还是需要根据PC值来确定究竟是哪个驱动程序。
先看看加载的驱动程序的地址范围。
在开发板目录下 : cat /proc/kallsyms >> kallsyms.txt (内核函数的地址、加载的函数的地址)
kallsyms.txt文件中的内容介绍 //T : 表示全局函数 t : 表示静态函数
从这些信息里找到一个相近的地址, 这个地址<=0xbf000018
比如找到了:
bf000000 t first_drv_open [first_drv]
3. 找到了first_drv.ko
在PC上反汇编它: arm-linux-objdump -D lcd.ko > lcd.dis
在dis文件里找到first_drv_open
first_drv.dis文件里 insmod后
00000000 : bf000000 t first_drv_open [first_drv]
00000018 pc = bf000018
18: e5923000 ldr r3, [r2] //r2的值在下面可以找到,是56000050
此时,要通过汇编语言来找到对应的c语言的语句。考验汇编能力的时候。
./firstdrvtest on
//1,一段文本描述信息
Unable to handle kernel paging request at virtual address 56000050
内核使用56000050来访问时发生了错误
pgd = c3eb0000
[56000050] *pgd=00000000
//2,oops信息的序号,#1,表示是第1次。
Internal error: Oops: 5 [#1]
//3,内核中加载的模块的名称
Modules linked in: first_drv
//4,发送错误时,CPU的序号,对于单处理器系统,序号为0。
CPU: 0 Not tainted (2.6.22.6 #1)
//5,PC就是发生错误时,指令的地址。
//大多时候,PC值只会给出一个地址,不会指示说是在哪个函数里面。
PC is at first_drv_open+0x18(该指令的偏移)/0x3c(该函数的总大小) [first_drv]
PC就是发生错误的指令的地址
大多时候,PC值只会给出一个地址,不到指示说是在哪个函数里
//__init_begin = c0008000, PC=__init_begin+0x3fff8000=
//6,LR寄存器的值。
LR is at chrdev_open+0x14c/0x164
LR寄存器的值
//7,发送错误时,CPU各个寄存器的值。
pc = 0xbf000018
pc : [] lr : [] psr: a0000013
sp : c3c7be88 ip : c3c7be98 fp : c3c7be94
r10: 00000000 r9 : c3c7a000 r8 : c049abc0
r7 : 00000000 r6 : 00000000 r5 : c3e740c0 r4 : c06d41e0
r3 : bf000000 r2 : 56000050 r1 : bf000964 r0 : 00000000
执行这条导致错误的指令时各个寄存器的值
Flags: NzCv IRQs on FIQs on Mode SVC_32 Segment user
Control: c000717f Table: 33eb0000 DAC: 00000015
//8,发生错误时,当前进程是它,并不是说发生错误的是这个进程
Process firstdrvtest (pid: 777, stack limit = 0xc3c7a258)
//发生错误时当前进程的名称是firstdrvtest
欢迎光临 电子技术论坛_中国专业的电子工程师学习交流社区-中电网技术论坛 (http://bbs.eccn.com/)
Powered by Discuz! 7.0.0