Bölümleme hatasına neden olan hatanın kodda nerede olduğu nasıl belirlenir ?
Derleyicim ( gcc
) programdaki hatanın yerini gösterebilir mi?
Bölümleme hatasına neden olan hatanın kodda nerede olduğu nasıl belirlenir ?
Derleyicim ( gcc
) programdaki hatanın yerini gösterebilir mi?
Yanıtlar:
GCC bunu yapamaz ama GDB (bir hata ayıklayıcı ) kesinlikle yapabilir. Şunun -g
gibi anahtarı kullanarak programınızı derleyin :
gcc program.c -g
Sonra gdb'yi kullanın:
$ gdb ./a.out
(gdb) run
<segfault happens here>
(gdb) backtrace
<offending code is shown here>
İşte GDB ile başlamanız için güzel bir öğretici.
Segfault'un meydana geldiği yer genellikle "neden olan hatanın" kodda nerede olduğuna dair bir ipucudur. Verilen konum, sorunun bulunduğu yer olmayabilir.
bt
bir kısaltma olarak kullanın backtrace
.
Ayrıca şunu valgrind
deneyebilirsiniz: kurup valgrind
çalıştırırsanız
valgrind --leak-check=full <program>
daha sonra programınızı çalıştırır ve herhangi bir segment hatası için yığın izlerini, ayrıca geçersiz bellek okuma veya yazma işlemlerini ve bellek sızıntılarını görüntüler. Gerçekten oldukça kullanışlıdır.
--leak-check=full
segfault'larda hata ayıklamaya yardımcı olmayacaktır. Yalnızca bellek sızıntılarında hata ayıklamak için kullanışlıdır.
Ayrıca bir çekirdek dökümü kullanabilir ve ardından gdb ile inceleyebilirsiniz. Yararlı bilgiler elde etmek için ayrıca -g
bayrakla birlikte derlemeniz gerekir .
Mesajı ne zaman alırsanız:
Segmentation fault (core dumped)
geçerli dizininize bir çekirdek dosya yazılır. Ve bunu komutla inceleyebilirsiniz
gdb your_program core_file
Dosya, program çöktüğünde belleğin durumunu içerir. Yazılımınızın konuşlandırılması sırasında bir çekirdek dökümü faydalı olabilir.
Sisteminizin çekirdek döküm dosyası boyutunu sıfır olarak ayarlamadığından emin olun. Aşağıdakilerle sınırsız olarak ayarlayabilirsiniz:
ulimit -c unlimited
Yine de dikkatli olun! bu çekirdek çöplükler çok büyük olabilir.
Segmentasyon hatalarının ayıklanmasına yardımcı olan bir dizi araç vardır ve en sevdiğim aracı listeye eklemek istiyorum: Adres Temizleyicileri (genellikle ASAN olarak kısaltılır) .
Modern derleyiciler kullanışlı bir -fsanitize=address
bayrakla birlikte gelir , biraz derleme süresi ve çalışma süresi ek yükü daha fazla hata denetimi yapar.
Belgelere göre bu kontroller, varsayılan olarak bölümleme hatalarının yakalanmasını içerir. Buradaki avantaj, gdb'nin çıktısına benzer bir yığın izleme elde etmenizdir, ancak programı bir hata ayıklayıcı içinde çalıştırmadan. Bir örnek:
int main() {
volatile int *ptr = (int*)0;
*ptr = 0;
}
$ gcc -g -fsanitize=address main.c
$ ./a.out
AddressSanitizer:DEADLYSIGNAL
=================================================================
==4848==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000 (pc 0x5654348db1a0 bp 0x7ffc05e39240 sp 0x7ffc05e39230 T0)
==4848==The signal is caused by a WRITE memory access.
==4848==Hint: address points to the zero page.
#0 0x5654348db19f in main /tmp/tmp.s3gwjqb8zT/main.c:3
#1 0x7f0e5a052b6a in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x26b6a)
#2 0x5654348db099 in _start (/tmp/tmp.s3gwjqb8zT/a.out+0x1099)
AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV /tmp/tmp.s3gwjqb8zT/main.c:3 in main
==4848==ABORTING
Çıktı, gdb'nin çıkardığından biraz daha karmaşık, ancak bazı artıları var:
Yığın izleme almak için sorunu yeniden oluşturmaya gerek yoktur. Geliştirme sırasında bayrağı etkinleştirmek yeterlidir.
ASAN'lar, segmentasyon hatalarından çok daha fazlasını yakalar. Bu bellek alanına işlem için erişilebilir olsa bile, sınır dışı erişimlerin çoğu yakalanacaktır.
Lucas'ın temel dökümler hakkındaki cevabı güzel. .Cshrc dosyamda:
alias core 'ls -lt core; echo where | gdb -core=core -silent; echo "\n"'
'çekirdek' girerek geri izlemeyi görüntülemek için. Ve doğru dosyaya baktığımdan emin olmak için tarih damgası :(.
Eklendi : Yığın bozulması hatası varsa, çekirdek dökümüne uygulanan geri izleme genellikle gereksizdir. Bu durumda, programı gdb içinde çalıştırmak, kabul edilen cevaba göre daha iyi sonuçlar verebilir (hatanın kolayca tekrarlanabilir olduğunu varsayarak). Ayrıca çekirdeği eşzamanlı olarak boşaltan birden fazla işlemden de sakının; bazı işletim sistemleri PID'yi çekirdek dosyanın adına ekler.
ulimit -c unlimited
en başta temel dökümleri etkinleştirmeyi unutmayın .
Yukarıdaki cevapların tümü doğru ve tavsiye edilir; Bu yanıt, yukarıda bahsedilen yaklaşımlardan hiçbirinin kullanılamaması durumunda, yalnızca son çare olarak düşünülmüştür.
Her şey başarısız olursa, programınızı her zaman fprintf(stderr, "CHECKPOINT REACHED @ %s:%i\n", __FILE__, __LINE__);
kodunuzun ilgili kısımları olduğuna inandığınız şeyler boyunca serpiştirilmiş çeşitli geçici hata ayıklama-yazdırma ifadeleriyle (örn. ) Yeniden derleyebilirsiniz . Ardından programı çalıştırın ve çökme meydana gelmeden hemen önce yazdırılan en son hata ayıklama yazısının ne olduğunu gözlemleyin - programınızın bu kadar ileri gittiğini biliyorsunuz, bu nedenle çökme bu noktadan sonra olmuş olmalı. Hata ayıklama baskılarını ekleyin veya kaldırın, yeniden derleyin ve tek bir kod satırına daraltıncaya kadar testi tekrar çalıştırın. Bu noktada hatayı düzeltebilir ve tüm geçici hata ayıklama baskılarını kaldırabilirsiniz.
Oldukça sıkıcı, ancak hemen hemen her yerde çalışma avantajına sahip - bunun olmayabileceği tek durum, herhangi bir nedenle stdout veya stderr'e erişiminizin olmaması veya düzeltmeye çalıştığınız hata bir yarışsa olabilir. -Programın zamanlaması değiştiğinde davranışı değişen koşul (çünkü hata ayıklama baskıları programı yavaşlatacak ve zamanlamasını değiştirecektir)