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

Ray*_*Ray 2 c++ ld gperftools

根据gperftools文档,可以使用以下任何方法启动探查器:

  1. CPUPROFILE环境变量设置为将保存配置文件信息的文件名
  2. 执行上述操作,并设置CPUPROFILESIGNAL和发送适当的信号以启动或停止采样.
  3. 呼叫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
Run Code Online (Sandbox Code Playgroud)

工作:

$ 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
Run Code Online (Sandbox Code Playgroud)

请注意,CPUPROFILE正在读取它,因为它的值会覆盖传递给ProfileStart()if if 的文件名.

Ray*_*Ray 5

解决这个问题所需的所有信息都分散在Stack Overflow中,但将它放在一个地方会很有用,所以现在就是这样.我已经在解决这个问题时提到了我发现有用的答案,以防有人在寻找更多信息.

在gperftools中,用于CpuProfiler检查CPUPROFILE和调用的构造函数ProfilerStart(getenv("CPUPROFILE"))(如果已设置)(加上或减去其他一些条件).CpuProfiler声明A profiler.cc以确保调用该函数.[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
Run Code Online (Sandbox Code Playgroud)

ProfileStop()实际上永远不会被调用,但由于它i是易失性的,编译器无法对其进行优化,因此链接器需要将libprofiler用于定义.默认情况下,-lprofiler只引入程序中实际出现的符号,在原始情况下它们都不是,所以它根本没有链接库,也CpuProfiler()从未被调用过.

修复是在链接之前将--no-as-needed标志传递给. [2]这导致它链接库,无论它是否在程序中使用它(ld手册页似乎表明这应该是默认行为,但它不适用于我).该则标志传递给将它关闭,一旦我们已经加载了我们所需要的.(顺便说一句,似乎是静态库的等价选项[3])ldlibprofiler.so--as-needed--whole-archive

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

$ 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
Run Code Online (Sandbox Code Playgroud)

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