Ответ 1
Существует краткое введение о трассировке стека в perf
от Gregg:
http://www.brendangregg.com/perf.html
4.4 Трассировка стека
Всегда компилируйте с помощью указателей на рамки. Опускание указателей на кадры - это злая оптимизация компилятора, которая прерывает отладчики, и, к сожалению, часто используется по умолчанию. Без них вы можете видеть неполные стеки с perf_events... Есть два способа исправить это: либо использовать данные карликов, чтобы разматывать стек, либо возвращать указатели на фреймы.
Гном
Начиная с ядра 3.9, perf_events поддерживает обходное решение для отсутствующих указателей на фреймы уровня пользователя: libunwind, который использует карлик. Это можно активировать с помощью "-g dwarf".... оптимизация компилятора (
-O2
), которая в этом случае опустила указатель на фрейм.... перекомпиляция.. с помощью-fno-omit-frame-pointer
:
Языки, не относящиеся к C, могут иметь другой формат кадра или могут также опускать указатели на рамки:
4,3. Символы JIT (Java, Node.js)
Программы, в которых виртуальные машины (виртуальные машины), такие как Java JVM и node v8, выполняют собственный виртуальный процессор, который имеет свой собственный способ выполнения функций и управления стеками. Если вы профилируете их с помощью perf_events, вы увидите символы для механизма VM. Perf_events имеет поддержку JIT для решения этой проблемы, которая требует, чтобы виртуальная машина поддерживала файл
/tmp/perf-PID.map
для перевода символов.Обратите внимание, что Java может не отображать полные стеки для начала, из-за того, что hotspot на x86 удаляет указатель кадра (точно так же, как gcc). В более новых версиях (JDK 8u60 +) вы можете использовать параметр
-XX:+PreserveFramePointer
, чтобы исправить это поведение,...
Сообщение в блоге Gregg о трассировке Java и стека: http://techblog.netflix.com/2015/07/java-in-flames.html ( "Fixing Frame Pointers" - исправлено в некоторых версиях JDK8 и в JDK9 путем добавления опции при запуске программы)
Теперь ваши вопросы:
Как утилита linux perf понимает трассировки стека?
perf
утилита в основном (в ранних версиях) просто анализирует данные, возвращаемые из подсистемы Linux linux < <26 > "(или иногда" events
"), доступ к которым осуществляется с помощью syscall perf_event_open
. Для трассировки стека вызовов есть опции PERF_SAMPLE_CALLCHAIN
/PERF_SAMPLE_STACK_USER
:
sample_type PERF_SAMPLE_CALLCHAIN Записывает цепочку звонков (backtrace стека).
PERF_SAMPLE_STACK_USER (since Linux 3.7)
Records the user level stack, allowing stack unwinding.
Ядро Linux отлично понимает трассировку стека?
Он может понять (если он реализован) и не может, в зависимости от вашей архитектуры процессора. Функция вызова (считывания/чтения вызова из живого процесса) callchain определена в независимой от архитектуры части ядра как __weak
с пустым телом:
http://lxr.free-electrons.com/source/kernel/events/callchain.c?v=4.4#L26
27 __weak void perf_callchain_kernel(struct perf_callchain_entry *entry,
28 struct pt_regs *regs)
29 {
30 }
31
32 __weak void perf_callchain_user(struct perf_callchain_entry *entry,
33 struct pt_regs *regs)
34 {
35 }
В 4.4 ядре пользовательского пространства callchain sampler переопределяется в зависимой от архитектуры части ядра для x86/x86_64, ARC, SPARC, ARM/ARM64, Xtensa, Tilera TILE, PowerPC, Imagination Meta:
http://lxr.free-electrons.com/ident?v=4.4;i=perf_callchain_user
arch/x86/kernel/cpu/perf_event.c, line 2279
arch/arc/kernel/perf_event.c, line 72
arch/sparc/kernel/perf_event.c, line 1829
arch/arm/kernel/perf_callchain.c, line 62
arch/xtensa/kernel/perf_event.c, line 339
arch/tile/kernel/perf_event.c, line 995
arch/arm64/kernel/perf_callchain.c, line 109
arch/powerpc/perf/callchain.c, line 490
arch/metag/kernel/perf_callchain.c, line 59
Чтение цепочек вызовов из пользовательского стека может быть не тривиальным для некоторых архитектур и/или для некоторых режимов.
Какую архитектуру процессора вы используете? Какие языки и виртуальные машины используются?
Где я могу узнать больше о том, как инструмент способен интроспективно отслеживать стеки процессов, даже если процессы написаны на совершенно разных языках?
Вы можете попробовать gdb
и/или отладчики для языка или backtrace
function libc или поддержку только для чтения в libunwind (есть локальный пример backtrace в libunwind, show_backtrace()
).
У них может быть больше поддержки синтаксического разбора кадров/лучшей интеграции с виртуальной машиной языка или с помощью размотки информации. Если gdb (с командой backtrace
) или другими отладчиками не может получить трассировки стека из запущенной программы, вообще не может быть трассировки стека.
Если они могут получить трассировку вызова, но perf
не может (даже после перекомпиляции с помощью -fno-omit-frame-pointer
для C/С++), может быть возможно добавить поддержку такой комбинации архитектуры + формат кадра в perf_events
и perf
.
Есть несколько блогов с некоторой информацией об общих проблемах и решениях backtracing:
- http://eli.thegreenplace.net/2015/programmatic-access-to-the-call-stack-in-c/ - локальная обратная трассировка с libunwind
- http://codingrelic.geekhold.com/2009/05/pre-mortem-backtracing.html gcc
__builtin_return_address(N)
vs glibcbacktrace()
vs libunwind local backtrace - http://lucumr.pocoo.org/2014/10/30/dont-panic/ backtrace и разматывание в ржавчине
- https://github.com/gperftools/gperftools/wiki/gperftools '-stacktrace-capture-methods-and-their-issues та же проблема обратного трассировки в библиотеке профилировщика с программным таймером gperftools
Поддержка карлика для perf_events
/perf
:
- https://lwn.net/Articles/499116/ [RFCv4 00/16] perf: Добавить backtrace post dwarf unwind, may 2012
- https://lwn.net/Articles/507753/ [PATCHv7 00/17] perf: Добавить backtrace post dwarf unwind, июль 2012
- https://wiki.linaro.org/LEG/Engineering/TOOLS/perf-callstack-unwinding - Развертывание гномов на ARM 7/8 для перформанса
- https://wiki.linaro.org/KenWerner/Sandbox/libunwind#libunwind_ARM_unwind_methods - не-карликовые методы тоже