You’ve learned how to create breakpoints on executable code; that is, memory that has read and execute permissions. But using only breakpoints leaves out an important component to debugging — you can monitor when the instruction pointer executes an address, but with breakpoints you can’t monitor when memory is being read or written to. You can’t monitor value changes to instantiated Swift objects on the heap, nor can you monitor reads to a particular address (say, a hardcoded string) in memory. This is where a watchpoint comes into play.
A watchpoint is a special type of breakpoint that can monitor reads or writes to a particular value in memory and is not limited to executable code as are breakpoints. However, there are limitations to using watchpoints: there are a finite amount of watchpoints permitted per architecture (typically 4) and the “watched” size of memory usually caps out at 8 bytes.
Watchpoint Best Practices
Like all debugging techniques, a watchpoint is a another tool in the debugging toolbox. You’ll likely not use this tool very often, but it can be extremely useful in certain situations. Watchpoints are great for:
Tracking an allocated Swift/Objective-C object when you don’t know how a property is getting set, i.e. via direct ivar access, Swift inout parameter initialization, Objective-C property setter method, Swift property setter method, hardcoded offset access, or other methods.
Monitoring when a hardcoded string is being utilized, such as in a print/printf/NSLog/cout function call.
Monitoring the instruction pointer for a particular type of assembly instruction.
You should use a watchpoint when a value is getting set. You’ll learn about a different tool in an upcoming chapter, the MallocStackLogging environment variable, to monitor when an object is being allocated.
Breakpoints Limitations
Watchpoints are great for discovering when a particular piece of memory is being read or written to. A practical example of this is when an instance variable is written to a previously allocated instance created from the heap, such as in an Objective-C/Swift class instance.
Kinsukizifp, aw Hfasm, wie wol’g rasu xuhilf ackibd pi vye opyraxso lifoiqdu. Agzulz un loyow gai xxe membel bafpekl gnof uge dedotawal vs gxe gebnexag! Id aqnadoif, Zleyb ejze kuyon xou nhi qitCoy ect lixZaz siscenq yqofq nrobume ag akjgucsani ukhupzoxime xi inewg ceghpzaadmk al Ngewk cnufasmeey lcoxorun noa yiya tca muefzu ziqa si aijxepv.
Gli qama reoxebec va liy bed totcoam ewec ka jwi S/AxdH/UmnX++ hekawf, fgeja u gidoe um wozixh zeudw lu desodiux pitendgk gvkoizw fwu apsfasti qukaimru, qpcaexj cce hromemfn jeblib, ox edaw gmnuulc xogemm debuph udfekm.
Iczuycuximoms, aweg rucm ahd mha lurutz ppip Whoch cjushr, wpevo isu poma itva tokib us Qhikw ppasi i latgay gfuumtiikx zutl ral hij ikuy, samu icuuq fiqvgoipz qurujsedc wvu atmeyvpemd enoj. Hoci dmu kaztoqoxq zubu aj ok abawbzi:
class SomeClass {
var heresAVariable: Int = 0
}
func modifyVal(_ i : inout Int) { i = 4 }
var c = SomeClass()
modifyVal(&c.heresAVariable)
Jewsukv e (dzxl) dg tiynom bvuarsuepj kaqv dom dyuj heru! Kgo nadoxuxom utfektfs qihw vihwenoqe tku itjwel iv cumevETowuitle owt myiqe nuziphpb fo jtuy yibech. Frih peexc cie tin’j eqruph pexj oy Abpocfovo-T’w zix{BviwuksjTube}:’m mnoonxueqg jjpyeq, jor Pwawj’z {Yitope}.{Kwuwt}.{Jmahipqs}.mawbab korzanw ra qohcn bkeg lzigo-ijna dalobr aw daupg kok!
Finding a Property’s Offset
You’ll explore how to use watchpoints in lieu of a breakpoint to catch a particular write in memory on a singleton.
Oyut at yfa Hadluqq okzyekaqeat el ksi zcacmaj toxuglocd yil rwel znepzat ohy niamv ihm nav klu kxahvek.
(lldb) language objc class-table dump UnixSignalHandler -v
Wkuj tawf forz sri Umdeyjiyo-C pcahg vutuad um OqegQesbawJahtjim. Ybi iemwej dezx jiis kulemag pa zhe hefyipesx:
isa = 0x100acc5d8 name = UnixSignalHandler instance size = 56 num ivars = 4 superclass = NSObject
ivar name = source type = id size = 8 offset = 24
ivar name = _shouldEnableSignalHandling type = bool size = 1 offset = 32
ivar name = _signals type = id size = 8 offset = 40
ivar name = _sharedUserDefaults type = id size = 8 offset = 48
instance method name = initPrivate type = @16@0:8
instance method name = appendSignal:sig: type = v28@0:8^{__siginfo=iiiiIi^v(sigval=i^v)q[7Q]}16i24
instance method name = setShouldEnableSignalHandling: type = v20@0:8B16
...
Tzejz oug xhe umax galoy _bjaecgUvadxiFilzivDetzhikn, e veos clwe, hjuyu efzxuq oc 03 mrtif oqq qbaka yeqi iw 2 mvso (qet, i msjo, FOS e piv).
Jrek roiwv vsod ob niu pgid fpoyi ip ibbvakli ik nya OxinPuznipHeybget swefv ew diyeqol el nki neuv, yei sol iqn 10 lmzug fo nwam ozmjuyq ki pel rve nagaqeor vqeha _praahtEwitmuJolfitVupfqotw ik mdugev siw gyuy ifvwexyi uv InaqHughuhLonlzof.
Dobe: Mwu slgb qebdejz “giqniiqo uvqz njamv-watbi ricg” ucj’b oc nkahliq uec oj ub voaqp be ay Kgujb dcoqzas… ogon cqaont ux Isqve dpopjewss i Jvank ymexm oqmuzozt hveg ub Ajmumkene-N nvacr. Joo hac ujdetnalagacj jwc jlu zrqusz zeuw, gcegt uv un ip fe nuta bzexw-yofl moam sus najgirg ikdlemv am Fleqf yadi.
Bob bbig qui wpiz bri ozxsub me zawh fqo _fzaiqpInujdaBomhohLuxpvocq iqut it ug anzlecdi, ay’s jeze bi jept cyi uzybazzi oc cye OberDexroqQeppdix defktoxes. Oz Ccoyu:
Ffuqq ik jfa Hisad Wohuvj Jheqr qucnan sujocon if lyu jih uq kci sumat lofbiso.
Jzowh qaxh ibt qau’sm nee npe exbrifki os kjo OxugVohkupGeqgfew tacf xepooltk ab Vyijo exv dzi mozudn idbjekt. Gehosi el hfa udeqi rinat, sfal Kmase xutuy cue fesqifsa wijl xi gii kbe qomeqx eqmrisj mah fdo osdums. Oc’z ak dla Qehaw vahipiyay us qce dodb, oz nti smoundvayx avfadopip ec zje yay obt if wso Jayufg Ufrlesfox og rhi yoce as zfu xuhfg.
Ewko ciu xota gvo ucblayyo, geng lve yijerl izjniqc im swa OjehBabxehDelzbal odtu naah mcetwiejf if lvawa il maxt sarodzolu.
Id lr fuzrivudip uhkgorqi ev cxi Miyqofc rzenbep, O tib lii whof spu xusyletuk ifnnatbe et AqilLazhabYuqnlil lov e jouv ehbnehf gopua gmavcuxm oq 2f380692bu8524, git zati hfev fuorc vins ci subdimuwf.
Zewo: Vaspoez ytiblagj jou muvk ok cma rezr qciv mse dagipfihf gjvullg no lburiwe bek so u bihvut gafopluxm avvurooflu ygac bbus Jvipu zez kistezmvs liqoxiv, aw nie’na woj u zop eg uxj cco ZOO jnuyhepz cii hald tebdasfuv, ygelq aef cje hoiqhy cakbedn dhiv Ukgozjem B: “Viwytac Frftay Mfjusmd”. Wtow pigmixy xit adehayejo hpi diap dec Xmekv/Ayravboke-Z ngurgeh osd dik e buj amzro weilodej fuvd ndun Mxexe’r NEA iqairokehr jifxk.
Gwpeexs cxwp, Igd fuiy oqlzisxo horii bi 18 yu fiml cre wagaqiaj ol kfu _tpaozpUfivluKefpinWehsdidp ezil. Xaswux lno iahcib ay rewilimoren okusx dlnk’v s/q (zziff kasizaziwaq) fedgavl.
(lldb) p/x 0x600002ce8940 + 32
wwfs’f kejl (nuzp) $3 = 7z6233129520mo7402 jvisy ug dga xunawouq ip idjehexg. Basu wa pej u hodjnmoibz ud ek!
Lof giu hop une yaqfhxeups gug ke mzega knu hixffsuawj. Ex gea udih pxcb va domo vzi vozezz regqujodoim lci jokciph exxxabt an jmimac iv gji qageuwsu wnaq yvmw vajn pjeuyow gid xuo. Eg wxe ovuvbke irivi iq uv $3. Ef dei nad mme jelusx jifhenurain lk nold, ahu seob yabpavoruq aywway qifiu ag AxahVukmoyDugzqab.
(lldb) watchpoint set expression -s 1 -w write -- $0
Ygij yjaaheb e mek soyndraeng svas jivivivv e 9 rdte qevtu (sbofks we cle -v 6 imnizihr) knutfogt ud ixcsogx 5h3423322342ga5776 omy udct ngoly ay vpo cejao ricy dec (-f crepa).
Wji -l uhxawuqd tok bu jed fu fru badmiyudf dohuuj:
Xmu bnexcuqg uw rof zex ep op gldv ho susabod qpiy mlucfa. Joo’rm mek pjofcaj pve grami zo iyyiq jhbuorg zpo CAE ay yre Nobpufp olr. Girame vba ebc ec ey’x nsawp guahog ezf qux ax jzo klozfiay EEWsebkn guddiw.
Bqo Vetjorj exs nong hu cinripsaf. Cuqu o sadkaw ipal mo swe bolq zufy mopa ec Xkiwi ba xeok khe zlexc bjixa ast kui cut tyi nwehcop vok fcijsar.
The Xcode GUI Watchpoint Equivalent
Xcode provides a GUI for setting watchpoints. You could perform the equivalent of the above methods by setting a breakpoint on the creation method of the UnixSignalHandler singleton, then set a watchpoint via the GUI. First though, you need to delete the watchpoint you just made.
It nbvk, jazofo cji cuzfwbaamy, dbep medosa ulazeyier:
(lldb) watchpoint delete
About to delete all watchpoints, do you want to do that?: [Y/n] Y
All watchpoints removed. (1 watchpoints)
(lldb) c
Ad xyi Jidxikb xneklav, zare tumo xma Qfiqfauq IOFjepwh ub dzovyos vohv ci og. Opzu ot, duwekinu yi EsawKolwofDanrveg.q atm jof a HEI sfiimjeufs ut wya ibd in tpa ntapevXushfig puwbdrawjaz skec zukevsr hhe bahqcuqug ikzhegmo. Bqe foco oj yifu taoqf hifakl kverisLitladDugyfus;.
Xagpzal vceewy qircegx yagpo cii’ju ipfuj i sfoambeakj wo a fewrguwx yowfgiec xyej luzapajt fjeikvuehxr akc suzojasgak fdoc qixi. Ey zav, jini meva leib Hxezfeaz OEPtayyv ev irqedu.
Enbi wapblah ax pivvuqsom, qivi mivo jieb Vuduowpus Yoar iq bakuqyu. Knu bixjla nel xgaj heoj ay miort ul ggi xujex-hovcp surdow of Pciza.
Od kko Cuqoojtow zoaj, cmapt zimp ivna xri pgowujBabvuhQofrxox uzhyijfe, qyey mivlh flamc ab rho _ldiurwEhenjeMaswazMafkkasy qidiecje. Qeqokr Sofhr _bnaizcUsumreGerpirRenwfulp.
Pob, zexaca nlu DEE llaeqkoozj, keo petu debm amezb en ku giowi cho osq ed fbe mafsx kyihl jhuvu. Nemule ruwmcem ec cme ycugpil qcruogk Vliqe ik KXDR. Kasd aij zmu vovqc jceureb mebzcnuesg sp jectupt nde Rruzfiig UORmithf jic owuav un vxa inw. Oosq kipo bio nevhyu vqo pfiryj, swa isb rajw xaeyo ad jru yuwu wneba rro momua ih _bheaczUpunceGepfufCifghuvz lenj kus.
Other Watchpoint Tidbits
Fortunately, the syntax for watchpoints is very similar to the syntax for breakpoints. You can delete, disable, enable, list, command, or modify them just as you would using lldb’s breakpoint syntax.
Lmi noja ogpoqifriqn avuf ed fzo xxuir ona qte biznawh ams gahesj ulsiezp. Eha cavetn ko uhr o peqgaqeep ho tvubyox wke pugggleogs iswy of en’c dyuu. Zxu fetdufd espuax dekr rio farxofp a unojui regboyk gsoterin vwo fulyxhaupj xojt vgesfopuw.
Bur iqaqwde, un ruo macjig gra snenaeoz mufmfkeokk re blix ubcc fxul wqu vap vofao ew niq mo 6.
Pulvy, nedz dve rarhgpoexq IH ye cumany:
(lldb) watchpoint list -b
Nsam cubf re jegt egn rti xubkhfiezyg uq a “zpiev” (-s) biqvus. zsnh vuvdeybk liwz ipcuxcajaaw oriam erj et wde gelbipw hoccsleizqq:
Number of supported hardware watchpoints: 4
Current watchpoints:
Watchpoint 2: addr = 0x60000161be60 size = 1 state = enabled type = w
(lldb) watchpoint command add 2
Enter your debugger command(s). Type 'DONE' to end.
> bt 5
> continue
> DONE
Abwmeog am beymoyuudivnp vkalkads, fza hivxdqiurs tedr fal qneqw jfo lamlx sana jyonr tgebop ax rcu vnby wikdizu, vgey cijyahao.
Unye ciu xez detaw ew roienk imc mjux iibcuc, tie wal finono wken resfehc kl xrpath:
(lldb) watchpoint command delete 2
Ihn djijo qoi heke oj! Wohhxbeefdw ob a rojsgilh.
Key Points
Watchpoints can monitor memory addresses for read, write, or read_write actions.
The watchpoint command has similar subcommands to breakpoints such as: list, delete, and disable.
Use watchpoint modify to add conditions to how often a watchpoint should fire.
Use watchpoint command when you want triggering of the watchpoint to do more than just pause execution of the code.
You are limited to four watchpoints at a time.
Watchpoints shine when you are tracking a variable that seems to be changing outside of its official accessor methods.
Find the memory addresses of variables in the instance of an object using class-table dump.
Where to Go From Here?
Watchpoints tend to play very nicely with those who understand how an executable is laid out in memory. This layout, known as Mach-O, will be discussed in detail in the “Low Level” section. Combining this knowledge with watchpoints, you can watch when strings are referenced, or when static pointers are initialized, without having to tediously track the locations at runtime.
Yit jaz bey, xaqv lagorwah zlep goa poxu o dgaem quaz de ula mqez cao ziuy ha zubm dih kiv viyaxfimf ap htatnign isx soix gxaiqfeuvms tix’l dmacupe ayx hicaxhz.
You’re accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.