Eu sei que não existe uma função C padrão para fazer isso. Fiquei me perguntando quais são as técnicas para fazer isso no Windows e * nix? (Windows XP é meu sistema operacional mais importante para fazer isso agora.)
Eu sei que não existe uma função C padrão para fazer isso. Fiquei me perguntando quais são as técnicas para fazer isso no Windows e * nix? (Windows XP é meu sistema operacional mais importante para fazer isso agora.)
Respostas:
glibc fornece a função 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)
que pode enviar a saída diretamente para stdout / err, por exemplo.
backtrace_symbols()
é uma merda. Requer a exportação de todos os símbolos e não oferece suporte a símbolos DWARF (depuração). O libbacktrace é uma opção muito melhor na maioria dos casos.
Existem backtrace () e backtrace_symbols ():
Na página de manual:
#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);
...
Uma maneira de usar isso de uma maneira / OOP mais conveniente é salvar o resultado de backtrace_symbols () em um construtor de classe de exceção. Portanto, sempre que você lançar esse tipo de exceção, terá o rastreamento de pilha. Em seguida, basta fornecer uma função para imprimi-lo. Por exemplo:
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();
}
Ta da!
Observação: habilitar sinalizadores de otimização pode tornar o rastreamento de pilha impreciso. O ideal seria usar esse recurso com sinalizadores de depuração ativados e sinalizadores de otimização desativados.
Para Windows, verifique a API StackWalk64 () (também no Windows de 32 bits). Para UNIX, você deve usar a maneira nativa do sistema operacional de fazer isso, ou recorrer ao backtrace () da glibc, se disponível.
Observe, entretanto, que realizar um Stacktrace em código nativo raramente é uma boa ideia - não porque não seja possível, mas porque você normalmente está tentando obter o resultado errado.
Na maioria das vezes as pessoas tentam obter um rastreamento de pilha em, digamos, uma circunstância excepcional, como quando uma exceção é detectada, uma afirmação falha ou - o pior e mais errado de todos - quando você obtém uma "exceção" fatal ou sinal como um violação de segmentação.
Considerando o último problema, a maioria das APIs exigirá que você aloque explicitamente a memória ou pode fazê-lo internamente. Fazer isso no estado frágil em que seu programa pode estar atualmente pode piorar ainda mais as coisas. Por exemplo, o relatório de travamento (ou coredump) não refletirá a causa real do problema, mas sua tentativa fracassada de lidar com ele).
Suponho que você esteja tentando obter aquela coisa de tratamento de erros fatais, como muitas pessoas parecem tentar quando se trata de obter um rastreamento de pilha. Nesse caso, eu confiaria no depurador (durante o desenvolvimento) e deixaria o processo coredump na produção (ou mini-dump no windows). Junto com o gerenciamento de símbolos adequado, você não deve ter problemas para descobrir a instrução causadora post-mortem.
Você deve usar a biblioteca de desdobramento .
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;
}
Sua abordagem também funcionaria bem, a menos que você faça uma chamada de uma biblioteca compartilhada.
Você pode usar o addr2line
comando no Linux para obter a função de origem / número da linha do PC correspondente.
Não existe uma maneira independente de plataforma para fazer isso.
A coisa mais próxima que você pode fazer é executar o código sem otimizações. Dessa forma, você pode anexar ao processo (usando o depurador visual c ++ ou GDB) e obter um rastreamento de pilha utilizável.
Solaris tem o comando pstack , que também foi copiado para o Linux.
Você pode fazer isso movendo a pilha para trás. Na realidade, porém, é frequentemente mais fácil adicionar um identificador a uma pilha de chamadas no início de cada função e colocá-lo no final, depois apenas percorrer a impressão do conteúdo. É um pouco como um PITA, mas funciona bem e vai poupar tempo no final.
Nos últimos anos, tenho usado o libbacktrace de Ian Lance Taylor. É muito mais limpo do que as funções da biblioteca GNU C, que exigem a exportação de todos os símbolos. Ele fornece mais utilidade para a geração de backtraces do que o libunwind. E por último, mas não menos importante, não é derrotado pelo ASLR, pois são abordagens que requerem ferramentas externas, comoaddr2line
.
Libbacktrace era inicialmente parte da distribuição GCC, mas agora está disponível pelo autor como uma biblioteca independente sob uma licença BSD:
https://github.com/ianlancetaylor/libbacktrace
No momento em que este artigo foi escrito, eu não usaria mais nada, a menos que precise gerar backtraces em uma plataforma que não seja compatível com libbacktrace.