監看點(watchpoint)
中斷點說的是「執行到這個地方就停」。但有時候你並不在乎程式碼走到哪裡——你在乎的是一個值。假設一個本該一直很小的計數器不知怎地變成了一個巨大的數,而你完全不曉得是上百行裡的哪一行幹的。監看點正是這時的工具:它不是盯著程式碼裡的某個位置,而是盯著記憶體裡的某筆資料,告訴除錯器「這個變數的值一改變就立刻停下來」,不管那個改變發生在程式的哪裡。
具體來說,你把監看點對準一個變數或一個記憶體位址——在 gdb 裡是「watch count」。從那一刻起,每當程式把一個新值寫進那個位置,除錯器就會暫停,並給你看舊值、新值,以及究竟是哪一行做了這個改動。你也可以監看讀取(讀取監看點)或任何存取。現代 CPU 幫忙把這件事做得很便宜:它們有幾個特殊的除錯暫存器,硬體會在每一次記憶體存取時檢查,所以一個硬體監看點幾乎不會拖慢程式。如果你要求監看的位置比硬體支援的還多,除錯器可能退回到軟體監看點:它把整個程式逐指令單步執行、每一步重新檢查那個值——正確,但慢得多。
為何重要:監看點是對付「是誰在破壞這個?」這類臭蟲的合適武器——一個莫名其妙被覆寫的欄位、一個無端變成 NULL 的指標、一個被某個不相干陣列的緩衝區溢位波及而被蓋掉的值。你讓程式跑,除錯器會在造成損害的那一次寫入精確地把你停下來,於是你可以讀呼叫堆疊追蹤、看出元兇。要注意的是範圍與生存期:一旦那個函式返回、區域變數的堆疊位置消失,設在區域變數上的監看點就失去意義,除錯器會把它移除;監看堆積上的位址則只要那塊記憶體還在就一直有效。
在 gdb 裡,在某個函式中停下來之後: watch total 接著 continue。程式會一直跑,直到有任何一行寫入 total,gdb 才停下來、印出「Old value = 3, New value = 1000000」連同做這件事的那一行——立刻指向那次出問題的寫入。
監看一個變數;除錯器會在改動它的那次確切寫入處停下來。
硬體監看點的數量有限(常常只有四個)且只能監看小範圍;要求更多,除錯器可能默默切換成把一切逐指令單步執行的軟體監看點,把執行拖慢好幾個數量級。