zoukankan      html  css  js  c++  java
  • Crash log符号化与调试信息

    这篇文章主要整理了crash log的符号化解析和调试信息与配置相关的一些内容。

    对于做移动App开发的来说,质量和体验都是很重要的。一个客户端应用如果经常“闪退”,是产品质量很差的一个体现,用户体验就更不用提了。所以开发一个优秀的App,首先是保证自身的技术质量,尽量杜绝“闪退”,也就是“Crash”。但客户端上线后,偶尔出现一个隐藏很深的bug也在所难免。我们所能做的就是尽可能的收集问题相关的信息,争取在将来的新版本中解决和改进。

    0. Crash

    一个App启动之后,用着用着就突然被iOS系统关闭,或者干脆就起不来,在打开的一瞬间关闭,这就是Crash,俗称“闪退”“崩溃”。

    iOS上的App闪退有各种各样的原因,手机过热、响应超时、内存过低都是有可能的crash原因。但更多情况下是App程序自身的运行逻辑存在问题、缺陷。比如调用用了Objective-C对象根本不支持的方法(发送消息),非法内存访问,数组越界,参数不符合要求等。

    这些问题在调试阶段,我们都可以很容易的通过断点和console中提供的信息快速定位并解决。

    但对于已发布的App,如果想重现并利用上述办法来解决,恐怕会比较费时费事。

    最有帮助最直接的办法就是根据出现问题时的闪退日志,分析和判断crash的原因,快速准确的定位和解决。

    1. Crash log

    在iOS上运行的App出现crash的时候,通常会生成一个crash log,记载问题发生时的具体状况。开发者可以在iTunes Connect(相当于App Store后台)中特定App下找到收集上来的crash log。不过客户端用户可以选择不发送诊断信息,这样收集上来的信息就不一定是全面的。

    不过开发者可以对exception和signal设置自定义的handler做额外处理,以收集现场信息。现在也有很多第三方的工具很流行,比如Crashlytics,国内的友盟等。

    闪退日志里面包含了Crash发生的App、运行软硬件环境、发生时间、错误类型、方法调用异常栈、各线程状态、寄存器和内存信息。

    而其中对我们开发人员来说意义最为重大的,可能就是异常线程的调用栈,例如:

     1 Last Exception Backtrace:
     2 0   CoreFoundation                  0x18517e950 __exceptionPreprocess + 132
     3 1   libobjc.A.dylib                 0x1916841fc objc_exception_throw + 60
     4 2   CoreFoundation                  0x185085910 -[__NSDictionaryM setObject:forKey:] + 900
     5 3   CrashDebugInfoTest              0x1000c2b90 0x1000bc000 + 27536
     6 4   CrashDebugInfoTest              0x1000c28dc 0x1000bc000 + 26844
     7 5   UIKit                           0x1881bc55c -[UIApplication _handleDelegateCallbacksWithOptions:isSuspended:restoreState:] + 316
     8 6   UIKit                           0x1881bbf08 -[UIApplication _callInitializationDelegatesForURL:payload:suspended:] + 1564
     9 7   UIKit                           0x1881b59ec -[UIApplication _runWithURL:payload:launchOrientation:statusBarStyle:statusBarHidden:] + 772
    10 8   UIKit                           0x1881498cc -[UIApplication handleEvent:withNewEvent:] + 3316
    11 9   UIKit                           0x188148ad0 -[UIApplication sendEvent:] + 104
    12 10  UIKit                           0x1881b5044 _UIApplicationHandleEvent + 672
    13 11  GraphicsServices                0x18ad63504 _PurpleEventCallback + 676
    14 12  GraphicsServices                0x18ad63030 PurpleEventCallback + 48
    15 13  CoreFoundation                  0x18513e890 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__ + 56
    16 14  CoreFoundation                  0x18513e7f0 __CFRunLoopDoSource1 + 444
    17 15  CoreFoundation                  0x18513ca14 __CFRunLoopRun + 1620
    18 16  CoreFoundation                  0x18507d6d0 CFRunLoopRunSpecific + 452
    19 17  UIKit                           0x1881b41c8 -[UIApplication _run] + 784
    20 18  UIKit                           0x1881aefdc UIApplicationMain + 1156
    21 19  CrashDebugInfoTest              0x1000c2c5c 0x1000bc000 + 27740
    22 20  libdyld.dylib                   0x191c77aa0 start + 4
    View Code

    其中从第二列来看,很多是开发库中的调用,而关键在于其间我们自己的App方法调用。可惜有些时候,这关键的信息竟然全是16进制的数据,我们很难看懂。比如:

    1 3 CrashDebugInfoTest 0x1000c2b90 0x1000bc000 + 27536

    那么要从十六进制的地址码,得到我们代码中对应的方法调用,就需要结合调试信息对crash log进行符号化。

    2. 符号化的各种方法

    符号化的方法多种多样,从网上社区论坛和个人经验看来,至少有如下办法:

    • 使用开发工具库中自带的symbolicatecrash

    • 使用atos

    • 使用dwarfdump

    更有牛人,自己写了个复杂的脚本来解决这个问题。下面我介绍我常使用的两种方法,一个是利用atos,一个是充分利用Xcode自带的工具。其它的大家都可以到网上参看相关文章,一搜一大筐。

    atos,就是address to symbol,把地址翻译成符号。上面那段我提到了,要想把十六进制的地址翻译为符号,需要调试信息。最好用的调试信息就是我们在每次给App打包时生成的dSYM文件。而atos最好用的方式就是:

    1
    atos -o XXX.app.dSYM/Contents/Resources/DWARF/XXX -l address0 targetAddress

    其中:

    • XXX是AppName

    • address0是当前进程在内存中加载的起始地址,至于为什么需要这个,那就有必要去了解下ASLR

    • targetAddress就是你想要符号化的地址了,比如0x1000c2b90

    除了atos外,我想介绍的另一个办法就是使用Xcode自带的crash log分析工具,在老版本的Xcode中是在Organizer里,在新版本里是在Devices中。

    有的朋友可能会说,那里面显示的可还是十六进制的地址啊!那是因为它“没看到”App和dSYM文件啊。那怎么办?简单:

    把App和dSYM放在一个目录中,并用mdimport把目录加入到Spotlight的索引中即可。

    怎么样,这招是不是更快更好用?symbolicatecrash神马的就不需要了吧!

    3. 针对framework静态库的crash定位和调试选项设置

    之前本人曾经以framework(iOS Universal Framework)的方式开发了好多SDK供别人用。可当使用了framework库的App闪退了的时候,即使是SDK中的逻辑问题,异常栈中显示的也是App的名字。

    更重要的是,默认情况下,异常栈的最右一列根本没法符号化。

    这是因为framework实际上是一种静态库,在Build App时,它已经完全“融入”了,静态链接到App产物中。而在framework生成的时候,调试信息已经被抽取掉了。

    我们打开SDK的工程文件,在Build Settings里搜索Strip,会发现有好几个选项:

    • Strip Debug Symbol During Copy

    • Strip Linked Product

    • Strip Style

    • Use Separate Strip

    对于这个问题,我们只要在Strip Linked Product一项中选择No就行了。这样在Build出的SDK framework中,包的体积会变大,因为它容纳了本要去除掉的调试信息。

    按我在之前的Blog的办法,我们看看在Mach-O文件中多了什么:

    debugSym.jpg

    Debug Info

    是的,正是DWARF格式的数据。DWARF是一种通用的调试信息格式,可以认为是Debugging With Attributed Records Format的缩写。感兴趣的可以前往: http://www.dwarfstd.org

  • 相关阅读:
    Servlet深层知识
    HTTP协议
    Web开发中的主要概念
    Schema约束
    连接池基础
    Mysql基础3
    JDBC常用接口详解
    Mysql基础2
    Mysql基础1
    使用12c的DBCA创建数据库的时候报错TNS-04404
  • 原文地址:https://www.cnblogs.com/spring286/p/4300511.html
Copyright © 2011-2022 走看看