Watchpoint (pozorovací bod) — mechanismus ladění, který pozastaví provádění programu při změně hodnoty zadané proměnné nebo oblasti paměti. Na rozdíl od breakpointu, který je vázán na řádek kódu, watchpoint sleduje změnu dat bez ohledu na to, ve které části programu k úpravě došlo. Podle Apple Developer Documentation jsou watchpointy nepostradatelné při ladění datových závitů, neočekávaných změn vlastností a sledování životního cyklu objektů.
Hlavní body
Watchpoint — mechanismus debuggeru, který pozastaví provádění programu, když se změní hodnota na zadané adrese paměti. Zatímco breakpoint reaguje na dosažení konkrétního řádku kódu, watchpoint reaguje na zápis do konkrétní paměťové buňky — bez ohledu na to, která část kódu zápis provedla. To jej činí nepostradatelným při hledání neočekávaných modifikací proměnných, zejména ve vícevláknových aplikacích.
Princip fungování je založen na hardwarové podpoře procesoru. Moderní čipy ARM (Apple Silicon, Qualcomm Snapdragon) poskytují 4 až 8 hardwarových registrů watchpoint. Když procesor provede instrukci zápisu na adresu shodující se s watchpointem, vznikne hardwarové přerušení, které debugger zachytí. Pokud hardwarové registry dojdou, LLDB přepne do softwarového režimu — kontroluje každou instrukci zápisu krok za krokem, což zpomalí provádění desetinásobně.
Podle ARM Architecture Reference Manual pracují hardwarové watchpointy na úrovni modulu Data Watchpoint and Trace (DWT) a nevyžadují úpravu spustitelného kódu. Doba reakce na aktivaci je v řádu nanosekund, což umožňuje sledovat i vysokofrekvenční změny proměnných ve smyčkách zpracování dat, jako jsou audio buffery nebo video snímky.
Watchpoint je nepostradatelný, když víte, která proměnná se mění, ale nevíte odkud. Typické scénáře: hodnota vlastnosti frame v UIView se mění bez zjevného důvodu; čítač v backendovém vlákně se resetuje nepředvídatelně; příznak isLoading se přepne dříve, než dokončí síťový požadavek. V každém z těchto případů je nepraktické umisťovat breakpointy na všechna místa, kde může dojít k zápisu. Watchpoint řeší problém jedním příkazem.
Xcode a LLDB podporují tři typy watchpointů: watchpoint set variable — pro pozorování lokální proměnné; watchpoint set expression — pro pozorování výrazu vracejícího adresu; watchpoint set — pro pozorování surové adresy paměti. Každý typ má svou oblast použití.
| Typ watchpointu | Příkaz LLDB | Použití |
|---|---|---|
| Proměnná | watchpoint set variable -w write self.count | Lokální a globální proměnné, vlastnosti struktur |
| Výraz | watchpoint set expression -w write -- &self->mutex.lock | Pole struktur přes ukazatel, prvky polí podle indexu |
| Adresa | watchpoint set -w write 0x600000c4b80 | Konkrétní adresa paměti z předchozího výstupu LLDB |
V Xcode lze watchpoint nastavit přes Debug Area: zastavte se na breakpointu, najděte požadovanou proměnnou v panelu Variables View, klikněte pravým tlačítkem a vyberte Watch Variable. Xcode automaticky provede příkaz watchpoint set variable se správným názvem proměnné a kontextem. Poté se debugger zastaví při každé změně hodnoty — vhodné pro rychlé ladění bez přepínání do konzole. Tato metoda však funguje pouze dokud je proměnná v rozsahu viditelnosti aktuálního rámce.
func processItems(_ items: [String]) {
var index = 0
// Nastavte watchpoint na index přes GUI:
// zastavte se zde, klikněte pravým tlačítkem → Watch Variable
for item in items {
index += 1
print("Item \(index): \(item)")
}
}
LLDB poskytuje kompletní sadu příkazů pro správu watchpointů z konzole. To poskytuje větší kontrolu než GUI: můžete určit velikost oblasti pozorování, nastavit podmínku aktivace, vytvářet watchpointy na adresy získané z vypočítaných výrazů a automaticky provádět akce při aktivaci. Rozhraní příkazového řádku je užitečné zejména při ladění složitých scénářů, kde je třeba rychle měnit parametry pozorování.
Příkaz watchpoint set variable přijímá název proměnné s ohledem na rozsah viditelnosti: pro self vlastnosti v Objective-C použijte self->_property, pro Swift — self.property. Parametr -w write nastavuje sledování zápisu, -w read — čtení (režim dostupný pouze na některých architekturách), -s size — velikost oblasti v bajtech. Po nastavení watchpointu lze seznam zobrazit příkazem watchpoint list.
(lldb) watchpoint set variable -w write -s 8 self.balance
Watchpoint 1: addr = 0x600000c4b80 size = 8 state = enabled type = w
watchpoint spec: 'self.balance'
(lldb) watchpoint set expression -w write -- self->items._storage
Watchpoint 2: addr = 0x600003a4c00 size = 8
(lldb) watchpoint list
1: location = 0x600000c4b80, type = write, variable = '_balance'
2: location = 0x600003a4c00, type = write, expression = 'self->items._storage'
Stejně jako breakpointy, i watchpointy podporují podmíněnou aktivaci. Parametr -c nastavuje podmínku ve Swift nebo Objective-C. Například watchpoint s podmínkou newValue > 1000 se zastaví pouze když zapsaná hodnota překročí tisíc. To je kritické při ladění smyček nebo senzorů generujících tisíce změn za sekundu — jinak by se debugger zastavoval při každé změně, což by znemožnilo práci.
(lldb) watchpoint set variable -w write self.temperature -c "(int)$newValue > 100"
Watchpoint 3: addr = 0x600000e4a20, condition = '(int)$newValue > 100'
(lldb) watchpoint modify 3 -C "po self.temperature" -G true
// Automaticky logovat hodnotu a pokračovat v provádění
Zvláštnost watchpointů pro vlastnosti objektů Objective-C a Swift — watchpoint se nastavuje nikoli na název vlastnosti, ale na adresu pole ivar (instance variable) v paměti objektu. To znamená, že při každém novém přidělení objektu (například při znovuvytvoření ViewControlleru) se watchpoint stává neplatným, protože adresa paměti se změnila. Pro trvalé pozorování vlastnosti napříč restarty je třeba watchpoint znovu nastavit v okamžiku inicializace objektu.
Pozorování prvků polí a slovníků vyžaduje výpočet adresy konkrétního prvku. Například pro pozorování třetího prvku pole použijte watchpoint set expression -- &array[2]. Pokud pole přerozdělí svůj interní buffer (při přidávání prvků nad kapacitu), watchpoint se stane neplatným — LLDB ohlásí chybu Watchpoint 1 has an invalid address. V takových případech je třeba watchpoint znovu nastavit po změně velikosti kolekce.
(lldb) expr var $arr = [10, 20, 30, 40, 50]
(lldb) watchpoint set expression -w write -- &$arr[2]
Watchpoint 4: addr = 0x1000a4b20, size = 8
(lldb) expr $arr[2] = 99
Watchpoint 4 hit: old value: 30, new value: 99
V Objective-C lze sledovat retain count objektu nastavením watchpointu na pole retainCount ve struktuře objc_object. LLDB to provádí přes watchpoint set expression -w write -- (int*)[object retainCount]. U Swift objektů s ARC (Automatic Reference Counting) však retain count není přímo dostupný — místo toho použijte Instruments nebo Memory Graph Debugger pro analýzu úniků paměti. Podle Apple fungují watchpointy na retain count pouze v Debug sestaveních s vypnutou optimalizací ARC.
Watchpointy mají řadu omezení, která je třeba zohlednit. Hlavní — počet hardwarových watchpointů je omezen na 4–8 registrů na architektuře ARM a až 4 na x86. Když jsou všechny hardwarové registry obsazeny, LLDB přepne do režimu softwarového watchpointu: upraví každou instrukci zápisu ve sledovaném rozsahu pro vygenerování výjimky. To zpomalí provádění 10–50krát, proto se v praxi doporučuje používat nejvýše 2–3 aktivní watchpointy současně.
Druhé omezení — neplatnost watchpointů při přerozdělení paměti. Při činnosti garbage collectoru nebo ARC, když je objekt přesouván v paměti (v jazycích s kompaktizací haldy), adresa watchpointu se stává nesprávnou. Ve Swift a Objective-C ARC objekty nepřesouvá, ale realokace polí a řetězců vede ke stejnému efektu. LLDB na to upozorňuje zprávou Watchpoint N address (0x...) doesn't contain a valid allocation.
Třetí omezení — viditelnost proměnných. Watchpoint na lokální proměnnou funguje pouze dokud je tato proměnná v rozsahu viditelnosti aktuálního rámce zásobníku. Jakmile funkce skončí, watchpoint je automaticky odstraněn. Pro pozorování globálních proměnných nebo polí dlouho žijících objektů zůstává watchpoint zachován až do explicitního odstranění přes watchpoint delete nebo do ukončení procesu.
(lldb) watchpoint delete 1
1 watchpoints deleted.
(lldb) watchpoint delete # Smazat všechny watchpointy
2 watchpoints deleted.
(lldb) watchpoint disable 1 # Dočasně zakázat
(lldb) watchpoint enable 1 # Znovu povolit
Podle ARM podporují watchpointy na Apple Silicon (M1–M4) velikost sledované oblasti od 1 do 8 bajtů. Pro pozorování struktur větších než 8 bajtů je třeba nastavit několik watchpointů na každé pole. To je důležité při ladění složitých datových struktur, jako jsou CGRect (16 bajtů) nebo UIEdgeInsets (16 bajtů).
Často kladené otázky
Breakpoint je vázán na řádek kódu — zastaví se při jeho dosažení. Watchpoint je vázán na adresu paměti — zastaví se při zápisu na tuto adresu z libovolného místa programu. Watchpoint hledá „kdo mění hodnotu“, breakpoint — „co se děje na tomto řádku“.
4–8 hardwarových na ARM (včetně Apple Silicon). Překročení přepne watchpoint do softwarového režimu, což zpomalí provádění 10–50krát. Doporučuje se nejvýše 2–3 aktivní watchpointy současně.
Watchpointy se neukládají mezi relacemi ladění. Při každém novém spuštění se adresy paměti mění a watchpoint je třeba nastavit znovu. Výjimka — watchpointy na globální proměnné s pevnou adresou.
Ano, ale watchpoint se nastavuje na ivar (podpůrné úložiště) vlastnosti, nikoli na vlastnost samotnou. Ve Swift použijte watchpoint set variable self.property — LLDB automaticky najde odpovídající ivar podle názvu vlastnosti.
Hardwarový watchpoint neovlivňuje výkon — přerušení probíhá na úrovni procesoru. Softwarový (při vyčerpání hardwarových registrů) zpomalí provádění 10–50krát, protože LLDB kontroluje každou instrukci zápisu.
Shrnutí
Vyvineme mobilní aplikaci na klíč
IT Sectr vytváří aplikace pro iOS a Android pro startupy a podniky od roku 2017. Poradíme vám a navrhneme nejlepší řešení.
Přečtěte si také