Pergunta

Quais métodos existem para obter automaticamente um rastreamento de pilha em sistemas Unix?Não me refiro apenas a obter um arquivo principal ou anexar interativamente ao GDB, mas a ter um manipulador SIGSEGV que despeja um backtrace em um arquivo de texto.

Pontos de bônus para os seguintes recursos opcionais:

  • Coleta de informações extras no momento do acidente (ex.arquivos de configuração).
  • Envie por e-mail um pacote de informações sobre falhas para os desenvolvedores.
  • Capacidade de adicionar isso em um dlopenbiblioteca compartilhada ed
  • Não requer uma GUI
Foi útil?

Solução

Se você estiver em sistemas com BSD backtrace funcionalidade disponível (Linux, OSX 1.5, BSD, é claro), você pode fazer isso programaticamente em seu manipulador de sinal.

Por exemplo (backtrace código derivado do exemplo da IBM):

#include <execinfo.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>

void sig_handler(int sig)
{
    void * array[25];
    int nSize = backtrace(array, 25);
    char ** symbols = backtrace_symbols(array, nSize);

    for (int i = 0; i < nSize; i++)
    {
        puts(symbols[i]);;
    }

    free(symbols);

    signal(sig, &sig_handler);
}

void h()
{
    kill(0, SIGSEGV);
}

void g()
{
    h();
}

void f()
{
    g();
}

int main(int argc, char ** argv)
{
    signal(SIGSEGV, &sig_handler);
    f();
}

Saída:

0   a.out                               0x00001f2d sig_handler + 35
1   libSystem.B.dylib                   0x95f8f09b _sigtramp + 43
2   ???                                 0xffffffff 0x0 + 4294967295
3   a.out                               0x00001fb1 h + 26
4   a.out                               0x00001fbe g + 11
5   a.out                               0x00001fcb f + 11
6   a.out                               0x00001ff5 main + 40
7   a.out                               0x00001ede start + 54

Isso não ganha pontos de bônus pelos recursos opcionais (exceto não exigir uma GUI), no entanto, tem a vantagem de ser muito simples e não exigir nenhuma biblioteca ou programa adicional.

Outras dicas

PARA SUA INFORMAÇÃO,

a solução sugerida (usando backtrace_symbols em um manipulador de sinal) está perigosamente quebrada.NÃO USE ISSO -

Sim, backtrace e backtrace_symbols produzirão um backtrace e o traduzirão para nomes simbólicos, no entanto:

  1. backtrace_symbols aloca memória usando malloc e você usa free para liberá-la - Se você estiver travando por causa de corrupção de memória, é muito provável que sua arena malloc esteja corrompida e cause uma falha dupla.

  2. malloc e free protegem a arena malloc com uma trava interna.Você pode ter falhado no meio de um malloc/free com o bloqueio obtido, o que fará com que essas funções ou qualquer coisa que as chame para bloqueio morto.

  3. Você usa puts que usa o fluxo padrão, que também é protegido por um bloqueio.Se você errou no meio de um printf, mais uma vez você terá um impasse.

  4. Em plataformas de 32 bits (por ex.seu PC normal de 2 anos atrás), o kernel irá plantar um endereço de retorno para uma função glibc interna em vez de sua função com falha em sua pilha, então a informação mais importante em que você está interessado - em qual função o programa falhou , será realmente corrompido nessas plataformas.

Portanto, o código no exemplo é o pior tipo de erro - PARECE que está funcionando, mas realmente irá falhar de maneiras inesperadas na produção.

Aliás, interessado em fazer isso certo?verificar esse fora.

Saúde, Gilad.

Aqui está um exemplo de como obter mais informações usando um desmantelador.Como você pode ver, este também registra o stacktrace no arquivo.

#include <iostream>
#include <sstream>
#include <string>
#include <fstream>
#include <cxxabi.h>

void sig_handler(int sig)
{
    std::stringstream stream;
    void * array[25];
    int nSize = backtrace(array, 25);
    char ** symbols = backtrace_symbols(array, nSize);
    for (unsigned int i = 0; i < size; i++) {
        int status;
        char *realname;
        std::string current = symbols[i];
        size_t start = current.find("(");
        size_t end = current.find("+");
        realname = NULL;
        if (start != std::string::npos && end != std::string::npos) {
            std::string symbol = current.substr(start+1, end-start-1);
            realname = abi::__cxa_demangle(symbol.c_str(), 0, 0, &status);
        }
        if (realname != NULL)
            stream << realname << std::endl;
        else
            stream << symbols[i] << std::endl;
        free(realname);
    }
    free(symbols);
    std::cerr << stream.str();
    std::ofstream file("/tmp/error.log");
    if (file.is_open()) {
        if (file.good())
            file << stream.str();
        file.close();
    }
    signal(sig, &sig_handler);
}

A solução de Derek é provavelmente a melhor, mas aqui está uma alternativa:

A versão recente do kernel Linux permite canalizar core dumps para um script ou programa.Você pode escrever um script para capturar o core dump, coletar qualquer informação extra necessária e enviar tudo de volta.Esta é uma configuração global, portanto, se aplicaria a qualquer programa com falha no sistema.Também exigirá direitos de root para configurar.Ele pode ser configurado através do arquivo /proc/sys/kernel/core_pattern.Defina isso como algo como '| /home/myuser/bin/my-core-man-script '.

O pessoal do Ubuntu também usa esse recurso.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top