Вопрос

Я знаю, что для этого нет стандартной функции 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 <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();
}

Та да!

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

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

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

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

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

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

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

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

Для 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 для получения исходной функции / номера строки соответствующего компьютера.

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

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

Посмертная отладка

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

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

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

https://github.com/ianlancetaylor/libbacktrace

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top