Как можно получить трассировку стека в C? - PullRequest
78 голосов
/ 20 сентября 2008

Я знаю, что для этого нет стандартной функции C. Мне было интересно, какие методы для этого в Windows и * nix? (Windows XP - моя самая важная ОС, чтобы сделать это прямо сейчас.)

Ответы [ 11 ]

78 голосов
/ 20 сентября 2008

glibc предоставляет функцию backtrace ().

http://www.gnu.org/software/libc/manual/html_node/Backtraces.html

28 голосов
/ 25 февраля 2010

Есть 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 () в конструкторе класса исключений. Таким образом, всякий раз, когда вы генерируете исключение такого типа, у вас есть трассировка стека. Затем просто предоставьте функцию для распечатки. Например:

<code>
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();
}
</code>

Та да!

Примечание: включение флагов оптимизации может сделать полученную трассировку стека неточной. В идеале эту функцию можно использовать при включенных флагах отладки и отключенных флагах оптимизации.

22 голосов
/ 20 сентября 2008

Мы использовали это для наших проектов:

https://www.codeproject.com/kb/threads/stackwalker.aspx

Код немного грязный ИМХО, но он работает хорошо. Только для Windows.

20 голосов
/ 24 сентября 2008

Для Windows проверьте API StackWalk64 () (также в 32-битной Windows). Для UNIX вы должны использовать собственный способ ОС или сделать возврат к glibc backtrace (), если таковой имеется.

Однако обратите внимание, что использование Stacktrace в нативном коде редко является хорошей идеей - не потому, что это невозможно, а потому, что вы обычно пытаетесь достичь неправильного результата.

В большинстве случаев люди пытаются получить стековую трассировку, скажем, в исключительных обстоятельствах, например, при обнаружении исключения, сбое утверждения или - худшем и наиболее неправильном из них - когда вы получаете фатальное «исключение» или сигнал, как нарушение сегментации.

Учитывая последнюю проблему, большинство API потребует от вас явного выделения памяти или может сделать это внутренне. Выполнение этого в хрупком состоянии, в котором ваша программа может находиться в данный момент, может на самом деле ухудшить ситуацию. Например, отчет о сбое (или coredump) не будет отражать фактическую причину проблемы, но ваша неудачная попытка ее устранить).

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

4 голосов
/ 31 января 2010

Вы должны использовать библиотеку для перемотки .

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, чтобы получить функцию источника / номер строки соответствующего ПК.

4 голосов
/ 02 октября 2008

Для Windows CaptureStackBackTrace() также является опцией, которая требует меньше подготовительного кода на стороне пользователя, чем StackWalk64(). (Кроме того, для аналогичного сценария, который у меня был, CaptureStackBackTrace() в итоге работал лучше (более надежно), чем StackWalk64().)

4 голосов
/ 20 сентября 2008

Нет независимого от платформы способа сделать это.

Самое близкое, что вы можете сделать, это запустить код без оптимизации. Таким образом, вы можете подключиться к процессу (используя отладчик Visual C ++ или GDB) и получить трассировку пригодного для использования стека.

2 голосов
/ 27 февраля 2009

Могу ли я указать вам на мою статью. Это всего лишь несколько строк кода.

Отладка после смерти

Хотя в настоящее время у меня есть проблемы с x64 реализацией этого .

2 голосов
/ 22 сентября 2008

Solaris имеет команду pstack , которая также была скопирована в Linux.

0 голосов
/ 08 февраля 2018

В течение последних нескольких лет я использовал libbacktrace Иэна Лэнса Тейлора. Это намного чище, чем функции в библиотеке GNU C, которые требуют экспорта всех символов. Он предоставляет больше полезности для генерации обратных трасс, чем libunwind. И последнее, но не менее важное, ASLR не побеждает его, как и подходы, требующие внешних инструментов, таких как addr2line.

Libbacktrace изначально была частью дистрибутива GCC, но теперь она доступна автору как отдельная библиотека под лицензией BSD:

https://github.com/ianlancetaylor/libbacktrace

На момент написания я не использовал бы ничего другого, если бы мне не нужно было генерировать обратные трассировки на платформе, которая не поддерживается libbacktrace.

...