Compiler bisa menangkap tipe yang salah, tetapi tidak bisa menangkap logika yang salah: fungsi yang mengembalikan nilai tak terduga, variabel yang berubah di titik yang salah, atau kondisi yang tidak pernah tercapai. Debugger menyelesaikan masalah itu dengan cara menghentikan eksekusi di titik yang kita pilih, lalu membiarkan kita memeriksa isi memori saat program sedang berhenti. Delve adalah debugger standar komunitas Go, dibuat khusus untuk memahami goroutine, defer, dan struktur internal runtime Go. Pada chapter ini kita akan memasang Delve, menyiapkan program target, lalu menjalankan sesi debugging nyata dengan breakpoint dan pencetakan variabel.

Memasang Delve #

Delve didistribusikan sebagai module Go, jadi pemasangannya cukup satu perintah go install dari repositori resminya:

bash
go install github.com/go-delve/delve/cmd/dlv@latest

Perintah tersebut mengunduh source code Delve, mengompilasinya, lalu menaruh binary dlv di $GOPATH/bin (biasanya ~/go/bin). Pastikan direktori itu berada di PATH Anda, lalu verifikasi pemasangan:

bash
dlv version

Output yang Dihasilkan:

text
Delve Debugger
Version: 1.27.2
Build: $Id: 360e7b2181d3da54115aaee4f569954bca95cfe3 $

Menyiapkan Program Target #

Berkas contoh.go di bawah halaman menghitung upah lembur Budi: jam ke atas 40 mendapat bonus setengah tarif. Program ini berjalan mulus dan keluar 0, tetapi angka 2.600.000 di output akan kita selidiki — apakah perhitungan bonusnya benar-benar sesuai aturan? Mari kita hentikan eksekusi di tengah jalan dan intip variabel-variabelnya:

go
package main

import "fmt"

// hitungLembur menghitung upah lembur; jam di atas 40 mendapat bonus setengah tarif.
func hitungLembur(jam int, tarif int) int {
	upah := jam * tarif
	if jam > 40 {
		upah += (jam - 40) * tarif / 2
	}
	return upah
}

func main() {
	jamBudi := 48
	tarifBudi := 50000

	upahBudi := hitungLembur(jamBudi, tarifBudi)
	fmt.Println("Upah lembur Budi:", upahBudi)
}

Output yang Dihasilkan:

text
Upah lembur Budi: 2600000

Mari kita telaah baris demi baris program di atas:

  1. Baris 6 (func hitungLembur): fungsi target debugging; parameter jam dan tarif adalah variabel yang akan kita inspeksi.
  2. Baris 7 (upah := jam * tarif): perhitungan dasar 48 × 50.000 = 2.400.000.
  3. Baris 8–10: blok bonus untuk jam di atas 40 — kandidat kuat sumber kejutan angka, karena menambah 8 × 25.000 = 200.000.
  4. Baris 15–18: main memanggil fungsi target; breakpoint akan dipasang di hitungLembur dan di baris return upah.

Sesi Debugging dengan Breakpoint #

Delve bekerja pada package, bukan berkas tunggal, jadi kita salin program ke folder terpisah yang sudah menjadi module. Sesi berikut dijalankan dari folder itu dan memakai dlv debug:

bash
mkdir -p debugdemo && cp contoh.go debugdemo/main.go
cd debugdemo
go mod init debugdemo
printf 'break main.hitungLembur\ncontinue\nprint jam\nprint tarif\nbreak 11\ncontinue\nprint upah\nstack\ncontinue\nquit\n' | dlv debug --allow-non-terminal-interactive=true

Output yang Dihasilkan:

text
Type 'help' for list of commands.
(dlv) Breakpoint 1 set at 0x4b6384 for main.hitungLembur() ./main.go:6
(dlv) > [Breakpoint 1] main.hitungLembur() ./main.go:6 (hits goroutine(1):1 total:1) (PC: 0x4b6384)
     1:	package main
     2:	
     3:	import "fmt"
     4:	
     5:	// hitungLembur menghitung upah lembur; jam di atas 40 mendapat bonus setengah tarif.
=>   6:	func hitungLembur(jam int, tarif int) int {
     7:		upah := jam * tarif
     8:		if jam > 40 {
     9:			upah += (jam - 40) * tarif / 2
    10:		}
    11:		return upah
(dlv) 48
(dlv) 50000
(dlv) Breakpoint 2 set at 0x4b63cf for main.hitungLembur() ./main.go:11
(dlv) > [Breakpoint 2] main.hitungLembur() ./main.go:11 (hits goroutine(1):1 total:1) (PC: 0x4b63cf)
     6:	func hitungLembur(jam int, tarif int) int {
     7:		upah := jam * tarif
     8:		if jam > 40 {
     9:			upah += (jam - 40) * tarif / 2
    10:		}
=>  11:		return upah
    12:	}
    13:	
    14:	func main() {
    15:		jamBudi := 48
    16:		tarifBudi := 50000
(dlv) 2600000
(dlv) 0  0x00000000004b63cf in main.hitungLembur
   at ./main.go:11
1  0x00000000004b6413 in main.main
   at ./main.go:18
2  0x0000000000450d27 in runtime.main
   at /home/z/tools/go/src/runtime/proc.go:302
3  0x0000000000489b21 in runtime.goexit
   at /home/z/tools/go/src/runtime/asm_amd64.s:1264
(dlv) Upah lembur Budi: 2600000
Process 6451 has exited with status 0
(dlv) 

Membaca Alur Sesi #

Sesi di atas memperlihatkan pola kerja debugger secara utuh. Perintah break main.hitungLembur memasang breakpoint pertama di pintu masuk fungsi; continue menjalankan program sampai titik itu, dan Delve menampilkan cuplikan source code dengan penanda => pada baris aktif. Perintah print jam dan print tarif mencetak nilai parameter (48 dan 50000) tanpa mengubah apa pun. Breakpoint kedua dipasang lewat break 11 — nomor baris pada berkas aktif — lalu print upah mengungkap nilai 2600000 tepat sebelum dikembalikan, dan stack menampilkan call stack: hitungLembur dipanggil dari main baris 18, yang dipanggil runtime.main. Terakhir, continue kedua membiarkan program selesai dan mencetak output aslinya.

Perintah DelveSingkatFungsi
break main.fn / break 11bPasang breakpoint pada fungsi atau baris
continuecJalankan sampai breakpoint berikutnya
nextnEksekusi satu baris, lewati pemanggilan fungsi
stepsMasuk ke dalam fungsi yang dipanggil
print xpCetak nilai variabel atau ekspresi
locals—Cetak semua variabel lokal frame aktif
stackbtTampilkan call stack
quitqKeluar dari sesi

Debugger menjawab “berapa nilainya sekarang”, tetapi untuk menjawab “jalur mana yang ditempuh program di production” kita butuh jejak yang lebih murah: logging terstruktur — topik chapter berikutnya.

Referensi #

Berkas Sumber

Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.

contoh.go

Unduh berkas
go
package main

import "fmt"

// hitungLembur menghitung upah lembur; jam di atas 40 mendapat bonus setengah tarif.
func hitungLembur(jam int, tarif int) int {
	upah := jam * tarif
	if jam > 40 {
		upah += (jam - 40) * tarif / 2
	}
	return upah
}

func main() {
	jamBudi := 48
	tarifBudi := 50000

	upahBudi := hitungLembur(jamBudi, tarifBudi)
	fmt.Println("Upah lembur Budi:", upahBudi)
}