Как можно захватить трассировку стека в C?


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

11 74

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.

вы можете сделать это, пройдя стек назад. На самом деле, однако, часто проще добавить идентификатор в стек вызовов в начале каждой функции и вытащить его в конце, а затем просто пройти эту печать содержимого. Это немного Пита, но он хорошо работает и сэкономит вам время в конце концов.