我已经设置了 CPUPROFILE 环境变量并链接了 -lprofiler。为啥 gperftools 没有启动分析器?

Posted

技术标签:

【中文标题】我已经设置了 CPUPROFILE 环境变量并链接了 -lprofiler。为啥 gperftools 没有启动分析器?【英文标题】:I've set the CPUPROFILE environment variable and linked -lprofiler. Why is gperftools not starting the profiler?我已经设置了 CPUPROFILE 环境变量并链接了 -lprofiler。为什么 gperftools 没有启动分析器? 【发布时间】:2015-11-12 01:06:58 【问题描述】:

根据gperftools documentation,可以使用以下任何一种方法启动分析器:

    CPUPROFILE 环境变量设置为配置文件信息将保存到的文件名 执行上述操作,同时设置CPUPROFILESIGNAL 并发送适当的信号以开始或停止采样。 直接从您的代码调用 ProfilerStart(filename)ProfileStop()

所有三种方法都要求libprofiler.so 也被链接。

当我尝试这个时,第三种方法有效,但是当我只设置CPUPROFILE时,没有生成任何分析信息。

不起作用:

$ cat foo.c
#include <stdio.h>

int main(void) 
    printf("Hello, world!\n");

$ gcc foo.c -std=c99 -lprofiler -g && CPUPROFILE=foo.prof ./a.out
Hello, world!
$ ls foo.prof
ls: cannot access foo.prof: No such file or directory

有效:

$ cat bar.c
#include <stdio.h>
#include <gperftools/profiler.h>

int main(void) 
    ProfilerStart("bogus_filename");
    printf("Hello, world!\n");
    ProfilerStop();

$ gcc -std=c99 bar.c -lprofiler -g && CPUPROFILE=foo.prof ./a.out 
Hello, world!
PROFILE: interrupts/evictions/bytes = 0/0/64
$ ls foo.prof
foo.prof
$ ls bogus_filename
ls: cannot access bogus_filename: No such file or directory
$ ./a.out
Hello, world!
PROFILE: interrupts/evictions/bytes = 0/0/64
$ ls bogus_filename
bogus_filename

请注意,CPUPROFILE 正在被读取,因为它的值会覆盖传递给 ProfileStart() 的文件名(如果已设置)。

【问题讨论】:

文档链接已损坏 @ofloveandhate 已修复。感谢您提请我注意。 【参考方案1】:

解决此问题所需的所有信息都分散在 Stack Overflow 中,但将它放在一个地方会很有用,所以现在它是。我已经包含了对解决此问题时发现有用的答案的参考,以防有人正在寻找更多信息。

在 gperftools 中,CpuProfiler 的构造函数会检查 CPUPROFILE 并在设置时调用 ProfilerStart(getenv("CPUPROFILE"))(加上或减去一些其他条件)。在profiler.cc 中声明了CpuProfiler,以确保调用该函数。 [1] 当然,只有在链接了 libprofiler.so 时才会发生这种情况。

以下代码揭示了问题:

$ cat baz.c 
#include <stdlib.h>
#include <stdio.h>
#include <gperftools/profiler.h>

int main(void) 
    volatile int i = 0;
    if (i) ProfilerStop();

    printf("Hello, world!\n");
    return 0;


$ gcc -std=c99 baz.c -lprofiler -g && CPUPROFILE=foo.prof ./a.out 
Hello, world!
PROFILE: interrupts/evictions/bytes = 0/0/64

ProfileStop() 永远无法真正调用,但由于i 是易失性的,编译器无法对其进行优化,因此链接器需要引入 libprofiler 进行定义。默认情况下,-lprofiler 只引入了实际出现在程序中的符号,在原始情况下没有任何符号,因此它根本没有链接库,并且 CpuProfiler() 从未被调用。

解决方法是在链接libprofiler.so 之前将--no-as-needed 标志传递给ld。 [2] 这会导致它链接库,无论它是否在程序中使用它(ld 手册页似乎建议这应该是默认行为,但对我来说不是这样)。一旦我们加载了我们需要的内容,然后传递--as-needed 标志以将其关闭。 (顺便说一句,--whole-archive 似乎是静态库[3] 的等效选项)

使分析适用于原始文件的编译命令:

$ gcc -std=c99 foo.c -Wl,--no-as-needed,-lprofiler,--as-needed -g && CPUPROFILE=foo.prof ./a.out 
Hello, world!
PROFILE: interrupts/evictions/bytes = 0/0/64

【讨论】:

您也可以正常编译程序(即根本没有-lprofiler),然后使用LD_PRELOAD=.../libprofiler.so 拉入分析机器。这也消除了对分析器的任何编译时依赖。

以上是关于我已经设置了 CPUPROFILE 环境变量并链接了 -lprofiler。为啥 gperftools 没有启动分析器?的主要内容,如果未能解决你的问题,请参考以下文章

麻烦大神,我已经配置了java的环境变量,再配置Python环境变量会起冲突吗?Python怎么设

如何设置qwt路径或环境变量

设置环境变量后 Elasticsearch 报告默认堆内存大小

Vs2010 中怎么包含自定义的环境变量?

vs2015项目的环境变量设置在哪?我安装了ogresdk,系统里的OGRE_HOME已经设

PHP 在命令提示符 Windows 中不可执行,已设置环境变量