380 likes | 586 Views
嵌入式系统的调试. 陈文智 wzchen@cad.zju.edu.cn 浙江大学计算机学院 2005 年 4 月. 提纲. 1. 远程调试环境的搭建原理 2. 使用 GDB 3. Linux 内核调试 4. 实验 实验一:利用 GDB 进行本地调试 实验二:利用 GDB 进行远程调试 实验三: arm-linux 内核调试. 1. 远程调试环境的搭建原理. 通用的桌面操作系统与嵌入式操作系统在调试环境上存在明显的差别 远程调试,调试器运行于通用桌面操作系统的应用程序,被调试的程序则运行于基于特定硬件平台的嵌入式操作系统(目标操作系统).
E N D
嵌入式系统的调试 陈文智 wzchen@cad.zju.edu.cn 浙江大学计算机学院 2005年4月
提纲 • 1. 远程调试环境的搭建原理 • 2. 使用GDB • 3. Linux内核调试 • 4. 实验 • 实验一:利用GDB进行本地调试 • 实验二:利用GDB进行远程调试 • 实验三:arm-linux内核调试
1. 远程调试环境的搭建原理 • 通用的桌面操作系统与嵌入式操作系统在调试环境上存在明显的差别 • 远程调试,调试器运行于通用桌面操作系统的应用程序,被调试的程序则运行于基于特定硬件平台的嵌入式操作系统(目标操作系统)
远程调试带来以下问题: • 调试器与被调试程序如何通信 • 被调试程序产生异常如何及时通知调试器 • 调试器如何控制、访问被调试程序 • 调试器如何识别有关被调试程序的多任务信息并控制某一特定任务 • 调试器如何处理某些与目标硬件平台相关的信息
1.1 插桩(stub) • Stub方案是在目标操作系统和调试器内分别加入某些功能模块,二者互通信息来进行调试 • 这一方案需要目标操作系统提供支持远程调试协议的通信模块和多任务调试接口,并改写异常处理的有关部分 • 目标操作系统还需要定义一个设置断点的函数
1.2 GDB介绍 • GDB是GNU C自带的调试工具,使用GDB可以完成下面这些任务: • 运行程序,可以给程序加上所需的调试任何条件 • 在给定的条件下让程序停止 • 检查程序停止时的运行状态 • 通过改变一些数据,可以更快地改正程序的错误
1.3 GDB远程调试功能介绍 • 如果需要调试的程序和GDB所运行的环境不同,或者说需要调试的环境上根本无法运行起GDB,就需要使用远程调试功能 • 指定需要调试的远程机器的方法是使用target remote命令 • 在远程机器上,需要实现一个stub文件,在这个文件里面提供串口连接的协议,和传送数据信息的方法
应用程序 GDB/XGDB 串口或TCP/IP连接 Xwindow环境 内核 Stub程序 本地主机 远程主机 • GDB远程调试环境原理图:
2. 使用GDB • 2.1 GDB命令 • 补齐功能 • 键入gdb gdb_test命令来启动GDB并载入程序 gdb_test,命令行进入了GDB模式。
2.2 断点与条件断点 • GDB中的断点有四种状态: • 有效(Enabled) • 禁止(Disabled) • 一次有效(Enabled once) • 有效后删除(Enabled for deletion) • 条件断点的设置语句:
3. Linux内核调试 • 需要在Linux内核里面做一些修改,并且提供一个stub文件 • 把stub,串口驱动程序和Linux内核编译连接在一起 • 利用这个核心启动的系统,在需要进行调试的时候,激活程序的断点,等待本地主机的连接 • 然后,就可以进行内核的调试了
3.1 内核调试技术 • 调试内核代码的时候,则可以用 printk()显示监视信息 • printk()可以指定一个记录级别,在头文件 <linux/kernel.h> 中定义了 8 种可用的日志级别字符串 : • KERN_EMERG 用于紧急事件消息,它们一般是系统崩溃之前提示的消息。 • KERN_ALERT 用于需要立即采取动作的情况。
KERN_CRIT 临界状态,通常涉及严重的硬件或软件操作失败。 • KERN_ERR 用于报告错误状态;设备驱动多用此级来报告来自硬件的问题。 • KERN_WARNING 警告可能出现问题,这类情况通常不会对系统造成严重问题。 • KERN_NOTICE 正常情形的提示。许多与安全相关的状况用这个级别进行汇报。 • KERN_INFO 提示性信息。很多设备驱动启动时,用此级别打印相应的硬件信息。 • KERN_DEBUG 用于调试信息。
3.1 内核调试技术(1) • 在标准的Linux系统上,用户空间的守护进程klogd从记录缓冲区中获取内核消息 ,通过syslogd守护进程将他们保存在系统日志文件中 • 根据日志级别,内核可能会把消息打印到当前控制台上 • 内核在遇到运行错误时,会显示发生错误时处理器的状态
3.1 内核调试技术(2) • 大部分错误都是由于 NULL指针的使用或其他不正确的指针值的使用,这些错误通常会导致一个 oops 消息 • oops消息给开发者许多信息,但是往往这些原始信息都是一些十六进制的内存地址,有两个工具可用来将其解析为符号: • klogd • ksymoops
3.2 KGDB分析 • KGDB适用于两种情况下的调试: • 开发者需要调试内核 • 开发者需要调试驱动模块 • 使用KGDB进行内核调试的步骤如下: • 下载当前使用内核的kgdb patch • 在PC主机端安装patch • 运行make menuconfig命令,确保kgdb选项中的KERNEL_HACKING被选上
重新编译内核。 • 把新生成的内核镜像zImage拷贝到开发板。 • 在启动开发板上的内核前需要设置:gdb gdbttyS=0 gdbbaud=38400。 • 在PC主机端,使用命令
3.2 KGDB分析(1) • 串口驱动程序模块和数据包传送函数用来初始化串口,定义在drivers/char/serial.c中,部分声明如下: • struct serial_state* gdb_serial_setup(int ttyS, int baud) 参数ttyS为串口号,baud为传输波特率。函数返回该串口的状态。 • int getDebugChar() 调用read_char()从串口获得一个字节的数据,并且返回它 • char* getpacket() 利用getDebugChar()函数,分析从调试主机传过来的数据包,将调试信息和校验信息区分开来 • static int read_data_bfr(void) 直接从硬件端口读取一个字节的数据,并且返回它 • static void write_char(int chr) 直接向串口端口写一个字节的数据
3.2 KGDB分析(2) • stub程序的函数接口如下: • void set_debug_traps(void)向系统注册调试过程中的处理函数:handle_exception() • void break_point(void)进入中断,运行中断处理程序 • int handle_exception(int exceptionVector, int signo, int err_code, struct pt_regs* linux_regs)在这个函数中一边响应本地主机上的GDB发送过来的控制信号,一边控制远程主机上Linux内核的运行状况
3.3 内核进入调试状态的路径 • 内核进入调试状态有两种方法 : • 方法一:通过在内核启动的时候向内核传入参数,这时可以调试系统启动过程内核的运行状况 • 方法二:在内核完全导入系统正常运行的情况下,通过使用一个gdbstart工具将驱动串口设备,内核的控制权交给本地主机
3.3 内核进入调试状态——方法一 • KGDB定义了三个可以向内核传递的参数,用来指定本地主机和远程主机进行连接的方法。这三个参数是: • Gdb参数表示在内核启动的时候就需要进行内核的调试 • gdbttyS是用来指定本地机器和远程主机需要连接的串口号 • gdbbaud是该串口连接的数据传输波特率
3.3 内核进入调试状态——方法一(1) • 然后,系统进入gdb_hook()函数 • 调用gdb_serial_setup()函数和注册中断处理函数 • 运行set_debug_traps()交出控制权,从而进行内核启动过程的调试
3.3 内核进入调试状态——方法一(2) • gdb_hook()函数:
3.3 内核进入调试状态——方法二 • gdbstart是一个由KGDB提供的工具,源程序放在arch/i386/kernel/gdbstart.c里面 • 交出控制权的方法和刚才所描述的不同,它是采用ioctl系统调用进入的 • 对需要连接的串口调用ioctl系统调用,引发初始化串口和进入gdb_hook()函数。 • 运行gdbstart [-s speed] [-t tty-dev]命令,就可以和本地的gdb的remote方式连接,从而进入调试过程
4. 实验 • 实验一 利用GDB进行本地调试 • 实验二 利用GDB进行远程调试 • 实验三 am-linux内核调试
实验一 利用GDB进行本地调试 • 在编译时用-g开关,如:gcc -g -o bubble bubble.c • info source,可以获得程序名,目录,文件大小,语言等信息。 • 可以用info br,察看设置的断点信息 • 键入 run执行程序
实验二 利用GDB进行远程调试 • 首先编译宿主机上的GDB调试器 : • 解压gdb-5.3.tar.gz • 运行./configure --target=arm-linux --prefix=/usr/local/arm-gdb –v创建 make 文件,执行make,make install • arm-linux-gdb将生成在 /usr/local/arm-gdb/bin
实验二 利用GDB进行远程调试(1) • 接着编译目标机上的stub程序 : • 首先进入GDB目录编译,执行export PATH=$PATH:/usr/local/arm-gdb/bin,然后执行./configure --target=arm-linux --host=arm-linux • 在gdbserver目录,键入make CC = /usr/local/xsbase-arm-linux-R1/bin/arm-linux-gcc编译用于目标机的stub程序 • 如果没有编译错误gdbserver将生成在gdb/gdbserver目录下
实验二 利用GDB进行远程调试(2) • 然后,建立宿主机和目标板之间的TCP/IP连接 • 默认XSBase 的IP地址是192.168.100.50。在这里,假设主机平台的 IP是192.168.100.216 • 换用arm-linux-gcc编译bubble.c文件 ,执行arm-linux-gcc -g bubble.c -o test • 下载test和gdbserver到XSBase目标板
实验二 利用GDB进行远程调试(3) • 下面演示如何建立gdb和gdbserver的连接 • 在XSBase255目标板上运行gdbserver • 拷贝test程序到主机平台上的/usr/local/arm-gdb/bin目录下,然后执行arm-linux-gdb。
连接到xsbase开发板 • 如果连接成功的话,在XSBase255上将出现下面的信息
实验三 am-linux内核调试 • 在宿主机上,建立一个脚本文件 debug,用于启动远程主机上的stub程序,交出内核运行的控制权 表示通过/dev/ttyS0,波特率为38400的速度进行连接。
实验三 am-linux内核调试(1) • 本地主机运行gdb,然后就可以随意使用gdb的命令了 • 需要调试unlink系统调用,那么可以如下操作:
实验三 am-linux内核调试(2) • 这个命令让远程机器上的内核运行下去,然后可以在远程机器上触发一个unlink的系统调用,比如说删除一个文件 • 当发生这件事情的时候,系统遇到断点,交出控制权,进入如下界面: