Debugging pada Odin berlapis tiga, dan ketiganya tersedia sejak build pertama. Lapis pertama adalah panik runtime yang selalu menunjuk berkas dan baris kejadian, lapis kedua adalah build -debug yang menyimpan simbol sehingga jejak tumpukan berisi nama proc, dan lapis ketiga adalah debugger eksternal seperti gdb atau lldb yang melangkahi program baris demi baris. Topik ini mempraktikkan ketiga lapis tersebut pada program yang sengaja mengakses array di luar batas, lalu memperbaiki kesalahan tersebut.
Membaca panik dan jejak kejadiannya#
Program berikut mencetak empat nilai suhu lalu sengaja mengakses indeks kelima dari array berisi empat elemen. Saat dijalankan, program mencetak keempat nilai yang sah, berhenti pada akses yang keliru, dan menampilkan pesan panik dengan lokasi persis.
package main
import "core:fmt"
ambil :: proc(catatan: []f64, indeks: int) -> f64 {
return catatan[indeks]
}
main :: proc() {
suhu := [4]f64{28.5, 29.0, 30.5, 27.5}
for i in 0 ..< 5 {
fmt.println("hari", i, ":", ambil(suhu[:], i))
}
}{ odin run main.odin -file 2>panic.log || true; } 2>/dev/null
cat panic.log
rm panic.loghari 0 : 28.5
hari 1 : 29
hari 2 : 30.5
hari 3 : 27.5
/home/kal/my-project/content/odin/perkakas-dan-pengiriman/debugging/main.odin(6:17) Index 4 is out of range 0..<4Baris terakhir keluaran adalah pesan panik bawaan runtime Odin. Lokasinya ditulis lengkap dengan berkas, baris, dan kolom, yaitu 6:17 yang tepat berada pada catatan[indeks] di proc ambil, disertai penjelasan Index 4 is out of range 0..<4. Pesan ini muncul tanpa flag apa pun karena runtime menyertakan lokasi sumber pada setiap panik, sehingga kebanyakan kesalahan batas terjawab sebelum debugger dibuka. Program berhenti dengan sinyal ilegal, dan shell menampilkan pula pesan sinyalnya sendiri. Nomor proses pada pesan itu berubah setiap eksekusi, sehingga pola perintah di atas menampung pesan panik ke berkas sementara agar keluaran yang ditampilkan tetap stabil.
Perbaikannya menggeser tanggung jawab pemeriksaan batas ke proc ambil, yang kini mengembalikan pasangan nilai dan status, dan pemanggil menangani status tersebut secara eksplisit.
package main
import "core:fmt"
ambil :: proc(catatan: []f64, indeks: int) -> (f64, bool) {
if indeks < 0 || indeks >= len(catatan) {
return 0, false
}
return catatan[indeks], true
}
main :: proc() {
suhu := [4]f64{28.5, 29.0, 30.5, 27.5}
for catatan, i in suhu {
fmt.println("hari", i, ":", catatan)
}
nilai, ok := ambil(suhu[:], 4)
if ok {
fmt.println("hari 4 :", nilai)
} else {
fmt.println("indeks 4 di luar batas data")
}
}odin run main-baik.odin -filehari 0 : 28.5
hari 1 : 29
hari 2 : 30.5
hari 3 : 27.5
indeks 4 di luar batas dataKasus di luar batas kini menjadi nilai kembalian biasa yang ditangani, dan program berakhir dengan kode keluar 0.
Build debug dan ukuran binari#
Flag -debug mengompilasi program dengan optimasi yang ditiadakan dan simbol debug yang dipertahankan, sehingga binarinya lebih besar namun paling informatif saat ditelusuri. Perbedaannya terlihat langsung pada ukuran berkas. Program jejak yang sama dipakai untuk tiga build dengan tingkat yang berbeda, lalu ukurannya dibaca dengan stat.
odin build jejak.odin -file -debug -out:jejak-debug
odin build jejak.odin -file -out:jejak-biasa
odin build jejak.odin -file -o:speed -out:jejak-cepat
stat -c "%n: %s byte" jejak-debug jejak-biasa jejak-cepatjejak-debug: 980104 byte
jejak-biasa: 411320 byte
jejak-cepat: 234488 byteBuild debug berukuran dua kali build biasa dan empat kali build -o:speed, karena simbol debug dan hilangnya optimasi menambahkan data yang memang tidak terkandung pada build rilis. Kebiasaan yang tepat adalah mengembangkan dengan -debug dan mengirim dengan -o:speed, sebagaimana dibahas pada topik kompilasi silang.
Jejak tumpukan dengan core:debug/trace#
Pustaka core:debug/trace menangkap jejak tumpukan saat program berjalan dan mencetaknya sebagai daftar frame. Untuk mendapatkan nama proc dan baris sumber pada hasilnya, program harus dibangun dengan -debug, karena resolusi nama membutuhkan simbol yang tersimpan pada binari debug.
package main
import "core:fmt"
import "core:debug/trace"
lapor :: proc() {
lokasi, err := trace.resolve(trace.capture())
if err != nil {
fmt.println("jejak tidak dapat diselesaikan:", trace.resolve_err_string(err))
return
}
defer trace.locations_destroy(lokasi)
fmt.println("jejak tumpukan proc main:")
trace.print(lokasi)
}
main :: proc() {
lapor()
}odin build jejak.odin -file -debug -out:jejak-debug
./jejak-debug 2>jejak.log
cat jejak.log
rm jejak.logjejak tumpukan proc main:
#0 main::lapor at /home/kal/my-project/content/odin/perkakas-dan-pengiriman/debugging/jejak.odin(7)
#1 main::main at /home/kal/my-project/content/odin/perkakas-dan-pengiriman/debugging/jejak.odin(19)
#2 main at /home/kal/my-project/tools/odin/base/runtime/entry_unix.odin(60)
#3 +0x29ca8 at /lib/x86_64-linux-gnu/libc.so.6
#4 __libc_start_main at /lib/x86_64-linux-gnu/libc.so.6
#5 _start at ./jejak-debugFrame main::lapor dan main::main kini memuat nama proc beserta baris pemanggilnya, sedangkan frame dari libc hanya berupa alamat karena pustaka sistemnya tidak dibangun dengan simbol debug Odin. Bila program yang sama dibangun tanpa -debug, seluruh frame Odin hanya berupa alamat mentah, dan inilah alasan utama memakai build debug saat menelusuri masalah alur pemanggilan.
Debugger eksternal#
Binari debug juga dapat dibuka pada debugger eksternal seperti gdb atau lldb, dengan alur kerja yang sama pada keduanya. Program dijalankan dengan run, eksekusi berhenti pada breakpoint yang ditetapkan dengan break beserta nama proc atau berkas dan barisnya. Langkah kecil dilakukan dengan next, langkah ke dalam proc dengan step, dan nilai variabel dibaca dengan print. Tampilan variabel Odin yang bertipe string, slice, atau map dipetakan secara rapi oleh dukungan debugger pada editor seperti VS Code dan Neovim yang mengintegrasikan Odin melalui language server. Debugger eksternal tidak terpasang pada sandbox tempat topik ini ditulis, dan keberadaannya memang bergantung pada lingkungan kerja masing-masing.
command -v gdb || echo "gdb tidak ditemukan"gdb tidak ditemukanMeski debugger eksternal tidak selalu ada, dua lapis pertama berjalan di mana pun compiler berada, sehingga penyelidikan awal tidak pernah bergantung pada instalasi tambahan.
Kesalahan umum#
Kesalahan pertama adalah mengirim binari debug ke pengguna. Ukurannya lebih besar, lajunya lebih lambat, dan simbolnya membocorkan struktur program, sedangkan build -o:speed atau rilis adalah tujuan pengiriman. -debug milik lingkungan pengembangan, dan pemisahan ini sudah terlihat jelas pada perbandingan ukuran di atas.
Kesalahan kedua adalah mengira pesan panik membutuhkan -debug. Pesan panik bawaan selalu mencantumkan berkas dan baris kejadian pada build mana pun, sedangkan -debug berperan pada resolusi nama untuk jejak tumpukan pustaka trace dan debugger eksternal. Membedakan keduanya mempercepat diagnosis karena sumber informasinya berbeda.
Kesalahan ketiga adalah menaruh beberapa proc main dalam satu folder lalu menjalankan odin run .. Folder adalah satu package, sehingga dua deklarasi main bertabrakan dan build gagal. Pola yang benar untuk contoh ganda seperti topik ini adalah mode berkas dengan odin run main-baik.odin -file.
Ringkasan#
- Panik runtime selalu menunjuk berkas, baris, dan kolom kejadian pada build mana pun, sehingga sebagian besar kesalahan batas terjawab tanpa alat tambahan.
-debugmenyimpan simbol dan meniadakan optimasi, membuat binari lebih besar namun memberi nama proc pada jejak tumpukan dan debugger eksternal.core:debug/tracemenangkap dan mencetak jejak tumpukan secara programatik, dengan resolusi nama proc yang membutuhkan simbol debug.- Debugger eksternal melengkapi penjejakan langkah dengan
break,next, danprint, dan binari debug adalah sasarannya.
Referensi#
- Overview — Odin Documentation — Panduan resmi bahasa yang menjadi dasar perilaku build dan panik runtime.
- Odin-lang/Odin — GitHub — Repositori compiler yang memuat pustaka core:debug beserta sumber runtime-nya.