The LLDB Debugger - Part 3: Watchpoint
lldb debug watchpoint Estimated reading time: 8 minutesDebugging - is not just a process for finding and fixing bugs - for me, this is a great way to find out how actually the program works. To do so, we should be able to detect any change at any memory address.
Often, breakpoints
can help a lot within this task, but for some cases, such technique simply can’t help us - imagine a case, when we would like to detect a moment, when some constant is read from the memory, of some variable is write to memory. In other words - when some value at a certain memory address is read or written. Breakpoints have no power here. Of cause, accessors (like getter and setter) can help a lot in some cases, but, not always.
Watchpoint instead, does not require some instruction in code to be set, all that needs for them - is an address in memory which we would like to monitor and inspect.
Articles in this series:
- The LLDB Debugger - Part 1: Basics
- The LLDB Debugger - Part 2: Breakpoints
- The LLDB Debugger - Part 3: Watchpoint
watch the memory
context
Thus watchpoint
works with memory address and monitor memory change, we should somehow obtain the address of a variable or use the name of the variable that is available in the current scope.
If we talking about the name of the variable - everything is quite simple - just use its name. But, if we would like to get a notification when some ivar
changes (for example in Obj-C), we should use a memory address. To deal with it, we can use the next command:
Let’s play a bit. First, let’s create a class:
then, we can create an instance and play a bit with this command:
The first try:
Let’t add superclass for SomeClass
as NSObject
and repeat operation:
As u can see, swift doesn’t provide direct access to ivars
, so an alternative to watchpoints
here is willGet
and willSet
.
Within Obj-C, we can see offset for each ivar
from the base address. To check this out, let’s create a pure Obj-C class:
DemoClass.h
DemoClass.m
And repeating the same operation:
Now, we can see that ivar
_someVariable
has size 8 and offset 8. Using this information we can grab the base address and add this offset to get ivar
memory address.
p
(short forlldb
that allows u to format types in a certain manner./x
meanhex
. Here is the full format list available inlldb
.
Here, we grab the base address of the object in the heap and add an offset equal to 8 (according to class-dumb info). As result, we got the address of ivar
, which holds the NSString
value.
adding the watchpoint
To add a watchpoint
we can use the next command:
w e s
is short forwatchpoint expression set
-lldb
commands can be invoked with a short version of first symbols from commands if there is no conflict within other commands.
Output:
And here one more helpful command - list
. These commands similar to the one used within breakpoints
- simply return the list of available watchpoints
:
Now, we can test this. To do so we should simply update the value of someVariable
:
The result will be interrupted due to the existing watchpoint
:
As u can see, our watchpoint
works as expected. To be more concrete - u can try to change the variable by any other action (for example button press) - the code will be interrupted and paused on created watchpoint
.
Also, if we check the current value of the someVariable
- it’s updated as expected:
We also can check variable directly:
we used here double dereference, because we passing a pointer to
NSString *
, and this pointer also a pointer…for debugging purpose we can ommit
__unsafe_unretained
and simply call*(( NSString **)(0x600000794618));
more about
__unsafe_unretained
. Here also a good explanation by Brad Larson
As u can see, watchpoint
is a powerful tool for monitoring any memory-related changes in objects and variables. Above I mention that we can monitor either address of memory either variable - keep in mind, that under variable I mean that we can set watchpoint
even to some ivar
inside another object that is currently available in context:
Again,
w s
is just a short forwatchpoint set
options
set type
We can also configure few options for watchpoint set expression
:
example:
set conditions
Another good moment - we can add a condition to watchpoint
as it was done within breakpoint
previously. To do so just create watchpoint
and then modify it using -c
flag:
Here, we list all watchpoints
, then modify the one using condition:
“stop when someVariable
in demoClass
instance object isEqual to 15”.
I added a button and increment
tapCount
value, and insomeVariable
storedstringRepresentation
oftapCount
value.
When this condition is true
, watchpoint is called:
edit
Editing a watchpoint
can be done using modify
command. There are not many ways to edit it:
Real example of modification already shown above :] .
list
Listing available watchpoints
also can be done using similar command list
:
delete
To delete a watchpoint
simple use the same-name command:
to delete all - simply omit the last param
understanding context
Setting the watchpoint
is just a half of the job - another part is to understand whats causes the change.
To do so we can use same commands as we used for breakpoints
(thread backtrace
or frame variable <name>
, etc) (read about breakpoints here).
One more good point to mention is when watchpoint hit, we will see a specific message:
This is nothing but values. To check the values:
In addition, we can use few more useful commands:
On my M1 mac I got an error - unsupported flavor:
read more about this command here
Articles in this series:
- The LLDB Debugger - Part 1: Basics
- The LLDB Debugger - Part 2: Breakpoints
- The LLDB Debugger - Part 3: Watchpoint
Resources
Share on: