Debugging iOS Applications using Xcode

In this post, we will be looking at using Xcode to debug iOS applications. The process described here can also be used to debug OS X apps with little or no modifications. LLDB will be given a good coverage in this post.

Debugging — A Thought Process

The most effective debugging tool is still careful thought, coupled with judiciously placed print statement
“Unix for Beginners”, Brian Kernighan

Debugging in software is still a thought process, where you form hypothesis, as to why your program is not behaving the way it should and trying to validate that hypothesis before coming up with a fix for it. Careful thought into the program’s behavior can yield better clarity than using multiple tools/debuggers.

Xcode Debugging Environment

Xcode Debugging View

The above shows the structure of Xcode’s debug view. The debug area consists of the variables view window and the debug console. The view can show the variables in the current scope, locally defined or even register values. The debug console shows the debug output and also contains the LLDB REPL command line. This is a powerful interface to the debugged program as we shall see later. The debug navigator shows the various resource monitors and the various threads used by the program. The stack trace is shown in the thread which breaks. It is the sequence of program execution until that point. Clicking on the individual area will show its own view in the editor window. The various keyboard shortcuts for the view are:

  • Show debug navigator — ⌘6
  • Show the breakpoints navigator — ⌘7
  • Hide debug area — ⇧⌘Y
  • Activate the console — ⇧⌘C
  • Jump to instruction pointer — ⌃⌘P

In addition, the various buttons to pass through the debugged program is shown in the debug area.

Xcode Debug Buttons
  • Deactivate breakpoints (⌘Y) — This deactivates all current breakpoints
  • Continue (⌃⌘Y) — This continues the debugged program infinitely or until the next breakpoint is encountered
  • Step over (F6) — This steps over the current module and treats it as a black box
  • Step into (F7) — This steps into the current module while debugging
  • Step out (F8) — This steps out of the current module and continues the execution after the module that it has come out of

Next, we shall look at the various kinds of debugging methods that we can use to debug applications.

Print Debugging

This is the most basic debugging technique and this is just print() and debugPrint() statements littered around the code. If you need to just print scalar values, you can just print them out explicitly. If you need to print out objects, you need to implement the CustomStringConvertible and CustomDebugStringConvertible protocols (Swift 2.0) in your class. Then when you print out the object in code or using po command in lldb, the description or the debug description is returned.

class FooBar: CustomStringConvertible, CustomDebugStringConvertible {
  var description: String { return “FooBar” }
  var debugDescription: String { return “FooBar Debug” }
}
print(FooBar()) // Prints “FooBar”
debugPrint(FooBar()) // Prints “FooBar Debug”

Breakpoints

You place breakpoints in your code to pause execution on a particular line of the program. You can place breakpoints using the GUI by clicking on the gutter in your editor or through ⌘\ keyboard shortcut. You can also set breakpoints using LLDB as shown later. Breakpoints in Xcode can be better managed using the breakpoint navigator (⌘7). You can add, delete and disable the breakpoints through it. You can create advanced breakpoints according to your debugging needs. We shall see some of them below:

Conditional Breakpoints

Conditional Breakpoint

You can edit a breakpoint to add conditions to break on. The program is paused only when the condition is true. You also have the options to ignore the breakpoint for an arbitrary number of times. You can execute some defined action when the breakpoint is hit. You can execute a LLDB command, print something, execute an AppleScript or a shell script, play a sound etc as an action.

Exception Breakpoints

Exception Breakpoint

Using the exception breakpoints, you can pause the program and and examine its state when an exception occurs. There are various options like breaking on a C++ exception, breaking on catch and as above, you can have multiple action to be run when the exception is hit. You can read in detail about exception breakpoints here.

Symbolic Breakpoints

Symbolic Breakpoint

When you need to debug some library or third party code, you can use a symbolic breakpoint to break on a module. You can also specify a condition on which the debugger should be hit. You can also ignore it for an arbitrary number of times and execute actions when the debugger is hit. You can read in detail about symbolic breakpoints here.

Debugging View Hierarchies

Sometimes, you need to able to visualize the view hierarchy in a graphical form along with its constraints and object properties to find a bug with the UI. You can now use Xcode to get a snapshot of the view hierarchy in real time at a given state in the program. You can choose to debug the view hierarchy using Debug > View Debugging > Capture View Hierarchy. You can print the description of the view, show the constraints, hide/show views etc. You can also get the visualization of the views in 3D.

Xcode View Hierarchy Debugging

You can read more about debugging view hierarchies here.

LLDB

LLDB is the debugger available in Xcode. It has a REPL command line and has support for C++ and Python plugins. It is also blazing fast and much more memory efficient than GDB at loading symbols. It is open source and is part of LLVM toolchain. The debugging done through Xcode UI earlier is under the hood handled by LLDB. It has lots of commands to aid debugging of iOS applications. We will be looking at the workings of LLDB and its various command options. We will also be looking at Chisel and its various options (Chisel is a open source collection of LLDB scripts released by Facebook).

When you pause a program to debug its state, you get a LLDB REPL command line in the debug console where you can run various commands to manage your debug sessions.

To get a help of all commands,

(lldb) help

To get a help of a command,

(lldb) help command sub-command

To modify a value of a variable in the debugged program,

(lldb) expression someVar = “Changed Value”
(String!) $R3 = “Changed Value”

The changed value is stored in the $R3 location and you can use it later in the session.

To print the value of a variable,

(lldb) p someVar
(String!) $R4 = “Changed Value”

To print objects,

(lldb) po someObject

You can print the value of the property in various formats like hexa-decimal, binary, octal, address etc. You can check out the various formats supported here.

To examine the stack frame,

(lldb) frame info

To list all breakpoints in the program,

(lldb) br li

To disable a breakpoint,

(lldb) br dis 5

To delete a breakpoint,

(lldb) br del 5

To create a breakpoint,

(lldb) br LoginViewController.swift:111

To view the entire view heirarchy starting from a view using Chisel,

(lldb) pview someView

Various Chisel commands can be found here.

That’s it.

You can view my projects on github.

You can follow me on twitter.

References

LLDB guide

LLDB and GDB command mapping

LLDB tutorial

Chisel

Some debugging stories & more

Apple docs on debugging in Xcode

Udacity — Xcode debugging

Show your support

Clapping shows how much you appreciated raj’s story.