Obtenir automatiquement des traces de stack sur les systèmes Unix

Quelles sont les méthodes pour obtenir automatiquement une trace de stack sur les systèmes Unix? Je ne veux pas dire simplement obtenir un fichier de base ou attacher interactivement avec GDB, mais avoir un gestionnaire SIGSEGV qui renvoie une trace vers un fichier texte.

Points bonus pour les fonctionnalités optionnelles suivantes:

  • Collecte d’informations supplémentaires au moment de la panne (par exemple, fichiers de configuration).
  • Envoyez un lot d’informations sur le plantage aux développeurs.
  • Possibilité d’append ceci dans une bibliothèque partagée de dlopen ed
  • Ne nécessitant pas d’interface graphique

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

Par exemple ( code de backtrace dérivé d’un exemple IBM ):

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

Sortie:

 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'obtient pas de points bonus pour les fonctionnalités optionnelles (sauf ne nécessitant 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.

FYI,

la solution suggérée (utilisation de backtrace_symbols dans un gestionnaire de signal) est dangereusement rompue. NE L’UTILISE PAS –

Oui, backtrace et backtrace_symbols produiront un backtrace et le traduiront en noms symboliques, cependant:

  1. backtrace_symbols alloue de la mémoire en utilisant malloc et que vous l’utilisez gratuitement – Si vous tombez en panne à cause d’une corruption de mémoire, votre centre commercial risque fort d’être corrompu et de provoquer une double erreur.

  2. malloc et free protègent l’arène malloc avec un verrou en interne. Vous avez peut-être commis une faute au milieu d’un malloc / free avec la serrure prise, ce qui entraînera ces fonctions ou tout ce qui les appelle à un verrou mortel.

  3. Vous utilisez des mises qui utilisent le stream standard, qui est également protégé par un verrou. Si vous avez commis une faute au milieu d’une impression, vous avez encore une fois une impasse.

  4. Sur les plates-formes 32 bits (par exemple, votre PC normal d’il y a 2 ans), le kernel va placer une adresse de retour dans une fonction glibc interne au lieu de la fonction de défaut dans votre stack. fonction a fait la faute du programme, sera effectivement corrompu sur ces plates-formes.

Donc, le code dans l’exemple est le pire type d’erreur – il aime bien que cela fonctionne, mais cela vous fera vraiment perdre des moyens inattendus dans la production.

BTW, intéressé à le faire correctement? vérifier ceci

Cheers, Gilad.

Voici un exemple de la façon d’obtenir plus d’informations en utilisant un démangler. Comme vous pouvez le voir, celui-ci enregistre également la stacktrace à classer.

 #include  #include  #include  #include  #include  void sig_handler(int sig) { std::ssortingngstream 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 Dereks est probablement la meilleure, mais voici une alternative:

La version récente du kernel Linux vous permet de transférer des core dumps vers un script ou un programme. Vous pouvez écrire un script pour récupérer le core dump, collecter les informations supplémentaires dont vous avez besoin et tout envoyer par courrier. Ceci est un paramètre global cependant, donc il devrait s’appliquer à n’importe quel programme en panne sur le système. Il faudra également des droits root pour configurer. Il peut être configuré via le fichier / proc / sys / kernel / core_pattern. Réglez cela à quelque chose comme ‘| / home / myuser / bin / my-core-handler-script ‘.

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