690 likes | 838 Views
The success's road. Linux 调试. www.farsight.com.cn. 写在前面. Linux 内核或设备驱动程序的调试远比应用程序的调试繁琐、复杂! 调试内核的关键是对内核的深刻理解 要有汇编基础 ! 要有耐心! 要有好运!. 2.4 内核中对调试的支持. 2.6 内核中对调试的支持. 使用 printk 进行调试. printk 的健壮性. printk 的脆弱性. 随时调用 在中断中调用 在进程上下文中调用 在持有锁时调用 在多处理器上同时使用. 终端启动前无法调用. early_printk ().
E N D
The success's road Linux调试 www.farsight.com.cn
写在前面 • Linux内核或设备驱动程序的调试远比应用程序的调试繁琐、复杂! • 调试内核的关键是对内核的深刻理解 • 要有汇编基础! • 要有耐心! • 要有好运!
使用printk进行调试 printk的健壮性 printk的脆弱性 • 随时调用 • 在中断中调用 • 在进程上下文中调用 • 在持有锁时调用 • 在多处理器上同时使用 • 终端启动前无法调用 early_printk()
printk的记录等级 printk( KERN_WARNING“This is a warning\n”); printk( KERN_DEBUG“This is a warning!\n” ); printk( “No LogLevel is specified!\n” );
重要性逐渐减弱 可供使用的记录等级 记录等级 说明 0. KERN_EMERG 紧急情况 KERN_ALERT 需要立即被注意到的 KERN_CRIT 临界情况 KERN_ERR 错误 KERN_WARNING 警告 KERN_NOTICE 普通的,可能需要注意 KERN_INFO 非正式的 KERN_DEBUG 一般的调试信息
用户空间的守护进程--klogd • 用来从记录缓冲区获取内核消息; • 只有日记级别小于console_loglevel,消息才能显示出来, console_loglevel的值可以通过sys_syslogd系统调用进行修改; • 载入klogd时,可以使用-c标志改变终端的记录等级; • 运行klogd后,消息将追加到/var/log/messages; • 没有运行klogd,消息不会传递到用户空间,此时可以查看/proc/kmsg文件。
syslogd进程 • 保存klogd进程获取的内核消息到系统日志文件中; • 默认的文件是/var/log/messages; • 可通过/etc/syslog.conf文件重新配置; • 如果没有运行klogd进程,数据将保留在循环缓冲区中,直到某个进程读取和缓冲区溢出为止。
调试信息数据流传递流程图 其他应用程序调试信息printf klogd进程 syslog库函数 控制台或log文件 用户空间 syslog进程 内核空间 sys_syslog系统调用 __log_buf循环缓冲区 printk函数
提高日志级别 • 要查看调试信息,必须提高日志级别; • 读写/proc/sys/kernel/printk文件 • [root@vm-2.6]#cat /proc/sys/kernel/printk • 6 4 1 7 • 设置当前日志级别的命令 • [root@vm-2.6]#echo 8 > /proc/sys/kernel/printk
strace命令 • 显示程序调用的所有系统调用 • 从内核接收信息,而且不需要以任何特殊的方式来构建内核。 • -t:显示调用发生的时间 • -T:显示调用花费的时间 • -e:限定被跟踪的调用类型 • -o:将输出定向到一个文件中 strace cp -o aa
oops • 产生oops的原因: • 内存访问越界 • 非法指令 • 使用了NULL指针 • 使用了不正确的指针值 • oops的内容: • CPU寄存器内容 • 页描述符表的位置 • 其他信息
有问题的write程序 ssize_t faulty_write ( struct file *filp, const char __user *buf, size_t count, loff_t *pos ) { *(int *)0 = 0; return 0; }
有问题的read程序 ssize_t faulty_read ( struct file *filp, const char __user *buf, size_t count, loff_t *pos ) { int ret; char stack_buf[4]; memset(stack_buf,0xff,20); if(count > 4) count=4; ret = copy_to_user(buf,stack_buf,count); if(!ret) return count; return ret; }
oops产生时的转储信息 EIP: 0010:[<00000000>] Unable to handle kerel paging request at virtual address ffffffff printing eip: ffffffff Oops: 0000[#5] SMP CPU: 0 EIP: 0060:[<ffffffff>] Not tainted EFLAGS: 00010296 (2.6.6) EIP is at 0xfffffff eax:0000000c ebx:ffffffff ecx:00000000 edx:bfffda7c esi:cf434f00 edi:ffffffff ebp:00002000 esp:c27fff78 ds:007b es:007b ss:0068
oops产生时的转储信息(2) Process Head (pid:2331,threadinfo=c27fe000 task=c3226150) Stack: ffffffff bfffda70 00002000 cf434f20 00000001 00000286 cf434f00 fffffff bfffda70 c27fe000 c0150612 cf434f00 bfffda70 00002000 cf434f20 00000000 00000003 00002000 c0103f8f 00000003 bfffda70 00002000 00002000 bfffda70 Call Trace: [<c0150612>] sys_read + 0x42/0x70 [<c0103f8f>] syscall_call + 0x7/0xb Code: Bad EIP value
oop分析器 • klogd:符号表与当前内核必须匹配。 • ksymoops:查看/proc/modules获得模块的符号信息,从/proc/ksyms中取得内核符号表。 ksymoops filename.oops
格式化 Oops 消息 错误 代码 oops消息 运行kysmoops 格式化后的 oops消息
oops产生时的转储信息 EIP: 0010:[<00000000>] Unable to handle kerel paging request at virtual address ffffffff printing eip: ffffffff Oops: 0000[#5] SMP CPU: 0 EIP: 0060:[<ffffffff>] Not tainted EFLAGS: 00010296 (2.6.6) EIP is at 0xfffffff eax:0000000c ebx:ffffffff ecx:00000000 edx:bfffda7c esi:cf434f00 edi:ffffffff ebp:00002000 esp:c27fff78 ds:007b es:007b ss:0068
查看日志获取系统状态 • Linux的3个主要的日志子系统: • 连接时间日志 /var/log/wtmp和/var/run/utmp • 进程统计日志 pacct或acct • 错误日志 由syslogd(8)执行
常用的日志文件 • access-log --> 记录网络传输 • acct/pacct --> 记录用户命令 • btmp --> 记录失败的记录 • lastlog --> 最后登陆记录 • messages --> 从syslog中记录信息 • sudolog --> sudo发出的命令 • sulog --> su命令的使用 • utmp --> 记录当前登陆用户 • xferlog --> 记录ftp会话
gdb调试器 • 运行程序,给程序加所需的调试条件 • 在给定的条件下让程序停止 • 检查程序停止时的运行状态 • 通过改变数据,更改程序的错误
gdb基本命令 • file • quit • run • info • list • break watch print set step next continue help
远程调试 目标操纵系统初始化到应用程序入口 运行调试器 主动触发异常 指定调试通信端口向目标系统发送消息 异常处理程序转到调试端口进行通信
gdb远程调试环境原理图 应用程序 GDB/XGDB 内核 串口/网络 X Windows 环境 stub程序 本地主机 远程主机
arm-linux-gdb的创建 • download gdb-<version>.tar.bz2 • tar jxvf gdb-<version>.tar.bz2 • ./configure --target = arm-linux --prefix = /usr/local/arm-gdb –v • make • make install
编译stub程序 • export PATH=$PATH:/usr/local/arm-gdb/bin • ./configure --target=arm-linux --host=arm-linux • 在gdbserver目录运行: • make CC=/usr/local/xxx/bin/arm-linux-gcc • 在gdb目录下生成gdbserver,该程序在开发板上运行 • 建立主机和开发板之间的TCP/IP连接
建立gdb和gdbserver连接 • 在目标板上运行gdbserver • [root@vm /root]# ./gdbserver 192.168.1.1:2345 hello • Process hello created; pid=1000 • Listening on port 2345 • 将hello程序复制到主机的相应目录,执行arm-linux-gdb: • ./arm-linux-gdb hello • 连接到开发板 • (gdb) target remote 192.168.1.1:2345
KDB介绍 • Linux内核调试器是Linux内核的补丁,提供了一种在系统能运行时对内核内存和数据结构进行检查的办法。 • 不允许进行源代码级别上的调试。 • 可以添加额外的命令,给出该数据结构的标识或地址,这些命令便可以格式化和显示基本的系统数据结构。
KDB允许的内核操作 • 处理器单步执行 • 执行到某条特定指令时停止 • 当存取(或修改)某个特定的虚拟内存位置时停止 • 当存取输入/输出地址空间中的寄存器时停止 对当前活动的任务和所有其它任务进行堆栈回溯跟踪(通过进程 ID) • 对指令进行反汇编 • 追击内存溢出
配置KDB • tar jxvf linux-xxx.tar.bz2 • bzip2 -d xxx.patch.bz2 • patch -p1 < xxx.patch • 设置CONFIG_KDB选项 • Kernel hacking ->Built-in Kernel Debugger support • 设置CONFIG_FRAME_POINTER选项 • Compile the kernel with frame pointers • 设置CONFIG_KDB_OFF选项 • KDB off by default
激活KDB • 激活: • echo “1” > /proc/sys/kernel/kdb • 取消激活: • echo “0” > /proc/sys/kernel/kdb
KDB命令(1) • 运行 • go:继续程序执行 • ss:单步执行程序 • ssb:执行到分支或者函数调用时停止
KDB命令(2) • 内存显示和修改 • md :显示内存内容 • mdr :显示原始内存的内容 • mm :修改内存内容 (thinkingmmW) • mds :以符号的方式显示内存的内容 例1: 显示从0x30000000开始的30行内存 [0] kdb > md 0x30000000 15 例2: 修改0x20000000上的内存 [0] kdb > mm 0x20000000 0x33
KDB命令(3) • 寄存器修改和显示 • rd :显示寄存器内容 • rm :修改寄存器的内容 • ef :显示异常帧 例1: 显示通用寄存器组 [0] kdb > rd 例2: 设置eax寄存器内容 [0] kdb > rm %eax,0x22
KDB命令(4) • 断点 • bp/bl :设置或者显示断点 • bc/be :清除/激活断点 • bd :使断点无效 • bpa :设置或者显示全局断点 • bph/bpha:设置硬件断点或者显示所有断点 例1: 为函数设置断点 [0] kdb > bp func_1 例2: 列出断点 [0] kdb > bl 例3: 清楚断点 [0] kdb > bc 1
KDB命令(5) • 堆栈 • bt :显示调用堆栈 • btp :显示进程的堆栈 • bta :显示所有进程的堆栈 • btc :命令对每个活动 CPU 上正在运行的进程执行堆栈回溯 例1: 跟踪当前活动线程的堆栈 [0] kdb > bt 例2: 跟踪某进程的堆栈 [0] kdb > btp 888
KDB命令(6) • 其他数十个命令 • help :获得帮助 • ? :获得帮助 • ……
KGDB 使用两台计算机开发: stty ispeed 115200 ospeed 115200 -F /dev/ttyS0 echo hello > /dev/ttyS0 stty ispeed 115200 ospeed 115200 -F /dev/ttyS1cat /dev/ttyS1
安装KGDB • 需要软件: • 内核源代码:linux-x.x.x.tar.bz2 • Kgdb补丁:linux-x.x.x-kgdb-1.9.patch • gdbmod-x.x.bz2 • 配置内核 make; make bzImage