In daily opening, we inevitably encounter some crashes. In most cases, Xcode helped us find the problem, but in some cases, Xcode gave us unreadable addresses that made analyzing the problem much more difficult.

Here are a few ways to make an unreadable address understandable.

symbolicatecrash

The dSYM file

DSYM is a transfer file that stores the address mapping information of hexadecimal functions. All symbols we debug are included in this file. A new dSYM file is generated each time a project is compiled, and we should save the dSYM file for each official release so that we can better debug problems. DSYM and.app files are stored in the Archives. Path is:

~/Library/Developer/Xcode/Archives
Copy the code

DSYM files are not generated in debug mode by default, we go to Build Settings -> Debug Information Format and modify DWARF to DWARF with dSYM File. To create a.dsym file, go directly to the Products directory of the project.

What is “Symbols”?

To quote from The Self-Cultivation of the Programmer:

In links, we refer to functions and variables collectively as symbols, and the function or variable Name is the Symbol Name. We can think of symbols as the glue in the linking process, and it is based on symbols that the whole linking process can be done correctly.

So, symbols are function names or variable names.

Find symbolicatecrash

Symbolicatecrash is Xcode’s own crash log analysis tool. We need to find it first:

find /Applications/Xcode.app -name symbolicatecrash -type f
Copy the code

Several paths will be returned after execution. Mine is:

/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/Library/PrivateFrameworks/DVTFou ndation.framework/symbolicatecrashCopy the code

Let’s go to this path and copy symbolicatecrash and put it in a folder.

Get the crash log file

We can write any code that forces a crash:

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
	NSArray *arr = @[];
	arr[100];
}
Copy the code

Then use the real machine to make a bag. After the package is packed, instead of using Xcode build, we can run the code that causes crash directly with the package, thus generating.crash log file.

After that, we go to Xcode -> Window -> Devices and Simulators or the shortcut Command + Shift + 2

Locate the.crash file for that point in time and right-click Export Log.

Get the.app file

The.app file can be compiled on the real machine and obtained from project Products or Archives.

Symbol resolution

Using the dSYM

Place.dsym,. Crash, and Symbolicatecrash in the same file and run:

/ symbolicatecrash. crash file path. dSYM File path > name. crashCopy the code

Using the app

Place.app,. Crash, and Symbolicatecrash in the same file and run the following command:

File path. app/appName Path > name. CrashCopy the code

Error may be reported:

Error: "DEVELOPER_DIR" is not defined at ./symbolicatecrash line 69.
Copy the code

Just execute the command:

export DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer
Copy the code

Then create a new.crash file.

We can compare unsymbolized and symbolized files. Here’s an example I tested myself, iPhone5, iOS 10.2, which might be different:

Last Exception Backtrace:
0   CoreFoundation                	0x1df60df2 __exceptionPreprocess + 126
1   libobjc.A.dylib               	0x1d1c3072 objc_exception_throw + 33
2   CoreFoundation                	0x1dee62f2 -[__NSArray0 objectAtIndex:] + 105
3   DreamDemo                     	0x0008088e 0x7c000 + 18574
4   UIKit                         	0x2319eb44 forwardTouchMethod + 289
5   UIKit                         	0x2319ea10 -[UIResponder touchesBegan:withEvent:] + 29
6   UIKit                         	0x23041c58 -[UIWindow _sendTouchesForEvent:] + 1599
7   UIKit                         	0x2303ca62 -[UIWindow sendEvent:] + 2657
8   UIKit                         	0x2300d870 -[UIApplication sendEvent:] + 315
9   UIKit                         	0x237a8998 __dispatchPreprocessedEventFromEventQueue + 2615
10  UIKit                         	0x237a25de __handleEventQueue + 829
11  CoreFoundation                	0x1df1c716 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 7
12  CoreFoundation                	0x1df1c220 __CFRunLoopDoSources0 + 433
13  CoreFoundation                	0x1df1a4f6 __CFRunLoopRun + 757
14  CoreFoundation                	0x1de6952e CFRunLoopRunSpecific + 481
15  CoreFoundation                	0x1de6933c CFRunLoopRunInMode + 99
16  GraphicsServices              	0x1f640bf8 GSEventRunModal + 151
17  UIKit                         	0x230789a2 -[UIApplication _run] + 569
18  UIKit                         	0x230730cc UIApplicationMain + 145
19  DreamDemo                     	0x000834cc 0x7c000 + 29900
20  libdyld.dylib                 	0x1d633506 _dyld_process_info_notify_release + 23
Copy the code

The problem is obvious, but because the third line (DreamDemo) is not symbolized, we are not sure exactly where to call it.

Let’s look at the symbolized:

Last Exception Backtrace:
0   CoreFoundation                	0x1df60df2 __exceptionPreprocess + 126
1   libobjc.A.dylib               	0x1d1c3072 objc_exception_throw + 33
2   CoreFoundation                	0x1dee62f2 -[__NSArray0 objectAtIndex:] + 105
3   DreamDemo                     	0x0008088e -[ViewController touchesBegan:withEvent:] + 18574 (ViewController.m:84)
4   UIKit                         	0x2319eb44 forwardTouchMethod + 289
5   UIKit                         	0x2319ea10 -[UIResponder touchesBegan:withEvent:] + 29
6   UIKit                         	0x23041c58 -[UIWindow _sendTouchesForEvent:] + 1599
7   UIKit                         	0x2303ca62 -[UIWindow sendEvent:] + 2657
8   UIKit                         	0x2300d870 -[UIApplication sendEvent:] + 315
9   UIKit                         	0x237a8998 __dispatchPreprocessedEventFromEventQueue + 2615
10  UIKit                         	0x237a25de __handleEventQueue + 829
11  CoreFoundation                	0x1df1c716 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 7
12  CoreFoundation                	0x1df1c220 __CFRunLoopDoSources0 + 433
13  CoreFoundation                	0x1df1a4f6 __CFRunLoopRun + 757
14  CoreFoundation                	0x1de6952e CFRunLoopRunSpecific + 481
15  CoreFoundation                	0x1de6933c CFRunLoopRunInMode + 99
16  GraphicsServices              	0x1f640bf8 GSEventRunModal + 151
17  UIKit                         	0x230789a2 -[UIApplication _run] + 569
18  UIKit                         	0x230730cc UIApplicationMain + 145
19  DreamDemo                     	0x000834cc main + 29900 (main.m:15)
20  libdyld.dylib                 	0x1d633506 _dyld_process_info_notify_release + 23
Copy the code

As you can see, the third line is parsed out, so that we can clearly see the specific page.

Use the command line tool ATos

Symbolicatecrash helps us analyze crash logs well, but it has limitations — it’s not flexible enough. We need symbolicatecrash,.crash, and.dsym files to parse.

The atos command is more flexible than symbolicatecrash, especially if you need to write an automated analysis script for crash files in different channels.

However, this approach also has an inconvenience: for an online App, users may use different versions, and each version corresponds to a different.dsym. To be properly symbolized, you must ensure that the.crash and.dsym files match, as long as they have the same UUID. Before that, a brief introduction to UUID:

What is a UUID?

A UUID is a set of 32 – digit hexadecimal numbers. Each executable program has a unique build UUID identifier. The crash log contains the build UUID of the application that crashed and the build UUID of all library files loaded by the application when the crash occurred.

Get UUID

.crash UUID

Execute command:

grep --after-context=2 "Binary Images:" *crash
Copy the code

Output:

T.crash:Binary Images:
T.crash-0x7c000 - 0x87fff DreamDemo armv7  <d009f8671129397a8aab9cb2b8e506ff> /var/containers/Bundle/Application/DEEBE571-D512-4E8F-B712-ED4D19CE64F9/DreamDemo.app/DreamDemo
T.crash-0xa9000 - 0xd4fff dyld armv7s  <cd60ff3403063c0aa8e54dff11e42527> /usr/lib/dyld
Copy the code

See the above output is d009f8671129397a8aab9cb2b8e506ff DreamDemo UUID of the project.

.dSYM UUID

Execute command:

dwarfdump --uuid DreamDemo.app.dSYM
Copy the code

Output:

UUID: D009F867-1129-397A-8AAB-9CB2B8E506FF (armv7) DreamDemo.app.dSYM/Contents/Resources/DWARF/DreamDemo
Copy the code

.app UUID

Execute command:

dwarfdump --uuid DreamDemo.app/DreamDemo
Copy the code

Output:

UUID: D009F867-1129-397A-8AAB-9CB2B8E506FF (armv7) DreamDemo.app/DreamDemo
Copy the code

You can find that the UUID of the two files is the same, that is, the matching condition, can be correctly parsed!

Atos parsing

Let’s review the unparsed stack:

2 CoreFoundation 0x1dee62f2 -[__NSArray0 objectAtIndex:] + 105 3 DreamDemo 0x0008088e 0x7c000 + 18574 4 UIKit 0x2319eb44  forwardTouchMethod + 289 5 UIKit 0x2319ea10 -[UIResponder touchesBegan:withEvent:] + 29Copy the code

Execute command:

xcrun atos -o DreamDemo.app.dSYM/Contents/Resources/DWARF/DreamDemo -arch armv7 -l 0x7c000
Copy the code

Then enter the address 0x0008088e, and the terminal output is as follows:

As you can see, the correct resolution comes out!

In addition to match.dSYMFiles, we can also use.appFile to parse:

Execute command:

xcrun atos -o DreamDemo.app/DreamDemo -arch armv7 -l 0x7c000
Copy the code

Also enter the address 0x0008088e, the effect is the same.

tool

After all, there is a tool on GitHub that can help us parse dSYMTools, a Mac client interface that looks like this:

It’s also easy to use, just drag the corresponding dSYM file in and it automatically recognizes the UUID. Our corresponding input parameter address is ok:

Symbolic parsing of system libraries

Careful people can find that our above analysis is for DreamDemo, this own project. In fact, many system method stacks can be resolved because there are already system library symbolic files, stored in the following directory:

/ User/User name XXX/Resource library /Developer/Xcode/iOS DeviceSupportCopy the code

The versions of these libraries correspond to those in the.crash file:

OS Version:          iPhone OS 10.2 (14C5077b)
Copy the code

Once I delete the 10.2 (14C5077b) system’s symbolic library, the.crash file will look like this:

Last Exception Backtrace: 0 CoreFoundation 0x1df60df2 0x1de5f000 + 1056242 1 libobjc.A.dylib 0x1d1c3072 0x1d1bc000 + 28786 2 CoreFoundation 0x1dee62f2 0x1de5f000 + 553714 3 DreamDemo 0x000bc66e -[ViewController touchesBegan:withEvent:] + 18030 (ViewController.m:78) 4 UIKit 0x2319eb44 0x22ffe000 + 1706820 5 UIKit 0x2319ea10 0x22ffe000 + 1706512 6 UIKit 0x23041c58  0x22ffe000 + 277592 7 UIKit 0x2303ca62 0x22ffe000 + 256610 8 UIKit 0x2300d870 0x22ffe000 + 63600 9 UIKit 0x237a8998 0x22ffe000 + 8038808 10 UIKit 0x237a25de 0x22ffe000 + 8013278 11 CoreFoundation 0x1df1c716 0x1de5f000 + 775958 12 CoreFoundation 0x1df1c220 0x1de5f000 + 774688 13 CoreFoundation 0x1df1a4f6 0x1de5f000 + 767222 14 CoreFoundation 0x1de6952e 0x1de5f000 + 42286 15 CoreFoundation 0x1de6933c 0x1de5f000 + 41788 16 GraphicsServices 0x1f640bf8 0x1f637000 + 39928 17 UIKit 0x230789a2 0x22ffe000 + 502178 18 UIKit 0x230730cc 0x22ffe000 + 479436 19 DreamDemo 0x000bf332 main + 29490 (main.m:15) 20 libdyld.dylib 0x1d633506 0x1d630000 + 13574Copy the code

It is obvious that the system library stack has become a stack of addresses.

The new version, whenever our phone is connected to Xcode, will automatically import the current version of the system symbol library into/user/user name XXX/repository /Developer/Xcode/iOS DeviceSupport directory. But there are so many iOS versions, how to get the old system symbol library before? Someone has sorted out ios-system-Symbols, so we just need to download the corresponding System symbolic file to the directory according to the version information of the. Crash file.

conclusion

  • Using symbolicatecrash resolution, you can parse the entire.crashThe log stack resolves, but due to dependenciessymbolicatecrash,.crashAs well as.dSYMThree files, or.app.crashsymbolicatecrashThree files, resulting in less flexibility.
  • usingatosThe command just needs to.crashand.dSYMOr,.crashand.app, the corresponding stack address can be parsed, which is convenient for automatic script analysis. However, crash stack may need to be collected by itself.

reference

Wufawei.com/2014/03/sym…