Kesalahan pada program Fortran muncul dalam dua bentuk, yaitu kode yang ditolak kompiler saat kompilasi dan perilaku keliru yang baru terlihat saat program berjalan. Topik ini membahas perangkat untuk bentuk kedua: opsi -g dan -O0 yang membuat program dapat ditelusuri, pemeriksaan batas array dengan -fcheck=bounds yang mengubah pembacaan di luar batas menjadi error yang jelas, serta sesi gdb dalam mode batch untuk memeriksa variabel dan menetapkan breakpoint pada nama Fortran. Program contohnya tersedia di direktori topik ini, dan semua sesi dijalankan dari direktori tersebut.
Flag compiler untuk debug#
Kompilasi untuk debug memakai -g agar informasi baris sumber tertanam pada program, dan -O0 agar urutan eksekusi tetap mengikuti tulisan kode. Kompilasi teroptimasi boleh mengurutkan ulang pernyataan, menyisipkan fungsi (inlining), dan menghapus variabel sementara, sehingga debugger dapat menampilkan posisi yang tidak sesuai baris sumbernya. Dengan -O0 setiap variabel tetap menempati memorinya, sehingga perintah print pada gdb menampilkan nilai yang benar-benar tersimpan dan bukan salinan yang sudah dibuang kompiler. Kombinasi -g -O0 adalah titik mulai baku sebelum penelusuran dilakukan.
Sebagian kesalahan indeks tidak perlu menunggu waktu berjalan untuk terdeteksi. Ketika indeks yang ditulis adalah literal, kompiler dapat menilainya saat kompilasi: program yang menulis y(7) pada array berlima elemen menghasilkan peringatan statis berikut. Temuan semacam ini adalah yang paling murah karena menyebut berkas dan barisnya sebelum program pernah dijalankan, sementara kesalahan waktu berjalan baru muncul setelah input tertentu mengalir masuk.
# tulis contoh ke berkas sementara lalu kompilasi
printf 'program cek\n real :: y(5)\n y(7) = 1.0\nend program cek\n' > /tmp/contoh-batas.f90
gfortran -fcheck=bounds /tmp/contoh-batas.f90 -o /tmp/contoh-batas/tmp/contoh-batas.f90:3:4:
3 | y(7) = 1.0
| 1
Warning: Array reference at (1) is out of bounds (7 > 5) in dimension 1Kompiler mengetahui batas y dari deklarasinya, sehingga indeks literal di luar batas dilaporkan tanpa perlu menjalankan program. Ketika indeks baru diketahui saat program berjalan, misalnya karena diambil dari argumen prosedur atau dari batas perulangan yang berubah, analisis statis tidak dapat menjamin apa pun. Pemeriksaan untuk indeks semacam itu harus dipindah ke waktu berjalan, dan itulah pekerjaan opsi pada bagian berikut.
Pemeriksaan batas array saat berjalan#
Opsi -fcheck=bounds menyisipkan pemeriksaan indeks pada setiap akses array. Program berikut mengisi x dengan lima nilai, lalu membacanya dalam perulangan sampai indeks n yang bernilai tujuh, sehingga pembacaan ke-6 dan ke-7 berada di luar batas.
program batas
implicit none
real :: x(5)
integer :: i, n
n = 7
do i = 1, 5
x(i) = real(i) * 0.5
end do
do i = 1, n
print '(a, i0, a, f5.1)', "x(", i, ") = ", x(i)
end do
end program batas# kompilasi dengan pemeriksaan batas, lalu jalankan
gfortran -g -O0 -fcheck=bounds batas.f90 -o batas
./batas 2>&1 | grep -vE '0x|^[[:space:]]+at 'x(1) = 0.5
x(2) = 1.0
x(3) = 1.5
x(4) = 2.0
x(5) = 2.5
At line 10 of file batas.f90
Fortran runtime error: Index '6' of dimension 1 of array 'x' above upper bound of 5
Error termination. Backtrace:Pembacaan x(6) menghentikan program dengan pesan yang menyebut indeksnya, batas atasnya, dan baris sumbernya. Baris At line 10 of file batas.f90 adalah bagian keluaran itu yang paling berguna: ia menunjuk langsung posisi sumber yang memicu error, sehingga pembaca dapat melompat ke sana tanpa menelusuri alamat memori apa pun. Saringan grep pada perintah di atas membuang baris alamat memori pada jejak tumpukan (backtrace) karena alamat itu berbeda pada setiap eksekusi. Tanpa -fcheck=bounds, pembacaan yang sama berjalan tanpa pesan apa pun, dan nilai yang tercetak adalah isi memori yang kebetulan berada di sana, sebagaimana diperlihatkan sesi gdb berikutnya.
Sesi gdb pada nama Fortran#
gdb dalam mode -batch menjalankan rangkaian perintah lalu berhenti, sehingga satu baris perintah menghasilkan sesi yang dapat diulang. Program berikut memuat modul hitung_mod dengan fungsi kuadrat, dan sesi gdb berhenti di fungsi tersebut untuk memeriksa argumennya.
module hitung_mod
implicit none
contains
pure function kuadrat(x) result(hasil)
real, intent(in) :: x
real :: hasil
hasil = x * x
end function kuadrat
end module hitung_mod
program utama
use hitung_mod, only : kuadrat
implicit none
print '(f6.2)', kuadrat(2.5)
end program utama# kompilasi untuk debug
gfortran -g -O0 hitung.f90 -o hitung
# berhenti di kuadrat, periksa argumennya, lalu jalankan sampai selesai
gdb -batch -ex "break kuadrat" -ex run -ex "print x" -ex finish ./hitung 2>&1 |
grep -vE 'Inferior|Thread debug|libthread_db' | sed 's/0x[0-9a-f]*/0xXXX/g'Breakpoint 1 at 0xXXX: file hitung.f90, line 7.
Breakpoint 1, hitung_mod::kuadrat (x=2.5) at hitung.f90:7
7 hasil = x * x
$1 = 2.5
0xXXX in utama () at hitung.f90:14
14 print '(f6.2)', kuadrat(2.5)
Value returned is $2 = 6.25Perintah break kuadrat menerima nama fungsi sebagaimana tertulis di sumber, dan gdb menampilkannya dengan kualifikasi modulnya, yaitu hitung_mod::kuadrat. Baris Breakpoint 1, hitung_mod::kuadrat (x=2.5) at hitung.f90:7 patut dibaca sebagai tiga informasi sekaligus: penanda breakpoint yang kena, nama prosedur beserta nilai argumennya saat itu, dan posisi sumber tempat eksekusi berhenti berikut cuplikan barisnya. Perintah print x menampilkan nilai argumen x, sedangkan finish menjalankan fungsi sampai selesai dan melaporkan nilai kembaliannya. Alamat memori pada keluaran diganti 0xXXX oleh sed karena berbeda antareksekusi, sedangkan bagian yang tersisa identik setiap kali dijalankan.
Sesi gdb juga memperlihatkan apa yang terjadi bila pemeriksaan batas tidak diaktifkan. Program batas.f90 dikompilasi ulang tanpa -fcheck=bounds, gdb berhenti pada baris perulangan pembacaan, dan perintah continue menjalankan program sampai selesai. Pemeriksaan n dan x sebelum continue menegaskan bahwa data yang masuk benar, sehingga satu-satunya penjelasan bagi nilai yang keliru setelahnya adalah pembacaan di luar batas itu sendiri.
# kompilasi tanpa pemeriksaan batas
gfortran -g -O0 batas.f90 -o batas-g
# periksa n dan x, lalu lanjutkan hingga program selesai
gdb -batch -ex "break 9" -ex run -ex "print n" -ex "print x" -ex continue ./batas-g 2>&1 |
grep -vE 'Inferior|Thread debug|libthread_db' | sed 's/0x[0-9a-f]*/0xXXX/g'Breakpoint 1 at 0xXXX: file batas.f90, line 9.
Breakpoint 1, batas () at batas.f90:9
9 do i = 1, n
$1 = 7
$2 = (0.5, 1, 1.5, 2, 2.5)
x(1) = 0.5
x(2) = 1.0
x(3) = 1.5
x(4) = 2.0
x(5) = 2.5
x(6) = 0.0
x(7) = 0.0Nilai n adalah 7 dan x berisi lima nilai yang benar, tetapi perulangan membaca x(6) dan x(7) tanpa pesan error dan mencetak isi memori yang bukan milik array. Bandingkan dengan sesi -fcheck=bounds sebelumnya yang menghentikan program tepat pada pembacaan pertama yang melanggar: di sana posisi kesalahan dilaporkan otomatis, di sini ia hanya dapat diturunkan dari inspeksi manual. Perilaku semacam ini adalah sumber hasil keliru yang paling sulit dilacak, dan itulah alasan -fcheck=bounds diaktifkan pada bangun pengembangan.
Kesalahan umum#
- Men-debug program teroptimasi. Pada
-O2, gdb dapat menunjuk baris yang tampak melompat dan variabel yang nilainya sudah dihapus dari memori. Telusuri dengan-g -O0, dan ukur performa dengan bangun teroptimasi. - Mengandalkan nilai program tanpa pemeriksaan batas. Program yang berjalan tanpa error belum tentu benar, sebagaimana
x(6)yang terbaca sebagai 0.0. Aktifkan-fcheck=boundsselama pengembangan. - Menulis breakpoint dengan nama internal kompiler. Rangkaian
__hitung_mod_MOD_kuadratadalah nama bentukan gfortran yang termangkus (mangled), sedangkan gdb pada sesi ini menerimakuadratatauhitung_mod::kuadratlangsung.
Ringkasan#
- Kompilasi untuk debug memakai
-g -O0, sedangkan-fcheck=boundsmengubah akses array di luar batas menjadi runtime error yang menyebut indeks dan barisnya. - gdb mode
-batch -exmemeriksa variabel dan menetapkan breakpoint pada nama Fortran sebagaimana tertulis, dengan kualifikasi modul sepertihitung_mod::kuadrat. - Pembacaan di luar batas tanpa pemeriksaan berjalan diam-diam dan mengembalikan isi memori yang kebetulan ada di sana.
Referensi#
- The GNU Fortran Compiler — GNU Project
— Manual gfortran yang merinci
-g,-O0, dan keluarga opsi-fcheck=. - Debugging with GDB — GNU Project
— Dokumentasi resmi gdb, termasuk mode batch,
break, danfinish. - Learn — fortran-lang — Materi belajar resmi komunitas Fortran yang mencakup praktik debugging.