锐单电子商城 , 一站式电子元器件采购平台!
  • 电话:400-990-0325

你真的了解符号化么?

时间:2023-05-04 08:37:00 703f振动变送器

f9ef8999172fb8f1e7241a05af7e90a9.png

本文字数:3946

预计阅读时间:8分钟

iOS下的符号化

前情提要

?

关于符号化,我想iOS开发相关人员并不陌生,也经常处理日常开发,在线符号文章可以说飞,但没有文章可以说很全面,所以文章的诞生,当然,这也是作者在工作中的一些总结,如果有任何问题,请联系:

weiniu@sohu-inc.com

以下文章的符号化是针对以下文章的iOS下面,例子也是iOS下的Project

目录表

  • 什么是符号化?

    • 符号化程度

  • 为什么要符号化?

  • 崩溃文件分析

    • Crash分析日志各部分

  • 符号化流程

    • 符号化所需文件

  • 符号化工具

    • symbolicatecrash

    • atos

  • 扩伸延展

  • 参考文档

什么是符号化?

  • 关于符号化

    • 一般来说,符号化是将一些机器语言转化为人类可读符号,而在这里的环境中,则是指iOS或者Mac OS一些异常信息(16进制符号表示)通过某些方式转换为开发人员可读的高级代码片段,从而进一步定位异常来源,快速修复

符号化程度

  • 完全符号化

Thread0name:Dispatchqueue:com.apple.main-thread Thread0Crashed: 0libswiftCore.dylib0x00000001bd38da70specialized_fatalErrorMessage 2378352(_:_:file:line:flags:) 384 1libswiftCore.dylib0x00000001bd38da70specialized_fatalErrorMessage 2378352(_:_:file:line:flags:) 384 2libswiftCore.dylib0x00000001bd15958c_ArrayBuffer._checkInoutAndNativeTypeCheckedBounds 66956(_:wasNativeTypeChecked:) 200 3libswiftCore.dylib0x00000001bd15c814Array.subscript.getter 88 4TouchCanvas0x00000001022cbfa8Line.updateRectForExistingPoint(_:)(inTouchCanvas) 656 5TouchCanvas0x00000001022c90b0Line.updateWithTouch(_:)(inTouchCanvas) 464 6TouchCanvas0x00000001022e7374CanvasView.updateEstimatedPropertiesForTouches(_:)(inTouchCanvas) 708 7TouchCanvas0x00000001022df754ViewController.touchesEstimatedPropertiesUpdated(_:)(inTouchCanvas) 304 8TouchCanvas0x00000001022df7e8@objcViewController.touchesEstimatedPropertiesUpdated(_:)(inTouchCanvas) 120 9UIKitCore0x00000001b3da6230forwardMethod1 136 10UIKitCore0x00000001b3da6230forwardMethod1 136 11UIKitCore0x00000001b3e01e24-[_UIEstimatedTouchRecorddispatchUpdateWithPressure:stillEstimated:] 340
  • 部分符号化

Thread0name:Dispatchqueue:com.apple.mainthread
Thread 0 Crashed:
0   libswiftCore.dylib                0x00000001bd38da70 specialized _fatalErrorMessage+ 2378352 (_:_:file:line:flags:) + 384
1   libswiftCore.dylib                0x00000001bd38da70 specialized _fatalErrorMessage+ 2378352 (_:_:file:line:flags:) + 384
2   libswiftCore.dylib                0x00000001bd15958c _ArrayBuffer._checkInoutAndNativeTypeCheckedBounds+ 66956 (_:wasNativeTypeChecked:) + 200
3   libswiftCore.dylib                0x00000001bd15c814 Array.subscript.getter + 88
4   TouchCanvas                       0x00000001022cbfa8 0x1022c0000 + 49064
5   TouchCanvas                       0x00000001022c90b0 0x1022c0000 + 37040
6   TouchCanvas                       0x00000001022e7374 0x1022c0000 + 160628
7   TouchCanvas                       0x00000001022df754 0x1022c0000 + 128852
8   TouchCanvas                       0x00000001022df7e8 0x1022c0000 + 129000
9   UIKitCore                         0x00000001b3da6230 forwardMethod1 + 136
10  UIKitCore                         0x00000001b3da6230 forwardMethod1 + 136
11  UIKitCore                         0x00000001b3e01e24 -[_UIEstimatedTouchRecord dispatchUpdateWithPressure:stillEstimated:] + 340
  • 未符号化

Thread 0 name:  Dispatch queue: com.apple.main-thread
Thread 0 Crashed:
0   libswiftCore.dylib                0x00000001bd38da70 0x1bd149000 + 2378352
1   libswiftCore.dylib                0x00000001bd38da70 0x1bd149000 + 2378352
2   libswiftCore.dylib                0x00000001bd15958c 0x1bd149000 + 66956
3   libswiftCore.dylib                0x00000001bd15c814 0x1bd149000 + 79892
4   TouchCanvas                       0x00000001022cbfa8 0x1022c0000 + 49064
5   TouchCanvas                       0x00000001022c90b0 0x1022c0000 + 37040
6   TouchCanvas                       0x00000001022e7374 0x1022c0000 + 160628
7   TouchCanvas                       0x00000001022df754 0x1022c0000 + 128852
8   TouchCanvas                       0x00000001022df7e8 0x1022c0000 + 129000
9   UIKitCore                         0x00000001b3da6230 0x1b3348000 + 10871344
10  UIKitCore                         0x00000001b3da6230 0x1b3348000 + 10871344
11  UIKitCore                         0x00000001b3e01e24 0x1b3348000 + 11247140

为什么要符号化

  • 通过上一个问题可以得知,为了让开发人员可以快速找到异常堆栈,从而定位问题,解决问题,保证程序的良好运行

崩溃文件解析

  • 工具善其事,必先利其器。你想解决某件事,你首先得清楚这件事是如何构成的,以及每个部分代表什么,接下来我们来聊一聊崩溃日志文件中的各个字段的含义

  • 下面是某个项目中的一段原始崩溃日志截取

Incident Identifier: xxxxx-xxxx-xxxx-xxxx-89D2A9086DFE
CrashReporter Key:   xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Hardware Model:      iPhone10,3
Process:             bdnews [22785]
Path:                /private/var/containers/Bundle/Application/7FFC365D-8DBE-4B2E-9E6B-372047CA2A7A/bdnews.app/bdnews
Identifier:          com.bd.newspaper.inhouse
Version:             224.1 (6.6.20)
Code Type:           ARM-64 (Native)
Role:                Foreground
Parent Process:      launchd [1]
Coalition:           com.bd.newspaper.inhouse [2953]


Date/Time:           2021-07-08 08:04:46.6235 +0800
Launch Time:         2021-07-08 07:12:05.1466 +0800
OS Version:          iPhone OS 14.1 (18A8395)
Release Type:        User
Baseband Version:    6.02.01
Report Version:      104

Exception Type:  EXC_CRASH (SIGKILL)
Exception Codes: 0x0000000000000000, 0x0000000000000000
Exception Note:  EXC_CORPSE_NOTIFY
Termination Reason: Namespace SPRINGBOARD, Code 0x8badf00d
Termination Description: SPRINGBOARD, :22785 exhausted real (wall clock) time allowance of 10.00 seconds | ProcessVisibility: Foreground | ProcessState: Running | WatchdogEvent: scene-update | WatchdogVisibility: Background | WatchdogCPUStatistics: ( | "Elapsed total CPU time (seconds): 6.730 (user 6.730, system 0.000), 11% CPU", | "Elapsed application CPU time (seconds): 0.448, 1% CPU" | ) reportType:CrashLog maxTerminationResistance:Interactive>
Triggered by Thread:  0

Thread 0 name:  Dispatch queue: com.apple.main-thread
Thread 0 Crashed:
0   libsystem_kernel.dylib         0x00000001b8d48900 semaphore_wait_trap + 8
1   libdispatch.dylib              0x000000018de4ec3c _dispatch_sema4_wait$VARIANT$armv81 + 24
2   libdispatch.dylib              0x000000018de4f28c _dispatch_semaphore_wait_slow + 128
3   CFNetwork                      0x000000018e89fddc CFURLConnectionSendSynchronousRequest + 352
4   CFNetwork                      0x000000018e81d9cc 0x18e81a000 + 14796
5   Foundation                     0x000000018f4008a0 -[NSData+ 247968 (NSData) initWithContentsOfURL:] + 228
6   ImageIO                        0x000000018f6ff110 IIOImageSource::IIOImageSource+ 655632 (__CFURL const*, IIODictionary*) + 608
7   ImageIO                      0x000000018f703f98 CGImageSourceCreateWithURL + 196
8   bdunews                       0x0000000104a2d620 0x10412c000 + 9442848
9   bdnews                       0x0000000104a2d43c 0x10412c000 + 9442364
10  bdnews                       0x0000000105511c68 0x10412c000 + 20864104
11  bdnews                       0x00000001055109cc 0x10412c000 + 20859340
12  bdnews                       0x0000000104442d50 0x10412c000 + 3239248
13  bdnews                       0x0000000104b6a3ac 0x10412c000 + 10740652
14  bdnews                       0x0000000104b6c658 0x10412c000 + 10749528
15  bdnews                       0x0000000104b669fc 0x10412c000 + 10725884
16  bdnews                       0x0000000104be62bc 0x10412c000 + 11248316
17  bdnews                       0x0000000104be4980 0x10412c000 + 11241856
18  bdnews                       0x0000000105acfd44 0x10412c000 + 26885444
19  bdnews                       0x0000000105aea2dc 0x10412c000 + 26993372
20  libdispatch.dylib              0x000000018de7d298 _dispatch_call_block_and_release + 24
21  libdispatch.dylib              0x000000018de7e280 _dispatch_client_callout + 16
22  libdispatch.dylib              0x000000018de60608 _dispatch_main_queue_callback_4CF$VARIANT$armv81 + 936
23  CoreFoundation                 0x000000018e1c4c30 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 12
24  CoreFoundation                 0x000000018e1bf0e8 __CFRunLoopRun + 2480
25  CoreFoundation                 0x000000018e1be200 CFRunLoopRunSpecific + 572
26  GraphicsServices               0x00000001a433b598 GSEventRunModal + 160
27  UIKitCore                      0x0000000190a87bcc -[UIApplication _run] + 1052
28  UIKitCore                      0x0000000190a8d1a0 UIApplicationMain + 164
29  bdnews                       0x0000000105310464 0x10412c000 + 18760804
30  libdyld.dylib                  0x000000018de9d588 start + 4

...

Binary Images:
0x10412c000 - 0x106f0ffff sohunews arm64  <255d5329b68c378a834d829abcfdbd42> /var/containers/Bundle/Application/7FFC365D-8DBE-4B2E-9E6B-372047CA2A7A/sohunews.app/sohunews
0x109c60000 - 0x109c6bfff libobjc-trampolines.dylib arm64   /usr/lib/libobjc-trampolines.dylib
0x109cc8000 - 0x109d33fff dyld arm64  <90ec9373b0653c5b986074202c172829> /usr/lib/dyld
0x109db4000 - 0x109e1ffff RevealServer arm64   /var/containers/Bundle/Application/7FFC365D-8DBE-4B2E-9E6B-372047CA2A7A/sohunews.app/Frameworks/RevealServer.framework/RevealServer

...

Crash日志各部分分析

  • Crash日志信息头

    • Incident Identifier:崩溃日志的唯一标识符,两份报告从来不会相同

    • CrashReporter Key:崩溃日志的关键字

    • Hardware Model: 设备类型,iPhone10,3

    • Process:进程名称,通常是ipa中可执行文件的名称,对应Xcode项目中的"product name"

    • Path:设备存储中应用可执行文件的路径

    • Identifier: 应用的bundle id

    • Version: 应用的Build (Version),也就是CFBundleVersion

    • Code Type: 可执行文件对应的CPU架构类型,这里是ARM64处理器

    • Role:在应用终止的时分配给该进程的任务角色,对于分析报告来说,这个字段没有用

    • Parent Process: 父进程,通常是launch id之类的,如果是连接xcode调试则可能是debug process之类的

    • Coalition:包含该应用程序的进程的联盟的名称

    • Date/Time: 崩溃的时间

    • OS Version: 设备操作系统版本

    • Report Version: Crash report的版本,不同版本crash信息格式有些差别

    • Exception Type:异常类型,EXC_CRASH (SIGKILL),这段代码解读为,在mach内核环境下发生exc_crash的异常,然后被unix系统下的sigkill信号捕获

    • Exception Codes: 关于异常的处理器特定信息,编码为一个或多个64位十六进制数字。通常情况下,这个字段不会出现,因为操作系统在本部分的其他字段中把信息作为可读的信息

    • Exception Subtype: 异常代码的可读部分

    • Exception Message: 从异常代码中提取的可读信息

    • Exception Note: 不是特定于一种异常类型的附加信息。如果这个字段中包含了EXC_CORPSE_NOTIFY,那么这个崩溃并不是来自于硬件陷阱(Trap),要么是因为进程被操作系统终止,要么是进程调用了abort(),如果这个字段包含了SIMULATED这不是一个崩溃,进程没有崩溃,但是操作系统随后可能要求终止该进程,如果这个字段包含NON-FATAL CONDITION,这也不是一个崩溃,进程没有终止,因为产生崩溃报告得到的问题并不是致命的。

    • Termination Reason: 当操作系统终止一个进程时指定退出的原因信息。关键的操作系统组件,包括进程内部和外部,在遇到致命错误时终止进程,并在这个字段中记录原因。你可以在这个字段中找到相关信息,比如一个无效的代码签名,一个缺失的依赖库,或者在没有相关字符串的情况下访问隐私信息等

    • Triggered by Thread or Crashed Thread: 发生异常的线程

  • 崩溃栈 (从左到右)

    图-401
    • 0.调用栈序号

    • 1.对应二进制的镜像(image)名称,包含应用的可执行文件,系统动态库,framework中的二进制文件等

    • 2.调用函数在相应镜像文件中的地址,这里也包含三部分(从左到右)

      • 2.0.调用函数的地址,十六进制数字组成

      • 2.1.可执行指令部分相对镜像文件中的起始加载地址,十六进制数字组成

      • 2.2.函数的地址地相对于加载地址的偏移量,由十进制组成,其实这的关系就是这部分的地址+第二部分地址等于第一部分地址

  • 镜像文件信息,分为六个部分 (从左到右)

    • 二进制图像在进程中的地址范围。该地址是二进制的加载地址

    • 这个地址是和上一个地址组成了二进制图像的进程地址范围

    • 镜像文件的名称

    • CPU架构类型,跟信息头的Code Type表示一样

    • 镜像文件的UUID,这个是镜像文件的唯一标识,同时也是对应的符号文件的UUID,可以用来判断符号文件是否正确

    • 镜像文件在设备中的路径


符号化流程

符号化所需文件

  • 符号化需要崩溃日志文件,dSYM文件以及符号化工具,这3个元素缺一不可,你需要提前准备好,任何第三方的工具也都需要这些元素,只是表现形式不同而已

  • 崩溃文件

    • 文件表现形式:通常崩溃文件都是以xxx.crash结尾的文件呈现

    • 获取方式:获取的方式分调试开发以及线上进行不同的方式获取,首先如果是开发测试阶段,可以通过Xcode编译工具从测试手机导出,当然你也可以先自行在目标手机上看是否存在你需要的崩溃,查看方式就是设置-隐私-分析与改进-分析数据这个文件下就是你整部手机的一些异常信息,也是Jetsam机制产生的(有兴趣可以继续深入了解下Jetsam机制,这个与本文无关,暂且忽略哈)

    • 开发测试导出

      • Xcode-Window-Devices and simulators-View Device Logs(左侧工具栏选中你要导出的目的设备)

线上获取导出

  • 如果是线上问题出现了,比如一些可以被信号量捕获的问题,就可以在Xcode以相同的方式导出,但是还有一些不能捕获的崩溃就需要我们从代码层面进行控制,然后存储上报,具体方式根据自己的业务进行制定即可。

dSYM文件

  • dSYM是一个调试符号文件(debug symbols file),它是在你的项目中构建设置中启用Strip Debug Symbols开启时生成的。

  • 生成流程:当这个设置Strip Debug Symbols开启时,你的对象的符号名称会从编译后的二进制文件中删除,这个是为了防止黑客对你的代码进行逆向工程而采取的措施之一,该文件每次编译时都会发生变化,因为时间戳不同,与项目设置无关

  • 作用:崩溃文件只是告诉你了内存的虚拟地址和加载地址,dSYM文件则辅助的定位到了对象的调用堆栈,并允许开发者可读

  • 如何获取dSYM,分为Bitcode EnabledBitcode disabled

  • Bitcode Enabled

    • 你首先要连接到App Store Connect

    • 打开App详情页面

    • 点击活动

    • 从 "所有构建 "列表中,选择一个与崩溃相匹配的版本

    • 点击dSYM的下载链接

  • Bitcode disabled

    • 打开Xcode

    • 点击Window > Organizer

    • 从 "所有构建 "列表中,选择一个与崩溃相匹配的版本

    • 右击并点击Show in Finder

    • .xcarchive右击文件并点击显示包内容

    • 发现.app.dSYMdSYM文件下发现该文件

符号化工具

symbolicatecrash

  • symbolicatecrash工具的查找

该工具是Apple提供的一个命令行工具,你通过以下的方式找到它

备注:以下所有xxx代表工程名字

// find symbolicatecrash for Applications
$ sudo find /Applications -name symbolicatecrash

/**
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/iOSSupport/Library/PrivateFrameworks/DVTFoundation.framework/Versions/A/Resources/symbolicatecrash
/Applications/Xcode.app/Contents/Developer/Platforms/WatchSimulator.platform/Developer/Library/PrivateFrameworks/DVTFoundation.framework/symbolicatecrash
/Applications/Xcode.app/Contents/Developer/Platforms/AppleTVSimulator.platform/Developer/Library/PrivateFrameworks/DVTFoundation.framework/symbolicatecrash
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/Library/PrivateFrameworks/DVTFoundation.framework/symbolicatecrash
/Applications/Xcode.app/Contents/SharedFrameworks/DVTFoundation.framework/Versions/A/Resources/symbolicatecrash
*/

// find the path which contains DVTFoundation.framework and copy the path to the clipboard

// Set the DEVELOPER_DIR environment variable. Write this command
$ export DEVELOPER_DIR=”/Applications/Xcode.app/Contents/Developer”

// The last step process
// This tool takes two arguments. The first argument is the path to your crash file. The second argument is the path to the dSYM file. So, the final command may look like 

// run the tool
$symbolicatecrash -d  -o  



$/Applications/Xcode.app/Contents/SharedFrameworks/DVTFoundation.framework/Versions/A/Resources/symbolicatecrash xxx.crash xxxApp.dSYM > Custom.crash

For symbolicatecrash detail use /path/symbolicatecrash --help


  • 符号解析无效果

如果在符号化过程中没有效果或者无法符号化,那就要比较你当前的dSYM的uuid是否和崩溃文件中镜像文件部分列出的构建uuid相匹配

// find uuid for dSYM
$ dwarfdump –uuid xxxApp.dSYM/Contents/Resources/DWARF/xxxApp
/**
// It looks that
UUID: 1A36EBE0-AE8D-3A2F-8D76-2B786C707305 (armv7) xxx.app.dSYM/Contents/Resources/DWARF/xxx
UUID: 9B8D679D-C302-31DA-9573-A8D5A6588627 (arm64) xxx.app.dSYM/Contents/Resources/DWARF/xxx
*/

// find uuid for Binary Images 
$ dwarfdump --uuid 
$ dwarfdump --uuid /usr/lib/libobjc-trampolines.dylib
 
/**
// It looks that
UUID: 1BFE69F2-6C6C-3E82-A8CA-781EE7C87C4B (x86_64) /usr/lib/libobjc-trampolines.dylib
UUID: 361143B8-E66E-3402-85B5-C20893AAB9C9 (x86_64h) /usr/lib/libobjc-trampolines.dylib
UUID: 3358BF92-85AC-3174-9344-C99A5EA74E6B (arm64e) /usr/lib/libobjc-trampolines.dylib
 
 // The resulf of run all platoms for uuid,then check someone platom of crash log and dSYM's uuid
*/

atos

  • 关于atos

    • atos命令将十六进制地址转换为源代码中可识别的函数名称和行号

  • 命令格式

atos -arch  -o /Contents/Resources/DWARF/ -l  

// explain parameters
load adress:可执行指令部分相对镜像文件中的起始加载地址
address to symbolicate:调用函数的地址


atos -o xxx.app.dSYM/Contents/Resources/DWARF/xxx -l 0x00000001c4fe7000 -arch arm64

atos -arch arm64 -o  xxx.app.dSYM/Contents/Resources/DWARF/xxx -l 0x00000001c4fe7000 0x00000001a2d6e29c

// extension about xcrun
// xcrun can accept address to symbolicate on the way in command line.

xcrun atos -o xxx.app.dSYM/Contents/Resources/DWARF/xxx -l 0x1040dc000 -arch arm64
0x1052c0464
__63-[GCDAsyncUdpSocket asyncResolveHost:port:withCompletionBlock:]_block_invoke.118 (in xxx) (GCDAsyncUdpSocket.m:1209)
0x104ba9094
-[BDAboutController tapButton:atIndex:] (in xxx) (BDAboutController.m:251)

图片-4021

扩伸延展

  • 关于一些第三方的APM(Application Performance Management),即应用性能管理

    • 目前的一些APM都是基于以上原理进行符号解析,但是由于这样或者那样的原因导致某些解析是有出入,所以需要你二次确认,这需要你在日常工作中有敏锐的观察力,毕竟工具是人写的不可能不出错,在这里就不列举了,在用的小伙伴可以自行校验

  • 关于第三方解析工具

    • 目前暂无推荐,可以自己搜索关键词进行尝试,目前笔者是使用了自己编写脚本语言进行符号化,参考资料也已给出

参考文档

  1. https://developer.apple.com/documentation/xcode/adding-identifiable-symbol-names-to-a-crash-report

  2. https://developer.apple.com/documentation/xcode/examining-the-fields-in-a-crash-report

  3. https://stackoverflow.com/questions/22460058/how-is-a-dsym-file-created/22460268

  4. https://github.com/adam-roth/litesymbols

后记

符号化在平常不过了,但是需要在熟练的基础上再去考虑如何压缩时间,提高效率,快速定位问题。该篇只是入门,符号化之路还有很多坑等着大家,祝大家填坑愉快。

也许你还想看

(▼点击文章标题或封面查看)

Caffeine如何变热?

2021-09-09

iOS:制作简易的 AAC 播放器 —— 了解音频的播放流程

2021-08-26

iOS的CoreData技术笔记

2021-08-19

遵循 Google 应用指南的 Retrofit + Coroutine 封装

2021-08-12

基于条件LayerNorm的多任务文本分类模型

2021-08-05

锐单商城拥有海量元器件数据手册IC替代型号,打造电子元器件IC百科大全!

相关文章