Comment générer un core dump sous Linux sur un défaut de segmentation?

217

J'ai un processus sous Linux qui obtient un défaut de segmentation. Comment puis-je lui dire de générer un vidage de mémoire en cas d'échec?

Nathan Fellman
la source
1
Comment le voir après: stackoverflow.com/questions/8305866/…
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Réponses:

249

Cela dépend du shell que vous utilisez. Si vous utilisez bash, la commande ulimit contrôle plusieurs paramètres relatifs à l'exécution du programme, par exemple si vous devez vider le core. Si vous tapez

ulimit -c unlimited

alors cela indiquera à bash que ses programmes peuvent vider des cœurs de n'importe quelle taille. Vous pouvez spécifier une taille telle que 52M au lieu d'un nombre illimité si vous le souhaitez, mais en pratique cela ne devrait pas être nécessaire car la taille des fichiers de base ne sera probablement jamais un problème pour vous.

Dans tcsh, vous taperiez

limit coredumpsize unlimited
Eli Courtwright
la source
21
@lzprgmr: Pour clarifier: la raison pour laquelle les vidages de mémoire ne sont pas générés par défaut est que la limite n'est pas définie et / ou définie sur 0, ce qui empêche le vidage du cœur. En fixant une limite illimitée, nous garantissons que les vidages mémoire peuvent toujours être générés.
Eli Courtwright,
6
Ce lien va plus loin et donne quelques options supplémentaires pour permettre la génération de vidages mémoire sous Linux. Le seul inconvénient est que certaines commandes / paramètres restent inexpliqués.
Salsa
6
Sur bash 4.1.2 (1), les limites de libération telles que 52M ne peuvent pas être spécifiées, ce qui entraîne un message d'erreur de numéro non valide. La page de manuel indique que "Les valeurs sont par incréments de 1024 octets".
a1an
4
Eh bien, j'avais un "petit" projet OpenGL, qui a fait quelque chose de bizarre et qui a provoqué un crash du serveur X. Lorsque je me suis reconnecté, j'ai vu un joli petit fichier de base de 17 Go (sur une partition de 25 Go). C'est certainement une bonne idée de limiter la taille du fichier core :)
IceCool
1
@PolarisUser: Si vous voulez vous assurer que votre partition ne soit pas mangée, je recommande de définir une limite de quelque chose comme 1 gig. Cela devrait être assez grand pour gérer tout vidage de mémoire raisonnable, sans menacer d'utiliser tout l'espace disque restant.
Eli Courtwright
60

Comme expliqué ci-dessus, la vraie question posée ici est de savoir comment activer les vidages mémoire sur un système où ils ne sont pas activés. On répond ici à cette question.

Si vous êtes venu ici dans l'espoir d'apprendre à générer un vidage de mémoire pour un processus bloqué, la réponse est

gcore <pid>

si gcore n'est pas disponible sur votre système,

kill -ABRT <pid>

N'utilisez pas kill -SEGV car cela invoquera souvent un gestionnaire de signal, ce qui rendra plus difficile le diagnostic du processus bloqué

George Co
la source
Je pense qu'il est beaucoup plus probable que -ABRTcela invoque un gestionnaire de signaux -SEGV, car un abandon est plus susceptible d'être récupéré qu'un défaut de segmentation. (Si vous gérez un défaut de segmentation, il se déclenchera normalement à nouveau dès que votre gestionnaire se terminera.) Un meilleur choix de signal pour générer un vidage de mémoire est -QUIT.
celticminstrel
32

Pour vérifier où les vidages mémoire sont générés, exécutez:

sysctl kernel.core_pattern

ou:

cat /proc/sys/kernel/core_pattern

%eest le nom du processus et %tl'heure du système. Vous pouvez le changer /etc/sysctl.confet recharger par sysctl -p.

Si les fichiers de base ne sont pas générés (test par: sleep 10 &et killall -SIGSEGV sleep), vérifier les limites par: ulimit -a.

Si la taille de votre fichier principal est limitée, exécutez:

ulimit -c unlimited

pour le rendre illimité.

Ensuite, testez à nouveau, si le vidage du cœur est réussi, vous verrez «(cœur vidé)» après l'indication de défaut de segmentation comme ci-dessous:

Erreur de segmentation: 11 (core dumped)

Voir aussi: core dumped - mais le fichier core n'est pas dans le répertoire courant?


Ubuntu

Dans Ubuntu, les vidages mémoire sont gérés par Apport et peuvent être localisés dans /var/crash/. Cependant, il est désactivé par défaut dans les versions stables.

Pour plus de détails, veuillez vérifier: Où puis-je trouver le vidage de mémoire dans Ubuntu? .

macOS

Pour macOS, voir: Comment générer des vidages mémoire sous Mac OS X?

Kenorb
la source
3
Pour Ubuntu, pour revenir rapidement à un comportement normal (vidage d'un fichier principal dans le répertoire actuel), arrêtez simplement le service d'allocation avec "sudo service alloc stop". Notez également que si vous utilisez Docker, ce paramètre est contrôlé sur le système hôte et non dans le conteneur.
Digicrat
26

Ce que j'ai fait à la fin était d'attacher gdb au processus avant qu'il ne plante, puis quand il a eu le défaut de segmentation, j'ai exécuté la generate-core-filecommande. Cette génération forcée d'un vidage de mémoire.

Nathan Fellman
la source
Comment avez-vous attaché gdb au processus?
Chani
6
Pour répondre à Ritwik G, pour attacher un processus à gdb, lancez simplement gdb et entrez 'attach <pid>' où <pid> est le numéro pid du processus que vous voulez attacher.
Jean-Dominique Frattini
(abrégé en ge)
user202729
S'ils ont une nouvelle question, ils devraient poser une nouvelle question au lieu de la poser dans un commentaire.
user202729
Chose étrange, je suis déjà réglé ulimit -csur unlimited, mais le fichier core n'est toujours pas créé, le generate-core-filefichier dans la session gdb crée le fichier core, merci.
CodyChan
19

Peut-être que vous pourriez le faire de cette façon, ce programme est une démonstration de la façon de piéger une erreur de segmentation et de la décoder vers un débogueur (c'est le code d'origine utilisé ci-dessous AIX) et imprime la trace de la pile jusqu'au point d'une erreur de segmentation. Vous devrez changer la sprintfvariable à utiliser gdbdans le cas de Linux.

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

static void signal_handler(int);
static void dumpstack(void);
static void cleanup(void);
void init_signals(void);
void panic(const char *, ...);

struct sigaction sigact;
char *progname;

int main(int argc, char **argv) {
    char *s;
    progname = *(argv);
    atexit(cleanup);
    init_signals();
    printf("About to seg fault by assigning zero to *s\n");
    *s = 0;
    sigemptyset(&sigact.sa_mask);
    return 0;
}

void init_signals(void) {
    sigact.sa_handler = signal_handler;
    sigemptyset(&sigact.sa_mask);
    sigact.sa_flags = 0;
    sigaction(SIGINT, &sigact, (struct sigaction *)NULL);

    sigaddset(&sigact.sa_mask, SIGSEGV);
    sigaction(SIGSEGV, &sigact, (struct sigaction *)NULL);

    sigaddset(&sigact.sa_mask, SIGBUS);
    sigaction(SIGBUS, &sigact, (struct sigaction *)NULL);

    sigaddset(&sigact.sa_mask, SIGQUIT);
    sigaction(SIGQUIT, &sigact, (struct sigaction *)NULL);

    sigaddset(&sigact.sa_mask, SIGHUP);
    sigaction(SIGHUP, &sigact, (struct sigaction *)NULL);

    sigaddset(&sigact.sa_mask, SIGKILL);
    sigaction(SIGKILL, &sigact, (struct sigaction *)NULL);
}

static void signal_handler(int sig) {
    if (sig == SIGHUP) panic("FATAL: Program hanged up\n");
    if (sig == SIGSEGV || sig == SIGBUS){
        dumpstack();
        panic("FATAL: %s Fault. Logged StackTrace\n", (sig == SIGSEGV) ? "Segmentation" : ((sig == SIGBUS) ? "Bus" : "Unknown"));
    }
    if (sig == SIGQUIT) panic("QUIT signal ended program\n");
    if (sig == SIGKILL) panic("KILL signal ended program\n");
    if (sig == SIGINT) ;
}

void panic(const char *fmt, ...) {
    char buf[50];
    va_list argptr;
    va_start(argptr, fmt);
    vsprintf(buf, fmt, argptr);
    va_end(argptr);
    fprintf(stderr, buf);
    exit(-1);
}

static void dumpstack(void) {
    /* Got this routine from http://www.whitefang.com/unix/faq_toc.html
    ** Section 6.5. Modified to redirect to file to prevent clutter
    */
    /* This needs to be changed... */
    char dbx[160];

    sprintf(dbx, "echo 'where\ndetach' | dbx -a %d > %s.dump", getpid(), progname);
    /* Change the dbx to gdb */

    system(dbx);
    return;
}

void cleanup(void) {
    sigemptyset(&sigact.sa_mask);
    /* Do any cleaning up chores here */
}

Vous devrez peut-être ajouter en plus un paramètre pour que gdb vide le noyau comme indiqué ici dans ce blog ici .

t0mm13b
la source
16

D'autres éléments peuvent influencer la génération d'un vidage de mémoire. Je les ai rencontrés:

  • le répertoire du vidage doit être accessible en écriture. Par défaut, il s'agit du répertoire actuel du processus, mais il peut être modifié en définissant /proc/sys/kernel/core_pattern.
  • dans certaines conditions, la valeur du noyau dans /proc/sys/fs/suid_dumpablepeut empêcher la génération du noyau.

D'autres situations peuvent empêcher la génération décrites dans la page de manuel - essayez man core.

mlutescu
la source
9

Pour activer le vidage de mémoire, procédez comme suit:

  1. En /etc/profilecommentaire la ligne:

    # ulimit -S -c 0 > /dev/null 2>&1
  2. En /etc/security/limits.confcommentaire sur la ligne:

    *               soft    core            0
  3. exécutez le cmd limit coredumpsize unlimitedet vérifiez-le avec cmd limit:

    # limit coredumpsize unlimited
    # limit
    cputime      unlimited
    filesize     unlimited
    datasize     unlimited
    stacksize    10240 kbytes
    coredumpsize unlimited
    memoryuse    unlimited
    vmemoryuse   unlimited
    descriptors  1024
    memorylocked 32 kbytes
    maxproc      528383
    #
  4. pour vérifier si le fichier core est écrit, vous pouvez tuer le processus correspondant avec cmd kill -s SEGV <PID>(ne devrait pas être nécessaire, juste au cas où aucun fichier core ne serait écrit, cela peut être utilisé comme vérification):

    # kill -s SEGV <PID>

Une fois le corefile écrit, assurez-vous de désactiver à nouveau les paramètres du coredump dans les fichiers associés (1./2./3.)!

Edgar Jordi
la source
9

Pour Ubuntu 14.04

  1. Vérifier le vidage de mémoire activé:

    ulimit -a
  2. L'une des lignes devrait être:

    core file size          (blocks, -c) unlimited
  3. Si non :

    gedit ~/.bashrcet ajouter ulimit -c unlimitedà la fin du fichier et enregistrer, réexécuter le terminal.

  4. Créez votre application avec des informations de débogage:

    Dans Makefile -O0 -g

  5. Exécutez l'application qui crée le vidage de mémoire (le fichier de vidage de mémoire avec le nom 'core' doit être créé près du fichier nom_application):

    ./application_name
  6. Exécutez sous gdb:

    gdb application_name core
mrgloom
la source
À l'étape 3, comment «réexécuter» le terminal? Voulez-vous dire redémarrer?
Naveen
@Naveen non, fermez simplement le terminal et ouvrez-en un nouveau, il semble également que vous pouvez simplement mettre le ulimit -c unlimitedterminal pour une solution temporaire, car seule l'édition ~/.bashrcnécessite un redémarrage du terminal pour que les modifications prennent effet.
mrgloom
4

Par défaut, vous obtiendrez un fichier core. Vérifiez que le répertoire actuel du processus est accessible en écriture, sinon aucun fichier principal ne sera créé.

Mark Harrison
la source
4
Par "répertoire courant du processus", voulez-vous dire le $ cwd au moment où le processus a été exécuté? ~ / abc> / usr / bin / cat def si cat plante, le répertoire courant est-il en question ~ / abc ou / usr / bin?
Nathan Fellman
5
~ / abc. Hmm, les commentaires doivent contenir 15 caractères!
Mark Harrison,
5
Ce serait le répertoire courant au moment du SEGV. En outre, les processus exécutés avec un utilisateur et / ou un groupe effectif différent de l'utilisateur / groupe réel n'écriront pas les fichiers de base.
Darron
2

Mieux vaut activer le vidage de mémoire par programmation à l'aide d'un appel système setrlimit.

exemple:

#include <sys/resource.h>

bool enable_core_dump(){    
    struct rlimit corelim;

    corelim.rlim_cur = RLIM_INFINITY;
    corelim.rlim_max = RLIM_INFINITY;

    return (0 == setrlimit(RLIMIT_CORE, &corelim));
}
kgbook
la source
pourquoi est-ce mieux?
Nathan Fellman
fichier de base généré après un crash, pas besoin de ulimit -c unlimiteddans l'environnement de ligne de commande, puis réexécutez l'application.
kgbook
Je ne veux pas de vidage de mémoire chaque fois qu'il se bloque, uniquement lorsqu'un utilisateur me contacte en tant que développeur pour l'examiner. S'il se bloque 100 fois, je n'ai pas besoin de 100 dumps de base pour regarder.
Nathan Fellman
Dans ce cas, mieux vaut utiliser ulimit -c unlimited. Vous pouvez également compiler avec la définition marco, l'application n'inclura pas de enable_core_dumpsymbole si elle ne définit pas cette macro lors de la publication, et vous obtiendrez un remplacement de vidage de mémoire par une version de débogage.
kgbook
même si elle est qualifiée par une macro, cela m'oblige toujours à recompiler si je veux générer un core dump, plutôt que d'exécuter simplement une commande dans le shell avant de la réexécuter.
Nathan Fellman
1

Il convient de mentionner que si vous avez configuré un système , les choses sont un peu différentes. La configuration aurait généralement les fichiers de base être canalisés, au moyen de la core_patternvaleur sysctl, à travers systemd-coredump(8). La taille de fichier de base rlimit serait généralement déjà configurée comme "illimitée".

Il est alors possible de récupérer les vidages mémoire à l'aide de coredumpctl(1).

Le stockage des vidages mémoire, etc. est configuré par coredump.conf(5). Il y a des exemples de comment obtenir les fichiers principaux dans la page de manuel de coredumpctl, mais en bref, cela ressemblerait à ceci:

Trouvez le fichier principal:

[vps@phoenix]~$ coredumpctl list test_me | tail -1
Sun 2019-01-20 11:17:33 CET   16163  1224  1224  11 present /home/vps/test_me

Obtenez le fichier principal:

[vps@phoenix]~$ coredumpctl -o test_me.core dump 16163
Pawel Veselov
la source
0

Ubuntu 19.04

Toutes les autres réponses elles-mêmes ne m'ont pas aidé. Mais le résumé suivant a fait le travail

Créez ~/.config/apport/settingsavec le contenu suivant:

[main]
unpackaged=true

(Cela indique à répart d'écrire également les vidages de mémoire pour les applications personnalisées)

vérifier: ulimit -c. S'il sort 0, corrigez-le avec

ulimit -c unlimited

Juste au cas où le redémarrage serait réparti:

sudo systemctl restart apport

Les fichiers de plantage sont maintenant écrits /var/crash/. Mais vous ne pouvez pas les utiliser avec gdb. Pour les utiliser avec gdb, utilisez

apport-unpack <location_of_report> <target_directory>

Plus d'informations:

  • Certaines réponses suggèrent de changer core_pattern. Sachez que ce fichier peut être écrasé par le service d'allocation au redémarrage.
  • Arrêter simplement alloc n'a pas fait le travail
  • La ulimit -cvaleur peut être modifiée automatiquement lorsque vous essayez d'autres réponses sur le Web. Assurez-vous de le vérifier régulièrement lors de la configuration de votre création de vidage de mémoire.

Références:

DarkTrick
la source