Menjalankan program berulang kali dengan tambahan printf memang dapat menyingkap bug, tetapi cara itu melelahkan dan meninggalkan jejak pada kode. GDB, debugger resmi proyek GNU, menawarkan jalan yang lebih terarah. Program dihentikan pada baris yang dipilih, lalu dijalankan satu pernyataan demi satu pernyataan, dan isi variabelnya diperiksa pada saat program sedang berhenti. Topik ini membahas kompilasi dengan informasi debug, penggunaan breakpoint dan stepping, inspeksi variabel, serta pengantar pemeriksaan core dump, yaitu rekaman memori program yang mati mendadak. Karena gdb tidak terinstal pada sandbox ini, transkrip sesinya diperlihatkan sebagai pola umum tanpa blok keluaran, sedangkan program contohnya tetap dikompilasi dan dijalankan seperti biasa.
Kompilasi dengan informasi debug#
Opsi -g menyuruh compiler menyisipkan informasi debug (debug information) ke dalam program, yaitu pemetaan antara kode mesin dan kode sumbernya. Pemetaan itu mencakup nama berkas, nomor baris, dan nama variabel. Dengan informasi ini, laporan debugger dapat menyebut bug-rata-rata.c:12 alih-alih deretan alamat memori yang harus dicocokkan secara manual. Program yang didistribusikan kepada pengguna biasanya dikompilasi tanpa -g beserta optimasi penuh. Build untuk pengembangan menyertakan -g dan mematikan optimasi agar baris yang ditampilkan debugger sesuai dengan kode sumbernya.
Program berikut menjadi bahan seluruh sesi pada topik ini. Programnya menghitung rata-rata empat nilai ujian, tetapi keluarannya jelas keliru karena seharusnya 75, bukan 100. Pada program sepuluh baris, penyebabnya terbaca langsung, sedangkan pada program ratusan baris, pencarian manual semacam ini tidak lagi praktis.
#include <stdio.h>
int main(void)
{
int nilai[] = {70, 80, 90, 60};
int jumlah = 0;
int i;
for (i = 0; i < 4; i = i + 1) {
jumlah = jumlah + nilai[i];
}
printf("Rata-rata: %d\n", jumlah / 3);
return 0;
}Rata-rata: 100Tidak ada crash pada program ini dan kode keluarnya 0, karena kesalahannya murni logika. Kesalahan jenis inilah yang paling cocok dilacak dengan debugger, karena tidak ada pesan apa pun yang menunjuk baris penyebabnya. Kompilasi dengan -g lalu memulai GDB dilakukan seperti berikut.
# tidak-dijalankan
# gdb tidak terinstal pada sandbox ini, sehingga urutan berikut diperlihatkan sebagai pola umum
gcc -g bug-rata-rata.c -o bug-rata-rata
gdb ./bug-rata-rataGDB menyambut dengan layar perkenalan singkat dan prompt (gdb) yang menunggu perintah. Program tidak berjalan sampai perintah run diketik, sehingga masih ada kesempatan menyiapkan breakpoint terlebih dahulu.
Menetapkan breakpoint dan menjalankan program#
Breakpoint adalah penanda yang menghentikan eksekusi pada baris tertentu sebelum baris itu dijalankan. Perintah break 12 menetapkan breakpoint pada baris 12, sedangkan break main menetapkannya di awal fungsi main. Setelah run diketik, program berjalan dari awal dan berhenti tepat sebelum baris 12 dieksekusi. GDB menampilkan nomor breakpoint, nama berkas, dan nomor barisnya, lalu prompt (gdb) muncul kembali.
| Perintah | Fungsi |
|---|---|
break 12 | menetapkan breakpoint pada baris 12 |
break main | menetapkan breakpoint di awal fungsi main |
run | menjalankan program sampai breakpoint pertama |
continue | melanjutkan eksekusi sampai breakpoint berikutnya |
quit | mengakhiri sesi GDB |
Breakpoint juga dapat dipasang lebih dari satu, sehingga penyelidikan dapat dibagi menjadi beberapa titik henti pada perjalanan program. Perintah continue melompat dari satu titik henti ke titik henti berikutnya, dan setiap run mengulang program dari awal dengan kondisi yang persis sama. Sifat yang dapat diulang ini membedakan debugger dari cetakan printf sementara, karena hipotesis dapat diuji berulang kali tanpa menyunting kode.
# tidak-dijalankan
# pola umum menetapkan breakpoint lalu menjalankan program
(gdb) break 12
(gdb) run
(gdb) quitMelangkah dan menginspeksi variabel#
Saat program berhenti pada breakpoint, eksekusinya belum melewati baris tersebut, sehingga setiap pernyataan dapat dijalankan sambil mengamati akibatnya satu per satu. Perintah next menjalankan satu pernyataan lalu berhenti kembali, sedangkan step melakukan hal yang sama tetapi masuk ke dalam fungsi yang dipanggil oleh pernyataan itu. Untuk bug aritmetika pada program contoh, next sudah cukup karena tidak ada fungsi buatan sendiri yang perlu dimasuki.
Perintah print menampilkan nilai variabel atau ekspresi C apa pun pada saat program berhenti, misalnya print jumlah, print nilai[0], atau print jumlah / 4. Ekspresi yang lebih kompleks boleh ditulis langsung, sehingga hipotesis dapat diuji tanpa menambah satu baris pun pada kode.
# tidak-dijalankan
# pola umum melangkah dan menginspeksi variabel
(gdb) break 12
(gdb) run
(gdb) next
(gdb) print jumlah
(gdb) print jumlah / 4
(gdb) quitPada program contoh, print jumlah dilaporkan bernilai 300 setelah perulangan selesai, dan angka itu memang benar karena 70 + 80 + 90 + 60 = 300. Artinya, perulangannya sehat dan dugaan berpindah ke pembaginya. print jumlah / 4 menghasilkan 75, yaitu nilai yang diharapkan. Pembagi yang tertulis 3 pada baris 12 terbukti menjadi penyebabnya, dan perbaikannya tinggal satu angka.
Pola di atas adalah bentuk debugging yang paling umum, yaitu menyempitkan rentang dugaan dengan mengamati nilai pada titik-titik tertentu sampai penyebabnya tersisa satu baris. Diagram berikut merangkum siklus kerjanya.
Core dump#
Core dump (cadangan inti) adalah berkas yang memuat citra memori program pada saat mati karena sinyal fatal, misalnya SIGSEGV. Berkas ini memungkinkan pemeriksaan setelah kejadian (post-mortem), karena program yang sudah berhenti dapat dibuka di GDB dan kondisi terakhirnya dibaca ulang. Banyak distribusi menonaktifkan pembuatan core secara bawaan. Langkah pertama adalah mengizinkannya melalui ulimit -c unlimited pada shell yang sama sebelum program dijalankan.
# tidak-dijalankan
# pola pemeriksaan core dump pada mesin yang mengizinkannya
ulimit -c unlimited
./program-crash
gdb ./program-crash core
(gdb) backtrace
(gdb) quitKetika GDB membuka berkas core, ia melaporkan sinyal penyebab kematian program dan menyiapkan pemeriksaan tanpa memerlukan perintah run. Perintah backtrace menampilkan rantai pemanggilan fungsi mulai dari titik crash sampai ke main, dan baris teratasnya biasanya adalah lokasi crash itu sendiri. Nama berkas core dapat berbeda antarsistem, misalnya core atau core.1234 dengan nomor proses pada akhirnya. Pola penamaannya dikendalikan kernel melalui pengaturan kernel.core_pattern.
Kesalahan umum#
- Men-debug program yang dikompilasi tanpa
-g. GDB tetap dapat menjalankannya, tetapi laporannya berisi alamat alih-alih nama berkas dan nomor baris, sehingga sulit dihubungkan dengan kode. Kompilasi ulang dengan-gsebelum menyelidik. - Men-debug build yang dioptimasi penuh. Optimasi dapat menyusun ulang pernyataan atau menghilangkan variabel, sehingga breakpoint tampak melompat dan
printmelaporkan variabel yang tidak ada. Simpan build khusus debugging tanpa optimasi. - Menaruh breakpoint di belakang lokasi gejala. Nilai yang diperiksa sudah terlanjur salah sejak langkah sebelumnya, sehingga temuannya menyesatkan. Pasang breakpoint pada titik terakhir yang belum salah, lalu langkahi maju.
- Mengira
nextdanstepberperilaku sama.stepmasuk ke dalam fungsi yang dipanggil, sedangkannextmelewatinya, sehingga pemakaian yang terbalik membuat penyelidikan menyasar fungsi pustaka alih-alih kode sendiri.
Ringkasan#
- Opsi
-gmenyertakan pemetaan kode mesin ke kode sumber, dan build pengembangan sebaiknya memakainya tanpa optimasi. - Breakpoint menghentikan program sebelum baris terpilih dieksekusi, lalu
next,step, danprintmenjalankan penyelidikan tanpa mengubah kode. - Core dump menyimpan kondisi memori saat program mati, dan
backtracepada GDB menunjuk rantai pemanggilan hingga baris crash.
Referensi#
- Setting Breakpoints — Debugging with GDB
— Kaidah perintah
breakbeserta bentuk lokasi yang dapat dipilih. - Running Programs Under GDB — Debugging with GDB
— Perilaku perintah
rundan cara GDB menyiapkan program. - Examining Data — Debugging with GDB
— Perintah
printbeserta kaidah ekspresi yang diterimanya. - Examining the Stack — Debugging with GDB
— Perintah
backtraceyang menjadi tulang punggung pemeriksaan core dump.