Using debugDescription with GCD and XPC objects
By: . Published: . Categories: gcd xpc obj-c debugging.dispatch_debug
and xpc_copy_description
are inconvenient,
particularly during impromptu debugging.
Mountain Lion’s Obj-C-ification of GCD and XPC objects lets you use your comfortable Obj-C tools:
NSLog
with%@
,- the
debugDescription
method, and po obj
while in the debugger.
dispatch_debug
If you first learned GCD back before Mountain Lion,
you might have played around with the dispatch_debug
function:
void
dispatch_debug(dispatch_object_t object, const char *message, ...);
This function is the NSLog
of Grand Central Dispatch land.
If you need to pin down what exactly is going on with a complex
network of dispatch objects, this can be a useful tool,
especially since you can use the libdispatch
source code
to illuminate the more cryptic debug info.
But it’s also kind of annoying: unless you remembered to set
LIBDISPATCH_LOG=stderr
in the environment before starting your
process, you’ll have to watch the system log for your
dispatch_debug
output;
it won’t show up in Xcode’s debug console.
Changing the
value of the environment variable after startup
also doesn’t seem to affect dispatch_debug
's behavior,
so by the time you realize you’ve forgotten to set this
environment variable,
it’s already too late.
xpc_copy_description
If you wanted to log information about an XPC object without leaking,
you used to have to xpc_copy_description
,
log the string,
then free
the returned pointer when you’re done with it:
char *desc = xpc_copy_description(obj);
NSLog(@"%s: xpc obj %p %s", __func__, obj, desc);
free(desc);
debugDescription
Well, good news: As of Mountain Lion,
GCD and XPC objects are all also
NSObject
s, so you can use them
as the target for the %@
format specifier
and as the target for the -debugDescription
instance method. The latter dumps
all the information you used to get from
dispatch_debug
.
As an example:
2013-01-08 23:53:13.451 debug[80089:707] dispatch queue: description:
<OS_dispatch_queue: com.jeremywsherman.demo[0x7f8980c07f80]>
2013-01-08 23:53:13.453 debug[80089:707] dispatch queue: debugDescription:
<OS_dispatch_queue: com.jeremywsherman.demo[0x7f8980c07f80] = {
xrefcnt = 0x2, refcnt = 0x1, suspend_cnt = 0x0, locked = 0,
target = com.apple.root.default-priority[0x7fff72c47d00],
width = 0x7fffffff, running = 0x0, barrier = 0 }>
XPC objects are pretty verbose even with description
, but you get
a bit – sometimes quite a bit – more info if you send debugDescription
:
2013-01-08 23:53:13.453 debug[80089:707] xpc connection: description:
<OS_xpc_connection: <connection: 0x7f8980e017f0> {
name = com.jeremywsherman.conn, listener = false,
PID = 0, EUID = 4294967295,
EGID = 4294967295, ASID = 4294967295 }>
2013-01-08 23:53:13.454 debug[80089:707] xpc connection: debugDescription:
<OS_xpc_connection: connection[0x7f8980e017f0]: {
refcnt = 1, xrefcnt = 2,
name = com.jeremywsherman.conn, type = named, state = new,
queue = 0x7f8980e00420->0x0, error = 0x0, mach = false,
privileged = false, bssendp = 0x0, recvp = 0x0, sendp = 0x0,
pid/euid/egid/asid = 0/4294967295/4294967295/4294967295 }
<connection: 0x7f8980e017f0> {
name = com.jeremywsherman.conn, listener = false,
PID = 0, EUID = 4294967295, EGID = 4294967295, ASID = 4294967295 }>
2013-01-08 23:53:13.454 debug[80089:707] xpc bool: description:
<OS_xpc_bool: <bool: 0x7fff7244d320>: true>
2013-01-08 23:53:13.455 debug[80089:707] xpc bool: debugDescription:
<OS_xpc_bool: bool[0x7fff7244d320]: {
refcnt = 80000000, xrefcnt = 80000000, value = true }
<bool: 0x7fff7244d320>: true>
The odd trailer to the XPC objects’ debug descriptions is not a typo – the XPC objects really do include their regular description as a component of their debug description.
print-object (po)
debugDescription
also happens to be what
gets printed when you print-object
(or po
for short) an object
while debugging.
Treating a GCD/XPC object as a regular
Objective-C object is particularly
handy during impromptu debugging,
since you no longer need to futz about
with dispatch_debug
and xpc_copy_description
.
Instead, just use po obj
when debugging:
% lldb ./debug
(lldb) Current executable set to './debug' (x86_64).
b debug.m:34
breakpoint set --file 'debug.m' --line 34
Breakpoint created: 1: file ='debug.m', line = 34, locations = 1
(lldb) r
Process 80337 launched: '/Users/jeremy/Documents/Blog/GCDTips/debug' (x86_64)
Process 80337 stopped
* thread #1: tid = 0x1c03, 0x0000000100000db7 debug`main + 135
at debug.m:34, stop reason = breakpoint 1.1
frame #0: 0x0000000100000db7 debug`main + 135 at debug.m:34
31 Log(@"xpc connection", conn);
32
33 xpc_object_t pred = xpc_bool_create(true);
-> 34 Log(@"xpc bool", pred);
35 }
36 return 0;
37 }
(lldb) fr var
(dispatch_queue_t) q = 0x0000000100107fa0
(xpc_connection_t) conn = 0x0000000100400830
(xpc_object_t) pred = 0x00007fff7244d320
(lldb) po q
(dispatch_queue_t) $0 = 0x0000000100107fa0 <OS_dispatch_queue:
com.jeremywsherman.demo[0x100107fa0] = { xrefcnt = 0x1, refcnt = 0x2,
suspend_cnt = 0x0, locked = 0, target =
com.apple.root.default-priority[0x7fff72c47d00], width = 0x7fffffff,
running = 0x0, barrier = 0 }>
(lldb) po conn
(xpc_connection_t) $1 = 0x0000000100400830 <OS_xpc_connection:
connection[0x100400830]: { refcnt = 1, xrefcnt = 1,
name = com.jeremywsherman.conn, type = named, state = new,
queue = 0x100400530->0x0, error = 0x0, mach = false, privileged = false,
bssendp = 0x0, recvp = 0x0, sendp = 0x0,
pid/euid/egid/asid = 0/4294967295/4294967295/4294967295 }
<connection: 0x100400830> { name = com.jeremywsherman.conn,
listener = false, PID = 0, EUID = 4294967295, EGID = 4294967295,
ASID = 4294967295 }>
(lldb) po pred
(xpc_object_t) $2 = 0x00007fff7244d320 <OS_xpc_bool: bool[0x7fff7244d320]:
{ refcnt = 80000000, xrefcnt = 80000000, value = true } <bool:
0x7fff7244d320>: true>