Di posting ini saya sekedar ingin sharing metode dan tools yang biasa saya pakai untuk debugging program. Di lingkungan yang ideal, kita seharusnya punya debugger yang canggih, cepat dan reliable, tapi sayangnya sering kali ini tidak tersedia. Ini contoh beberapa lingkungan pengembangan yang “mengesalkan” yang pernah saya jumpai:
- Symbian versi awal (versi 6): untuk bisa mendebug, perlu IDE khusus dengan license khusus yang mahal. Symbian versi terbaru sudah semakin baik (tapi sekarang ini Symbian sudah akan berakhir).
- BlackBerry: startup time debugger sangat lama, dan debugger sering error. Setiap kali menginstall versi program baru di BlackBerry, perlu restart (butuh sekitar 5 menit tiap kali restart).
- Mendebug aplikasi Android di emulator. Startup emulator sangat lambat (bahkan di processor AMD quad core 3 ghz memori 16 GB) , lebih cepat mencoba langsung di HP. Masalahnya adalah ketika ada laporan: ada bug kecil di Android versi 2.2, tapi HP saya memakai Android 2.3
- Debugging PHP di web hosting. Kadang ada masalah yang hanya muncul di web hosting, tapi tidak muncul ketika dicoba di server sendiri. Beberapa kemungkinan adalah karena versi PHP yang tidak sama, versi library yang tidak sama. Di server sendiri bisa mudah menginstall Xdebug, tapi tidak tersedia di tempat hosting.
- Nurit Point Of Sales. Device ini banyak bugnya, tidak tersedia debugger, dan jika crash, akan memprint isi memori di atas kertas thermal sekitar 1/2 meter.
- Kernel FreeBSD. Ketika porting tahap awal, sangat sulit mendebug apa yang terjadi di level CPU. Masalahnya karena saya tidak menggunakan development board yang memiliki JTAG.
Sebenarnya tidak semua lingkungan itu benar-benar mengesalkan jika kita punya uang, contohnya: jika saya punya uang beberapa ribu dollar, saya akan bisa mendebug Symbian dengan mudah. Jika saya punya beberapa HP Android berbagai versi, debugging Android juga lebih mudah. Jika saya punya development board resmi dengan JTAG, porting kernel akan lebih mudah. Jika saya hosting VPS dengan versi PHP saya sendiri, maka tidak akan ada masalah dengan PHP, dan andaikan ada masalah, saya bisa menginstall XDebug. Pada kenyataannya seringkali tidak tersedia dana untuk menyelesaikan berbagai masalah tersebut.
Hal pertama yang saya lakukan sekarang adalah mencari lingkungan ternyaman untuk mengembangkan fitur utama. Biasanya ini di desktop. Misalnya ketika saya mengembangkan pembaca Alkitab, saya akan membuat dulu engine parsing file PDB di Desktop. Kadang saya menggunakan IDE, tapi lebih sering menggunakan command line. Setelah bisa dianggap selesai, baru saya membuat user interfacenya. Hal yang sama saya lakukan ketika membuat aplikasi HTML5 di tablet, saya mengembangkan dulu algoritma utama di browser desktop, baru setelah itu tampilan dipoles di device yang sesungguhnya. Jika mungkin, biasanya saya akan membuat skrip khusus, supaya aplikasinya bisa dibuild di desktop (setidaknya bagian algoritmanya) dan di device target.
Di berbagai compiler dan interpreter, biasanya ada opsi untuk menyalakan warning untuk menangkap kesalahan-kesalahan umum. Ini akan saya nyalakan sampai level maksimum. Untuk gcc saya menggunakan -Wall
, untuk perl saya mengguanakan use strict;
, untuk PHP saya menggunakan error_reporting(E_ALL);
. Tapi tidak semua bahasa punya fitur ini (atau tidak perlu fitur semacam ini), misalnya Python. Sedangkan di Java, secara default warning akan muncul kecuali dimatikan.
Di desktop ada banyak tools yang bisa membantu mencari kesalahan program, misalnya ada PMD untuk java. Biasanya masalah ketika membuat algoritma utama di desktop adalah masalah memori dan kecepatan. Di desktop, semua terlihat cepat, tapi ketika dicoba di device, sangat lambat. Ini bisa jadi karena pemakaian memori yang terlalu banyak (garbage collector di HP tidak sebagus di desktop), atau memang algoritmanya kurang bagus. Untuk aplikasi Java, biasanya saya akan menggunakan fitur profiler bawaan SDK Java (Java -XProf
dan java -Xrunhprof
).
Untuk aplikasi native (C/C++), saya menggunakan valgrind di Linux. Sebenarnya valgrind juga tersedia untuk beberapa OS lain, tapi dari pengalaman, versi Linux lebih reliable. Valgrind bisa dipakai untuk memeriksa aneka kesalahan dalam managemen memori. Valgrind juga punya fitur untuk mendeteksi kemungkinan terjadi deadlock pada program multithreading. Untuk program yang tidak terlalu rumit, kadang saya menggunakan Tiny C compiler, ada fitur pengecekan array yang sangat berguna (untuk mengecek index out of bounds, terutama array yang di stack, untuk array di heap, bisa dideteksi valgrind).
Untuk aplikasi native di Linux, kadang saya menggunakan juga “strace” dan “ltrace“. Fungsi kedua program tersebut adalah mencetak system call (strace) dan library call (ltrace) dan hasilnya. Kedua program tersebut berguna untuk menemukan kesalahan-kesalahan dasar, seperti misalnya jika ternyata path file tidak ditemukan atau gagal dibaca karena masalah permission. Jika sudah terbiasa melihat output strace, maka akan bisa langsung melihat jika ada file yang tidak ditemukan.
Satu fitur menarik dari compiler gcc yang baru adalah stack protector. Tujuan utama fitur ini adalah mencegah agar program tidak bisa diekploitasi dengan buffer overflow. Jika ada buffer overflow, maka program akan berhenti. Fitur ini berguna karena akan membuat program “crash” di titik ketika terjadi buffer overflow, dan bukan di fungsi lain, jadi lebih mudah didebug.
Untuk aplikasi HTML5/JS, saya menggunakan Chrome dan/atau Firebug. Sekarang saya lebih banyak menggunakan Chrome, karena enginenya menggunakan webkit (sama seperti yang dipakai di iPad/iPhone/BlackBerry Playbook/Android), dan Inspector di Chrome sangat bagus. Saya belum banya memprogram hal kompleks dengan JavaScript, dan mungkin seharusnya saya mencoba memakai JSLint untuk mengecek kode Javascript saya.
Kadang walaupun sudah menggunakan tools terbaik, tetap saja ada bug-bug yang muncul. Misalnya: engine pembaca Alkitab sudah dites sempurna di desktop, tapi gagal di device sesungguhnya. Biasanya yang saya lakukan adalah menggunakan print
untuk mencetak kondisi program saat itu. Mendebug dengan menggunakan print memang sangat primitif, tapi kadang-kadang cara seperti ini adalah cara terbaik, dan bahkan kadang-kadang itu satu-satunya cara. Misalnya ketika saya porting kernel Linux/FreeBSD, debugger belum bisa jalan, jadi satu-satunya cara mendebug adalah dengan menggunakan print. Dalam kasus BlackBerry, menjalankan simulator dengan mode debug butuh waktu lebih dari 1 menit, sedangkan tanpa mode debug, hanya butuh beberapa belas detik saja.
Biasanya saya akan membiarkan pesan-pesan debug di dalam program, tapi saya tambahkan #ifdef DEBUG
(atau if (debug)
di bahasa yang tidak memiliki preprocessor). Biasanya pesan debug ini akan terpakai lagi, jadi tidak saya hapus.
Jika masih belum ketemu bugnya, saya akan mencoba menggunakan debugger. Kadang-kadang mensetup lingkungan debugger tidak mudah, misalnya untuk mendebug PHP baris demi baris, perlu setup environment khusus, karena biasanya saya menulis kode PHP di text editor, tidak menggunakan IDE seperti PHPEclipse atau yang sejenis. Atau untuk mendebug aplikasi Flash/ActionScript, butuh flash player versi debug. Jika saya mengembangkan proyeknya untuk jangka panjang, biasanya saya menyempatkan diri mensetup environment untuk debugging.
Di Java (Desktop) ada fitur setDefaultExceptionHandler
untuk mencegat exception apapun yang tidak tertangkap. Fitur ini sangat berguna untuk menemukan error tersembunyi (exception yang mungkin hanya terlihat jika dijalankan dengan console). Sayangnya fitur ini tidak tersedia di BlackBerry, jadi biasanya saya menambahkan try catch extra untuk menangkap error yang tidak terduga. Salah satu hal yang pernah saya temui adalah, ada exception yang dilempar yang tidak terdokumentasi di API (misalnya menurut dokumentasi, hanya akan melempar IOException
, tapi ternyata bisa melempar IllegalArgumentException
).
Jadi secara singkat yang saya lakukan:
- Mencoba membuat algoritma utama di lingkungan yang nyaman, dengan tools yang lengkap.
- Menambah pesan debug (mencetak dengan print)
- Agresif menambahkan try/catch, atau menambahkan banyak if untuk memastikan semua kondisi terpenuhi
- Menggunakan level warning maksimum pada compiler/interpreter untuk menangkap kesalahan yang mungkin ditemui
- Menggunakan tools-tools untuk menemukan kesalahan tanpa banyak berpikir, misalnya strace dan ltrace untuk melihat jalannya program, valgrind untuk mencari memory error, deadlock, dsb
- Menggunakan debugger
Saya berusaha menggunakan debugger semaksimal mungkin, karena biasanya saya hanya memakainya untuk error yang sangat sulit dicari. Beberapa fitur debugger yang sebaiknya diketahui oleh semua orang adalah:
- Fitur untuk step, stepover, stepout/finish (dan juga step n, step over n, karena sering saya lihat orang berkutat lama di debugger mengklik next-next-next).
- Fitur untuk melihat stack trace/call trace.
- Fitur untuk melihat isi memori dan variabel.
- Fitur untuk mengubah isi memori dan variabel.
- Fitur breakpoint dengan kondisi (misalnya break jika i>5).
Biasanya debugger yang saya pakai adalah gdb tanpa interface khusus. Saya membiasakan memakai gdb karena ini bisa dipakai untuk debugging hampir apa saja, di OS apa saja, dan di mana saja (misalnya debugging di server remote).
Ternyata cerita mengenai debugging saja bisa sangat panjang, padahal banyak yang belum saya ceritakan (posting ini hanya bercerita secara umum, tidak sampai detail teknis). Mungkin detail teknis akan saya ceritakan di posting lain, terutama jika ada yang berminat detail debugging tertentu (misalnya spesifik debugging PHP atau Java).
Bisa minta emailnya gan. Biar bisa belajar banyak nih. Pleaseā¦.