Jak wygenerować zrzut pamięci w systemie Linux z powodu błędu segmentacji?

217

Mam proces w Linuksie, który powoduje błąd segmentacji. Jak mogę nakazać generowanie zrzutu pamięci, gdy się nie powiedzie?

Nathan Fellman
źródło
1
Jak zobaczyć to po stronie: stackoverflow.com/questions/8305866/...
Ciro Santilli 14 冠状 病 六四 事件 法轮功

Odpowiedzi:

249

Zależy to od używanej powłoki. Jeśli używasz bash, to polecenie ulimit kontroluje kilka ustawień związanych z wykonywaniem programu, takich jak to, czy powinieneś zrzucić rdzeń. Jeśli wpiszesz

ulimit -c unlimited

to powie bashowi, że jego programy mogą zrzucać rdzenie dowolnego rozmiaru. Jeśli chcesz, możesz określić rozmiar taki jak 52M zamiast nieograniczonego, ale w praktyce nie powinno to być konieczne, ponieważ rozmiar podstawowych plików prawdopodobnie nigdy nie będzie dla ciebie problemem.

W tcsh wpisałeś

limit coredumpsize unlimited
Eli Courtwright
źródło
21
@lzprgmr: Aby wyjaśnić: powodem, dla którego zrzuty rdzenia nie są generowane domyślnie, jest to, że limit nie jest ustawiony i / lub ustawiony na 0, co zapobiega zrzutowi rdzenia. Ustawiając limit nieograniczony, gwarantujemy, że zrzuty rdzenia mogą być zawsze generowane.
Eli Courtwright,
6
Ten link jest głębszy i daje kilka dodatkowych opcji umożliwiających generowanie zrzutów rdzenia w systemie Linux. Jedyną wadą jest to, że niektóre polecenia / ustawienia pozostają niewyjaśnione.
Salsa
6
W wersji bash 4.1.2 (1) nie można określić limitów zwolnienia, takich jak 52M, co powoduje wyświetlenie komunikatu o błędnym numerze. Strona podręcznika mówi, że „Wartości są w przyrostach 1024-bajtowych”.
a1an
4
Cóż, miałem „mały” projekt OpenGL, który kiedyś zrobił coś dziwnego i spowodował awarię serwera X. Kiedy się ponownie zalogowałem, zobaczyłem ładny mały plik podstawowy 17 GB (na partycji 25 GB). Zdecydowanie dobrym pomysłem jest ograniczenie rozmiaru pliku rdzenia :)
IceCool,
1
@PolarisUser: Jeśli chcesz mieć pewność, że twoja partycja nie zostanie zjedzona, zalecam ustawienie limitu około 1 gig. Powinno to być wystarczająco duże, aby poradzić sobie z każdym rozsądnym zrzutem rdzenia, a jednocześnie nie grozić wykorzystaniem całej pozostałej przestrzeni na dysku twardym.
Eli Courtwright
60

Jak wyjaśniono powyżej, prawdziwym pytaniem jest tutaj, jak włączyć zrzuty pamięci w systemie, w którym nie są włączone. Odpowiedź na to pytanie znajduje się tutaj.

Jeśli przyszedłeś tutaj z nadzieją, aby dowiedzieć się, jak wygenerować zrzut podstawowy dla zawieszonego procesu, odpowiedź brzmi:

gcore <pid>

jeśli gcore nie jest dostępny w twoim systemie, to

kill -ABRT <pid>

Nie używaj kill -SEGV, ponieważ często wywołuje to procedurę obsługi sygnału, co utrudnia zdiagnozowanie zablokowanego procesu

George Co
źródło
Myślę, że jest o wiele bardziej prawdopodobne, że -ABRTwywoła procedurę obsługi sygnału niż -SEGV, ponieważ przerwanie jest bardziej prawdopodobne do odzyskania niż segfault. (Jeśli poradzisz sobie z segfault, normalnie uruchomi się on ponownie, jak tylko wyjdzie twój program obsługi.) Lepszym wyborem sygnału do wygenerowania zrzutu rdzenia jest -QUIT.
celticminstrel
32

Aby sprawdzić, gdzie generowane są zrzuty pamięci, uruchom:

sysctl kernel.core_pattern

lub:

cat /proc/sys/kernel/core_pattern

gdzie %ejest nazwa procesu i %tczas systemowy. Możesz to zmienić /etc/sysctl.confi ponownie załadować sysctl -p.

Jeśli rdzeń pliki nie są generowane (test go: sleep 10 &a killall -SIGSEGV sleep) sprawdzić granice, których autorem jest: ulimit -a.

Jeśli rozmiar twojego podstawowego pliku jest ograniczony, uruchom:

ulimit -c unlimited

aby było nieograniczone.

Następnie przetestuj ponownie, jeśli zrzut rdzenia zakończy się powodzeniem, po wskazaniu błędu segmentacji zobaczysz komunikat „(zrzut rdzenia)”:

Błąd segmentacji: 11 (zrzut rdzenia)

Zobacz także: zrzut rdzenia - ale pliku rdzenia nie ma w bieżącym katalogu?


Ubuntu

W Ubuntu zrzuty rdzenia są obsługiwane przez Apport i mogą być zlokalizowane w /var/crash/. Jest jednak domyślnie wyłączony w stabilnych wersjach.

Aby uzyskać więcej informacji, sprawdź: Gdzie mogę znaleźć zrzut podstawowy w Ubuntu? .

System operacyjny Mac

W przypadku systemu macOS zobacz: Jak generować zrzuty pamięci w Mac OS X?

kenorb
źródło
3
W przypadku Ubuntu, aby szybko przywrócić normalne zachowanie (zrzut pliku podstawowego w bieżącym katalogu), po prostu zatrzymaj usługę apport za pomocą „sudo service apport stop”. Należy również pamiętać, że jeśli działasz w oknie dokowanym, to ustawienie jest kontrolowane w systemie hosta, a nie w kontenerze.
Digicrat,
26

Na końcu podłączyłem gdb do procesu, zanim się zawiesił, a potem, kiedy dostał błąd, wykonałem generate-core-filepolecenie. To wymuszone generowanie zrzutu rdzenia.

Nathan Fellman
źródło
Jak dołączyłeś gdb do procesu?
Chani,
6
Aby odpowiedzieć na Ritwik G, aby dołączyć proces do gdb, po prostu uruchom gdb i wpisz „attach <pid>”, gdzie <pid> to numer pid procesu, który chcesz dołączyć.
Jean-Dominique Frattini,
(w skrócie jako ge)
user202729
Jeśli mają nowe pytanie, powinni zadać nowe pytanie zamiast zadawać komentarz.
user202729
Najdziwniejsze jest już ustawiony ulimit -cdo unlimited, ale plik rdzeń jest tworzony nie uspokaja The generate-core-fileplików w sesji gdb ma utworzyć plik core, dzięki.
CodyChan
19

Być może mógłbyś to zrobić w ten sposób, ten program pokazuje, jak złapać błąd segmentacji i wysyła do debuggera (jest to oryginalny kod użyty poniżej AIX) i drukuje ślad stosu aż do błędu segmentacji. Będziesz musiał zmienić sprintfzmienną, aby użyć jej gdbw przypadku Linuksa.

#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 */
}

Być może trzeba będzie dodatkowo dodać parametr, aby uzyskać gdb zrzucić rdzeń jak pokazano tutaj w tym blogu tutaj .

t0mm13b
źródło
16

Jest więcej rzeczy, które mogą wpływać na generowanie zrzutu pamięci. Spotkałem te:

  • katalog zrzutu musi być zapisywalny. Domyślnie jest to bieżący katalog procesu, ale można go zmienić przez ustawienie /proc/sys/kernel/core_pattern.
  • w niektórych warunkach wartość jądra /proc/sys/fs/suid_dumpablemoże uniemożliwić wygenerowanie rdzenia.

Jest więcej sytuacji, które mogą uniemożliwić generowanie, które są opisane na stronie man - try man core.

mlutescu
źródło
9

Aby aktywować zrzut główny, wykonaj następujące czynności:

  1. W /etc/profilekomentarzu wiersz:

    # ulimit -S -c 0 > /dev/null 2>&1
  2. W /etc/security/limits.confWykomentuj linii:

    *               soft    core            0
  3. uruchom cmd limit coredumpsize unlimitedi sprawdź za pomocą 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. aby sprawdzić, czy kill -s SEGV <PID>plik rdzenia zostanie zapisany, możesz zabić odpowiedni proces za pomocą cmd (nie powinno to być potrzebne, na wypadek gdyby nie zapisano żadnego pliku rdzenia, można to wykorzystać jako sprawdzenie):

    # kill -s SEGV <PID>

Po zapisaniu pliku podstawowego upewnij się, że ponownie dezaktywujesz ustawienia zrzutu rdzeniowego w powiązanych plikach (1./2./3.)!

Edgar Jordi
źródło
9

Dla Ubuntu 14.04

  1. Sprawdź zrzut zrzutu pamięci włączony:

    ulimit -a
  2. Jedna z linii powinna być:

    core file size          (blocks, -c) unlimited
  3. Jeśli nie :

    gedit ~/.bashrci dodaj ulimit -c unlimitedna końcu pliku i zapisz, uruchom ponownie terminal.

  4. Zbuduj swoją aplikację z informacjami debugowania:

    W Makefile -O0 -g

  5. Uruchom aplikację, która tworzy zrzut pamięci (plik zrzutu pamięci o nazwie „core” powinien zostać utworzony w pobliżu pliku nazwa_aplikacji):

    ./application_name
  6. Uruchom pod gdb:

    gdb application_name core
mrgloom
źródło
W kroku 3, jak „ponownie uruchomić” terminal? Masz na myśli restart?
Naveen
@Naveen nie, wystarczy zamknąć terminal i otworzyć nowy, wydaje się, że można po prostu ulimit -c unlimitedpodłączyć terminal do tymczasowego rozwiązania, ponieważ tylko edycja ~/.bashrcwymaga ponownego uruchomienia terminala, aby zmiany odniosły skutek.
mrgloom
4

Domyślnie otrzymasz plik podstawowy. Sprawdź, czy bieżący katalog procesu jest zapisywalny, czy nie zostanie utworzony żaden plik podstawowy.

Mark Harrison
źródło
4
Przez „bieżący katalog procesu” masz na myśli $ cwd w momencie uruchomienia procesu? ~ / abc> / usr / bin / cat def w przypadku awarii kota, czy bieżący katalog to pytanie ~ / abc lub / usr / bin?
Nathan Fellman
5
~ / abc. Hmm, komentarze muszą mieć 15 znaków!
Mark Harrison
5
Byłby to katalog bieżący w czasie SEGV. Ponadto procesy działające z innym efektywnym użytkownikiem i / lub grupą niż rzeczywisty użytkownik / grupa nie będą zapisywać plików podstawowych.
Darron
2

Lepiej włączyć zrzut pamięci programowo za pomocą wywołania systemowego setrlimit.

przykład:

#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
źródło
dlaczego to jest lepsze?
Nathan Fellman
plik podstawowy wygenerowany po awarii, nie trzeba ulimit -c unlimitedtego robić w środowisku wiersza poleceń, a następnie ponownie uruchomić aplikację.
kgbook
Nie chcę zrzutu rdzenia za każdym razem, gdy ulega awarii, tylko wtedy, gdy użytkownik skontaktuje się ze mną jako programistą, aby go zobaczyć. Jeśli ulegnie awarii 100 razy, nie potrzebuję 100 zrzutów rdzenia, aby na to spojrzeć.
Nathan Fellman
W takim przypadku lepiej użyć ulimit -c unlimited. Możesz także skompilować z definicją marco, aplikacja nie będzie zawierała enable_core_dumpsymbolu, jeśli nie zdefiniuje tego makra w momencie wydania, a otrzymasz zrzut pamięci podstawowej zastąpiony wersją debugowania.
kgbook
nawet jeśli jest zakwalifikowany przez makro, to wciąż wymaga ode mnie ponownej kompilacji, jeśli chcę wygenerować zrzut pamięci, zamiast po prostu wykonać polecenie w powłoce przed ponownym uruchomieniem.
Nathan Fellman
1

Warto wspomnieć, że jeśli masz skonfigurowane ustawienia systemowe , rzeczy wyglądają nieco inaczej. W konfiguracji zwykle przesyłane są pliki podstawowe za pomocą core_patternwartości sysctl systemd-coredump(8). Rozmiar pliku rlimit rdzenia zwykle byłby już skonfigurowany jako „nieograniczony”.

Następnie można odzyskać zrzuty pamięci za pomocą coredumpctl(1).

Przechowywanie zrzutów pamięci itp. Jest konfigurowane przez coredump.conf(5). Istnieją przykłady, jak uzyskać podstawowe pliki na stronie podręcznika coredumpctl, ale w skrócie wyglądałoby to tak:

Znajdź plik podstawowy:

[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

Pobierz plik podstawowy:

[vps@phoenix]~$ coredumpctl -o test_me.core dump 16163
Paweł Veselov
źródło
0

Ubuntu 19.04

Wszystkie inne odpowiedzi same mi nie pomogły. Ale poniższe podsumowanie wykonało zadanie

Utwórz ~/.config/apport/settingsz następującą zawartością:

[main]
unpackaged=true

(To mówi apportowi, aby również zapisywał zrzuty pamięci dla niestandardowych aplikacji)

sprawdzić: ulimit -c. Jeśli wyświetla 0, napraw to za pomocą

ulimit -c unlimited

Na wszelki wypadek, gdy uruchomisz ponownie apport:

sudo systemctl restart apport

Pliki awarii są teraz zapisywane w /var/crash/. Ale nie można ich używać z gdb. Aby użyć ich z gdb, użyj

apport-unpack <location_of_report> <target_directory>

Dalsza informacja:

  • Niektóre odpowiedzi sugerują zmianę core_pattern. Pamiętaj, że ten plik może zostać zastąpiony przez usługę apport po ponownym uruchomieniu.
  • Samo zatrzymanie apportu nie zadziałało
  • ulimit -cWartość może przebrać się automatycznie, gdy starasz inne odpowiedzi wstęgi. Pamiętaj o regularnym sprawdzaniu tego podczas konfigurowania tworzenia zrzutu pamięci.

Bibliografia:

DarkTrick
źródło