Linux内核调试4

调试嵌入式 Linux内核的方法如下。
 1 目标机“插桩”,如打上KGDB补丁,这样主机上的GDB可与目标机的KGDB
通过串口或网口通信。
2  使用仿真器,仿真器可直接连接目标机的JTAG/BDM,这样主机的GDB 就
可以通过与仿真器的通信来控制目标机。
3 在目标板上通过printk()、oops、strace 等软件方法进行“观察”调试,这些

方法不具备查看和修改数据结构、断点、单步等功能。


1、内核打印信息—printk()


 printk()这种最原始的方法却更广泛地被应用


 通过如下命令可以使得Linux 内核的任何printk都被输出

 echo 8 > /proc/sys/kernel/printk


驱动调试之打印到proc虚拟文件

      

  dmesg 打印缓冲区的信息

 用户也可以直接使用“cat /proc/kmsg”命令来显示内核信息


2、驱动调试之段错误分析_根据oops信息

定出错的代码位置

二. 根据内核打印的段错误信息分析
a. 作为模块:
(根据pc=0xbf000018 找到“事发现场”)
1. 根据pc值确定该指令属于内核还是外加的模块
pc=0xbf000018 它属于什么的地址?是内核还是通过insmod加载的驱动程序?
先判断是否属于内核的地址: 看System.map(vi System.map)确定内核的函数的地址范围:c0004000~c03265a4
CONFIG_FRMAE_POINTER  针指针,可以打印回溯信息

如果不属于System.map里的范围,则它属于insmod加载的驱动程序


2. 假设它是加载的驱动程序引入的错误,怎么确定是哪一个驱动程序?
先看看加载的驱动程序的函数的地址范围
cat /proc/kallsyms  (内核函数、加载的函数的地址)
cat /proc/kallsyms >/kallsyms.txt
从这些信息里找到一个相近的地址, 这个地址<=0xbf000018
比如找到了:
bf000000 t first_drv_open [first_drv]


3. 找到了first_drv.ko
在PC上反汇编它: arm-linux-objdump -D first_drv.ko > frist_drv.dis
在dis文件里找到first_drv_open


    first_drv.dis文件里              insmod后
00000000 <first_drv_open>:       bf000000 t first_drv_open [first_drv]
00000018                         pc = bf000018

./firstdrvtest on
Unable to handle kernel paging request at virtual address 56000050
内核使用56000050来访问时发生了错误


pgd = c3eb0000
[56000050] *pgd=00000000
Internal error: Oops: 5 [#1]
Modules linked in: first_drv
CPU: 0    Not tainted  (2.6.22.6 #1)
PC is at first_drv_open+0x18(该指令的偏移)/0x3c(该函数的总大小) [first_drv]
PC就是发生错误的指令的地址
大多时候,PC值只会给出一个地址,不到指示说是在哪个函数里


LR is at chrdev_open+0x14c/0x164
LR寄存器的值


pc = 0xbf000018


pc : [<bf000018>]    lr : [<c008d888>]    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
Process firstdrvtest (pid: 777, stack limit = 0xc3c7a258)
发生错误时当前进程的名称是firstdrvtest



Stack: (0xc3c7be88 to 0xc3c7c000)
be80:                   c3c7bebc c3c7be98 c008d888 bf000010 00000000 c049abc0 
bea0: c3e740c0 c008d73c c0474e20 c3e766a8 c3c7bee4 c3c7bec0 c0089e48 c008d74c 
bec0: c049abc0 c3c7bf04 00000003 ffffff9c c002c044 c3d10000 c3c7befc c3c7bee8 
bee0: c0089f64 c0089d58 00000000 00000002 c3c7bf68 c3c7bf00 c0089fb8 c0089f40 
bf00: c3c7bf04 c3e766a8 c0474e20 00000000 00000000 c3eb1000 00000101 00000001 
bf20: 00000000 c3c7a000 c04a7468 c04a7460 ffffffe8 c3d10000 c3c7bf68 c3c7bf48 
bf40: c008a16c c009fc70 00000003 00000000 c049abc0 00000002 bec1fee0 c3c7bf94 
bf60: c3c7bf6c c008a2f4 c0089f88 00008520 bec1fed4 0000860c 00008670 00000005 
bf80: c002c044 4013365c c3c7bfa4 c3c7bf98 c008a3a8 c008a2b0 00000000 c3c7bfa8 
bfa0: c002bea0 c008a394 bec1fed4 0000860c 00008720 00000002 bec1fee0 00000001 
bfc0: bec1fed4 0000860c 00008670 00000002 00008520 00000000 4013365c bec1fea8 
bfe0: 00000000 bec1fe84 0000266c 400c98e0 60000010 00008720 00000000 00000000 


Backtrace: (回溯)
[<bf000000>] (first_drv_open+0x0/0x3c [first_drv]) from [<c008d888>] (chrdev_open+0x14c/0x164)
[<c008d73c>] (chrdev_open+0x0/0x164) from [<c0089e48>] (__dentry_open+0x100/0x1e8)
 r8:c3e766a8 r7:c0474e20 r6:c008d73c r5:c3e740c0 r4:c049abc0
[<c0089d48>] (__dentry_open+0x0/0x1e8) from [<c0089f64>] (nameidata_to_filp+0x34/0x48)
[<c0089f30>] (nameidata_to_filp+0x0/0x48) from [<c0089fb8>] (do_filp_open+0x40/0x48)
 r4:00000002
[<c0089f78>] (do_filp_open+0x0/0x48) from [<c008a2f4>] (do_sys_open+0x54/0xe4)
 r5:bec1fee0 r4:00000002
[<c008a2a0>] (do_sys_open+0x0/0xe4) from [<c008a3a8>] (sys_open+0x24/0x28)
[<c008a384>] (sys_open+0x0/0x28) from [<c002bea0>] (ret_fast_syscall+0x0/0x2c)
Code: e24cb004 e59f1024 e3a00000 e5912000 (e5923000) 

Segmentation fault


3、驱动调试之段错误分析_根据栈信息确

定函数调用过程






4、驱动调试之自制工具_寄存器编辑器







5、驱动调试之修改系统时钟中断定位系统僵死问题

          思路:其实是通过在时钟的中断函数里面打印出 PC值,然后 确定是内核的PC ,还是insmod引入的,通过System.map(内核的),kallsyms(非内核的),找打对应的出错的函数, 然后把对应的文件反汇编,找到对应的出错地点   

       内核的时钟是一直在运行的,所以可以在内核的时钟中断里面,打印一些信息

      (1) 在内核程序里面搜 Timer_Tick,找到中断处理函数

         

在内核的定时器中断添加处理代码,打印信息,连续10S 都是同一个进程就打印进程号和名字,这样可以知道在哪个进程发生错误。

 



中断的处理过程,会保存现场,把pc 值打印出来就可以知道在哪里发生错误



中断异常处理的过程,找到asm_do_IRQ



找到中断的asm_do_IRQ ,找到保存现场的结构体 pt_regs ,找到

PC 值



修改中断的总入口函数asm_do_IRQ,当中断号为 30 (系统中断的中断号),和10S 内都是同一个进程,然后打印出PC值,进程名称和进程号。






根据PC(是内核的,还是imsmod 引进的)反推哪里出问

题,要用同一个内核


引出符号表,打开符号表(包含函数地址的信息),找一个相

近的地址,然后就 找到发生错误的函数,就可以反汇编该函

数,找到在那条指令附近出错。


 



猜你喜欢

转载自blog.csdn.net/qq_26690505/article/details/79287084