我尝试做UT测试时,发现有些测试的情况UT不能覆盖,于是想动手扩大范围,不管它算是什么测试,反正能自动化地验证程序是个好事。在这个过程中我使用了一些极端变态的做法来测试,比如:
程序要动态地,周期性地读取/proc/stat 文件的内容,以此计算cpu负载。该文件第一行的格式如下:
cpu 39794 68 117063 34980272 185392 0 2746 6106
cpu 后面的4个数字分别代表 用户空间, nice 过的用户空间, system空间和idle的时间, 单位是1/100秒。
原来的程序用unsigned long来保存这个数字,但至少2.6内核中,内核本身早就用64位整数来保存这个数了。所以一个4核的CPU在运行几个月后,unsigned long会溢出。顺便说一句,典型的windows或linux下,
sizeof(unsigned long)与sizeof(unsigned int)一样,都是4字节整数。
出现这个溢出之后,程序在计算CPU负载时总是会返回100%,这显然是个错误的值。
那么怎么样在测试中模拟这个 /proc/stat文件,手工把它加到 unsigned int的上限 4294967295呢?
如果它是普通文件,就简单了,但它是个内核中的pseudo file system, 而且只读不写。这就麻烦了。我前后想过几个方案:
1. 写一个内核模块, 或许是filter模块(貌似是windows驱动的概念),需要时把它insmod到内核中,动态修改。
2. chroot.
这两个方法都涉及大量的工作,其中chroot貌似简单一些,但在产品级的程序中,一个ELF可执行文件往往依赖很多.so,需要模拟出的root 目录很大,而且很容易漏掉一些,导致程序行为异常。最终放弃这个的原因是snmp agent不能连接到snmpd的socket上,即使用mksock 创建了相应的socket设备文件也不行。于是放弃。
3. 通过LD_PRELOAD指定一个.so文件,在main执行之前就修改进程。类似的方法我在其它地方用过,肯定可以工作,但涉及额外的编程和调试,因为先想到了下面用gdb动态修改程序变量的办法,所以没有继续尝试下去。
好了,终于说到正题了。我希望通过gdb 随时暂停一个程序,修改它的一些变量,然后让它继续运行,程序的行为预先会根据变量进行调整,具体说,我把 /proc/stat 保存在一个
static char s_proc_stat_fname[]中,程序会打开这个文件读取其中内容。gdb只需要修改这个变量即可。下面是命令:
gdb -p $(pidof prog) --silent -ex 'x/s strcpy(s_proc_stat_fname, "/prox/stat")' -ex 'set confirm off' -ex 'quit' 2>/dev/null
其中set confirm off很重要,避免了与gdb的交互,让它静默地执行预定义的命令,然后功成身退。
测试脚本希望预先知道可执行程序能否被gdb加载调试符号。
交互式使用gdb时,给定了要调试的文件, gdb会在启动后向标准输出输出一条信息, 标明是否成功地加载了符号信息。
可以利用这一点,在gdb脚本中预先判断gdb能否成功加载被调试程序的符号信息。
# detect whether debug symbols for a given program can be load successfully by gdb
# arg 1: target program to detect
function has_debug_symbol()
{
prog="$1"
gdb --silent -ex 'quit' $prog 2> /dev/null | grep -i 'no debugging symbols found'
}
如果程序有一大堆.so文件,它们各自另有符号文件,gdb会向stderr上输出它们的加载信息, 很多, 很烦。 2>/dev/null 为的是避免这个。
顺便说一下,判断调试器使用的调试信息,使用file 命令的输出是不行的。它输出的是否stripped不是指调试器要使用的调试信息。
阅读(1631) | 评论(0) | 转发(0) |