Watchpoint (müşahidə nöqtəsi) — göstərilən dəyişənin və ya yaddaş sahəsinin dəyəri dəyişdikdə proqramın icrasını dayandıran debug mexanizmi. Breakpoint kod sətrinə bağlı olduğu halda, watchpoint dəyişikliyin proqramın hansı hissəsində baş verməsindən asılı olmayaraq məlumat dəyişikliyini izləyir. Apple Developer Documentation-ya görə, watchpoint-lər data race-lərin, gözlənilməz xüsusiyyət dəyişikliklərinin aradan qaldırılmasında və obyektlərin həyat dövrünün izlənməsində əvəzolunmazdır.
Əsas məqamlar
Watchpoint — göstərilən yaddaş ünvanındakı dəyər dəyişdikdə proqramın icrasını dayandıran debugger mexanizmidir. Breakpoint müəyyən kod sətrinə çatdıqda reaksiya verirsə, watchpoint müəyyən yaddaş hüceyrəsinə yazılışa reaksiya verir — bu yazılışı kodun hansı hissəsinin etməsindən asılı olmayaraq. Bu, onu çoxthreadli tətbiqlərdə gözlənilməz dəyişən modifikasiyalarının axtarışında əvəzolunmaz edir.
İş prinsipi prosessorun aparat dəstəyinə əsaslanır. Müasir ARM çipləri (Apple Silicon, Qualcomm Snapdragon) 4-dən 8-ə qədər aparat watchpoint registri təmin edir. Prosessor watchpoint ilə üst-üstə düşən ünvanda yazma təlimatını icra etdikdə aparat kəsməsi yaranır və onu debugger ələ keçirir. Aparat registrləri bitərsə, LLDB proqram rejiminə keçir — hər yazma təlimatını addım-addım yoxlayır, bu da icranı dəfələrlə ləngidir.
ARM Architecture Reference Manual-a görə, aparat watchpoint-ləri Data Watchpoint and Trace (DWT) modulu səviyyəsində işləyir və icra olunan kodun dəyişdirilməsini tələb etmir. İşə düşmə reaksiya müddəti nanosaniyələr təşkil edir ki, bu da audio buferlər və ya video kadrlar kimi məlumat emal dövrlərində yüksək tezlikli dəyişən dəyişikliklərini izləməyə imkan verir.
Watchpoint hansı dəyişənin dəyişdiyini bildiyiniz, lakin haradan dəyişdiyini bilmədiyiniz hallarda əvəzolunmazdır. Tipik ssenarilər: UIView-də frame xüsusiyyətinin dəyəri görünən səbəb olmadan dəyişir; fon thread-də sayğac gözlənilmədən sıfırlanır; isLoading bayrağı şəbəkə sorğusu bitməmişdən əvvəl dəyişir. Bu halların hər birində yazmanın baş verə biləcəyi bütün yerlərə breakpoint qoymaq qeyri-praktikdir. Watchpoint problemi bir əmrlə həll edir.
Xcode və LLDB üç növ watchpoint dəstəkləyir: watchpoint set variable — lokal dəyişəni müşahidə etmək üçün; watchpoint set expression — ünvan qaytaran ifadəni müşahidə etmək üçün; watchpoint set — xam yaddaş ünvanını müşahidə etmək üçün. Hər növün öz tətbiq sahəsi var.
| Watchpoint növü | LLDB əmri | Tətbiq sahəsi |
|---|---|---|
| Dəyişən | watchpoint set variable -w write self.count | Lokal və qlobal dəyişənlər, struktur xüsusiyyətləri |
| İfadə | watchpoint set expression -w write -- &self->mutex.lock | Göstərici ilə struktur sahələri, indeks üzrə massiv elementləri |
| Ünvan | watchpoint set -w write 0x600000c4b80 | LLDB-nin əvvəlki çıxışından konkret yaddaş ünvanı |
Xcode-da watchpoint Debug Area vasitəsilə qurula bilər: breakpoint-da dayanın, Variables View panelində lazım dəyişəni tapın, sağ klikləyin və Watch Variable seçin. Xcode avtomatik olaraq düzgün dəyişən adı və kontekstlə watchpoint set variable əmrini icra edəcək. Bundan sonra debugger hər dəyər dəyişikliyində dayanacaq — konsola keçmədən sürətli debug üçün əlverişlidir. Lakin bu metod yalnız dəyişən cari freymin görünmə sahəsində olduqda işləyir.
func processItems(_ items: [String]) {
var index = 0
// Index-ə watchpoint-i GUI vasitəsilə qurun:
// burada dayanın, sağ klik → Watch Variable
for item in items {
index += 1
print("Item \(index): \(item)")
}
}
LLDB konsoldan watchpoint-ləri idarə etmək üçün tam əmrlər dəsti təqdim edir. Bu, GUI-dən daha çox nəzarət verir: müşahidə sahəsinin ölçüsünü təyin etmək, işə düşmə şərti qurmaq, hesablanmış ifadələrdən alınan ünvanlarda watchpoint yaratmaq və işə düşdükdə avtomatik hərəkətlər icra etmək olar. Əmr sətri interfeysi müşahidə parametrlərini tez dəyişmək tələb olunan mürəkkəb ssenarilərin debugunda xüsusilə faydalıdır.
watchpoint set variable əmri görünmə sahəsi nəzərə alınmaqla dəyişən adı qəbul edir: Objective-C self xüsusiyyətləri üçün self->_property, Swift üçün self.property istifadə edin. -w write parametri yazmanın izlənməsini, -w read — oxumanı (yalnız bəzi arxitekturalarda mövcud olan rejim), -s size — sahənin baytla ölçüsünü təyin edir. Watchpoint qurulduqdan sonra siyahını watchpoint list əmri ilə görmək olar.
(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'
Breakpoint-lər kimi, watchpoint-lər də şərti işə düşməni dəstəkləyir. -c parametri Swift və ya Objective-C-də şərt təyin edir. Məsələn, newValue > 1000 şərti olan watchpoint yalnız yazılan dəyər mini keçdikdə dayanacaq. Bu, saniyədə minlərlə dəyişiklik yaradan dövrlərin və ya sensorların debugunda kritik əhəmiyyət daşıyır — əks halda debugger hər dəyişiklikdə dayanaraq işi qeyri-mümkün edərdi.
(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
// Avtomatik olaraq dəyəri logla və icranı davam etdir
Objective-C və Swift obyekt xüsusiyyətləri üçün watchpoint-lərin özəlliyi — watchpoint xüsusiyyət adına deyil, obyektin yaddaşındakı ivar (instance variable) sahəsinin ünvanına qoyulur. Bu o deməkdir ki, hər yeni obyekt ayrıldıqda (məsələn, ViewController yenidən yaradıldıqda) watchpoint etibarsız olur, çünki yaddaş ünvanı dəyişib. Xassəni restartlar boyu daimi izləmək üçün watchpoint obyektin inisializasiyası anında yenidən qurulmalıdır.
Massiv və lüğət elementlərinin müşahidəsi konkret elementin ünvanının hesablanmasını tələb edir. Məsələn, massivin üçüncü elementini izləmək üçün watchpoint set expression -- &array[2] istifadə edin. Massiv daxili buferi yenidən yerləşdirərsə (capacity-dən artıq element əlavə edildikdə), watchpoint etibarsız olacaq — LLDB Watchpoint 1 has an invalid address xətası bildirəcək. Belə hallarda kolleksiyanın ölçüsü dəyişdikdən sonra watchpoint yenidən qurulmalıdır.
(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-də objc_object strukturundakı retainCount sahəsinə watchpoint qoyaraq obyektin retain count-unu izləmək olar. LLDB bunu watchpoint set expression -w write -- (int*)[object retainCount] vasitəsilə icra edir. Lakin ARC (Automatic Reference Counting) ilə Swift obyektləri üçün retain count birbaşa əlçatan deyil — bunun əvəzinə sızmaların analizi üçün Instruments və ya Memory Graph Debugger istifadə edin. Apple-a görə, retain count üzrə watchpoint-lər yalnız ARC optimallaşdırması söndürülmüş Debug qurmalarında işləyir.
Watchpoint-lərin nəzərə alınmalı bir sıra məhdudiyyətləri var. Əsas odur ki, aparat watchpoint-lərinin sayı ARM arxitekturasında 4–8 registr və x86-da 4 registrlə məhdudlaşır. Bütün aparat registrləri məşğul olduqda, LLDB proqram watchpoint rejiminə keçir: izlənilən diapazonda hər yazma təlimatını istisna yaratmaq üçün dəyişdirir. Bu, icranı 10–50 dəfə ləngidir, buna görə də praktikada eyni anda 2–3 aktiv watchpoint-dən çox istifadə etmək tövsiyə edilmir.
İkinci məhdudiyyət — yaddaşın yenidən yerləşdirilməsi zamanı watchpoint-lərin etibarsızlaşması. Zibil yığan və ya ARC işləyərkən, obyekt yaddaşda köçürüldükdə (yığının kompaktlaşdırılması olan dillərdə), watchpoint ünvanı düzgün olmur. Swift və Objective-C-də ARC obyektləri köçürmür, lakin massiv və sətirlərin realokasiyası eyni effektə gətirib çıxarır. LLDB bu barədə Watchpoint N address (0x...) doesn't contain a valid allocation mesajı ilə xəbərdarlıq edir.
Üçüncü məhdudiyyət — dəyişənlərin görünməsi. Lokal dəyişənə qoyulmuş watchpoint yalnız həmin dəyişən cari stack freyminin görünmə sahəsində olduqda işləyir. Funksiya başa çatan kimi watchpoint avtomatik silinir. Qlobal dəyişənlərin və ya uzunömürlü obyektlərin sahələrinin müşahidəsi üçün watchpoint watchpoint delete ilə açıq şəkildə silinənə və ya proses bitənədək saxlanılır.
(lldb) watchpoint delete 1
1 watchpoints deleted.
(lldb) watchpoint delete # Bütün watchpoints-ləri sil
2 watchpoints deleted.
(lldb) watchpoint disable 1 # Müvəqqəti söndür
(lldb) watchpoint enable 1 # Yenidən yandır
ARM-a görə, Apple Silicon (M1–M4) üzərində watchpoint-lər 1-dən 8 bayta qədər izlənilən sahə ölçüsünü dəstəkləyir. 8 baytdan böyük strukturları izləmək üçün hər sahəyə bir neçə watchpoint qoyulması tələb olunur. Bu, CGRect (16 bayt) və ya UIEdgeInsets (16 bayt) kimi mürəkkəb məlumat strukturlarının debugunda vacibdir.
Tez-tez verilən suallar
Breakpoint kod sətrinə bağlıdır — ona çatdıqda dayanır. Watchpoint yaddaş ünvanına bağlıdır — proqramın istənilən yerindən həmin ünvana yazıldıqda dayanır. Watchpoint “kimin dəyəri dəyişdiyini”, breakpoint isə “bu sətirdə nə baş verdiyini” axtarır.
ARM-də (Apple Silicon daxil) 4–8 aparat watchpoint-i. Həddi aşmaq watchpoint-i proqram rejiminə keçirir, bu da icranı 10–50 dəfə ləngidir. Eyni anda 2–3 aktiv watchpoint-dən çox istifadə etmək tövsiyə edilmir.
Watchpoint-lər debug sessiyaları arasında saxlanılmır. Hər yeni işə salışda yaddaş ünvanları dəyişir və watchpoint yenidən qurulmalıdır. İstisna — sabit ünvanlı qlobal dəyişənlərdəki watchpoint-lərdir.
Bəli, lakin watchpoint xüsusiyyətin özünə deyil, onun ivar (backing storage) sahəsinə qoyulur. Swift-də watchpoint set variable self.property istifadə edin — LLDB xüsusiyyət adına uyğun ivar-ı avtomatik tapacaq.
Aparat watchpoint performansa təsir etmir — kəsmə prosessor səviyyəsində baş verir. Proqram (aparat registrləri bitdikdə) icranı 10–50 dəfə ləngidir, çünki LLDB hər yazma təlimatını yoxlayır.
Nəticə
Açar təslim mobil tətbiq hazırlayacağıq
IT Sectr 2017-ci ildən startaplar və bizneslər üçün iOS və Android tətbiqləri yaradır. Sizə məsləhət verəcəyik və ən yaxşı həlli təklif edəcəyik.
Həm də oxuyun