Skip to content

Instantly share code, notes, and snippets.

@SheldonWangRJT
Last active July 12, 2018 15:30
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save SheldonWangRJT/dafee3d96460cbd53ddcf52aaa04dda6 to your computer and use it in GitHub Desktop.
Save SheldonWangRJT/dafee3d96460cbd53ddcf52aaa04dda6 to your computer and use it in GitHub Desktop.
WWDC18 Notes by Sheldon - Session 412 - iOS Advanced Debugging with LLDB

WWDC18 Notes - Session 412 - Advanced Debugging with Xcode and LLDB

All session list link: Here
Session Link: Here
Download session slides: Here

This notes is written by Sheldon after watching WWDC18 session 412. You can find me with #iOSBySheldon in Github, Youtube, Facebook, etc.

Quick Tab for Breakpoint


When breakpoint hits, a tab called "Debug" will be created. // this can be set in Preferences -> Behaviors -> Pauses

Change Code without Re-Compiling


Change value of any property on when debugging: For example we have a value called didReachSelectedHeight

(lldb) expression didReachSelectedHeight = true

This could be extremely helpful if you are in the middle of debugging and realize you may miss a line of code such as:

dynamicAnimator.delegate = self

You can use expression to add this line to your code to avoid compiling your code again.

Demo here shows some animation using UIDynamicAnimator, worth take a look.

One-shot Breakpoint & Symbolic Breakpoint


Next trick is to set a one-shot symbolic breakpoint. Say, you know label text should be changed after line 10 in your code. You can then put a breakpoint in line 10, and then add action:

$ breakpoint set --one-shot true --name "-[UILable setText:]"

One-shot breakpoint will be removed automatically after it is hit. Symbolic breakpoint will like make you land on the assembly debug page, in there you can type:

(lldb) po $arg1

to see if that is the label (in our example) you wanted.

Skip x Line(s)


Once breakpoint is hit, we can drag the burger indicator of the breakpoint to jump lines (meaning let the system skip the code we wrote for following lines). We can also do this with lldb command:

(lldb) thread jump --by 1       # jump one line

View Hierarchy in LLDB


Debugging view hierarchy can be easy with the view debugging tool but we can also do it in lldb commands. The format should be something like:

(lldb) po self.view.recursiveDescription()

However, this won't work because in Swift code base, lldb closed partial debugging functions, including the one above. To use it, we need the help with objective-c

(lldb) expression -l objc -O -- [`self.view` recursiveDescription]

Here is the explanation of the above line, expression is to run a line of code, -l is setting the language, -O is equal to po, -- means no more commands, [self.view recursiveDescription] is to loop all the subviews in self.view and show their and their subviews' description. Note: You have to add "`" before and after self.view because by running command this way, it will not evaluate self.view as a parameter, "`" will make it evaluate first.

Command Alias


The following command is pretty helpful as we explained in the above paragraph, but we can make it even easier with command alias.

(lldb) expression -l objc -O -- [`self.view` recursiveDescription]

To use command alias:

(lldb) command alias poc expression -l objc -O --
# next we can use it
(lldb) poc 0x7fb3afc3f480
# to get the info of a UIElement with its memory address

UnsafeBitCast


UnsafeBitCast if a function built for Swift to cast an memory address to a type, but no result is gauranteed. But it will be helpful if you know the memory address and know its type, you can use it like:

(lldb) po unsafeBitCast(0x7fb3afc3f480, ScoreboardView.self)

Framebuffer


Say, we get one object from the unsafeBitCast, we can now play around with it and access its properties like position, center, etc. If we want to move it in the view, we can type:

(lldb) po unsafeBitCast(0x7fb3afc3f480, ScoreboardView.self).center.y = 100

But after this, nothing will be changed in the view of your simulator because the views are stored in the framebuffer. To make the view updated, we can use:

(lldb) expression CATransaction.flush() 

This time, the view will be changed.

LLDB with python


Python script file can open the full potential of all the lldb command. There are examples as a template to write the python script. But to make your lldb works with your python script, you just need to

  • Open file ~/.lldbinit
  • Add command script import /path/to/your/python/file.py to import file
  • Add command alias poc expression -l objc -O -- to direct add commands

Find python script for lldb built by Apple Here

Addition note for po, p, frame varaiable


po, p, frame varaiable are doing similar things but they have their differences. po is to print the debug description, which can be overridden by the following extension:

extension YourClass: CustomDebugStringConvertible {
    var debugDescription: String {
        return "<\(type(of: self) \(property1) \(property2)>"
    }
}

In LLDB po <expression> is just same as expression --object-description -- <expression>

p <expression> in LLDB is same as just expression, so p will output LLDB-formatted description.

In case of po & p both not working, we can also try frame variable <name>, LLDB will read the values of <name> from memory and output LLDB-formatted description

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment