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.

bash
gcc -std=c11 -Wall -Wextra -g -O0 -o gcdemo gcdemo.c
gdb ./gcdemo

Perintah Dasar #

Berikut adalah perintah GDB yang paling sering kita pakai. Setiap perintah memiliki bentuk panjang dan singkat—misalnya run dan r ekuivalen.

Kontrol Eksekusi #

PerintahSingkatEfek
run [args]rMulai program dari awal
start—Mulai dan berhenti di main()
continuecLanjutkan eksekusi
nextnStep over (lewati pemanggilan fungsi)
stepsStep into (masuk ke fungsi)
finishfJalankan sampai fungsi saat ini return
until [loc]uJalankan sampai lokasi/baris tertentu
nexti / stepini / siStep instruksi mesin
killkHentikan program (tetap di GDB)
quitqKeluar dari GDB

Breakpoint dan Watchpoint #

PerintahEfek
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 breakpointsDaftar breakpoint
delete <n>Hapus breakpoint nomor n
deleteHapus 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 #

PerintahEfek
print <expr>Cetak nilai ekspresi
print/x <expr>Cetak dalam heksadesimal
print *arr@nCetak n elemen array
display <expr>Cetak otomatis setiap berhenti
undisplay <n>Hentikan display
info localsSemua variabel lokal frame saat ini
info argsArgumen fungsi saat ini
info variablesDaftar variabel global
backtracebt — call stack
backtrace fullStack + variabel lokal tiap frame
frame <n>Pindah ke frame n
up / downNaik/turun satu frame
info registersIsi semua register CPU
x/<n><u><f> <addr>Memeriksa memori (n unit, ukuran u, format f)
disassembleLihat kode mesin fungsi

Modifikasi dan Ekspresi #

PerintahEfek
set var <name>=<val>Ubah nilai variabel
set {int}0xaddr = 42Tulis 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.

c
/* 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:

bash
gcc -std=c11 -Wall -Wextra -g -O0 -o gcdemo gcdemo.c
./gcdemo

Output yang Dihasilkan:

text
sum = 150
5! = 120

Sesi Debugging Interaktif #

Berikut adalah transkrip sesi GDB yang menggunakan beberapa perintah inti. Prompt GDB ditandai (gdb). Output ini mewakili perilaku standar gdb versi 12+.

text
$ 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:

text
(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) quit

Mari kita telaah baris-baris perintah di atas:

  1. break sum_array dan break faktorial: GDB menerima nama fungsi sebagai lokasi breakpoint. Saat program mencapai awal fungsi tersebut, eksekusi berhenti sebelum baris pertama dieksekusi.
  2. backtrace (bt): Menampilkan call stack. Output #0 sum_array ... #1 main menunjukkan main memanggil sum_array. Frame #0 selalu adalah fungsi yang sedang dieksekusi.
  3. print *arr@5: Sintaks arr@n mencetak n elemen berturut-turut dari alamat arr. Berguna untuk memeriksa isi array tanpa loop manual.
  4. next vs step: next melompati pemanggilan fungsi (fungsi tetap dijalankan penuh, tapi tanpa masuk); step masuk ke dalam fungsi yang dipanggil. Untuk faktorial yang rekursif, step akan membawa kita ke pemanggilan berikutnya.
  5. finish: Melanjutkan eksekusi sampai fungsi saat ini return, lalu berhenti. Berguna saat kita tidak ingin menelusuri sisa fungsi baris per baris—di sini finish dari faktorial(3) berhenti tepat setelah return, dengan pesan Value returned is $5 = 6 yang menunjukkan 3! = 6.

Mode Non-Interaktif dengan -batch #

GDB dapat dijalankan tanpa prompt interaktif menggunakan -batch. Ini berguna untuk otomasi CI atau skrip testing.

bash
gdb -batch -nx \
    -ex "break sum_array" \
    -ex "run" \
    -ex "print n" \
    -ex "print *arr@5" \
    -ex "continue" \
    -ex "quit" \
    ./gcdemo

Perintah Lanjutan #

PerintahEfek
thread apply all btBacktrace semua thread
info threadsDaftar thread
thread <n>Pindah ke thread n
catch throwBerhenti saat C++ exception dilempar
catch syscall openBerhenti saat syscall open dipanggil
set follow-fork-mode childIkuti child setelah fork()
dump binary memory file addr1 addr2Dump memori ke file
restore file binary addr1 addr2Restore memori dari file
generate-core-fileBuat core dump untuk sesi kemudian