Domanda

Quali metodi esistono per ottenere automaticamente una traccia dello stack sui sistemi Unix?Non intendo semplicemente ottenere un file core o allegarlo in modo interattivo con GDB, ma avere un gestore SIGSEGV che scarica un backtrace in un file di testo.

Punti bonus per le seguenti funzionalità opzionali:

  • Raccolta di informazioni aggiuntive al momento dell'incidente (es.file di configurazione).
  • Invia tramite e-mail un pacchetto di informazioni sull'arresto anomalo agli sviluppatori.
  • Possibilità di aggiungerlo in a dlopened libreria condivisa
  • Non richiede una GUI
È stato utile?

Soluzione

Se utilizzi sistemi con BSD backtrace funzionalità disponibili (Linux, OSX 1.5, BSD ovviamente), puoi farlo a livello di codice nel tuo gestore di segnale.

Per esempio (backtrace codice derivato dall'esempio 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();
}

Produzione:

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

Questo non ottiene punti bonus per le funzionalità opzionali (tranne che non richiede una GUI), tuttavia ha il vantaggio di essere molto semplice e di non richiedere librerie o programmi aggiuntivi.

Altri suggerimenti

PER TUA INFORMAZIONE,

la soluzione suggerita (utilizzando backtrace_symbols in un gestore di segnali) è pericolosamente interrotta.NON USARLO -

Sì, backtrace e backtrace_symbols produrranno un backtrace e lo tradurranno in nomi simbolici, tuttavia:

  1. backtrace_symbols alloca la memoria usando malloc e usi free per liberarla - Se stai andando in crash a causa della corruzione della memoria, è molto probabile che la tua arena malloc sia corrotta e causi un doppio errore.

  2. malloc e free proteggono l'arena malloc con un lucchetto interno.Potresti aver commesso un errore nel mezzo di un malloc/free con il blocco preso, il che causerà questa funzione o qualsiasi cosa che li richiami al blocco morto.

  3. Utilizzi put che utilizza il flusso standard, anch'esso protetto da un blocco.Se hai commesso un errore nel bel mezzo di una printf, ti trovi ancora una volta in una situazione di stallo.

  4. Su piattaforme a 32 bit (es.il tuo normale PC di 2 anni fa), il kernel inserirà nello stack un indirizzo di ritorno a una funzione glibc interna invece della funzione che ha causato l'errore, quindi l'informazione più importante che ti interessa - in quale funzione il programma ha commesso l'errore , sarà effettivamente danneggiato su quelle piattaforme.

Quindi, il codice nell'esempio è il peggior tipo di errore: SEMBRA che funzioni, ma in realtà ti deluderà in modi inaspettati nella produzione.

A proposito, sei interessato a farlo bene?controllo Questo fuori.

Saluti, Gilad.

Ecco un esempio di come ottenere ulteriori informazioni utilizzando un demangler.Come puoi vedere, questo registra anche lo stacktrace su file.

#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);
}

La soluzione di Dereks è probabilmente la migliore, ma ecco comunque un'alternativa:

La recente versione del kernel Linux consente di reindirizzare i core dump a uno script o un programma.Potresti scrivere uno script per catturare il core dump, raccogliere tutte le informazioni aggiuntive di cui hai bisogno e rispedire tutto per posta.Questa è però un'impostazione globale, quindi si applicherà a qualsiasi programma che si blocca sul sistema.Richiederà inoltre i diritti di root per la configurazione.Può essere configurato tramite il file /proc/sys/kernel/core_pattern.Impostalo su qualcosa come '| /Home/Myuser/Bin/My-core-Handler-script '.

Anche le persone di Ubuntu usano questa funzionalità.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top