在客户项目那里混了半年,发现Top的客户确实是比我们牛逼。先说说调试的方法。
客户那边不依赖于GDB调试,因为他们可能觉得GDB依赖于系统实现,不利于移植吧,所以客户的程序完全是依赖于打印调试的。这点很佩服他们的软件规划能力和项目管理,实现能力。说老实话,如果换了一家中国公司,每人一个调试方法,要follow 一个rule是很不容易的。
- 完善的调试菜单。调试菜单并不难实现,只是一个打印和字符接受的函数。在其中控制是开放某些打印信息。
- 在每个模块中加上仔细规划打印输出,根据需求分成不同的基本。最好情况是在最高打印级别中可以可以发现所有的问题。打印级别可以很方便的动态控制。
- 函数调用LOG
如果能定位发生问题的模块,可以在该模块的在每个函数的调用入口加上打印一个函数名字+Enter,在返回处加上一个函数名字+Exit。对于每个模块用一个打印开关控制是否打印Trace信息。在调试菜单中控制这个打印开关。
如果懒得加打印语句,可以利用gcc 的-finstrument-functions 选项来快速的加入调试信息。-finstrumnet-function会是的编译器在函数调用的开始和退出处调用
void __cyg_profile_func_enter (void *this_fn, void *call_site)
void __cyg_profile_func_exit (void *this_fn, void *call_site)。 可以利用这两个函数来跟踪函数调用的过程。
在实现这两个函数时要加入__attribute__ ((no_instrument_function));以避免编译器再调用这两个函数的时候也调用__cyg_profile_func_enter 和 __cyg_profile_func_exit 而造成循环调用。
可以用dladdr()来获得this_fn的文件和函数名。code如下:
#define _GNU_SOURCE // 由于dladdr是GNU扩展,不是dl的标准函数,因此在这句话必须加在文件的开始处#include
#include
void __cyg_profile_func_enter (void *this_fn, void *call_site) __attribute__ ((no_instrument_function));
void __cyg_profile_func_exit (void *this_fn, void *call_site) __attribute__ ((no_instrument_function));
void __cyg_profile_func_enter (void *this_fn, void *call_site)
{
Dl_info DLInfo;
dladdr(this_fn, &DLInfo);
printf("Enter file %s\n",DLInfo.dli_fname);
printf("functio %s\n",DLInfo.dli_sname);
}
void __cyg_profile_func_exit (void *this_fn, void *call_site)
{
}
#include
void *array[10];
char **strings;
size_t size;
size_t i;
size = backtrace(array, 10);
strings = backtrace_symbols(array, size);
for(i=0;i {
printf ("%d, %p ,%s\n",i,array[i],strings[i]);
}
该方法需要编译器支持。
但是需要在编译的时候加上-rdynamic 否则只能输出在内存中的绝对地址。
在没有-rdynamic的时候,关于如何找到动态库的运行时地址还需要研究。
可以在系统运行的时候发送SIGSEGV给应用程序,产生当前进程的Coredump来获取动态库中函数的运行是地址。
用GDB获取backtrace的方法(在有-g选项的时候可以看到,不需要-rdynamic):
list <*address>
在没有-g的时候,又该如何呢?
阅读(937) | 评论(1) | 转发(0) |