Как можно захватить трассировку стека в C?
Я знаю, что для этого нет стандартной функции C. Мне было интересно, какие методы для этого на Windows и *nix? (Windows XP - это моя самая важная ОС, чтобы сделать это прямо сейчас.)
11 ответов:
мы использовали это для наших проектов:
https://www.codeproject.com/kb/threads/stackwalker.aspx
код немного грязный ИМХО, но он хорошо работает. Только окна.
glibc обеспечивает функцию backtrace ().
http://www.gnu.org/software/libc/manual/html_node/Backtraces.html
есть backtrace (), и backtrace_symbols ():
С главной страницы:
#include <execinfo.h> #include <stdio.h> ... void* callstack[128]; int i, frames = backtrace(callstack, 128); char** strs = backtrace_symbols(callstack, frames); for (i = 0; i < frames; ++i) { printf("%s\n", strs[i]); } free(strs); ...
один из способов использовать это более удобным способом/OOP-сохранить результат backtrace_symbols () в конструкторе класса исключений. Таким образом, всякий раз, когда вы бросаете этот тип исключения, у вас есть трассировка стека. Затем просто предоставьте функцию для его печати. Например:
class MyException : public std::exception { char ** strs; MyException( const std::string & message ) { int i, frames = backtrace(callstack, 128); strs = backtrace_symbols(callstack, frames); } void printStackTrace() { for (i = 0; i
...
try { throw MyException("Oops!"); } catch ( MyException e ) { e.printStackTrace(); }та-да!
Примечание: включение оптимизации флаги могут сделать результирующую трассировку стека неточной. В идеале, можно было бы использовать эту возможность с флагами отладки и флагами оптимизации.
для Windows проверьте API StackWalk64 () (также на 32-битных окнах). Для UNIX вы должны использовать собственный способ ОС, чтобы сделать это, или откат к glibc backtrace (), если доступно.
однако обратите внимание, что использование Stacktrace в машинном коде редко бывает хорошей идеей - не потому, что это невозможно, а потому, что вы обычно пытаетесь достичь неправильной вещи.
большую часть времени люди пытаются получить stacktrace, скажем, в исключительных обстоятельствах, например, когда исключение пойманный, утверждение терпит неудачу или - самое худшее и самое неправильное из них-когда вы получаете фатальное "исключение" или сигнал, подобный нарушению сегментации.
учитывая последнюю проблему, большинство API потребует от вас явного выделения памяти или может сделать это внутренне. Делая это в хрупком состоянии, в котором ваша программа может быть в настоящее время, может резко ухудшить ситуацию. Например, отчет о сбое (или coredump) не будет отражать фактическую причину проблемы, но ваш сбой попытка справиться с этим).
Я предполагаю, что вы пытаетесь достичь этой фатальной обработки ошибок, поскольку большинство людей, похоже, пытаются это сделать, когда дело доходит до получения stacktrace. Если это так, я бы полагался на отладчик (во время разработки) и позволял процессу coredump в производстве (или мини-дамп в windows). Вместе с правильным управлением символами у вас не должно возникнуть проблем с определением причинной инструкции посмертно.
Для Windows,
CaptureStackBackTrace()
также является опцией, которая требует меньше кода подготовки на конце пользователя, чемStackWalk64()
делает. (Кроме того, для аналогичного сценария у меня было,CaptureStackBackTrace()
в конечном итоге работает лучше (надежнее), чемStackWalk64()
.)
вы должны использовать расслабиться библиотека.
unw_cursor_t cursor; unw_context_t uc; unw_word_t ip, sp; unw_getcontext(&uc); unw_init_local(&cursor, &uc); unsigned long a[100]; int ctr = 0; while (unw_step(&cursor) > 0) { unw_get_reg(&cursor, UNW_REG_IP, &ip); unw_get_reg(&cursor, UNW_REG_SP, &sp); if (ctr >= 10) break; a[ctr++] = ip; }
ваш подход также будет работать нормально, если вы не сделаете звонок из общей библиотеки.
можно использовать
addr2line
команда на Linux, чтобы получить исходную функцию / номер строки соответствующего ПК.
нет независимого от платформы способа сделать это.
ближайшее, что вы можете сделать, это запустить код без оптимизации. Таким образом, вы можете подключиться к процессу (используя отладчик visual c++ или GDB) и получить полезную трассировку стека.
Солярис имеет pstack команда, которая также была скопирована в Linux.
могу ли я указать вам на мою статью. Это всего лишь несколько строк кода.
хотя у меня в настоящее время есть проблемы с x64 реализация этого.
в течение последних нескольких лет я использую libbacktrace Яна Лэнса Тейлора. Это намного чище, чем функции в библиотеке GNU C, которые требуют экспорта всех символов. Он обеспечивает больше полезности для генерации обратных следов, чем libunwind. И последнее, но не менее важное: он не побежден ASLR, поскольку подходы требуют внешних инструментов, таких как
addr2line
.Libbacktrace изначально был частью дистрибутива GCC, но теперь он доступен автором в виде автономная библиотека под лицензией BSD:
https://github.com/ianlancetaylor/libbacktrace
на момент написания статьи я бы не использовал ничего другого, если бы мне не нужно было генерировать обратные следы на платформе, которая не поддерживается libbacktrace.