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

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

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

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

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

    glibc обеспечивает функцию backtrace ().

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

    Есть backtrace () и backtrace_symbols ():

    На странице руководства:

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

    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 

    ...

     пытаться {
        throw MyException («Oops!»);
     } catch (MyException e) {
         e.printStackTrace ();
     }
    
    

    Та да!

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

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

    Обратите внимание, однако, что использование 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) и получить полезную трассировку стека.

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

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

    Сообщение от Mortem Debugging

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

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

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

    https://github.com/ianlancetaylor/libbacktrace

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

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

    Давайте будем гением компьютера.