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

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

Ответ 3

Здесь backtrace() и backtrace_symbols():

На странице man:

     #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);
     ...

Один из способов использования этого в более удобном/ООП-способе заключается в сохранении результата 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();
}

Ta da!

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

Ответ 4

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

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

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

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

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

Ответ 5

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

Ответ 6

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

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

Ответ 7

Для этого нет независимого от платформы способа.

Ближайшая вещь, которую вы можете сделать, - запустить код без оптимизации. Таким образом, вы можете подключиться к процессу (используя визуальный С++-отладчик или GDB) и получить полезную трассировку стека.

Ответ 8

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

Ответ 10

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

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

https://github.com/ianlancetaylor/libbacktrace

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

Ответ 11

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