Debugger GDB (GNU Debugger) adalah alat utama untuk mendiagnosis bug runtime pada program C. Dengan GDB, kita dapat menjalankan program langkah per langkah, memeriksa nilai variabel, melihat call stack, dan bahkan memodifikasi variabel saat program berjalan. Bab ini menyusun perintah GDB yang paling sering dipakai beserta contoh sesi debugging nyata.
Memulai Sesi GDB #
Untuk melakukan debugging, kompilasi program dengan flag -g agar binary menyimpan informasi debug (nomor baris, nama variabel). Tanpa -g, GDB hanya bisa menampilkan alamat memori—tidak nama simbol.
gcc -std=c11 -Wall -Wextra -g -O0 -o gcdemo gcdemo.c
gdb ./gcdemoPerintah Dasar #
Berikut adalah perintah GDB yang paling sering kita pakai. Setiap perintah memiliki bentuk panjang dan singkat—misalnya run dan r ekuivalen.
Kontrol Eksekusi #
| Perintah | Singkat | Efek |
|---|---|---|
run [args] | r | Mulai program dari awal |
start | — | Mulai dan berhenti di main() |
continue | c | Lanjutkan eksekusi |
next | n | Step over (lewati pemanggilan fungsi) |
step | s | Step into (masuk ke fungsi) |
finish | f | Jalankan sampai fungsi saat ini return |
until [loc] | u | Jalankan sampai lokasi/baris tertentu |
nexti / stepi | ni / si | Step instruksi mesin |
kill | k | Hentikan program (tetap di GDB) |
quit | q | Keluar dari GDB |
Breakpoint dan Watchpoint #
| Perintah | Efek |
|---|---|
break <loc> | Set breakpoint di lokasi (nama fungsi, file:baris, alamat) |
break <func> if <cond> | Conditional breakpoint |
tbreak <loc> | Breakpoint sementara (dihapus setelah terkena sekali) |
rbreak <regex> | Breakpoint di semua fungsi yang cocok regex |
info breakpoints | Daftar breakpoint |
delete <n> | Hapus breakpoint nomor n |
delete | Hapus semua breakpoint |
disable <n> / enable <n> | Nonaktifkan/aktifkan breakpoint |
clear <func> | Hapus breakpoint di fungsi |
watch <expr> | Berhenti saat nilai ekspresi berubah |
rwatch <expr> | Berhenti saat ekspresi dibaca |
awatch <expr> | Berhenti saat ekspresi dibaca/ditulis |
condition <n> <expr> | Tambah kondisi ke breakpoint n |
Pemeriksaan Status #
| Perintah | Efek |
|---|---|
print <expr> | Cetak nilai ekspresi |
print/x <expr> | Cetak dalam heksadesimal |
print *arr@n | Cetak n elemen array |
display <expr> | Cetak otomatis setiap berhenti |
undisplay <n> | Hentikan display |
info locals | Semua variabel lokal frame saat ini |
info args | Argumen fungsi saat ini |
info variables | Daftar variabel global |
backtrace | bt — call stack |
backtrace full | Stack + variabel lokal tiap frame |
frame <n> | Pindah ke frame n |
up / down | Naik/turun satu frame |
info registers | Isi semua register CPU |
x/<n><u><f> <addr> | Memeriksa memori (n unit, ukuran u, format f) |
disassemble | Lihat kode mesin fungsi |
Modifikasi dan Ekspresi #
| Perintah | Efek |
|---|---|
set var <name>=<val> | Ubah nilai variabel |
set {int}0xaddr = 42 | Tulis langsung ke alamat |
call <func>(args) | Panggil fungsi dari prompt GDB |
print <expr> = <val> | Ekspresi assignment |
Program Demo #
Program berikut memiliki fungsi rekursif faktorial dan fungsi iteratif sum_array. Kita akan memakai GDB untuk melacak eksekusi dan memeriksa variabel lokal pada masing-masing fungsi.
/* File: gcdemo.c - Demo sesi debugging dengan gdb */
#include <stdio.h>
static int faktorial(int n) {
if (n <= 1) return 1;
return n * faktorial(n - 1);
}
static int sum_array(int *arr, int n) {
int total = 0;
for (int i = 0; i < n; i++) {
total += arr[i];
}
return total;
}
int main(void) {
int data[] = {10, 20, 30, 40, 50};
int n = sizeof(data) / sizeof(data[0]);
int s = sum_array(data, n);
printf("sum = %d\n", s);
int f = faktorial(5);
printf("5! = %d\n", f);
return 0;
}Compile dan jalankan biasa untuk verifikasi output:
gcc -std=c11 -Wall -Wextra -g -O0 -o gcdemo gcdemo.c
./gcdemoOutput yang Dihasilkan:
sum = 150
5! = 120Sesi Debugging Interaktif #
Berikut adalah transkrip sesi GDB yang menggunakan beberapa perintah inti. Prompt GDB ditandai (gdb). Output ini mewakili perilaku standar gdb versi 12+.
$ gdb ./gcdemo
(gdb) break sum_array
Breakpoint 1 at 0x1149: file gcdemo.c, line 9.
(gdb) break faktorial
Breakpoint 2 at 0x1179: file gcdemo.c, line 4.
(gdb) run
Starting program: /tmp/cc/test/gcdemo
Breakpoint 1, sum_array (arr=0x7fffffffe2e0, n=5) at gcdemo.c:9
9 for (int i = 0; i < n; i++) {
(gdb) bt
#0 sum_array (arr=0x7fffffffe2e0, n=5) at gcdemo.c:9
#1 0x000055555555518f in main () at gcdemo.c:21
(gdb) print n
$1 = 5
(gdb) print arr[0]
$2 = 10
(gdb) print *arr@5
$3 = {10, 20, 30, 40, 50}
(gdb) next
10 total += arr[i];
(gdb) next
9 for (int i = 0; i < n; i++) {
(gdb) info locals
total = 10
i = 0
(gdb) continue
Continuing.
Breakpoint 1, sum_array (arr=0x7fffffffe2e0, n=5) at gcdemo.c:9
...Sesi rekursif pada faktorial(5) akan memicu breakpoint 2 beberapa kali—setiap pemanggilan rekursif berhenti. Berikut potongan interaksi saat faktorial(3) dipanggil:
(gdb) continue
Continuing.
sum = 150
Breakpoint 2, faktorial (n=5) at gcdemo.c:4
4 if (n <= 1) return 1;
(gdb) continue
Continuing.
Breakpoint 2, faktorial (n=4) at gcdemo.c:4
4 if (n <= 1) return 1;
(gdb) continue
Continuing.
Breakpoint 2, faktorial (n=3) at gcdemo.c:4
4 if (n <= 1) return 1;
(gdb) bt
#0 faktorial (n=3) at gcdemo.c:4
#1 0x00005555555551a4 in faktorial (n=4) at gcdemo.c:5
#2 0x00005555555551a4 in faktorial (n=5) at gcdemo.c:5
#3 0x00005555555551b8 in main () at gcdemo.c:24
(gdb) print n
$4 = 3
(gdb) finish
Run till exit from #0 faktorial (n=3) at gcdemo.c:4
0x00005555555551a4 in faktorial (n=4) at gcdemo.c:5
5 return n * faktorial(n - 1);
Value returned is $5 = 6
(gdb) continue
Continuing.
5! = 120
[Inferior 1 (process 12345) exited normally]
(gdb) quitMari kita telaah baris-baris perintah di atas:
break sum_arraydanbreak faktorial: GDB menerima nama fungsi sebagai lokasi breakpoint. Saat program mencapai awal fungsi tersebut, eksekusi berhenti sebelum baris pertama dieksekusi.backtrace(bt): Menampilkan call stack. Output#0 sum_array ...#1 mainmenunjukkanmainmemanggilsum_array. Frame#0selalu adalah fungsi yang sedang dieksekusi.print *arr@5: Sintaksarr@nmencetaknelemen berturut-turut dari alamatarr. Berguna untuk memeriksa isi array tanpa loop manual.nextvsstep:nextmelompati pemanggilan fungsi (fungsi tetap dijalankan penuh, tapi tanpa masuk);stepmasuk ke dalam fungsi yang dipanggil. Untukfaktorialyang rekursif,stepakan membawa kita ke pemanggilan berikutnya.finish: Melanjutkan eksekusi sampai fungsi saat ini return, lalu berhenti. Berguna saat kita tidak ingin menelusuri sisa fungsi baris per baris—di sinifinishdarifaktorial(3)berhenti tepat setelah return, dengan pesanValue returned is $5 = 6yang menunjukkan3! = 6.
Mode Non-Interaktif dengan -batch #
GDB dapat dijalankan tanpa prompt interaktif menggunakan -batch. Ini berguna untuk otomasi CI atau skrip testing.
gdb -batch -nx \
-ex "break sum_array" \
-ex "run" \
-ex "print n" \
-ex "print *arr@5" \
-ex "continue" \
-ex "quit" \
./gcdemoPerintah Lanjutan #
| Perintah | Efek |
|---|---|
thread apply all bt | Backtrace semua thread |
info threads | Daftar thread |
thread <n> | Pindah ke thread n |
catch throw | Berhenti saat C++ exception dilempar |
catch syscall open | Berhenti saat syscall open dipanggil |
set follow-fork-mode child | Ikuti child setelah fork() |
dump binary memory file addr1 addr2 | Dump memori ke file |
restore file binary addr1 addr2 | Restore memori dari file |
generate-core-file | Buat core dump untuk sesi kemudian |