Список всех вызовов функций, выполненных в приложении
Как мы можем перечислить все функции, вызываемые в приложении. Я пытался использовать GDB, но его список следов только до вызова основной функции.
Мне нужен более глубокий список, т.е. список всех функций, вызываемых основной функцией, и функции, вызываемой из этих вызываемых функций, и так далее.
Есть ли способ получить это в GDB? Или не могли бы вы дать мне советы, как это получить?
5 ответов
Как мы можем перечислить все функции, вызываемые в приложении
Для любого приложения реалистичного размера этот список будет содержать тысячи записей, что, вероятно, сделает его бесполезным.
Вы можете найти все функции, определенные (но не обязательно вызванные) в приложении с nm
команда, например
nm /path/to/a.out | egrep ' [TW] '
Вы также можете использовать GDB, чтобы установить точку останова для каждой функции:
(gdb) set logging on # collect trace in gdb.txt
(gdb) set confirm off # you wouldn't want to confirm every one of them
(gdb) rbreak . # set a breakpoint on each function
Как только вы продолжите, вы достигнете точки останова для каждой вызванной функции. Использовать disable
а также continue
Команды для продвижения вперед. Я не верю, что есть простой способ автоматизировать это, если только вы не хотите использовать скрипты Python.
Уже упоминалось gprof
это еще один хороший вариант.
Вы хотите график звонков. Инструмент, который вы хотите использовать, это не GDB, это gprof
, Вы компилируете свою программу с -pg
а затем запустить его. Когда он запускает файл gmon.out
будет произведено. Затем вы обрабатываете этот файл gprof
и наслаждайтесь выводом.
запись истории вызовов
https://sourceware.org/gdb/onlinedocs/gdb/Process-Record-and-Replay.html
Это должно стать отличной возможностью для аппаратного ускорения, если вы один из немногих (2015 г.) с процессором, поддерживающим Intel Processor Tracing (Intel PT, intel_pt
в /proc/cpuinfo
).
Документы GDB утверждают, что он может производить выходные данные, такие как:
(gdb) list 1, 10
1 void foo (void)
2 {
3 }
4
5 void bar (void)
6 {
7 ...
8 foo ();
9 ...
10 }
(gdb) record function-call-history /ilc
1 bar inst 1,4 at foo.c:6,8
2 foo inst 5,10 at foo.c:2,3
3 bar inst 11,13 at foo.c:9,10
Перед его использованием необходимо выполнить:
start
record btrace
где неработающий ЦП отказывает:
Target does not support branch tracing.
Поддержка ЦП обсуждается далее: Как запустить историю команд записи и историю вызовов функций в GDB?
Связанные темы:
- как отследить вызов функции в C?
- Есть ли функция компилятора для ввода кода пользовательской функции и выхода?
Для встраиваемых систем вы также рассматриваете JTAG и поддерживающее оборудование, например, ARM DSTREAM, но поддержка x86 выглядит не очень хорошо: отладка ядра x86 с использованием аппаратного отладчика
Этот вопрос может нуждаться в уточнении, чтобы выбрать между тем, что в настоящее время 2 ответа. Зависит от того, что вам нужно:
1) Вам необходимо знать, сколько раз каждая функция вызывается в формате прямого списка / графика функций, соответствующих количеству вызовов. Это может привести к неоднозначным / неубедительным результатам, если ваш код не является процедурным (то есть функции, вызывающие другие функции в структуре разветвления без неоднозначности того, что вызывает что). Это базовая функциональность gprof, которая требует перекомпиляции с флагом -pg.
2) Вам нужен список функций в том порядке, в котором они были вызваны, это зависит от вашей программы, которая является наилучшей / выполнимой опцией: a) Если ваша программа запускается и завершается без ошибок времени выполнения, вы можете использовать для этой цели gprof. б) Опция ELSE, описанная выше с использованием dbg с логированием и точками останова, - это оставленная опция, которую я изучил, прочитав это.
3) Вам нужно знать не только порядок, но, например, аргументы функции для каждого вызова. Моя текущая работа - моделирование в физике переноса частиц, так что это было бы АБСОЛЮТНО полезно для отслеживания того, откуда приходят аномальные результаты... т.е. когда аргументы, передаваемые вокруг, перестают иметь смысл. Я полагаю, что один из способов сделать это - это вариация того, что делал Employed Russian, за исключением использования следующего:
(GDB) информация аргументы
Регистрация результатов этой команды с каждой точкой останова (устанавливается при каждом вызове функции) дает аргументы текущей функции.
Сgdb
, если вы можете найти самую дочернюю функцию, вы можете перечислить всех ее предков следующим образом:
gdb <your-binary>
(gdb) b theMostChildFunction ## put breakpoint on the desired function
(gdb) r ## run the program
(gdb) bt ## backtrace starting from the breakpoint
В противном случае в Linux вы можете использоватьинструмент для отслеживания программ и вызовов их функций. Преимущество этого в том, что он отслеживает все процессы, включая дочерние процессы, а также показывает процент использования функций в программе.
Вы можете установить так:
sudo apt install linux-tools-generic
sudo apt install linux-cloud-tools-generic
Перед использованием вам также может потребоваться временно снять некоторые ограничения ядра:
sudo sh -c 'echo 0 >/proc/sys/kernel/kptr_restrict'
sudo sh -c 'echo 0 >/proc/sys/kernel/perf_event_paranoid'
sudo sh -c 'echo 0 >/proc/sys/kernel/yama/ptrace_scope'
После этого вы можете запустить двоичный файл вашей программы следующим образом:
perf record -g -s -a <your-binary-and-its-flags>
Затем вы можете посмотреть вывод на терминал следующим образом:
perf report
или в текстовом файле следующим образом:
perf report -i perf.data > output.txt
vim output.txt
когда вы записываете вызовы функций с помощьюperf
также вы можете захотеть отфильтровать вызовы ядра с помощью--all-user
флаг:
perf record -g -s -a --all-user <your-binary-and-its-flags>
Для получения дополнительной информации вы можете посмотреть здесь: https://perf.wiki.kernel.org/index.php/Tutorial