2008-09-19 3 views
68

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

+1

Для ядра: http://stackoverflow.com/questions/21143924/how-to-print-the-current-thread-stack-trace-in-c, версия C/C++: http: // stackoverflow. com/questions/3899870/print-call-stack-in-c-or-c – 2015-07-02 18:37:38

ответ

-1

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

+1

Не могли бы вы объяснить «ходьбу стека назад» более подробно? – Spidey 2012-08-31 20:45:55

3

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

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

+0

Это не помогает мне, когда происходит сбой на встроенном компьютере в поле. :( – Kevin 2008-09-19 21:19:18

+0

@Kevin: Даже на встроенных машинах обычно есть способ получить удаленный отладчик отладки или, по крайней мере, дамп ядра. Может быть, не раз он развернут в поле, хотя ... – ephemient 2010-01-31 06:49:37

+0

, если вы используете gcc-glibc on ваша платформа выбора windows/linux/mac ... then backtrace() и backtrace_symbols() будут работать на всех трех платформах. Учитывая это утверждение, я бы использовал слова «нет [портативного] способа сделать это». – 2011-09-09 18:00:58

73

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

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

+6

glibc FTW ... снова. (Это еще одна причина, по которой я считаю glibc абсолютным золотым стандартом, когда дело доходит до программирования на C (это и компилятор, который идет с ним). – 2011-09-09 17:53:04

+4

Но подождите, что еще есть!Функция backtrace() предоставляет только массив указателей void *, представляющих функции вызова. «Это не очень полезно. Не бойся! glibc предоставляет функцию, которая преобразует все адреса void * (адреса функции вызова) в символы, читаемые пользователем. `char ** backtrace_symbols (void * const * buffer, int size)` – 2011-09-09 17:55:16

+0

Я считаю void * символьным именем для функций -> IMO, который является довольно привлекательным voodoo-blackmagic. – 2011-09-09 17:55:55

2

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

20

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

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

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

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

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

4

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

4

Вы должны использовать unwind library.

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

23

Там в трассировку(), и 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(); 
} 
 

Та да!

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

0

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

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

https://github.com/ianlancetaylor/libbacktrace

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

Смежные вопросы