Testat în Ubuntu 20.04.
1. Activați fișierele de bază
În primul rând, fugi ulimit -c
pentru a vedea care este dimensiunea maximă permisă pentru fișierele de bază de pe sistemul dvs. Pe Ubuntu 20.04 pentru mine, al meu revine 0
, ceea ce înseamnă că nu poate fi creat niciun fișier de bază.
ulimit --ajutor
arată sensul lui -c
:
-c dimensiunea maximă a fișierelor de bază create
Deci, setați dimensiunea permisă a fișierului de bază la nelimitat
, așa cum se arată mai jos. Rețineți că cred că acest lucru se aplică numai la singurul terminal în care rulați asta, si o fac nu cred că este persistent la reporniri, așa că trebuie să rulați acest lucru de fiecare dată când doriți să fie create fișiere de bază și în fiecare terminal în care doriți să funcționeze:
# setați dimensiunea maximă a fișierului de descărcare de bază la nelimitat
ulimit -c nelimitat
# verificați că acum este setat la „nelimitat”
ulimit -c
Asta e! Acum, rulați programul și dacă se prăbușește și face un „core dump”, va arunca nucleul ca a miez
dosar în același director în care erai când ai apelat executabilul. Numele fișierului este pur și simplu „core”.
2. Vizualizați backtrace în gdb
Ar trebui să fi creat programul C sau C++ cu simboluri de depanare activate, pentru a vedea informații utile în fișierul de bază. Fără simboluri de depanare, puteți vedea numai adresele funcțiilor apelate, nu numele sau numerele de linii reale.
În gcc, utilizați -ggdb -O0
a porni debug
simboluri optimizate pentru gdb
G
NU d
eb
ugger. De asemenea, puteți utiliza -g -O0
, -g3 -O0
, etc, dar -ggdb -O0
e cel mai bine. Chiar avem nevoie de nivelul de optimizare 0 (-O0
) pentru asta? Da, da. Vezi aici raspunsul meu: Stack Overflow: Care este diferența dintre cele ale unui compilator -O0
opțiunea și -Og
opțiune?
Exemple de comenzi pentru construirea și rularea în C și C++: deci, comenzile complete de construire și rulare în C sau C++ ar putea arăta astfel:
# C construiți și rulați comanda pentru „hello_world.c”
gcc -Wall -Wextra -Werror -ggdb -O0 -std=c11 -o hello_world hello_world.c \
&& ./Salut Lume
# C++ construi și rulează comanda pentru „hello_world.c”
g++ -Wall -Wextra -Werror -ggdb -O0 -std=c++17 -o hello_world hello_world.c \
&& ./Salut Lume
Deschideți fișierul de bază în gdb
ca aceasta:
gdb path/to/my/path executable/to/core
Presupunând că tocmai ai fugit cale/la/meu/executabil
, apoi miez
fișierul va fi în același director în care erați tocmai când nucleul a fost aruncat, așa că puteți rula doar acest lucru:
calea gdb/către/nucleul meu/executable
În gdb
, vizualizați backtrace (stiva de apeluri de funcție în momentul accidentului) cu:
bt
# sau (exact aceeași comandă)
Unde
# SAU (pentru și mai multe detalii, cum ar fi vizualizarea tuturor argumentelor pentru funcții--
# mulțumesc lui Peter Cordes în comentariile de mai jos)
bt plin
# Pentru ajutor și detalii gdb, consultați:
ajutor bt
# sau
ajutor unde
IMPORTANT: atunci când are loc o descărcare de bază, aceasta NU suprascrie automat niciuna preexistentă miez
fișier în directorul curent cu unul nou, așa că trebuie îndepărtați manual bătrânii miez
dosar cu rm miez
ÎNAINTE de a genera noul fișier de bază atunci când programul se blochează, pentru a avea întotdeauna cel mai recent fișier de bază de analizat.
3. Încearcă-l
- Într-un terminal, fugi
dormi 30
pentru a începe un proces de somn timp de 30 de secunde.
- În timp ce funcționează, apăsați Ctrl + \ pentru a forța o descărcare de miez. Veți vedea acum un
miez
fișier în directorul în care vă aflați.
- Deoarece nu avem un executabil pentru aceasta cu simboluri de depanare în el, vom deschide doar fișierul de bază în gdb în loc de fișierul executabil cu simboluri + fișierul de bază. Deci, fugi
gdb -c miez
pentru a deschide fișierul de bază tocmai creat de accidentul forțat.
- Vei vedea asta. Observați că știe ce comandă ați sunat (
dormi 30
) când a avut loc descărcarea de miez:
Core a fost generat de „sleep 30”.
Programul s-a încheiat cu semnalul SIGQUIT, Quit.
#0 0x00007f93ed32d334 în ?? ()
(gdb)
- Alerga
bt
sau Unde
pentru a vedea urma. Veți vedea asta:
(gdb) bt
#0 0x00007f93ed32d334 în ?? ()
#1 0x000000000000000a în ?? ()
#2 0x00007f93ed2960a5 în ?? ()
#3 0x0000000000000000 în ?? ()
(gdb)
- Acestea sunt adresele funcțiilor apelate în stiva de apeluri. Dacă ați avea simboluri de depanare activate, ați vedea multe mai multe informații, inclusiv nume de funcții și numere de linie, ca aceasta (extras dintr-un program C al meu):
#10 0x00007fc1152b8ebf în __printf (format=<optimized out>) la printf.c:33
#11 0x0000562bca17b3eb în fast_malloc (num_bytes=1024) la src/fast_malloc.c:225
#12 0x0000562bca17bb66 în malloc (num_bytes=1024) la src/fast_malloc.c:496
4. Uitați de fișierele de bază și doar rulați programul la punctul de blocare în gdb direct!
După cum afirmă @Peter Cordes în comentariile de mai jos, puteți rula programul direct în gdb, lăsându-l să se blocheze acolo, astfel încât să nu aveți nevoie să deschideți un fișier de bază după fapt! El a declarat:
Aceste comenzi GDB nu sunt specifice fișierelor de bază, ele funcționează de fiecare dată când ești oprit la un punct de întrerupere. Dacă aveți o blocare reproductibilă, este adesea mai ușor/mai bine să rulați programul sub GDB (cum ar fi gdb ./a.out
) astfel încât GDB va avea procesul în memorie în loc de un fișier de bază. Principalul avantaj este că puteți seta undeva un punct de întrerupere sau un punct de vizionare inainte de lucrul care s-a prăbușit și un singur pas pentru a vedea ce se întâmplă. Sau cu facilitățile de înregistrare ale GDB, este posibil să puteți păși înapoi și vedeți ce a dus la prăbușire, dar asta poate fi fulgerător, lent și necesită mult memorie.
După cum sa menționat mai sus, ar fi trebuit să vă compilați programul cu simboluri de depanare pe și cu Nivelul de optimizare 0, folosind -ggdb -O0
. Vedeți exemplul complet de construire și rulare comenzi în C și C++ de mai sus.
Acum rulați programul în gdb:
# Deschideți executabilul în gdb
calea gdb/to/my/executable
# Rulați-l (dacă încă se blochează, veți vedea că se prăbușește)
r
# Vizualizați backtrace (stiva de apeluri)
bt
# Închideți când ați terminat
q
Și dacă vreodată trebuie să înregistrați manual backtrace într-un fișier jurnal pentru a-l analiza mai târziu, puteți face acest lucru astfel (adaptat din note în mine eRCaGuy_dotfiles repo aici):
setați fișierul de jurnal gdb_log.txt
setați conectarea
activați comenzile de urmărire
arată înregistrarea # dovediți că înregistrarea este activată
culoare
pune imprimare destul de pe
bt # vezi backtrace
setați deconectarea
afișează înregistrarea # dovedește că înregistrarea a fost dezactivată
Terminat! Acum ați salvat backtrace gdb în fișierul „gdb_log.txt”.
Referinte:
- [răspunsul de care aveam nevoie este chiar în această întrebare] https://stackoverflow.com/questions/2065912/core-dumped-but-core-file-is-not-in-the-current-directory
- https://stackoverflow.com/questions/5115613/core-dump-file-analysis
- https://stackoverflow.com/questions/8305866/how-do-i-analyze-a-programs-core-dump-file-with-gdb-when-it-has-command-line-pa/30524347#30524347
- [informații foarte utile, inclusiv. cel Ctrl + \ truc pentru a forța o descărcare de miez!] https://unix.stackexchange.com/questions/277331/segmentation-fault-core-dumped-to-where-what-is-it-and-why/409776#409776
- [referit de răspunsul de mai sus] https://unix.stackexchange.com/questions/179998/where-to-search-for-the-core-file-generated-by-the-crash-of-a-linux-application/180004#180004
- [răspunsul este în întrebarea însăși] Unde găsesc memoria centrală în ubuntu 16.04LTS?
- [raspunsul meu] Stack Overflow: Care este diferența dintre cele ale unui compilator
-O0
opțiunea și -Og
opțiune?
Lectură suplimentară de făcut
- [ÎNCĂ TREBUIE SĂ STUDIU ȘI ÎNCERC ASTA] Cum se utilizează
LD_PRELOAD
cu gdb
: https://stackoverflow.com/questions/10448254/how-to-use-gdb-with-ld-preload