Aleksandar • Vacić

iOS bits and pieces

How to symbolicate watchOS 2 extension crash logs from device

I’m working on the next version of Run 5k app, which includes standalone watchOS 2 app. In current Xcode 7 beta 4, it takes 30-40mins for the watch .app bundle to get copied from iPhone to the watch device. No idea why it takes so long; it’s even called out as issue in Xcode 7 beta 4 release notes (they promise it will be resolved in future seeds).

The main problem with this is that debugging is next to impossible. Due to this you can’t realistically debug with the devices attached to Xcode so the only way is to install the iOS app, let it copy the watch bundle over and then use the app once it’s done.

My last-night test ended in a very repeatable crash. Great. Crashes from the Apple Watch are always copied to its paired iPhone and you can get them by connecting the iPhone, then opening Windows / Devices in Xcode, then tap “Show device logs” button.

Xcode then goes and symbolicate each .crash log file for you. However, in my case it symbolicated everything except my own code.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
Incident Identifier: A50E4375-5E56-4B10-8594-47BCCDCEFA6C
CrashReporter Key:   61260f79cd0de5738a2dab7597318652dbea0943
Hardware Model:      Watch1,2
Process:             Go5k watchOS2 Extension [392]
Path:                /private/var/mobile/Containers/Bundle/Application/92A76ADB-6724-46B8-A4AD-8290A3FEC97F/com.codeaplus.Couchto5k.watchkitapp.app/PlugIns/Go5k watchOS2 Extension.appex/Go5k watchOS2 Extension
Identifier:          com.codeaplus.Couchto5k.watchkitapp.watchkitextension
Version:             2000 (6.0)
Code Type:           ARM (Native)
Parent Process:      launchd [1]

Date/Time:           2015-08-05 21:53:12.12 +0200
Launch Time:         2015-08-05 21:49:58.58 +0200
OS Version:          Watch OS 2.0 (13S5305d)
Report Version:      105

Exception Type:  EXC_CRASH (SIGABRT)
Exception Codes: 0x0000000000000000, 0x0000000000000000
Exception Note:  EXC_CORPSE_NOTIFY
Triggered by Thread:  0

Filtered syslog:
None found

Last Exception Backtrace:
0   CoreFoundation                    0x2e9fa2de __exceptionPreprocess + 122
1   libobjc.A.dylib                   0x3ad36d1e objc_exception_throw + 34
2   CoreFoundation                    0x2e9ffd08 -[NSObject(NSObject) doesNotRecognizeSelector:] + 188
3   CoreFoundation                    0x2e9fd5ee ___forwarding___ + 662
4   CoreFoundation                    0x2e93226c _CF_forwarding_prep_0 + 28
5   Go5k watchOS2 Extension           0x00044720 0x20000 + 149280
6   Go5k watchOS2 Extension           0x00035c5e 0x20000 + 89182
7   Go5k watchOS2 Extension           0x0002e4ce 0x20000 + 58574
8   WatchKit                          0x32f38100 __48-[SPRemoteInterface handlePlist:fromIdentifier:]_block_invoke_2749 + 52
9   libdispatch.dylib                 0x3b4218de _dispatch_call_block_and_release + 6
10  libdispatch.dylib                 0x3b4218ba _dispatch_client_callout + 2
11  libdispatch.dylib                 0x3b421590 _dispatch_main_queue_callback_4CF$VARIANT$up + 1464
12  CoreFoundation                    0x2e9bf0aa __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 6
13  CoreFoundation                    0x2e9bd596 __CFRunLoopRun + 1550
14  CoreFoundation                    0x2e913908 CFRunLoopRunSpecific + 376
15  Foundation                        0x2f754df6 -[NSRunLoop(NSRunLoop) runMode:beforeDate:] + 254
16  Foundation                        0x2f7a02c4 -[NSRunLoop(NSRunLoop) run] + 76
17  libxpc.dylib                      0x3b62a0ba _xpc_objc_main + 606
18  libxpc.dylib                      0x3b62b7de xpc_main + 166
19  Foundation                        0x2f8ecf7c -[NSXPCListener resume] + 160
20  PlugInKit                         0x3771b8e8 -[PKService run] + 516
21  WatchKit                          0x32f54e72 main + 130
22  libdyld.dylib                     0x3b46b8c6 tlv_get_addr + 58

I did not have .xcarchive since I directly installed the iOS build to the iPhone. However, all the build files were still there (I have not done any build nor build & run afterwards), yet Xcode was apparently not trying to use them.

Here’s how to manually symbolicate the problematic lines.

(1) First export the .crash log to some folder.

(2) Then open Window/Projects, tap your project then tap the little arrow next to derived data for the project

(3) Open Debug-watchos folder and copy the Go5k watchOS2 Extension.appex file to the same folder where you .crash log file is.

(4) Open Terminal in that folder and first make sure that .appex file is the same one which generated the crash. You do this by comparing the UUIDs for the file with the one from the log file.

In the .crash log file, look for the Binary Images section below all the Thread X stuff. First line is your extension:

1
2
Binary Images:
0x20000 - 0x4bfff Go5k watchOS2 Extension armv7k  <18a5bf9fc40934f2829b9bc29acb8f21> /var/mobile/Containers/Bundle/Application/92A76ADB-6724-46B8-A4AD-8290A3FEC97F/com.codeaplus.Couchto5k.watchkitapp.app/PlugIns/Go5k watchOS2 Extension.appex/Go5k watchOS2 Extension

This tells you two things:

  • extension’s architecture is armv7k
  • UUID is following it: <18a5bf9fc40934f2829b9bc29acb8f21>

Now use dwarfdump tool to extract UUID from the file itself:

1
2
3
4
5
$ /Applications/Xcode-beta.app/Contents/Developer/Platforms/WatchOS.platform/
Developer/usr/bin/dwarfdump —uuid 
Go5k\ watchOS2\ Extension.appex/Go5k\ watchOS2\ Extension

UUID: 18A5BF9F-C409-34F2-829B-9BC29ACB8F21 (armv7k) Go5k watchOS2 Extension.appex/Go5k watchOS2 Extension

(Of course the terminal command goes all in one line, I added breaks so it’s easier to read)
Not sure is it relevant but I specifically used dwarfdump inside WatchOS.platform, just to be safe it understands .appex format.

Ok, 18A5BF9F-C409-34F2-829B-9BC29ACB8F21 == 18a5bf9fc40934f2829b9bc29acb8f21, architectures are the same, all is fine.

(5) Now let’s symbolicate. My crash lines are these:

1
2
3
5   Go5k watchOS2 Extension          0x00044720 0x20000 + 149280
6   Go5k watchOS2 Extension           0x00035c5e 0x20000 + 89182
7   Go5k watchOS2 Extension           0x0002e4ce 0x20000 + 58574

The 0x20000 is the load address for the extension in memory. 0x00044720 is the actual stack address where it crashed.

Using those two addresses, atos can symbolicate the code but you need to revert their order. To be safe, I specifically used atos from the Xcode 7 beta:

1
2
3
4
5
6
$ /Applications/Xcode-beta.app/Contents/Developer/usr/bin/atos 
-arch armv7k 
-o Go5k\ watchOS2\ Extension.appex/Go5k\ watchOS2\ Extension 
-l 0x20000 0x00044720

-[RTTableRowInfoController populateWithData:] (in Go5k watchOS2 Extension) (RTTableRowInfoController.m:29)

There you go. Hope it helps, I see a lot of confused and outdated answers regarding this on Stack Overflow.