Mam proces w Linuksie, który powoduje błąd segmentacji. Jak mogę nakazać generowanie zrzutu pamięci, gdy się nie powiedzie?
Mam proces w Linuksie, który powoduje błąd segmentacji. Jak mogę nakazać generowanie zrzutu pamięci, gdy się nie powiedzie?
Odpowiedzi:
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
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
-ABRT
wywoł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
.
Aby sprawdzić, gdzie generowane są zrzuty pamięci, uruchom:
sysctl kernel.core_pattern
lub:
cat /proc/sys/kernel/core_pattern
gdzie %e
jest nazwa procesu i %t
czas systemowy. Możesz to zmienić /etc/sysctl.conf
i 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?
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? .
W przypadku systemu macOS zobacz: Jak generować zrzuty pamięci w Mac OS X?
Na końcu podłączyłem gdb do procesu, zanim się zawiesił, a potem, kiedy dostał błąd, wykonałem generate-core-file
polecenie. To wymuszone generowanie zrzutu rdzenia.
ge
)
ulimit -c
do unlimited
, ale plik rdzeń jest tworzony nie uspokaja The generate-core-file
plików w sesji gdb ma utworzyć plik core, dzięki.
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ć sprintf
zmienną, aby użyć jej gdb
w 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 .
Jest więcej rzeczy, które mogą wpływać na generowanie zrzutu pamięci. Spotkałem te:
/proc/sys/kernel/core_pattern
. /proc/sys/fs/suid_dumpable
moż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
.
Aby aktywować zrzut główny, wykonaj następujące czynności:
W /etc/profile
komentarzu wiersz:
# ulimit -S -c 0 > /dev/null 2>&1
W /etc/security/limits.conf
Wykomentuj linii:
* soft core 0
uruchom cmd limit coredumpsize unlimited
i 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
#
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.)!
Dla Ubuntu 14.04
Sprawdź zrzut zrzutu pamięci włączony:
ulimit -a
Jedna z linii powinna być:
core file size (blocks, -c) unlimited
Jeśli nie :
gedit ~/.bashrc
i dodaj ulimit -c unlimited
na końcu pliku i zapisz, uruchom ponownie terminal.
Zbuduj swoją aplikację z informacjami debugowania:
W Makefile -O0 -g
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
Uruchom pod gdb:
gdb application_name core
ulimit -c unlimited
podłączyć terminal do tymczasowego rozwiązania, ponieważ tylko edycja ~/.bashrc
wymaga ponownego uruchomienia terminala, aby zmiany odniosły skutek.
Domyślnie otrzymasz plik podstawowy. Sprawdź, czy bieżący katalog procesu jest zapisywalny, czy nie zostanie utworzony żaden plik podstawowy.
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));
}
ulimit -c unlimited
tego robić w środowisku wiersza poleceń, a następnie ponownie uruchomić aplikację.
ulimit -c unlimited
. Możesz także skompilować z definicją marco, aplikacja nie będzie zawierała enable_core_dump
symbolu, jeśli nie zdefiniuje tego makra w momencie wydania, a otrzymasz zrzut pamięci podstawowej zastąpiony wersją debugowania.
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_pattern
wartoś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
Ubuntu 19.04
Wszystkie inne odpowiedzi same mi nie pomogły. Ale poniższe podsumowanie wykonało zadanie
Utwórz ~/.config/apport/settings
z 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:
core_pattern
. Pamiętaj, że ten plik może zostać zastąpiony przez usługę apport po ponownym uruchomieniu.ulimit -c
Wartość może przebrać się automatycznie, gdy starasz inne odpowiedzi wstęgi. Pamiętaj o regularnym sprawdzaniu tego podczas konfigurowania tworzenia zrzutu pamięci.Bibliografia: