19/6/11 5:00 PMTechnical Note TN2239: Technical Note TN2239
Page 1 of 23http://developer.apple.com/library/ios/technotes/tn2239/_index.html
Technical Note TN2239
iOS Debugging Magic
iOS contains a number of 'secret' debugging facilities, including environment variables, preferences, routines
callable from GDB, and so on. This technote describes these facilities. If you're developing for iOS, you should
look through this list to see if you're missing out on something that will make your life easier.
Introduction
Basics
Enabling Debugging Facilities
Seeing Debug Output
Some Assembly Required
ARM
Intel 32-Bit
Architecture Gotchas
Controlled Crash
Instruments
CrashReporter
BSD
Memory Allocator
Standard C++ Library
Dynamic Linker (dyld)
Core Services
Core Foundation
Application Services
Core Animation
Cocoa and Cocoa Touch
Objective-C
Foundation
UIKit
Networking
Power
Push Notifications
iPhone Simulator
Document Revision History
Introduction
All Apple systems include debugging facilities added by Apple engineering teams to help develop and debug
specific subsystems. Many of these facilities remain in released system software and you can use them to
debug your code. This technote describes some of the more broadly useful ones. In cases where a debugging
facility is documented in another place, there's a short overview of the facility and a link to the existing
documentation.
Important: This is not an exhaustive list: not all debugging facilities are, or will be, documented.
Many of the details covered in this technote vary from platform to platform and release to release. As such,
you may encounter minor variations between platforms, and on older or newer systems. Known significant
variations are called out in the text.
This technote was written with reference to iOS 4.1.
19/6/11 5:00 PMTechnical Note TN2239: Technical Note TN2239
Page 2 of 23http://developer.apple.com/library/ios/technotes/tn2239/_index.html
Warning: The debugging facilities described in this technote are unsupported. Apple reserves the
right to change or eliminate each facility as dictated by the evolution of the OS; this has happened in
the past, and is very likely to happen again in the future. These facilities are for debugging only:
you must not ship a product that relies on the existence or functionality of the facilities
described in this technote.
In addition to this technical issue of binary compatibility, keep in mind that iOS applications must comply with
various legal agreements and the App Store Review Guidelines.
Note: If you also develop for Mac OS X, you may want to read Technical Note TN2124, 'Mac OS X Debugging
Magic'.
This technote covers advanced debugging techniques. If you're just getting started, you should consult the
following material:
GDB is the system's primary debugging tool. For a full description of GDB, see Debugging with GDB.
Xcode is Apple's integrated development environment (IDE). It includes a sophisticated graphical debugger,
implemented as a wrapper around GDB. For more information about Xcode, see the "Tools & Languages"
section of the Apple developer Reference Library.
This technote does not cover performance debugging. If you're trying to debug a performance problem, the
best place to start is the Getting Started with Performance document.
Back to Top
Basics
The later sections of this technote describe the debugging facilities in detail. Many of these facilities use
similar techniques to enable and disable the facility, and to see its output. This section describes these
common techniques.
Enabling Debugging Facilities
Some debugging facilities are enabled by default. However, most facilities must be enabled using one of the
techniques described in the following sections.
Environment Variables
In many cases you can enable a debugging facility by setting a particular environment variable. You can do
this using the executable inspector in Xcode. Figure 1 shows an example of this.
Figure 1: Setting environment variables in Xcode
19/6/11 5:00 PMTechnical Note TN2239: Technical Note TN2239
Page 3 of 23http://developer.apple.com/library/ios/technotes/tn2239/_index.html
Preferences
Some debugging facilities are enabled by setting a special preference. You can set such a debugging
preference by configuring a command line argument in Xcode. Figure 2 shows how this is done.
Figure 2: Setting command line arguments in Xcode
19/6/11 5:00 PMTechnical Note TN2239: Technical Note TN2239
Page 4 of 23http://developer.apple.com/library/ios/technotes/tn2239/_index.html
Callable Routines
Many system frameworks include routines that print debugging information to stderr. These routines may be
specifically designed to be callable from within GDB, or they may just be existing API routines that are useful
while debugging. Listing 1 shows an example of how to call a debugging routine from GDB; specifically, it
calls CFBundleGetAllBundles to get a list of all the bundles loaded in the application, and then prints that
list by calling the CFShow routine.
Listing 1: Calling a debugging routine from GDB
(gdb) call (void)CFShow((void *)CFBundleGetAllBundles())
{type = mutable-small, count = 59, values = (
19/6/11 5:00 PMTechnical Note TN2239: Technical Note TN2239
Page 5 of 23http://developer.apple.com/library/ios/technotes/tn2239/_index.html
0 : CFBundle 0x100234d00 …
[…]
12 : CFBundle 0x100237790 …
[…]
23 : CFBundle 0x100194eb0 …
[…]
)}
Note: When calling a routine like this, GDB must know the routine's return type (because some return types
can affect the way in which parameters are passed). If you're calling a routine without debug symbols, you
have to tell GDB the return type by adding a cast. For example, Listing 1 casts the return type of
CFBundleGetAllBundles to (void *) and the return type of CFShow to (void).
Similarly, if you call a routine with non-standard parameters, you may need to cast your arguments to ensure
that GDB passes them correctly.
If you don't see the output from the routine, you may need to look at the console log, as described in the
Seeing Debug Output section.
Important: If you use this technique for your own code, be warned that it doesn't always work for routines
that are declared static. The compiler's interprocedural optimizations may cause a static routine to
deviate from the standard function call ABI. In that case, it can't be reliably called by GDB.
In practice, this only affects Intel 32-bit code, as used by the iPhone Simulator.
Configuration Profiles
Some iOS subsystems have debugging facilities that can be enabled by installing a special configuration
profile. For an example of this, see Push Notifications. You install such a configuration profile in the usual
way:
put it on a web server, and then download it using Safari on the device
attach it to an email, mail it to an account accessible from the device, and then run Mail and open the
configuration profile attachment
You can learn more about configuration profiles by reading the various documents accessible from iPhone in
Business site.
Seeing Debug Output
Programs that generate debug output generally do so using one of following mechanisms:
NSLog
printing to stderr
system log
NSLog is a high-level API for logging which is used extensively by Objective-C code. The exact behaviour of
NSLog is surprisingly complex, and has changed significantly over time, making it beyond the scope of this
document. However, it's sufficient to know that NSLog prints to stderr, or logs to the system log, or both. So,
if you understand those two mechanisms, you can see anything logged via NSLog.
Printing to stderr is one of the most commonly used output mechanisms. Given this topic's importance, it is
covered in depth in the next section.
The easiest way to view the system log is in the Console tab in Xcode's Organizer window. If you're working
with users who don't want to install Xcode, they can view the system log using the iPhone Configuration
Utility.
19/6/11 5:00 PMTechnical Note TN2239: Technical Note TN2239
Page 6 of 23http://developer.apple.com/library/ios/technotes/tn2239/_index.html
Console Output
Many programs, and indeed many system frameworks, print debugging messages to stderr. The destination
for this output is ultimately controlled by the program: it can redirect stderr to whatever destination it
chooses. However, in most cases a program does not redirect stderr, so the output goes to the default
destination inherited by the program from its launch environment. This is typically one of the following:
If you launch a GUI application as it would be launched by a normal user, the system redirects any
messages printed on stderr to the system log. You can view these messages using the techniques
described earlier.
If you run a program from within Xcode, you can see its stderr output in Xcode's debugger Console
window (choose the Console menu item from the Run menu to see this window).
Attaching to a running program (using Xcode's Attach to Process menu, or the attach command in GDB) does
not automatically connect the program's stderr to your GDB window. You can do this from within GDB using
the trick described in the "Seeing stdout and stderr After Attaching" section of Technical Note TN2030, 'GDB
for MacsBug Veterans'.
Back to Top
Some Assembly Required
While it's very unusual to write a significant amount of code in assembly language these days, it's still useful
to have a basic understanding of that dark art. This is particularly true when you're debugging, especially
when you're debugging crashes that occur in libraries or frameworks for which you don't have the source
code. This section covers some of the most basic techniques necessary to debug programs at the assembly
level. Specifically, it describes how to set breakpoints, access parameters, and access the return address on all
supported architectures.
Where the difference is significant the assembly-level examples in this technote are from an armv7 binary
running on an iPhone 3GS. However, in most cases the difference is not significant and the example might be
from a variety of different architectures, perhaps even from the Mac. It's very easy to adapt such examples to
other architectures. The most significant differences are:
accessing parameters
getting the return address
And these are exactly the items covered by the following architecture-specific sections.
Important: The following architecture-specific sections contain rules of thumb. If the routine has any non-
standard parameters, or a non-standard function result, these rules of thumb do not apply, and you should
consult the documentation for the details.
In this context, standard parameters are integers (that fit in a single register), enumerations, and pointers
(including pointers to arrays and pointers to functions). Non-standard parameters are floating point numbers,
vectors, structures, integers bigger than a register, and any parameter after the last fixed parameter of a
routine that takes a variable number of arguments.
For a detailed description of the calling conventions for all iOS devices, see iOS ABI Function Call Guide. The
Intel 32-bit architecture used by the iPhone Simulator is described in the Mac OS X ABI Function Call Guide.
Before you read the following sections, it's critical that you understand one GDB subtlety. Because GDB is, at
heart, a source-level debugger, when you set a breakpoint on a routine, GDB does not set the breakpoint on
the first instruction of the routine; rather, it sets the breakpoint at the first instruction after the routine's
prologue. From a source-level debugging point of view this make perfect sense. In a source-level debugger
you never want to step through the routine's prologue. However, when doing assembly-level debugging, it's
easier to access parameters before the prologue runs. That's because the location of the parameters at the
first instruction of a routine is determined by the function call ABI, but the prologue is allowed to shuffle
things around at its discretion. Moreover, each prologue can do this in a slightly different way. So the only way
19/6/11 5:00 PMTechnical Note TN2239: Technical Note TN2239
Page 7 of 23http://developer.apple.com/library/ios/technotes/tn2239/_index.html
to access parameters after the prologue has executed is to disassemble the prologue and work out where
everything went. This is typically, but not always, quite easy, but it's still extra work.
The best way to tell GDB to set a breakpoint at the first instruction of a routine is to prefix the routine name
with a "*". Listing 2 shows an example of this.
Listing 2: Before and after the prologue
(gdb) b CFStringCreateWithFormat
Breakpoint 1 at 0x34427ff8
(gdb) info break
Num Type Disp Enb Address What
1 breakpoint keep n 0x34427ff8
-- The breakpoint is not at the first instruction.
-- Disassembling the routine shows that GDB has skipped the prologue.
(gdb) x/5i CFStringCreateWithFormat
0x34427ff0 : push {r2, r3}
0x34427ff2 : push {r7, lr}
0x34427ff4 : add r7, sp, #0
0x34427ff6 : sub sp, #4
0x34427ff8 : add r3, sp, #12
-- So we use a "*" prefix to disable GDB's 'smarts'.
(gdb) b *CFStringCreateWithFormat
Breakpoint 2 at 0x34427ff0
(gdb) info break
Num Type Disp Enb Address What
1 breakpoint keep n 0x34427ff8
2 breakpoint keep n 0x34427ff0
Important: Because iOS has no way to run programs from the command line, iOS-specific listings, like
Listing 2, are taken from Xcode's Console window. And because the Console window does not support the
'#' character for comments, comments are denoted by a leading '--'. If you try to replicate these examples
yourself, you must not enter these lines into the Console window.
In contrast, some other listings in this document were created on Mac OS X, and thus use traditional GDB
comments.
Finally, if you're looking for information about specific instructions, be aware that the Help menu in Shark
(included in the Xcode developer tools) has an instruction set reference for ARM, Intel and PowerPC
architectures.
ARM
In ARM programs the first four parameters are passed in registers. The return address is in register LR. Table
1 shows how to access these values from GDB when you've stopped at the first instruction of the function.
Table 1: Accessing parameters on
ARM
What GDB Syntax
return address $lr
first parameter $r0
second parameter $r1
third parameter $r2
fourth parameter $r3
19/6/11 5:00 PMTechnical Note TN2239: Technical Note TN2239
Page 8 of 23http://developer.apple.com/library/ios/technotes/tn2239/_index.html
On return from a function the result is in register R0 ($r0).
Because parameters are passed in registers, there's no straightforward way to access parameters after the
prologue.
Listing 3 shows an example of how to use this information to access parameters in GDB.
Listing 3: Parameters on ARM
-- We have to start the program from Xcode. Before we do that, we go to
-- Xcode's Breakpoints window and set a symbolic breakpoint on
-- CFStringCreateWithFormat.
GNU gdb 6.3.50-20050815 […]
-- We've stopped after the prologue.
(gdb) p/a $pc
$1 = 0x34427ff8
-- Let's see what the prologue has done to the registers
-- holding our parameters.
(gdb) x/5i $pc-8
0x34427ff0 : push {r2, r3}
0x34427ff2 : push {r7, lr}
0x34427ff4 : add r7, sp, #0
0x34427ff6 : sub sp, #4
0x34427ff8 : add r3, sp, #12
-- Hey, the prologue hasn't modified R0..R3, so we're OK.
--
-- first parameter is "alloc"
(gdb) p/a $r0
$2 = 0x3e801d60 <__kCFAllocatorSystemDefault>
-- second parameter is "formatOptions"
(gdb) p/a $r1
$3 = 0x0
-- third parameter is "format"
(gdb) call (void)CFShow($r2)
managed/%@/%@
-- It turns out the prologue has left LR intact as well.
-- So we can get our return address.
--
-- IMPORTANT: Bit zero of the return address indicates that it lies
-- in a Thumb routine. So when using a return address in LR or on
-- the stack, always mask off bit zero.
(gdb) p/a $lr & 0xfffffffe
$4 = 0x34427e18 <__CFXPreferencesGetManagedSourceForBundleIDAndUser+48>
-- Now clear the breakpoint and set a new one before the prologue.
(gdb) del 1
(gdb) b *CFStringCreateWithFormat
Breakpoint 2 at 0x34427ff0
(gdb) c
Continuing.
Breakpoint 2, 0x34427ff0 in CFStringCreateWithFormat ()
-- We're at the first instruction. The parameters are guaranteed
-- to be in the right registers.
(gdb) p/a $pc
$5 = 0x34427ff0
-- first parameter is "alloc"
(gdb) p/a $r0
$6 = 0x3e801d60 <__kCFAllocatorSystemDefault>
-- second parameter is "formatOptions"
(gdb) p/a $r1
$7 = 0x0
-- third parameter is "format"
(gdb) call (void)CFShow($r2)
%@/%@.plist
-- return address is in LR; again, we mask off bit zero
(gdb) p/a $lr & 0xfffffffe
$8 = 0x34427e7c <__CFXPreferencesGetManagedSourceForBundleIDAndUser+148>
(gdb) b *0x34427e7c
Breakpoint 3 at 0x34427e7c
-- Delete the other breakpoint to avoid thread-based confusion.
(gdb) del 2
-- Continue to the return address.
(gdb) c
Continuing.
19/6/11 5:00 PMTechnical Note TN2239: Technical Note TN2239
Page 9 of 23http://developer.apple.com/library/ios/technotes/tn2239/_index.html
Breakpoint 3, 0x34427e7c in __CFXPreferencesGetManagedSourceForBundleIDAndUser ()
-- function result
(gdb) p/a $r0
$9 = 0x1062d0
(gdb) call (void)CFShow($r0)
mobile/.GlobalPreferences.plist
Intel 32-Bit
The Intel 32-bit architecture is used in the iPhone Simulator.
In 32-bit Intel programs, parameters are passed on the stack. At the first instruction of a routine the top word
of the stack contains the return address, the next word contains the first (leftmost) parameter, the next word
contains the second parameter, and so on. Table 2 shows how to access these values from GDB.
Table 2: Accessing parameters on
Intel 32-bit
What GDB Syntax
return address *(int*)$esp
first parameter *(int*)($esp+4)
second parameter *(int*)($esp+8)
... and so on
After the routine's prologue you can access parameters relative to the frame pointer (register EBP). Table 3
shows the syntax.
Table 3: Accessing parameters after
the prologue
What GDB Syntax
previous frame *(int*)$ebp
return address *(int*)($ebp+4)
first parameter *(int*)($ebp+8)
second parameter *(int*)($ebp+12)
... and so on
On return from a function the result is in register EAX ($eax).
Listing 4 shows an example of how to use this information to access parameters in GDB.
Listing 4: Parameters on Intel 32-Bit
$ # Use the -arch i386 argument to GDB to get it to run the
$ # 32-bit Intel binary.
$ gdb -arch i386 /Applications/TextEdit.app
GNU gdb 6.3.50-20050815 (Apple version gdb-1346) […]
(gdb) fb CFStringCreateWithFormat
19/6/11 5:00 PMTechnical Note TN2239: Technical Note TN2239
Page 10 of 23http://developer.apple.com/library/ios/technotes/tn2239/_index.html
Breakpoint 1 at 0x31ec6d6
(gdb) r
Starting program: /Applications/TextEdit.app/Contents/MacOS/TextEdit
Reading symbols for shared libraries […]
Breakpoint 1, 0x940e36d6 in CFStringCreateWithFormat ()
(gdb) # We've stopped after the prologue.
(gdb) p/a $pc
$1 = 0x940e36d6
(gdb) # However, for 32-bit Intel we don't need to inspect
(gdb) # the prologue because the parameters are on the stack.
(gdb) # We can access them relative to EBP.
(gdb) #
(gdb) # first parameter is "alloc"
(gdb) p/a *(int*