我已经设置了 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怎么设
设置环境变量后 Elasticsearch 报告默认堆内存大小