Weird Backtrace in Perf - PullRequest
       93

Weird Backtrace in Perf

0 голосов
/ 02 марта 2020

Я использовал следующую команду для извлечения следов, ведущих к пользовательскому уровню L3-misses в простом тесте evince:

sudo perf record -d --call-graph dwarf -c 10000 -e mem_load_uops_retired.l3_miss:uppp /opt/evince-3.28.4/bin/evince

Как ясно, период выборки довольно велик (10000 событий между последовательные образцы). Для этого эксперимента в выводе perf script было несколько сэмплов, похожих на этот:

EvJobScheduler 27529 26441.375932:      10000 mem_load_uops_retired.l3_miss:uppp:     7fffcd5d8ec0         5080022 N/A|SNP N/A|TLB N/A|LCK N/A
    7ffff17bec7f bits_image_fetch_separable_convolution_affine+0x2df (inlined)
    7ffff17bec7f bits_image_fetch_separable_convolution_affine_pad_x8r8g8b8+0x2df (/usr/lib/x86_64-linux-gnu/libpixman-1.so.0.34.0)
    7ffff17d1fd1 general_composite_rect+0x301 (/usr/lib/x86_64-linux-gnu/libpixman-1.so.0.34.0)
  ffffffffffffffff [unknown] ([unknown])

В нижней части обратного следа есть символ с именем [unknown], который выглядит нормально. Но тогда вызывается строка в general_composite_rect(). Это обратная трассировка в порядке?

AFAIK, первый вызывающий в обратной трассировке должен быть что-то вроде _start() или __GI___clone(). Но обратный след не в этой форме. Что не так?

Есть ли способ решить проблему? Надежны ли усеченные (части) следы?

1 Ответ

1 голос
/ 05 марта 2020

TL; Процесс обратного отслеживания перфорирования DR может остановиться на некоторой функции, если в стеке не сохранен указатель фрейма или нет таблиц CFI для метода dwarf. Перекомпилируйте библиотеки с -fno-omit-frame-pointer или -g или получите debuginfo. С выпуском двоичных файлов и библиотек Perf часто останавливает обратную трассировку на ранней стадии, не имея возможности достичь main() или _start или clone()/start_thread() верхних функций. бинарный инструментарий): он программирует программный таймер или источник событий или блок мониторинга производительности оборудования (PMU) для генерации прерывания periodi c. В вашем примере -c 10000 -e mem_load_uops_retired.l3_miss:uppp используется для выбора аппаратного PMU в x86_64 в каком-то режиме PEBS (https://easyperf.net/blog/2018/06/08/Advanced-profiling-topics-PEBS-and-LBR) для генерации прерывания после 10000 mem_load_uops_retired (с маской l3_miss). Сгенерированное прерывание обрабатывается ядром Linux (подсистема perf_events , ядро ​​/ события и arch / x86 / events ). В этом обработчике PMU сбрасывается (перепрограммируется) для генерации следующего прерывания после 10000 дополнительных событий и генерации выборки. Дамп данных сэмплов сохраняется в файле perf.data командой perf report, но при каждом запуске инструмента можно сохранять тысячи сэмплов; Сэмплы могут быть прочитаны perf script или perf script -D.

обработчиком прерываний perf_events, что-то около __perf_event_overflow ядра / событий / ядра. c, имеет полный доступ к регистрирует текущую функцию и имеет некоторое время, чтобы выполнить дополнительный поиск данных для записи текущего времени, pid, et c. Частью такого процесса является https://en.wikipedia.org/wiki/Call_stack сбор данных. Но с x86_64 и -fomit-frame-pointer (часто включаемым для многих системных библиотек Debian / Ubuntu / others) нет места по умолчанию в регистрах или в стеке функций для хранения указателей фреймов:

https://gcc.gnu.org/onlinedocs/gcc-4.6.4/gcc/Optimize-Options.html#index -fomit_002dframe_002dpointer-692

-fomit-frame-pointer Не храните указатель кадра в регистре для функций, которые в нем не нуждаются. Это позволяет избежать инструкций по сохранению, настройке и восстановлению указателей кадров; это также делает дополнительный регистр доступным во многих функциях. Это также делает невозможной отладку на некоторых машинах.

Начиная с G CC версии 4.6, настройка по умолчанию (без оптимизации по размеру) для 32-битных Linux x86 и 32-битных целей Darwin x86 имеет был изменен на -fomit-frame-pointer. Значение по умолчанию можно вернуть к -fno-omit-frame-pointer, сконфигурировав G CC с опцией настройки --enable-frame-pointer.

С указателями кадров, сохраненными в трассировке стека функций / разматывать легко. Но для некоторых функций современные g cc (и другие компиляторы) могут не генерировать указатель кадра. Поэтому код обратной трассировки, такой как в обработчике perf_events, либо остановит обратную трассировку при такой функции, либо нуждается в другом методе восстановления указателя кадра. Опция -g method (--call-graph) из perf record выбирает метод, который будет использоваться. Это задокументировано в man perf-record http://man7.org/linux/man-pages/man1/perf-record.1.html:

--call-graph Настройка и включение записи графа вызовов (цепочка стека / обратная трассировка), подразумевает -g. По умолчанию установлено значение «fp».

Позволяет указать «fp» (указатель кадра) или «dwarf» (CFI DWARF - Информация о кадре вызова) или «lbr» (аппаратная запись последней ветви) в качестве метода сбора информация, используемая для отображения графиков вызовов.

В некоторых системах, где двоичные файлы создаются с помощью g cc
--fomit-frame-pointer, использование метода "fp" приведет к созданию поддельных графиков вызовов, с использованием «dwarf», если доступно (инструменты perf, связанные с библиотекой libunwind или libdw) должны использоваться вместо этого. Использование метода "lbr" не требует никаких параметров компилятора. Он будет генерировать графы вызовов из аппаратных регистров LBR. Основным ограничением является то, что он доступен только на новых платформах Intel, таких как Haswell. Это может получить только пользовательская цепочка вызовов. Он не работает с выборкой из стека ветвей одновременно.

Когда используется запись "dwarf", perf также записывает (пользовательский) дамп стека при выборке. Размер дампа стека по умолчанию составляет 8192 (байт). пользователь можно изменить размер, передав размер после запятой, например
"--call-graph dwarf, 4096".

Таким образом, метод dwarf повторно использует таблицы CFI для поиска размеров кадра стека и поиска стека вызывающей стороны Рамка. Я не уверен, что таблицы CFI удалены из библиотек релизов по умолчанию или нет; но у debuginfo, вероятно, они будут. LBR не поможет, потому что это довольно короткий аппаратный буфер. Обработка разбиения карликов (обработчик ядра сохраняет часть стека, а инструментальное пространство пользователя perf проанализирует его с помощью libdw + libunwind) может потерять некоторые части стека вызовов, поэтому попробуйте также увеличить дамп стека карликов с помощью --call-graph dwarf,10240 или --call-graph dwarf,81920 et c.

Обратное отслеживание реализовано в зависимой от арки части perf_events: arch / x86 / events / core. c: perf_callchain_user(); вызывается из kernel / events / callchain. c: get_perf_callchain() <- perf_callchain <- <a href="https://elixir.bootlin.com/linux/v4.19.107/source/kernel/events/core.c#L6445" rel="nofollow noreferrer"> perf_prepare_sample <- <a href="https://elixir.bootlin.com/linux/v4.19.107/source/kernel/events/core.c#L6569" rel="nofollow noreferrer"> __ perf_event_output <- <a href="https://elixir.bootlin.com/linux/v4.19.107/source/kernel/events/core.c#L10111" rel="nofollow noreferrer">*(event->overflow_handler) <- <code>READ_ONCE(event->overflow_handler)(event, data, regs); из __perf_event_overflow.

Грегг предупредил о неполных стеках вызовов perf: http://www.brendangregg.com/blog/2014-06-22/perf-cpu-sample.html

Неполные стеки обычно означают, что использовался -fomit-frame-pointer - оптимизация компилятора, которая мало что меняет в реальном мире, но ломает профилировщики стека. Всегда компилируйте с -fno-omit-frame-pointer. Более поздняя версия perf имеет опцию -g dwarf, чтобы использовать альтернативный метод libunwind / dwarf для извлечения стеков.

Я также писал о обратных трассировках в perf с некоторыми дополнительными ссылками: Как работает linux Утилита perf понимает следы стека?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...