Согласно документация по gperftools, профилировщик может быть запущен любым из следующих способов:
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()
если установлено.
Вся информация, необходимая для решения этой проблемы, разбросана по переполнению стека, но было бы полезно иметь ее в одном месте, так что теперь это так. Я включил ссылки на ответы, которые я нашел полезными при решении этой проблемы, если кто-то ищет дополнительную информацию.
В gperftools конструктор для CpuProfiler
проверяет CPUPROFILE
и звонки ProfilerStart(getenv("CPUPROFILE"))
если он установлен (плюс или минус несколько других условий). CpuProfiler
объявлен в 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
ProfileStop()
на самом деле никогда не может быть вызван, но так как i
является изменчивым, компилятор не может его оптимизировать, и поэтому компоновщик должен ввести libprofiler для определения. По умолчанию, -lprofiler
только вводил символы, которые фактически появились в программе, чего в первоначальном случае не было, поэтому он вообще не связывал библиотеку, и CpuProfiler()
никогда не звонил.
Исправление состоит в том, чтобы передать --no-as-needed
флаг для ld
перед связыванием libprofiler.so
, [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
Других решений пока нет …