Я установил переменную окружения CPUPROFILE и связал -lprofiler. Почему 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

Работает:

$ 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() если установлено.

2

Решение

Вся информация, необходимая для решения этой проблемы, разбросана по переполнению стека, но было бы полезно иметь ее в одном месте, так что теперь это так. Я включил ссылки на ответы, которые я нашел полезными при решении этой проблемы, если кто-то ищет дополнительную информацию.

В 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
4

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]