Question

Quelles méthodes existe-t-il pour obtenir automatiquement une trace de pile sur les systèmes Unix ?Je ne parle pas simplement d'obtenir un fichier principal ou de le joindre de manière interactive avec GDB, mais d'avoir un gestionnaire SIGSEGV qui sauvegarde une trace dans un fichier texte.

Points bonus pour les fonctionnalités optionnelles suivantes :

  • Collecte d'informations supplémentaires au moment de l'accident (par ex.fichiers de configuration).
  • Envoyez par e-mail un ensemble d'informations sur le crash aux développeurs.
  • Possibilité d'ajouter ceci dans un dlopenbibliothèque partagée ed
  • Ne nécessite pas d'interface graphique
Était-ce utile?

La solution

Si vous êtes sur des systèmes avec BSD backtrace fonctionnalité disponible (Linux, OSX 1.5, BSD bien sûr), vous pouvez le faire par programme dans votre gestionnaire de signaux.

Par exemple (backtrace code dérivé de l'exemple 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();
}

Sortir:

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

Cela n'apporte pas de points bonus pour les fonctionnalités optionnelles (sauf qu'il ne nécessite pas d'interface graphique), cependant, il a l'avantage d'être très simple et de ne nécessiter aucune bibliothèque ou programme supplémentaire.

Autres conseils

POUR VOTRE INFORMATION,

la solution suggérée (en utilisant backtrace_symbols dans un gestionnaire de signal) est dangereusement brisée.NE L'UTILISE PAS -

Oui, backtrace et backtrace_symbols produiront une trace arrière et la traduiront en noms symboliques, cependant :

  1. backtrace_symbols alloue de la mémoire à l'aide de malloc et vous utilisez free pour la libérer. Si vous plantez à cause d'une corruption de mémoire, votre arène malloc est très susceptible d'être corrompue et de provoquer une double erreur.

  2. malloc et free protègent l'arène malloc avec un verrou interne.Vous avez peut-être commis une erreur au milieu d'un malloc/free avec le verrou pris, ce qui entraînera le blocage de ces fonctions ou de tout ce qui les appelle.

  3. Vous utilisez des puts qui utilisent le flux standard, qui est également protégé par un verrou.Si vous avez commis une erreur au milieu d'une impression, vous vous retrouvez à nouveau dans une impasse.

  4. Sur les plateformes 32 bits (par ex.votre PC normal d'il y a 2 ans), le noyau plantera une adresse de retour vers une fonction interne de la glibc au lieu de votre fonction défaillante dans votre pile, donc l'information la plus importante qui vous intéresse - dans quelle fonction le programme a-t-il commis une erreur , sera en fait corrompu sur ces plateformes.

Ainsi, le code de l'exemple est le pire type d'erreur : il semble fonctionner, mais il vous fera vraiment échouer de manière inattendue en production.

BTW, ça vous intéresse de le faire, n'est-ce pas ?vérifier ce dehors.

Bravo, Gilad.

Voici un exemple de la façon d'obtenir plus d'informations à l'aide d'un démêlant.Comme vous pouvez le voir, celui-ci enregistre également le stacktrace dans un fichier.

#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 solution de Derek est probablement la meilleure, mais voici quand même une alternative :

La version récente du noyau Linux vous permet de diriger les vidages de mémoire vers un script ou un programme.Vous pouvez écrire un script pour récupérer le core dump, collecter toutes les informations supplémentaires dont vous avez besoin et tout renvoyer par courrier.Il s'agit cependant d'un paramètre global, il s'appliquerait donc à tout programme en panne sur le système.Il faudra également des droits root pour être configuré.Il peut être configuré via le fichier /proc/sys/kernel/core_pattern.Réglez cela sur quelque chose comme '| / home / myuser / bin / my-core-handler-script '.

Les utilisateurs d'Ubuntu utilisent également cette fonctionnalité.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top