Watchpoint (точка спостереження) — механізм налагодження, який призупиняє виконання програми при зміні значення вказаної змінної або області пам'яті. На відміну від breakpoint, який прив'язаний до рядка коду, watchpoint відстежує зміну даних незалежно від того, в якій ділянці програми відбулася модифікація. За даними Apple Developer Documentation, watchpoints незамінні при налагодженні гонок даних, несподіваних змін властивостей і відстеженні життєвого циклу об'єктів.
Головне
Watchpoint — це механізм налагоджувача, який призупиняє виконання програми, коли значення за вказаною адресою пам'яті змінюється. Якщо breakpoint реагує на досягнення певного рядка коду, то watchpoint реагує на запис у певну комірку пам'яті — незалежно від того, яка саме ділянка коду цей запис виконала. Це робить його незамінним при пошуку неочікуваних модифікацій змінних, особливо в багатопотокових додатках.
Принцип роботи базується на апаратній підтримці процесора. Сучасні ARM-чіпи (Apple Silicon, Qualcomm Snapdragon) надають від 4 до 8 апаратних watchpoint-регістрів. Коли процесор виконує інструкцію запису за адресою, що збігається з watchpoint, виникає апаратне переривання, яке перехоплює налагоджувач. Якщо апаратні регістри закінчилися, LLDB перемикається в програмний режим — він покроково перевіряє кожну інструкцію запису, що уповільнює виконання в десятки разів.
За даними ARM Architecture Reference Manual, апаратні watchpoints працюють на рівні модуля Data Watchpoint and Trace (DWT) і не потребують модифікації виконуваного коду. Час реакції на спрацьовування становить одиниці наносекунд, що дозволяє відстежувати навіть високочастотні зміни змінних у циклах обробки даних, таких як буфери аудіо або відеокадрів.
Watchpoint незамінний, коли ви знаєте яка змінна змінюється, але не знаєте звідки. Типові сценарії: значення властивості frame у UIView змінюється без видимої причини; лічильник у фоновому потоці скидається непередбачувано; прапорець isLoading перемикається раніше, ніж завершився мережевий запит. У кожному з цих випадків встановлення breakpoint на всі місця, де може відбуватися запис, — непрактично. Watchpoint вирішує проблему однією командою.
Xcode та LLDB підтримують три типи watchpoints: watchpoint set variable — для спостереження за локальною змінною; watchpoint set expression — для спостереження за виразом, що повертає адресу; watchpoint set — для спостереження за сирою адресою пам'яті. Кожен тип має свою область застосування.
| Тип watchpoint | Команда LLDB | Застосування |
|---|---|---|
| Змінна | watchpoint set variable -w write self.count | Локальні та глобальні змінні, властивості структур |
| Вираз | watchpoint set expression -w write -- &self->mutex.lock | Поля структур за вказівником, елементи масивів за індексом |
| Адреса | watchpoint set -w write 0x600000c4b80 | Конкретна адреса пам'яті з попереднього виведення LLDB |
У Xcode watchpoint можна встановити через Debug Area: зупиніться на breakpoint, знайдіть потрібну змінну в панелі Variables View, клацніть правою кнопкою та виберіть Watch Variable. Xcode автоматично виконає команду watchpoint set variable з правильним іменем змінної та контекстом. Після цього налагоджувач зупинятиметься при кожній зміні значення — зручно для швидкого налагодження без перемикання в консоль. Однак цей метод працює тільки поки змінна знаходиться в області видимості поточного фрейму.
func processItems(_ items: [String]) {
var index = 0
// Встановіть watchpoint на index через GUI:
// зупиніться тут, клацніть правою кнопкою → Watch Variable
for item in items {
index += 1
print("Елемент \(index): \(item)")
}
}
LLDB надає повний набір команд для керування watchpoints з консолі. Це дає більший контроль, ніж GUI: можна задавати розмір області спостереження, встановлювати умову спрацьовування, створювати watchpoints на адреси, отримані з обчислених виразів, і автоматично виконувати дії при спрацьовуванні. Командний інтерфейс особливо корисний при налагодженні складних сценаріїв, де потрібно швидко змінювати параметри спостереження.
Команда watchpoint set variable приймає ім'я змінної з урахуванням області видимості: для self-властивостей Objective-C використовуйте self->_property, для Swift — self.property. Параметр -w write задає відстеження запису, -w read — читання (режим, доступний тільки на деяких архітектурах), -s size — розмір області в байтах. Після встановлення watchpoint можна переглянути список командою 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'
Як і breakpoints, watchpoints підтримують умовне спрацьовування. Параметр -c задає умову на Swift або Objective-C. Наприклад, watchpoint з умовою newValue > 1000 зупиниться тільки коли записуване значення перевищує тисячу. Це критично важливо при налагодженні циклів або датчиків, що генерують тисячі змін за секунду — інакше налагоджувач зупинятиметься на кожній зміні, роблячи роботу неможливою.
(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
// Автоматично логувати значення та продовжувати виконання
Особливість watchpoints для Objective-C та Swift властивостей об'єктів — watchpoint встановлюється не на ім'я властивості, а на адресу поля ivar (змінної екземпляра) в пам'яті об'єкта. Це означає, що при кожному новому виділенні об'єкта (наприклад, при перестворенні ViewController) watchpoint стає недійсним, оскільки адреса пам'яті змінилася. Для постійного спостереження за властивістю через перезапуски потрібно перевстановлювати watchpoint у момент ініціалізації об'єкта.
Спостереження за елементами масивів та словників потребує обчислення адреси конкретного елемента. Наприклад, для спостереження за третім елементом масиву використовуйте watchpoint set expression -- &array[2]. Якщо масив перерозподілить внутрішній буфер (при додаванні елементів понад capacity), watchpoint стане недійсним — LLDB повідомить про помилку Watchpoint 1 has an invalid address. У таких випадках потрібно перевстановлювати watchpoint після зміни розміру колекції.
(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
В Objective-C можна відстежувати retain count об'єкта, встановивши watchpoint на поле retainCount у структурі objc_object. LLDB виконує це через watchpoint set expression -w write -- (int*)[object retainCount]. Однак для Swift-об'єктів з ARC (Automatic Reference Counting) retain count безпосередньо не доступний — натомість використовуйте Instruments або Memory Graph Debugger для аналізу витоків. За даними Apple, watchpoints на retain count працюють тільки в Debug-збірках з вимкненою оптимізацією ARC.
Watchpoints мають ряд обмежень, які важливо враховувати. Головне — кількість апаратних watchpoints обмежена 4–8 регістрами на архітектурі ARM і до 4 на x86. Коли всі апаратні регістри зайняті, LLDB перемикається в режим програмного watchpoint: модифікує кожну інструкцію запису в відстежуваному діапазоні для генерації виключення. Це уповільнює виконання в 10–50 разів, тому на практиці рекомендується використовувати не більше 2–3 активних watchpoints одночасно.
Друге обмеження — недійсність watchpoints при перерозподілі пам'яті. При спрацьовуванні збирача сміття або ARC, коли об'єкт переміщується в пам'яті (у мовах з компактизацією купи), адреса watchpoint стає некоректною. У Swift та Objective-C ARC не переміщує об'єкти, але перерозподіл масивів і рядків призводить до того ж ефекту. LLDB попереджає про це повідомленням Watchpoint N address (0x...) doesn’t contain a valid allocation.
Третє обмеження — видимість змінних. Watchpoint на локальну змінну працює тільки поки ця змінна знаходиться в області видимості поточного фрейму стеку. Як тільки функція завершується, watchpoint автоматично видаляється. Для спостереження за глобальними змінними або полями довгоживучих об'єктів watchpoint зберігається до явного видалення через watchpoint delete або до завершення процесу.
(lldb) watchpoint delete 1
1 watchpoints deleted.
(lldb) watchpoint delete # Удалить все watchpoints
2 watchpoints deleted.
(lldb) watchpoint disable 1 # Временно отключить
(lldb) watchpoint enable 1 # Включить снова
За даними ARM, watchpoints на Apple Silicon (M1–M4) підтримують розмір відстежуваної області від 1 до 8 байт. Для спостереження за структурами розміром більше 8 байт потрібно встановлювати кілька watchpoints на кожне поле. Це важливо при налагодженні складних структур даних, таких як CGRect (16 байт) або UIEdgeInsets (16 байт).
Часті запитання
Breakpoint прив'язаний до рядка коду — зупиняє при його досягненні. Watchpoint прив'язаний до адреси пам'яті — зупиняє при записі в цю адресу з будь-якого місця програми. Watchpoint шукає «хто змінює значення», breakpoint — «що відбувається на цьому рядку».
4–8 апаратних на ARM (включаючи Apple Silicon). Перевищення переводить watchpoint у програмний режим, що уповільнює виконання в 10–50 разів. Рекомендується не більше 2–3 активних watchpoints одночасно.
Watchpoints не зберігаються між сесіями налагодження. При кожному новому запуску адреси пам'яті змінюються, і watchpoint потрібно встановлювати заново. Виняток — watchpoints на глобальні змінні з фіксованою адресою.
Так, але watchpoint встановлюється на ivar (резервне сховище) властивості, а не на саму властивість. У Swift використовуйте watchpoint set variable self.property — LLDB автоматично знайде відповідний ivar за ім'ям властивості.
Апаратний watchpoint не впливає на продуктивність — переривання відбувається на рівні процесора. Програмний (при вичерпанні апаратних регістрів) уповільнює виконання в 10–50 разів, оскільки LLDB перевіряє кожну інструкцію запису.
Підсумки
Ми розробимо мобільний застосунок під ключ
IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.
Читайте також