Debugging

Menelusuri kesalahan dengan breakpoint, dbstop, dan alur debugging di editor.

Daftar isi

Kesalahan pada program MATLAB jarang berdiri sendiri, karena satu nilai yang salah menjalar ke seluruh perhitungan sesudahnya. Topik ini membahas cara menelusurinya secara sistematis: alur debugging di editor dengan breakpoint, perintah setara dbstop dari Command Window, kegagalan awal yang dibangkitkan dengan error dan assert, penangkapan error dengan try/catch, serta penelusuran sederhana dengan cetakan bertingkat. Setelah topik ini, kesalahan dapat dipersempit ke satu fungsi dan satu baris, bukan dikejar dengan menebak.

Alur debugging di editor#

Debugging di editor berjalan sebagai siklus yang tetap: hentikan program pada titik yang dicurigai, periksa keadaan saat itu, jalankan selangkah demi selangkah, lalu ulangi. Titik henti disebut breakpoint, dan pemasangannya dilakukan dengan mengklik tanda putus pada sisi kiri editor di sebelah baris yang dicurigai atau dengan tombol F12 pada Windows dan Linux. Siklus lengkapnya berlangsung sebagai berikut.

  1. Pasang breakpoint pada baris tempat hasil mulai mencurigakan, biasanya sebelum dan sesudah perhitungan yang dicurigai sebagai penyebab.
  2. Jalankan berkas dengan tombol Run (F5). Eksekusi berhenti tepat sebelum baris bertanda, dan prompt berubah menjadi K>> yang menandakan mode debug aktif.
  3. Periksa keadaan program saat berhenti: arahkan kursor ke nama variabel pada editor untuk melihat isinya, atau buka panel Workspace yang memuat seluruh variabel beserta ukurannya.
  4. Jalankan selangkah: Step (F10) menjalankan satu baris, Step In (F11) masuk ke dalam fungsi yang sedang dipanggil, dan Step Out (Shift+F11) menyelesaikan fungsi saat ini lalu berhenti kembali.
  5. Lanjutkan dengan Continue (F5) sampai breakpoint berikutnya, lalu perbaiki kode dan jalankan ulang untuk memastikan kesalahan hilang.

Alur ini memerlukan editor interaktif, sehingga tidak direproduksi sebagai keluaran contoh pada jalur ini. Seluruh langkahnya juga tersedia sebagai perintah teks, dan perintah itulah yang memungkinkan debugging berjalan tanpa tetikus.

Gagal awal dengan error dan assert#

Menolak masukan yang tidak sah di awal jauh lebih murah daripada menelusuri akibatnya setelah perhitungan berjalan jauh. Perintah error dengan identifier menghentikan eksekusi sambil membawa dua informasi: identifier penyebab dan pesan yang sudah terformat. Keduanya dapat diperiksa tanpa menghentikan skrip pemanggil karena try/catch menangkap error itu lebih dahulu.

matlab Gagal awal dengan error ber-identifier
try
  error('suhu:diBawahNolMutlak', 'nilai %g berada di bawah nol mutlak', -500);
catch e
  fprintf('id    : %s\n', e.identifier);
  fprintf('pesan : %s\n', e.message);
end
text Keluaran
id    : suhu:diBawahNolMutlak
pesan : nilai -500 berada di bawah nol mutlak

Pola yang sama dipasang di dalam fungsi supaya validasi terjadi pada batas masukan. Fungsi berikut menolak masukan kosong dan masukan di bawah nol mutlak sebelum menghitung suhu Celsius, dan pemanggilnya tetap mendapat kesempatan menangkap error itu. Keberadaan argumen ditangani paling awal melalui nargin, disusul pemeriksaan jenis data dan rentang nilainya.

matlab konversiSuhu.m
function c = konversiSuhu(f)
% KONVERSISUHU mengubah derajat Fahrenheit menjadi derajat Celsius.
%   KONVERSISUHU(f) memvalidasi masukan lebih dahulu, lalu menghitungnya.
%   Tanpa argumen, contoh 212 derajat Fahrenheit dipakai.

if nargin < 1
  f = 212;   % titik didih air sebagai contoh bawaan
end
if ~isnumeric(f) || isempty(f)
  error('suhu:masukanKosong', 'suhu Fahrenheit wajib berupa angka');
end
if any(f < -459.67)
  error('suhu:diBawahNolMutlak', 'nilai %.1f berada di bawah nol mutlak', min(f));
end
c = (f - 32) * 5 / 9;
end
matlab Validasi gagal awal di dalam fungsi
try
  konversiSuhu(-500);
catch e
  fprintf('id    : %s\n', e.identifier);
  fprintf('pesan : %s\n', e.message);
end
fprintf('212 F : %.1f C\n', konversiSuhu(212));
text Keluaran
id    : suhu:diBawahNolMutlak
pesan : nilai -500.0 berada di bawah nol mutlak
212 F : 100.0 C

assert menargetkan keadaan yang harus benar, bukan masukan yang harus diolah. Ketika kondisinya bernilai salah, assert membangkitkan error dengan pesan yang diberikan, sehingga cocok untuk janji antar bagian kode, misalnya data tidak boleh kosong. Penangkapan dengan try/catch memperlihatkan pesannya tanpa menghentikan skrip.

matlab assert dengan pesan kegagalan
dataSuhu = [];
try
  assert(numel(dataSuhu) > 0, 'data kosong tidak dapat diproses');
catch e
  fprintf('pesan assert: %s\n', e.message);
end
dataSuhu = [21 23 22];
try
  assert(numel(dataSuhu) > 0, 'data kosong tidak dapat diproses');
  fprintf('lanjut: %d data diproses\n', numel(dataSuhu));
catch e
  fprintf('pesan assert: %s\n', e.message);
end
text Keluaran
pesan assert: data kosong tidak dapat diproses
lanjut: 3 data diproses

Breakpoint dari Command Window dengan dbstop#

Seluruh kendali breakpoint tersedia sebagai perintah: dbstop memasang breakpoint, dbclear melepasnya, dbcont melanjutkan eksekusi yang sedang berhenti, dan dbquit keluar dari mode debug tanpa menyelesaikan program. Bentuk dbstop if error bahkan menghentikan program pada baris tempat error apa pun terjadi, sehingga titik kesalahan ditemukan tanpa perlu ditebak lebih dahulu. Urutan perintah berikut menelusuri fungsi konversiSuhu dari bagian sebelumnya.

matlab Perintah breakpoint dari Command Window
% tidak-dijalankan
% Perintah ini menuntut sesi interaktif karena eksekusi berhenti menunggu perintah berikutnya.
dbstop in konversiSuhu at 9    % berhenti sebelum baris validasi dijalankan
konversiSuhu(-500)             % berjalan lalu berhenti, prompt K>> muncul
dbcont                         % lanjutkan sampai akhir atau breakpoint berikutnya
dbclear in konversiSuhu       % lepas breakpoint pada berkas itu
dbquit                         % keluar dari mode debug tanpa menyelesaikan skrip

Saat program berhenti pada breakpoint, Command Window berpindah ke mode debug. Seluruh variabel fungsi yang sedang berjalan dapat diperiksa dengan mengetik namanya, dan fungsi lain juga dapat dipanggil untuk menguji dugaan perbaikan. Perintah dbstatus mendaftar breakpoint yang masih terpasang, dan dbstep menjalankan satu baris sebagai padanan tombol Step. Bentuk dbstop in konversiSuhu tanpa nomor baris berhenti pada baris pertama yang dapat dijalankan, dan dbclear all melepas seluruh breakpoint dalam satu perintah. Kombinasi lengkapnya menjadikan siklus pasang, periksa, dan lanjutkan dapat dijalankan sepenuhnya dari papan ketik.

Menangkap error dengan try/catch#

Blok try/catch membatasi ledakan error pada satu bagian yang memang berisiko gagal, misalnya pembacaan berkas yang mungkin tidak ada. Objek e pada klausa catch memuat identifier, pesan, dan jejak pemanggilan, dan ketiganya dapat dicetak sebagai catatan sebelum program memutuskan langkah berikutnya. Blok try sebaiknya dijaga sekecil mungkin agar error yang tertangkap memang milik operasi yang dimaksud, sebab cakupan yang terlalu lebar ikut menyembunyikan kegagalan dari bagian lain. Fungsi lasterror mengembalikan rekaman yang sama untuk kode yang memakai bentuk lama.

matlab Menangkap error dan mencetak konteksnya
try
  muat = load('catatanSuhuTidakAda.mat');
catch e
  fprintf('id kosong      : %d\n', isempty(e.identifier));
  fprintf('pesan          : %s\n', e.message);
  riwayat = lasterror();
  fprintf('bingkai riwayat: %d\n', numel(riwayat.stack));
end
fprintf('program tetap berlanjut sampai baris ini\n');
text Keluaran
id kosong      : 1
pesan          : load: unable to find file catatanSuhuTidakAda.mat
bingkai riwayat: 1
program tetap berlanjut sampai baris ini

Identifier kosong karena kegagalan pembacaan berkas pada implementasi ini tidak membawa identifier khusus, sedangkan pesannya menyebut berkas yang dicari. Nilai satu pada bingkai riwayat menyatakan error terjadi pada satu tingkat pemanggilan, dan pada program bertingkat, jejak itulah yang menunjukkan fungsi mana yang membangkitkan error. Teks pesan error seperti ini berbeda antar implementasi, tetapi informasinya tetap sama, yaitu berkas mana yang gagal dibaca. Setelah catatan tercetak, eksekusi berlanjut ke baris setelah blok, dan program dapat memutuskan untuk berhenti, memakai data cadangan, atau melanjutkan tanpa data itu.

Penelusuran dengan cetak bertingkat#

Ketika editor tidak tersedia, penelusuran paling sederhana adalah mencetak keadaan program pada tiap langkah dengan fprintf. Cetak bertingkat menirukan Step pada editor karena lekukan menunjukkan kedalaman, dan keadaan yang dicetak hanyalah yang benar-benar menentukan hasil. Pada contoh berikut, yang dicetak adalah pembanding dan nilai maksimum yang sedang dipegang pada tiap langkah, sehingga keputusan penggantian nilai terlihat pada keluarannya.

matlab Penelusuran dengan cetak bertingkat
dataSuhu = [14 8 22 5];
nilaiMaks = dataSuhu(1);
for k = 2:numel(dataSuhu)
  fprintf('langkah %d: bandingkan %d dengan %d\n', k, dataSuhu(k), nilaiMaks);
  if dataSuhu(k) > nilaiMaks
    nilaiMaks = dataSuhu(k);
    fprintf('  nilai maks baru: %d\n', nilaiMaks);
  end
end
fprintf('hasil: %d\n', nilaiMaks);
text Keluaran
langkah 2: bandingkan 8 dengan 14
langkah 3: bandingkan 22 dengan 14
  nilai maks baru: 22
langkah 4: bandingkan 5 dengan 22
hasil: 22

Pada keluaran di atas, baris berlekuk pada langkah ketiga memperlihatkan momen nilai maksimum diganti, dan empat langkah itu cukup untuk memastikan alurnya benar. Kelemahan cara ini adalah cetakan yang harus dibersihkan setelah penyebab ditemukan, sehingga cetak bertingkat cocok untuk penyelidikan singkat dan bukan alat tetap. Cetak bertingkat tetap berguna ketika kesalahan hanya muncul pada data tertentu atau saat editor tidak dapat dipakai, misalnya pada sesi jarak jauh yang hanya menampilkan teks.

Kesalahan umum#

  • Menelan error dengan catch kosong. Blok catch tanpa isi membuat kegagalan tidak terlihat dan hasil akhir kehilangan jaminan kebenarannya. Cetak konteks error lebih dahulu, lalu putuskan secara eksplisit antara melanjutkan, memakai cadangan, atau menghentikan program.
  • Membiarkan validasi menunggu sampai perhitungan dalam. Masukan yang tidak sah sebaiknya ditolak di ambang fungsi dengan error ber-identifier, karena error yang muncul jauh dari penyebabnya memaksa penelusuran mundur yang panjang.
  • Menelusuri dengan menebak tanpa mempersempit. Pasang breakpoint atau cetakan penelusuran pada titik tengah alur, bukan pada seluruh baris sekaligus. Satu titik henti yang dipilih dengan baik membagi wilayah pencarian menjadi dua, sehingga beberapa siklus saja sudah cukup menemukan baris penyebab.

Ringkasan#

  • Siklus debugging berjalan pasang, periksa, lanjutkan, baik lewat editor dengan breakpoint maupun lewat perintah dbstop, dbcont, dbclear, dan dbquit.
  • Validasi batas masukan memakai error ber-identifier, dan janji keadaan antar bagian kode memakai assert dengan pesan.
  • try/catch menangkap error beserta konteksnya pada objek e dan lasterror, sehingga kegagalan dapat dicatat sebelum program melanjutkan.
  • Cetak bertingkat dengan fprintf menjadi penelusuran darurat ketika editor tidak tersedia, dan dibersihkan setelah penyebab ditemukan.

Referensi#