在网络设备软件的系统测试中,经常要观察一些复杂的数据结构中的信息,由于这些信息的查看不便,测试人员很可能知难而退,就不去查看了。
而这往往会让近在眼前的BUG溜走。
如果一件事情变得简单,那么,大家去执行它的可能性就大了很多。
GDB脚本可以让这样的观察变得简单,但是GDB脚本的书写本身也是比较繁杂,我们能不能用C做这样的工作呢?
下面是一种可行的解决办法:GDB + 动态链接库
/× 作为测试插件的动态库,可以单独编译,访问被测试进程中的内存信息,用以帮助观察进程数据,此处只是示例
变参的支持需要做额外的工作
×/
#include <stdio.h>
void AccessPoint(int i)
{
printf("hello, Access point in dll\n");
switch (i)
{
case 1:
{
funcInDaemon01();
break;
}
case 2:
{
funcInDaemon02();
break;
}
default:
{
fprintf(stderr, "err access arg\n");
break;
}
}
}
/× 模拟被测试的用户态进程 ×/
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
typedef void (*ACCESSFUNC)(int i);
static void *g_Handle = NULL;
static ACCESSFUNC g_pfAccess = NULL;
void funcInDaemon01()
{
printf("hello func01 InDaemon\n");
}
void funcInDaemon02()
{
printf("hello func02 InDaemon\n");
}
int RemoveTestPlug()
{
if (NULL != g_Handle)
{
if (dlclose(g_Handle) < 0)
{
fprintf(stderr, "%s\n", dlerror());
exit(1);
}
g_Handle = NULL;
g_pfAccess = NULL;
}
return 0;
}
int LoadTestPlug()
{
int iRet = 0;
void *handle = NULL;
ACCESSFUNC func = NULL;
iRet = RemoveTestPlug();
if (0 == iRet)
{
handle = dlopen("./TestPlug.so", RTLD_LAZY);
if (NULL == handle)
{
fprintf(stderr, "%s\n", dlerror());
exit(1);
}
else
{
func = dlsym(handle, "AccessPoint");
if (NULL == func)
{
fprintf(stderr, "open Access func err\n");
dlclose(handle);
exit(1);
}
else
{
g_pfAccess = func;
g_Handle = handle;
}
}
}
return 0;
}
void Access(int i, ...)
{
if (NULL != g_pfAccess)
{
/* ...need do something to support multi args */
g_pfAccess(i);
}
else
{
fprintf(stderr, "you need load plug frist\n");
}
}
int main()
{
int iRet = 0;
iRet = LoadTestPlug();
Access(1);
Access(2);
return iRet;
}
makefile如下:
all : TestPlug.so fdmid
fdmid : fdmi.c
gcc -g -rdynamic fdmi.c -ldl -o fdmid
TestPlug.so : plug.c
gcc -shared -fPIC plug.c -o TestPlug.so
clean:
rm -f *.so fdmid
执行结果如下:
系统测试时可以在GDB中重修加载扩充了的动态库,然后调用访问函数来观察进程中的数据,访问函数根据测试的需要,可以在动态库中扩充。
需要注意的是,动态库中的代码应该严谨,不要引入副作用。