在Linux系统中提供实时内核死机调试方法,在系统中也给出了示例。示例存放路径是/usr/src/rtlinu-3.1/debugger目录,目录中的readme文件详细说明了实时内核死机调试方法。其调试方法的主要内容是:先加载实时调试模块rtl_debug.o,然后加载实时内核模块,当实时内核模块出现导致死机的条件时,rtl_debug.o模块将其挂起,再使用gdb进行调试。
运动模块MillMotMod.o是一个插入Linux系统内核的实时模块,在其导致死机时,可以使用Linux系统提供的实时内核死机调试方法查找导致死机的原因。本文档即以查找MillMotMod.o模块导致死机的原因为例,分步骤详细说明调试方法如下:
1.将rtl_debug.o模块与gdb应用程序复制到数控系统的/GJMill/bin目录下。
rtl_debug.o模块源文件在服务器/usr/src/rtlinu-3.1/debugger目录下。
gdb应用程序源文件在服务器/usr/bin目录下。
注:
复制gdb应用程序文件到数控系统上的/GJMill/bin目录之后,要在/GJMill/bin目录下使用ldd gdb命令检查gdb依赖的库文件是否完整、匹配;若不完整,需要将相应的依赖文件从服务器复制到依赖的库文件存储的目录;若不匹配,需要寻找一个能与当前数控系统中共享库相匹配的gdb应用程序,一般从服务器寻找。这里所说的服务器是指制作数控系统的文件系统所使用的服务器。
2.查看rtl_debug.o模块依赖的设备文件/dev/rtf10是否存在,若不存在,建立这个设备文件。
建立这个设备文件的方法如下:
mknod
/dev/rtf10 c 150 10
命令中c表示/dev/rtf10为字符设备,150是主设备号,10是次设备号。
注:
/dev/rtf10为rtl_debug.o模块默认使用的设备,rtl_debug.o模块还可以使用/dev/rtf11或/dev/rtf12,这两个也是字符设备,主设备号都是150,次设备号分别为11与12。
3.在数控系统目录中创建gdb的工作目录。将生成运动模块MillMotMod.o的源代码复制到工作目录。
本文档创建的gdb的工作目录为/GJMill/rtl_gdb,使用如下命令创建:
cd /GJMill
mkdir rtl_gdb
注:
当重新生成运动模块MillMotMod.o,而源代码改变时,需要更新工作目录中的代码文件。
4.修改数控系统启动文件,加入在运动模块MillMotMod.o插入之前将内核调试模块rtl_debug.o插入系统内核的语句。
方法:
打开数控系统/GJMill/目录下的GJMill.run文件。在语句
# run motion in background
echo -n "starting SYSTEM MOTION
PROGRAM -- $mot..."
之前加入
/sbin/insmod /GJMill/bin/rtl_debug.o
注:
当rtl_debug.o模块不使用默认设备/dev/rtf10,而使用/dev/rtf11或/dev/rtf12时,
需要将GJMill.run文件中
/sbin/insmod
/GJMill/bin/rtl_debug.o
语句加入fifo=NN的参数,分别修改为
/sbin/insmod /GJMill/bin/rtl_debug.o fifo=11
与
/sbin/insmod /GJMill/bin/rtl_debug.o fifo=12
5.将运动代码中的看门狗报警去掉。
方法:
将文件control.c中的emcmotController函数中的update_watchdog()语句注释掉,即将语句变成
//update_watchdog();
6.修改运动代码的编译文件Makefile,在编译中加入ggdb选项,在连接时去掉S选项。编译,编译时加入RTL_GDB=1参数,生成用于调试的运动模块MillMotMod.o。将MillMotMod.o复制到数控系统/GJMill/bin目录下。
方法:
(1)修改文件Makefile
在语句
ifdef BUILD_REALTIMEONLY
之前加入
ifdef RTL_GDB
CFLAGS += -ggdb
endif
在语句
RT_MODULE_LIBCM_LINK_FLAG = -S -O3
smallmath.o
之后加入
ifdef RTL_GDB
RT_MODULE_LIBCM_LINK_FLAG := -O3
smallmath.o
Endif
(2)编译生成用于调试的运动模块MillMotMod.o
编译G3WPX版本的运动模块MillMotMod.o的命令如下:
touch
*
make GJ301=1 PULSE_MODE=1 RTL_GDB=1
PLAT=rtlinux_3_1
编译后可以看到生成的模块与不加RTL_GDB=1参数相比,大小增加了很多。
(3)将编译生成的模块MillMotMod.o复制到数控机/GJMill/bin目录下。
注:
gcc编译选项ggdb作用是在可执行程序中包含gdb特性的大量调试信息。使用gdb调试时必须加入此编译选项。
ld链接选项S(即--strip-debug)作用是忽略输出文件中所有的“调试符号”信息(但不是所有符号)。该选项控制在链接各个模块时,不将各模块中的“调试符号”信息链接到最终的目标文件中。在需要调试时,该选项必须关闭。
strip命令会抛弃目标文件中的符号信息,其对目标文件执行后目标文件修改后的效果与ld –S生成目标文件的效果相同。因此,需要调试时,必须保证目标文件没有使用strip命令处理过。
7.在PC机上使用SSH软件连接到数控系统,使用cat
/proc/kmsg命令建立一个打印终端,用于自动打印实时信息。
8.启动数控系统。启动过程中或操作过程中运动模块MillMotMod.o出现导致死机的条件时,rtl_debug.o模块会将运动模块MillMotMod.o挂起,MillMotMod.o模块停止在挂起位置。系统不会死机。
注:
此时,在SSH软件连接到数控系统的打印终端会出现类似以下内容的语句:
rtl_debug: exception 0xe in MillMotMod
(EIP=0xcfbadaa1), thread id 0xcc548000; (re)start GDB to debug
9.进入/GJMill/bin目录运行gdb,使用gdb调试MillMotMod.o模块。
方法(其中/*与*/之间为注释):
cd /GJMill/bin
./gdb MillMotMod.o
GNU gdb 5.0
Copyright 2000 Free Software Foundation,
Inc.
GDB is free software, covered by the GNU
General Public License, and you are welcome to change it and/or distribute
copies of it under certain conditions.
Type "show copying" to see the
conditions.
There is absolutely no warranty for
GDB. Type "show warranty" for
details.
This GDB was configured as
"i386-redhat-linux"...
(gdb) /*此时已进入到gdb应用程序,出现gdb应用程序标志*/
(gdb) cd /GJMill/rtl_gdb /*修改gdb的工作目录。此目录为源代码存储目录,gdb调试过程从此目录寻找源代码。因此生成MillMotMod.o模块的源代码必须预先存入此目录*/
Working directory /GJMill/rtl_gdb.
(gdb) target remote
/dev/rtf10 /*指定实时内核远程调试设备*/
Remote debugging using /dev/rtf10
[New thread -866877440]
[Switching to thread -866877440]
tpScurveCalcuVEnd (tp=0xcfc041d4, thisTc=0xcfc09564,
t=1, nForesee=2) at tp.c:3380 /*给出导致模块MillMotMod.o挂起的函数*/
3380 vStart
= tmpTc->initVel; /*给出导致模块MillMotMod.o挂起的代码信息*/
(gdb) /*此时已将导致MillMotMod.o模块挂起的信息显示出来,之后,就可以使用gdb的命令进行分析调试。*/
……
……
……
……
……
……
(gdb) quit /*退出gdb*/
注:
使用quit为正常退出,之后还可以使用同样方法进入gdb,重新调试。
MillMotMod.o模块挂起时,设置断点命令bre有效,但由于当前模块为挂起状态,当前行的程序语句即导致挂起的语句,因此,使用cont命令,next命令,step命令时,不会继续执行,而会提示错误,因此查看完挂起的信息,即可使用quit命令正常退出。
以下为MillMotMod.o模块挂起时调试常用的gdb命令,其他gdb命令请查阅gdb使用手册。
cd:改变工作目录。如cd
/GJMill/rtl_gdb
pwd:显示当前工作目录。
bt(backtrace):显示线程堆栈信息,即生成导致线程挂起(一般为段错误)的
函数树。
list:列出正在调试的程序的源代码。从线程挂起前5行开始显示代码。
list n:从第n行显示代码。
bre(break):设置断点。如bre
39为设置39行为断点。
tbre(tbreak):设置临时断点。语法与bre相同,但临时断点执行一次后立即消失。
cont(continue):继续执行。
next:执行下一条源代码,但是不进入函数内部。
step:执行下一条源代码,进入函数内部。
info bre(break):显示当前断点列表,包括每个断点到达的次数。
info files:显示调试文件的信息。
info func(function):显示所有的函数名。
info local:显示当前函数的所有局部变量信息。
info prog(program):显示调试程序的执行状态。
info threads:显示所有线程的概要信息。
gdb按顺序显示:
1.线程号(gdb设置)。
2.目标系统的线程标识。
3.此线程的当前堆栈。
前面打*的线程表示是“当前调试”线程。
thread THREADNO:把线程号为THREADNO的线程设为“当前调试”的线程。gdb
调试命令对“当前调试”的线程起作用。
print:显示表达式的值。如print
vStart。
del(delete):删除断点。指定一个断点号码,则删除指定断点。不指定参数,
则删除所有断点。
shell:执行Linux
Shell命令。例如:shell ls
q(quit):退出gdb应用程序
10.当运动模块MillMotMod.o未出现导致死机的条件,rtl_debug.o模块未将运动模块挂起时,
不能使用gdb进行调试。
注:
此时若使用gdb进行调试,在语句
(gdb)
target remote /dev/rtf10
执行时,会一直等待,只能用Ctrl+c退回到(gdb)提示符下,然后使用quit命令退出。若使用Ctrl+z退出,则会直接退出gdb应用程序,并且gdb进程使用kill指令也无法清除。
附录:/dev/rtf10设备说明
RTLinux用FIFO管道来在Linux进程或者Linux内核与实时进程间传递数据,这种管道称为实时管道(real-time FIFOs),以区别UNIX IPC机制中的管道。
RT-FIFO管道是在内核地址空间的。通过一个整数来引用。RT-FIFOs的数目在编译系统时给定,可以重新编译系统改变大小
操作RT-FIFOs的函数包括创建。删除。读FIFO和写FIFO。读写操作是原子操作,不能中断。不可中断是为了避免优先级倒置问题。
在Linux进程中,把RT-FIFOs当作普通的字符设备,而不是一个系统调用。字符设备给用户一个与实时任务通信的全功能的应用程序接口(API)。这个接口对Linux进程来说是标准的设备接口,包括:open, close, read和write。
struct
file_operations rtf_fops
static struct
file_operations rtf_fops =
{
rtf_llseek,
rtf_read,
rtf_write,
NULL,
rtf_poll,
NULL,
NULL,
rtf_open,
NULL,
rtf_release,
NULL,
NULL,
NULL,
NULL,
NULL
};
下面是原始的存取例程:
int
rtf_create(unsigned int minor, int size)
int
rtf_destroy(unsigned int minor)
int
rtf_put(unsigned int minor, void *buf, int count)
int
rtf_get(unsigned int minor, void *buf, int count)
对于每个FIFO管道可以通过
int
rtf_create_handler(unsigned int minor,
int (*handler)
(unsigned int fifo))
安装自己的处理程序,当数据从FIFO中读出或写出时运行。