WWDC 2021: Debugging with Xcode and LLDB
Find hereafter a detailed summary of three videos that belong to a taxonomy of some WWDC footages.
The original videos are available on the official Apple website (wwdc18, wwdc19 and wwdc21).
"Discover advanced techniques, and tips and tricks for enhancing your Xcode debugging workflows. Learn how to take advantage of LLDB and custom breakpoints for more powerful debugging. Get the most out of Xcode's view debugging tools to solve UI issues in your app more efficiently."
"LLDB is a powerful tool for exploring and debugging your app at runtime. Discover the various ways to display values in your app, how to format custom data types, and how to extend LLDB using your own Python 3 scripts."
"Breakpoints can help you debug issues by allowing you to pause and inspect problems in the middle of a process. Discover the latest improvements to breakpoints in Xcode including column and unresolved breakpoints. We'll also go over best practices for general breakpoints and LLDB tips and tricks."
The various contents of this video are indicated hereunder:
Most of the illustrations are parts of the Apple presentations and may be available at the Resources
section inside the Overview
sheet of each video.
Hereafter, the underlined elements lead directly to the playback of the WWDC video at the appropriate moment.
LLDB: print variables #
The Low Level DeBugger (LLDB) contains many effective commands to deliver an output efficient format as a debugger component of the Low Level Virtual Machine (LLVM) project.
The print object command displays a default textual representation of the input parameter.
To customize the top-level description, conforming to the CustomStringDebugConvertible protocol is mandatory while the CustomReflectable protocol tackles rather the substructure configuration.
Besides printing variables, the po-command that is nothing more than an alias of the expression
command, can also perform evaluated expressions.
A representation of the phases followed by the po-command process is detailed to point out the different evaluations during these operations.
As an alias of the expression
command, the print command displays a raw representation of an object using the most out of the LLDB variable name convention and without taking into account the specific description of the po command.
A caveat is highlighted regarding the type that may be returned by this command.
The review of the different steps of the p-command process points out less work to be done than the po-command's while underlining a special attention to the returned type detailed in the previous example.
LLDB provides a default formatter that may be customized and that works for commonly used types.
The v-command displays the same results as the p-command's while being an alias of the frame
variable
command as well.
However, no code is compiled or executed with the v-command that gives rise to a faster way to get results.
Then, the use of p
or po
is mandatory if some computed properties must be reached because of the code execution requirement in this case.
The detailed study of the different steps of the v-command process highlights potential multiple dynamic type resolutions.
LLDB: custom data formatters #
LLDB introduces some helpers to tailor the representation of each displayed data type.
Using the filter formatter enables to process only the proper items instead of displaying the complete bunch of elements composing the analyzed object.
Some of the subcommands that permit to define custom display options are introduced in the next sheets of this section and are available in the variable formatting section of the LLVM site as well.
A summary is a customized way to render the major information of a type at first sight and may use regular text and variables with a dollar sign.
The solution to have a dynamic behavior regarding the flexible size of some elements relies on defining this kind of summaries in Python.
Launching the script command is the basics to begin writing some command lines interpreted by Python 3.
The stream of command lines can be merged into a single file in order to get the final result in a faster way.
This customized summary that's exposed in the console through LLDB may also be revealed in the Xcode's variable view.
This class file is used the same way as the summary provider is.
Finally, all the command lines used in the console are stored in a dedicated file.
Breakpoints #
Source file #
To increase the accuracy of the line breakpoint, a column breakpoint has been introduced in Xcode 13.
The column breakpoint may be especially useful for closures thanks to the line table created by the compiler under debug condition that maps source lines and columns to the compiled addresses.
Instead of repeatedly writing many command lines in the console to get a single result, it comes in handy to use the Debug Command action to make it faster.
This process is also precious to notice at a glance the effective change in code without recompiling...
... and some code can be put as an argument of the expression
command as well.
Modifying the line breakpoint selection only once is feasible by means of a specific option.
It's also possible to ignore the content of the breakpoint line while passing a command as a substitute.
An unresolved breakpoint may be also pointed out with a dashed icon and a pop-up containing the possible reasons of its appearing (more information about this topic are provided in the Breakpoint Symbolic section).
Symbolic #
Even if every interesting elements may be in assembly code, it's still possible to reach their value by inspecting the passed arguments.
If the symbolic breakpoint can't be resolved, a dashed icon is highlighted with a couple of reasons appearing in a pop-up.
A LLDB tip is pointed out to avoid a potential cumbersome searching in the Xcode Find
Navigator
.
Runtime issue #
The Xcode Issue
Navigator
renders the recorded backtrace of the runtime issue and the best way to deal with it is to create the proper runtime issue breakpoint.
According to the chosen breakpoint, it may be necessary to dive into the diagnostics tab of the scheme editor to enable the appropriate feature.
View debugging tips #
-
In Xcode, hold down the ⌘ button while clicking on the debug bar to invoke the capture of the view hierarchy (the app will be still in its active state).
-
The memory address of a selected object can be brought directly to the console to simplify its replication in a command line.
- Modifications can be immediately displayed to notice their efficiency before applying the final implementation in code.
The same rationale may be followed for adapting a text color with the Dark Mode
feature.
- When multiple windows are shown in the view debugger, the first tip detailed in this section isn't available anymore and the solution to keep on capturing these windows in their active state lies in browsing the debug navigator panel.
Miscellaneous #
Xcode #
-
Settings come in handy for having a specific debugging display thanks to the customized behaviors whenever the code pauses.
-
A spray can icon on the touch bar may be useful when developping in full screen mode for example by showing some options of the debug bar.
-
To locate an element in the view hierarchy when it's selected on the interface, the easiest way relies on choosing the
Reveal in the Debug Navigator
item in the navigation menu. -
Highlighting the constraints of an element and the clipped contents is quickly achieved by means of the shortcuts located at the bottom of the inferface.
-
Once validated, the access to the code into which the modifications should be put is straightly carried out through the interface.
-
While running, it's now possible to tweak the dark mode appearance in order to check the final results of the changings thanks to a button in the debug bar...
-
... whose function is also available in the touch bar.
Watchpoints #
Watchpoints are used to monitor the value of a variable for changes while triggering a pause in the debugger when these changes occur.
Recursive description #
The use of this debug function that permits to display the debug description for all the views in the view hierarchy while remaining in the console is a bit tricky:
- The
po
command is useful to find the memory address of a view with the appropriate property. - When it's not the case, the call of the
recursive
description
function is necessary. - The use of an objective-c syntax including the back ticks is crucial to reach the desired result.
- Check out the displayed view's memory address.
- The first solution is to create an alias involving an objective-c syntax to get the view properties.
- The other solution is to use the unsafeBitCast function with the
po
command... - ... that entitles to exploit functions and properties of the returned typed result...
- ... without forgetting to synchronize the screen's frame buffer with the changes.
Python wrap up #
The automation of many command lines to be rounded up and launched in a simple command is possible and detailed in this part.