Chinaunix首页 | 论坛 | 博客
  • 博客访问: 87814
  • 博文数量: 44
  • 博客积分: 2525
  • 博客等级: 少校
  • 技术积分: 316
  • 用 户 组: 普通用户
  • 注册时间: 2010-04-25 17:01
文章分类

全部博文(44)

文章存档

2010年(44)

我的朋友

分类: LINUX

2010-06-23 09:24:06

1.       编译/链接时加-pg标志。

gcc -pg -g main.c test.c -o test

2.       运行程序。

./test

3.       分析数据。

gprof –b test

结果:

Each sample counts as 0.01 seconds.

%   cumulative   self              self     total          

time   seconds   seconds    calls ms/call ms/call name   

100.72      0.07     0.07        1    70.50    70.50 test

够 简单吧! 不过当我把test.c编译成一个共享库时,就遇到了麻烦:

1.         编译。

gcc -pg -g test.c -shared -o libtest.so

gcc -pg -g main.c -L./ -ltest -o test

2.         运行程序。

./test

3.         分析数据。

gprof -b test

结 果:

Each sample counts as 0.01 seconds.

no time accumulated

%   cumulative   self              self     total          

time   seconds   seconds    calls Ts/call Ts/call name   

前 面的分析数据现在没了,你说奇怪不?难道gcc profiling不支持共享库?查了一个资料,即没有找到gcc profiling不支持共享库的论断,也没有找到支 持共享库的方法。只好去自己去分析。

1.         先反汇编,观察gcc对源代码的处理。

objdump -S test

我 们可以看到,gcc在 每个函数里都加了call   804841c 。可以猜测mcount是用来统计函数调用次数的。

      

2.         gdbmcount处设置断点。

Breakpoint 3, 0x001de9b0 in mcount () from /lib/libc.so.6

正 如所料的,mcount是 在libc中实现的。

3.         glibc中找到mcount的源代码(gmon/mcount.c)

mcount的实现有点复杂,它的功能正是增加函数调用次数count的。

4.         mcount记录了函数的调用次数,但运行时间是如何确定的呢?接下又看了gmon.c

l         monstartup是 初始化函数。它有两个参数:lowpc highpc。我们知道pc是程序计数器,lowpc/ highpc指向代码段的开始和结束。在csu/gmon-start.c中得到了证实:lowpc=_start,而highpc=etext

l         mcleanup是 退出函数。它负责保存profiling数 据。数据分为histogramcall-graphbasic-block三类。Histogram描述每一段指令执行的情况,call-graph描述调用关系和调用次数,basic-block描述每条语句的执行情况。gcc 4.00并不支持-a选项,估计basic-block不会工作。

l         moncontrol是 一个有趣的函数,它真正控制profiling的 开始和结束。它用不同的参数调用了__profil, 以决定是开始还是结束。起初我猜测__profil是 系统调用,不过在kernel里 没有发现它的实现。

5.         最后在sysdeps/posix/profil.c中找到了__profil的实现。

l         __profil 的 实现很简单,用setitimer设 置一个定时器,定时器到了之后,系统会给该进程发送SIGPROF信 号。

l         profil_counterSIGPROF信号的处理函数,profil_counter调用profil_count记录数据。

6.         可能只有明白了__profil的参数后才会清楚profil_count的实现。

l         sample_buffer: 很 明显是用于记录采样数据的buffer, 在profil_count函 数中也可以得到证实。

l         size: sample_buffer的 大小。

l         offset: 是 代码段的起始位置,在moncontrol中 可以得到证实,offset= lowpc

l         scale: 这 个参数有点让人费解了。再回去看看profil_count的 实现,才知道profil_count中 是以64K长度为统计 单位的,64K长的代 码可以包含上百个数函数,所以无疑粒度太大了。scale就 是用来控制统计粒度的,scale=1表 示以64K为采样单 位,scale=64K表 示以一个字节为采样单位。实际的采样粒度是在monstartup中 指定的,它根据代码段的大小决定。

有 了这些数据和setitimer的 时间间隔,就不难得到profiling数 据。

7.         我们知道了gcc profiling的工作原理,再回过头来看看原来的 问题: gcc profiling是 否支持共享库。答案是:不支持。证据有:

l         monstartup的 参数lowpchighpc,限制了只能对一个代码段进行profiling。而共享库的代码段往往是映射到不同的区 间的。

l         profil_count 中 的(i < nsamples)条 件,过滤了对共享库函数的时间统计。

l         mcount中 的(frompc > p->textsize) 条 件,过滤了对共享库函数的调用次数统计。

从 代码可以看出gcc profiling对 多线程的处理也有问题。多线程和共享库这么重要的问题都不解决,那gcc profiling到底有多用处?不明白gcc profiling的实现者是如何考虑的。通过LD_PRELOAD自己实现mcountSIGPROF信号处理函数并不复杂,但是真的非要自己写吗?

转 载时请注明出处和作者联系方式:http://blog.csdn.net/absurd

阅读(733) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~