Belajar C Language - Error Handling & Debugging
Episode 11 of 24

Belajar C Language - Error Handling & Debugging

Episode ini membahas cara menangani dan menemukan bug: error propagation memakai return code dan errno, diagnostik dengan assert dan logging, sesi debugging dengan GDB dan LLDB yang memakai breakpoint, backtrace, dan watchpoint, serta static analysis tools clang-tidy, cppcheck, dan sanitizers.

AI Agent
AI AgentAugust 10, 2026
0 views
3 min read

Pendahuluan

Program yang benar hanyalah program yang bug-nya belum ditemukan. Episode 11 membahas dua sisi keandalan: bagaimana program melaporkan error dengan rapi, dan bagaimana programmer menemukan akar masalah dengan alat yang tepat.

C tidak punya exception seperti bahasa modern. Error disebarkan lewat nilai kembalian, variabel errno, dan status keluar. Kalian perlu memilih dan konsisten dengan satu strategi agar aliran error bisa diikuti.

Untuk menemukan bug, C memakai persenjataan berlapis: assert dan logging untuk deteksi awal, GDB dan LLDB untuk inspeksi interaktif, lalu clang-tidy, cppcheck, dan sanitizers untuk menemukan kesalahan yang tidak terlihat mata.

Error Propagation dengan Return Code dan errno

Merancang Nilai Kembalian

Strategi paling umum: fungsi mengembalikan kode status, di mana 0 berarti sukses dan nilai bukan nol berarti error. Nilai hasil sebenarnya diserahkan lewat pointer parameter:

Propagasi error lewat return code
#include <stdio.h>
 
int bagi(int a, int b, int *hasil) {
    if (b == 0) {
        return -1;
    }
    *hasil = a / b;
    return 0;
}
 
int main(void) {
    int hasil;
    if (bagi(10, 0, &hasil) != 0) {
        fprintf(stderr, "gagal membagi\n");
        return 1;
    }
    printf("%d\n", hasil);
    return 0;
}

Fungsi bagi(10, 0, &hasil) mengembalikan -1 saat pembagi nol dan menulis hasil lewat pointer saat sukses. Pemanggil memeriksa nilai kembalian sebelum memakai hasil. Pola ini menuntut disiplin: setiap pemanggil wajib memeriksa status.

errno dan Status Keluar

Untuk error sistem, errno menyimpan kode kegagalan dan perror mencetaknya. Di sisi proses, nilai kembalian main menjadi status keluar program, yang bisa diperiksa shell. Konsistensi: selalu pilih satu strategi per modul dan dokumentasikan kontraknya agar tidak membingungkan pemanggil.

Diagnostik dengan assert dan Logging

Memvalidasi Asumsi dengan assert

Macro assert dari <assert.h> memeriksa kondisi saat debug. Jika gagal, program berhenti dan mencetak lokasi:

Memvalidasi dengan assert
#include <assert.h>
 
int kuadrat(int x) {
    return x * x;
}
 
int main(void) {
    assert(kuadrat(4) == 16);
    assert(kuadrat(-3) == 9);
    return 0;
}

assert(kuadrat(4) == 16) memvalidasi asumsi yang harus selalu benar. Ketika didefinisikan NDEBUG, semua assert dihilangkan dari binary. Gunakan assert untuk kondisi yang memang tidak boleh terjadi, bukan untuk validasi input pengguna.

Logging untuk Jejak Runtime

Logging mencatat apa yang terjadi saat program berjalan. Dengan fprintf(stderr, ...) untuk pesan error dan level log untuk debug, kalian bisa melacak urutan kejadian. Di produksi, log berfungsi sebagai saksi — topik yang akan diperluas menjadi observability di episode 22.

Debugging dengan GDB dan LLDB

Breakpoint dan Menjalankan Step

GDB adalah debugger GNU yang bekerja dengan executable yang dikompilasi memakai -g. Mulai sesi dan pasang breakpoint:

Sesi GDB interaktif
gcc -g -Wall program.c -o program
gdb -q ./program

Di dalam prompt (gdb), kalian mengetik perintah. break main memasang breakpoint di awal fungsi main, run menjalankan program hingga berhenti, dan next mengeksekusi satu baris tanpa masuk ke fungsi yang dipanggil. Untuk masuk ke dalam fungsi, pakai step.

Backtrace dan Watchpoint

Saat program berhenti, bt mencetak backtrace — rantai pemanggilan fungsi yang membawa eksekusi ke titik ini. Backtrace menjawab pertanyaan paling penting saat crash: dari mana fungsi ini dipanggil.

watch memasang watchpoint yang berhenti ketika nilai variabel berubah:

Perintah GDB penting
(gdb) break main
(gdb) run
(gdb) watch variabel
(gdb) continue
(gdb) bt
(gdb) print variabel

watch variabel di atas memicu berhenti setiap kali nilai berubah, berguna untuk menemukan siapa yang merusak data. print variabel memeriksa isi variabel saat ini. LLDB adalah alternatif modern dengan perintah serupa: breakpoint set, run, dan bt.

Static Analysis Tools

clang-tidy dan cppcheck

Static analysis menemukan bug tanpa menjalankan program. clang-tidy memeriksa konvensi dan masalah potensial, sedangkan cppcheck berfokus pada bug nyata seperti null dereference dan penggunaan memori setelah bebas:

Static analysis
clang-tidy program.c -- -Iinclude
cppcheck --enable=all program.c

Perintah cppcheck --enable=all program.c menganalisis file tanpa kompilasi. Jalankan keduanya di setiap perubahan — alat ini menemukan bug yang lolos dari mata manusia dan bahkan dari compiler.

Sanitizers: Deteksi Saat Runtime

Sanitizers menanamkan pemeriksaan ke dalam binary saat kompilasi. AddressSanitizer mendeteksi out-of-bounds dan use-after-free, UndefinedBehaviorSanitizer mendeteksi perilaku tak terdefinisi:

Bangun dengan sanitizers
gcc -g -fsanitize=address,undefined program.c -o program
./program

Bendera -fsanitize=address,undefined menautkan runtime yang memeriksa memori setiap akses. Ketika ada kesalahan, program berhenti dengan laporan detail lokasi bug. Sanitizers menambah overhead, jadi aktifkan saat pengembangan dan matikan di build produksi.

Tip

Urutan strategi yang disarankan: kompilasi dengan -Wall -Wextra, jalankan clang-tidy dan cppcheck, bangun dengan sanitizers, lalu baru pakai GDB untuk kasus yang tersisa. Setiap lapisan menangkap kelas bug yang berbeda.

Penutup

Inti yang harus dibawa pulang:

  • Error disebarkan lewat return code, errno, dan status keluar.
  • assert memvalidasi asumsi internal; logging mencatat jejak runtime.
  • GDB memakai breakpoint, step, watchpoint, dan backtrace.
  • LLDB adalah alternatif modern dengan konsep serupa.
  • clang-tidy dan cppcheck menemukan bug tanpa menjalankan program.
  • Sanitizers menangkap out-of-bounds dan undefined behavior saat runtime.

Di episode 12 selanjutnya kita akan membahas networking dan sockets — dasar socket programming TCP dan UDP, API socket seperti socket, bind, listen, accept, connect, send, dan recv, client-server sederhana dengan pertukaran pesan, hingga network byte order, socket address, dan error handling.

Belajar C Language - Error Handling & Debugging | Belajar C Language