Я знаю, что для этого нет стандартной функции C. Мне было интересно, как это можно сделать в Windows и * nix? (Windows XP - моя самая важная ОС для этого прямо сейчас.)
Я знаю, что для этого нет стандартной функции C. Мне было интересно, как это можно сделать в Windows и * nix? (Windows XP - моя самая важная ОС для этого прямо сейчас.)
Ответы:
glibc предоставляет функцию backtrace ().
http://www.gnu.org/software/libc/manual/html_node/Backtraces.html
char ** backtrace_symbols (void *const *buffer, int size)
void backtrace_symbols_fd(void *const *buffer, int size, int fd)
который, например, может отправлять вывод напрямую в stdout / err.
backtrace_symbols()
отстой. Он требует экспорта всех символов и не поддерживает символы DWARF (отладочные). libbacktrace - намного лучший вариант во многих (большинстве) случаях.
Есть 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 < frames; ++i) {
printf("%s\n", strs[i]);
}
free(strs);
}
};
...
try {
throw MyException("Oops!");
} catch ( MyException e ) {
e.printStackTrace();
}
Да да!
Примечание: включение флагов оптимизации может сделать итоговую трассировку стека неточной. В идеале эту возможность следует использовать с включенными флагами отладки и выключенными флагами оптимизации.
Для Windows проверьте API StackWalk64 () (также в 32-битной Windows). Для UNIX вы должны использовать собственный способ ОС или вернуться к функции backtrace () glibc, если она доступна.
Однако обратите внимание, что использование Stacktrace в машинном коде редко бывает хорошей идеей - не потому, что это невозможно, а потому, что вы обычно пытаетесь достичь неправильного результата.
В большинстве случаев люди пытаются получить трассировку стека, скажем, в исключительных обстоятельствах, например, когда обнаружено исключение, утверждение не выполняется или - что самое худшее и самое неправильное из всех - когда вы получаете фатальное «исключение» или сигнал вроде нарушение сегментации.
Учитывая последнюю проблему, большинство API-интерфейсов потребуют от вас явного выделения памяти или могут сделать это внутренне. Если вы сделаете это в том нестабильном состоянии, в котором ваша программа может находиться в настоящее время, это может действительно ухудшить ситуацию. Например, отчет о сбое (или coredump) будет отражать не фактическую причину проблемы, а вашу неудачную попытку ее решить).
Я предполагаю, что вы пытаетесь решить эту проблему с обработкой фатальных ошибок, поскольку большинство людей, кажется, пытаются это сделать, когда дело доходит до получения трассировки стека. Если это так, я бы положился на отладчик (во время разработки) и позволил бы процессу coredump в продакшене (или мини-дампу в Windows). Вместе с правильным управлением символами у вас не должно возникнуть проблем с посмертным выяснением причинной инструкции.
Вы должны использовать библиотеку размотки .
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, чтобы получить исходную функцию / номер строки соответствующего ПК.
Не существует независимого от платформы способа сделать это.
Ближайшее, что вы можете сделать, - это запустить код без оптимизации. Таким образом, вы можете подключиться к процессу (используя визуальный отладчик C ++ или GDB) и получить полезную трассировку стека.
Для Windows CaptureStackBackTrace()
это также вариант, который требует меньше кода подготовки на стороне пользователя, чем StackWalk64()
это требуется . (Кроме того, для аналогичного сценария, который у меня был, в CaptureStackBackTrace()
итоге работал лучше (более надежно), чем StackWalk64()
.)
В Solaris есть команда pstack , которая также была скопирована в Linux.
Вы можете сделать это, переместив стопку назад. В действительности, однако, часто проще добавить идентификатор в стек вызовов в начале каждой функции и вставить его в конце, а затем просто пройтись по нему, распечатав содержимое. Это немного похоже на PITA, но работает хорошо и в конечном итоге сэкономит ваше время.
Последние несколько лет я использую libbacktrace Яна Ланса Тейлора. Он намного чище, чем функции библиотеки GNU C, требующие экспорта всех символов. Он предоставляет больше возможностей для генерации трассировки, чем libunwind. И наконец, что не менее важно, ASLR не побеждает его, как подходы, требующие внешних инструментов, таких как addr2line
.
Libbacktrace изначально была частью дистрибутива GCC, но теперь она доступна автором как отдельная библиотека под лицензией BSD:
https://github.com/ianlancetaylor/libbacktrace
На момент написания я бы не стал использовать ничего другого, если мне не нужно генерировать обратные трассировки на платформе, которая не поддерживается libbacktrace.