Setelah breakpoint dipasang, kita perlu menelusuri kode baris demi baris untuk memahami alur eksekusi dan perubahan state. IntelliJ menyediakan empat operasi step utama: Step Over, Step Into, Step Out, dan Drop Frame. Setiap operasi memiliki semantik berbeda dan dipakai pada konteks berbeda. Pada chapter ini kita akan membedah masing-masing operasi, kapan menggunakannya, dan jebakan umum yang sering dialami pemula. Kode contoh akan menjelaskan skenario step yang dapat kita latih.

Empat Operasi Step #

OperasiShortcutPerilakuKapan dipakai
Step OverF8Eksekusi baris saat ini; tidak masuk ke methodMethod library sudah yakin benar
Step IntoF7Masuk ke method yang dipanggil di baris saat iniMethod curiga, ingin periksa detail
Step OutSHIFT+F8Selesaikan method saat ini, berhenti di pemanggilSudah selesai memeriksa method, ingin keluar
Drop Frametombol di panel FramesKembali ke pemanggil, ulang pemanggilanIngin mengulang dengan input berbeda

Kode Demo Step-Through #

Program berikut memanggil beberapa method bertingkat: urutkan, hitungStatistik, dan ulangiPesan. Kita akan memasang breakpoint di baris val hasil = urutkan(data) dan menelusuri eksekusi.

kotlin
fun main() {
    println("=== Demo Step-Through Debugging ===")

    val data = listOf(5, 3, 8, 1, 9, 2)

    // Step Over: eksekusi baris tanpa masuk ke fungsi `urutkan`
    val hasil = urutkan(data)
    println("Hasil urut: $hasil")

    // Step Into: masuk ke dalam fungsi `hitungStatistik`
    val stats = hitungStatistik(data)
    println("Statistik: min=${stats.first}, max=${stats.second}")

    // Demo Drop Frame: bila ingin mengulang pemanggilan fungsi
    ulangiPesan("Halo", 3)
}

fun urutkan(daftar: List<Int>): List<Int> {
    // Step Into akan masuk ke sorted()
    return daftar.sorted()
}

fun hitungStatistik(daftar: List<Int>): Pair<Int, Int> {
    val min = daftar.minOrNull() ?: 0
    val max = daftar.maxOrNull() ?: 0
    return Pair(min, max)
}

fun ulangiPesan(pesan: String, jumlah: Int) {
    for (i in 1..jumlah) {
        // Drop Frame dari sini akan kembali ke pemanggil ulangiPesan
        cetakPesan(i, pesan)
    }
}

fun cetakPesan(idx: Int, pesan: String) {
    println("  [$idx] $pesan")
}

Output yang Dihasilkan:

text
=== Demo Step-Through Debugging ===
Hasil urut: [1, 2, 3, 5, 8, 9]
Statistik: min=1, max=9
  [1] Halo
  [2] Halo
  [3] Halo

Step Over vs Step Into #

Step Over mengeksekusi baris saat ini secara penuh, termasuk semua method yang dipanggil di baris tersebut, tanpa kita masuki. Operasi ini cocok untuk method library yang sudah teruji seperti listOf, sorted, atau println. Step Into sebaliknya: bila baris memanggil method, debugger masuk ke method tersebut baris pertama. Operasi ini cocok untuk method yang kita curigai sebagai sumber bug.

Step Out #

Step Out melanjutkan eksekusi sampai method saat ini selesai, lalu berhenti di baris pemanggil. Operasi ini berguna bila kita sudah Step Into terlalu dalam dan ingin keluar. Misalnya setelah Step Into ke cetakPesan di dalam loop ulangiPesan, Step Out akan menyelesaikan cetakPesan dan berhenti kembali di baris pemanggil dalam loop.

Drop Frame #

Drop Frame adalah fitur kurang dikenal namun sangat berguna. Operasi ini membatalkan frame teratas dari call stack, sehingga debugger kembali ke method pemanggil seolah-olah pemanggilan method belum terjadi. State yang sudah diubah tidak otomatis di-rollback — variabel var yang sudah dimodifikasi tetap memuat nilai barunya. Namun kita dapat mengulang pemanggilan method dengan parameter yang sama atau berbeda.

Referensi Lanjutan #

Penutup #

Empat operasi step tersebut, dipadukan dengan breakpoint dari chapter sebelumnya, memberi kontrol penuh atas alur eksekusi. Pada chapter berikutnya kita akan beralih dari debugging interaktif ke observasi pasif: mencatat jejak eksekusi melalui logging, yang tetap berguna di lingkungan produksi di mana IDE debugger tidak tersedia.