Breakpoint menghentikan program berdasarkan lokasi (baris atau fungsi). Watchpoint menghentikan program berdasarkan data—berhenti setiap kali nilai sebuah ekspresi berubah. Mekanisme ini sangat berguna ketika sebuah variabel berubah nilai tanpa kita ketahui siapa yang mengubahnya, terutama bila variabel tersebut di-share lintas banyak fungsi atau diubah lewat pointer. Pada chapter ini kita akan mempelajari tiga jenis watchpoint gdb: watch (berhenti saat ekspresi ditulis), rwatch (berhenti saat ekspresi dibaca), dan awatch (berhenti saat dibaca atau ditulis). Format output gdb pada chapter ini mengikuti dokumentasi resmi di sourceware.org/gdb/current/onlinedocs/gdb/Set-Watchpoints.html.
Program Contoh #
Kita akan memakai program yang memanggil fungsi ubah_nilai tiga kali. Setiap pemanggilan mengubah nilai nilai ke step * 10. Tanpa watchpoint, kita harus memasang breakpoint pada setiap baris pemanggilan; dengan watchpoint, gdb otomatis berhenti pada baris manapun nilai diubah.
/* File: /tmp/test_watch1.c */
#include <stdio.h>
static void ubah_nilai(int *x, int step) {
*x = step * 10;
}
int main(void) {
int nilai = 0;
printf("awal: %d\n", nilai);
ubah_nilai(&nilai, 1);
printf("setelah step 1: %d\n", nilai);
ubah_nilai(&nilai, 2);
printf("setelah step 2: %d\n", nilai);
ubah_nilai(&nilai, 3);
printf("setelah step 3: %d\n", nilai);
return 0;
}gcc -g -O0 -std=c11 -Wall -Wextra /tmp/test_watch1.c -o /tmp/test_watch1
/tmp/test_watch1Output yang Dihasilkan:
awal: 0
setelah step 1: 10
setelah step 2: 20
setelah step 3: 30watch: Berhenti Saat Nilai Berubah #
Perintah watch <ekspresi> memasang watchpoint yang berhenti setiap kali nilai ekspresi tersebut berubah. gdb membandingkan nilai sebelum dan sesudah setiap instruksi mesin; bila berbeda, gdb berhenti dan menampilkan nilai lama serta nilai baru.
gdb --batch \
-ex 'break main' \
-ex 'run' \
-ex 'watch nilai' \
-ex 'continue' \
-ex 'continue' \
-ex 'continue' \
/tmp/test_watch1Output yang Dihasilkan (format resmi gdb):
Breakpoint 1 at 0x...: file /tmp/test_watch1.c, line 8.
Breakpoint 1, main () at /tmp/test_watch1.c:8
8 int nilai = 0;
Hardware watchpoint 2: nilai
Hardware watchpoint 2: nilai
Old value = 0
New value = 10
0x0000... in ubah_nilai (x=0x..., step=1) at /tmp/test_watch1.c:4
4 *x = step * 10;
Hardware watchpoint 2: nilai
Old value = 10
New value = 20
0x0000... in ubah_nilai (x=0x..., step=2) at /tmp/test_watch1.c:4
4 *x = step * 10;
Hardware watchpoint 2: nilai
Old value = 20
New value = 30
0x0000... in ubah_nilai (x=0x..., step=3) at /tmp/test_watch1.c:4
4 *x = step * 10;Mari kita bedah output watchpoint di atas:
break mainlalurun: kita memasang breakpoint padamainterlebih dahulu karena watchpoint memerlukan variabelnilaisudah ada di scope. Tanpa breakpoint ini, gdb tidak bisa memasang watchpoint karena program belum memasukimain.watch nilai: gdb membuat watchpoint bernomor2. KataHardware watchpointmenandakan gdb memakai dukungan hardware debugging dari CPU (debug register) sehingga watchpoint tidak memperlambat eksekusi. Tanpa hardware support, gdb akan jatuh kembali ke software watchpoint yang jauh lebih lambat.Old value = 0 / New value = 10: gdb menampilkan nilainilaisebelum dan sesudah perubahan. Watchpoint berhenti pada baris yang menyebabkan perubahan—yaitu*x = step * 10;di dalamubah_nilai. Backtrace pada titik berhenti menunjukkan fungsi mana yang melakukan perubahan.continuedua kali berikutnya: setiap kalinilaiberubah (menjadi 20, lalu 30), watchpoint berhenti kembali dengan pola yang sama.
rwatch dan awatch #
Selain watch yang hanya berhenti saat nilai ditulis, gdb menyediakan dua varian lain:
| Perintah | Berhenti saat |
|---|---|
watch expr | nilai expr ditulis (berubah) |
rwatch expr | nilai expr dibaca |
awatch expr | nilai expr dibaca atau ditulis |
rwatch berguna ketika kita ingin tahu siapa yang membaca sebuah variabel—misalnya memeriksa apakah password yang seharusnya terenkripsi tetap dibaca dalam bentuk plain text oleh kode lain. awatch adalah kombinasi keduanya.
gdb --batch \
-ex 'break main' \
-ex 'run' \
-ex 'awatch nilai' \
-ex 'continue' \
/tmp/test_watch1Output yang Dihasilkan (format resmi gdb):
Breakpoint 1 at 0x...: file /tmp/test_watch1.c, line 8.
Breakpoint 1, main () at /tmp/test_watch1.c:8
8 int nilai = 0;
Hardware access (read/write) watchpoint 2: nilai
Hardware access (read/write) watchpoint 2: nilai
Value = 0
0x0000... in main () at /tmp/test_watch1.c:9
9 printf("awal: %d\n", nilai);Watchpoint berhenti pada baris printf("awal: %d\n", nilai); karena printf membaca nilai nilai (akses baca). Baris Value = 0 menampilkan nilai yang sedang dibaca, tanpa menyebutkan nilai baru karena tidak ada penulisan.
Watchpoint pada Ekspresi #
Watchpoint tidak terbatas pada nama variabel sederhana. Kita bisa memasang watchpoint pada ekspresi seperti arr[i], node->next->data, atau total % 100. gdb mengevaluasi ekspresi pada setiap langkah mesin dan berhenti bila hasilnya berubah.
gdb --batch \
-ex 'break main' \
-ex 'run' \
-ex 'watch nilai > 15' \
-ex 'continue' \
-ex 'print nilai' \
/tmp/test_watch1Output yang Dihasilkan (format resmi gdb):
Breakpoint 1 at 0x...: file /tmp/test_watch1.c, line 8.
Breakpoint 1, main () at /tmp/test_watch1.c:8
8 int nilai = 0;
Hardware watchpoint 2: nilai > 15
Hardware watchpoint 2: nilai > 15
Old value = false
New value = true
0x0000... in ubah_nilai (x=0x..., step=2) at /tmp/test_watch1.c:4
4 *x = step * 10;
$1 = 20Mari kita uraikan elemen kunci pada output watchpoint ekspresi di atas:
watch nilai > 15: watchpoint dipasang pada ekspresi booleannilai > 15. Awalnyanilai = 0, sehingga ekspresi bernilaifalse.Old value = false / New value = true: setelahubah_nilai(&nilai, 2),nilaimenjadi20. Ekspresinilai > 15sekarang bernilaitrue—transisi yang memicu watchpoint.0x0000... in ubah_nilai ... at /tmp/test_watch1.c:4: baris yang menyebabkan transisi nilai adalah*x = step * 10;di dalamubah_nilai. Kita bisa langsung tahu fungsi mana yang membuatnilaimelewati ambang batas 15.$1 = 20: hasilprint nilaipada prompt berikutnya menunjukkan nilainilaisaat ini adalah20. Watchpoint tidak berhenti padaubah_nilai(&nilai, 1)(yang menghasilkannilai = 10) karena10 > 15masihfalse.
Watchpoint Software vs Hardware #
gdb mendukung dua mode watchpoint:
- Hardware watchpoint: memakai debug register CPU (x86 memiliki 4 register DR0-DR3). Sangat cepat (tidak memperlambat eksekusi) tetapi jumlahnya terbatas.
- Software watchpoint: gdb mengevaluasi ulang ekspresi setelah setiap instruksi mesin. Bisa menangani ekspresi kompleks dan tidak terbatas jumlahnya, tetapi memperlambat eksekusi hingga 1000x lebih lambat.
gdb otomatis memilih hardware watchpoint bila memungkinkan. Pesan Hardware watchpoint N: expr menandakan mode hardware aktif. Bila gdb menjatuhkan ke software, pesannya menjadi Watchpoint N: expr tanpa awalan Hardware.
Manajemen Watchpoint #
Watchpoint dikelola dengan perintah yang sama dengan breakpoint—keduanya disimpan di daftar bernomor yang sama dan bisa dimanipulasi dengan info breakpoints, delete N, disable N, dan enable N.
gdb --batch \
-ex 'break main' \
-ex 'run' \
-ex 'watch nilai' \
-ex 'info breakpoints' \
/tmp/test_watch1Output yang Dihasilkan (format resmi gdb):
Breakpoint 1 at 0x...: file /tmp/test_watch1.c, line 8.
Breakpoint 1, main () at /tmp/test_watch1.c:8
8 int nilai = 0;
Hardware watchpoint 2: nilai
Num Type Disp Enb Address What
1 breakpoint keep y 0x... in main at /tmp/test_watch1.c:8
breakpoint already hit 1 time
2 hw watchpoint keep y nilaiKolom Type membedakan breakpoint (breakpoint) dari watchpoint (hw watchpoint). Kolom What menampilkan ekspresi yang dipantau. Watchpoint bisa dihapus dengan delete 2 atau dinonaktifkan dengan disable 2.