谈uftrace之前,先谈谈ftrace。

 

ftrace是一个用于调试linux内核的工具,它可以用于调试内核的调用栈,performance等。

 

ftrace的核心是在编译内核代码时,通过制定-pg标志,在函数的调用入口插入桩mcount函数,这样,就可以在桩函数里收集函数调用的信息。至于mcount函数怎么来的,我们可以后续再谈。

 

uftrace应用了类似的机制,只不过它是针对user space的应用程序。使用uftrace的前提是要通过 “-pg”或者“-finstrument-functions”这个编译标志对应用程序代码进行编译。

 

"-pg"指定编译器在函数入口插入对mcount()桩函数的调用,而“-finstrument-functions”会指定编译器在函数入口插入对__cyg_profile_func_enter()函数的调用,在函数返回时插入对__cyg_profile_func_exit()函数的调用。 uftrace对这2种情况都能支持。正常情况下,c库提供了对这些桩函数的定义,所以我们链接程序时,是可以链接到c库里面的桩函数定义。 

 

同时呢,我们可以重新定义这些函数,让编译器链接到自定义的函数了。那么这里来了个问题,既然c库里面已经有同名的函数定义,链接器怎么知道该链接c库里面的定义还是自定义的函数?这里就用到了__attribute__((weak))这样一种属性,c库里面在定义桩函数时,在函数前面加了这个weak属性。链接器在链接这个函数符号时,如果只在c库里面找到了,那就链接到c库里面去。但是如果找到另外一个自定义的同名函数,且不是weak的,那么链接器就会优先链接到这个自定义的同名函数。

 

我们可以验证一把:

$ ldd test
	linux-vdso.so.1 =>  (0x00007ffc379b7000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fbd6d5fb000)
	/lib64/ld-linux-x86-64.so.2 (0x00007fbd6d9c5000)
$ readelf -s /lib/x86_64-linux-gnu/libc.so.6 |grep mcount
   113: 000000000010a350    91 FUNC    GLOBAL DEFAULT   13 _mcount@@GLIBC_2.2.5
   419: 0000000000143530    50 FUNC    GLOBAL DEFAULT   13 _dl_mcount_wrapper_check@@GLIBC_2.2.5
  1616: 0000000000143510    23 FUNC    GLOBAL DEFAULT   13 _dl_mcount_wrapper@@GLIBC_2.2.5
  2012: 000000000010a350    91 FUNC    WEAK   DEFAULT   13 mcount@@GLIBC_2.2.5

可以看到mcount()在libc.so.6里被定义为WEAK类型,这个验证了我们的猜想。

 

利用这个机制,我们可以玩很多花样,我们可以自己定义桩函数,然后链接到应用程序中,至于在桩函数里面干什么,那就海阔任鱼游了。

 

uftrace也利用了这点。

 

我们看看uftrace使用效果如何。

 

我们有下面这个小程序

void func_a(){

}

void func_b(){
    func_a();
}

int main(){
    func_a();
    func_b();
}

然后我们这样编译

$ gcc -pg -g test.c
$ ls
a.out  test.c  

运行

$ uftrace a.out 
# DURATION     TID     FUNCTION
   0.602 us [  3211] | __monstartup();
   0.604 us [  3211] | __cxa_atexit();
            [  3211] | main() {
   0.131 us [  3211] |   func_a();
            [  3211] |   func_b() {
   0.101 us [  3211] |     func_a();
   0.406 us [  3211] |   } /* func_b */
   1.113 us [  3211] | } /* main */

$ 

这里面打印了函数调用栈和每个函数运行时间,这些信息对于我们的运行时分析是非常有用的,特别是有回调函数之类的代码。

当然uftrace还有更多有用的功能。这个后续再谈。