首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

内核调试的方法

内核调试的方法

驱动程序的调试
一. 打印: 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
继承事业,薪火相传
返回列表