【问题标题】:Can an iOS app include its debug symbols (dSYM file) when installed directly from Xcode?直接从 Xcode 安装时,iOS 应用程序能否包含其调试符号(dSYM 文件)?
【发布时间】:2016-01-04 23:53:27
【问题描述】:

我们内部有几台 iOS 设备,我会定期使用自己开发的 iOS 应用对其进行更新。我只需将设备连接到我的开发人员机器并从 Xcode (Cmd+R) 运行应用程序。这样,应用就会安装在连接的设备上。

稍后,当应用程序在任何这些设备上崩溃时,我想将其恢复,再次将其连接到 Xcode,打开崩溃日志并查看符号化的回溯。从而读取发生崩溃的调用堆栈的文件名和行号。

据我所知,因此我需要保留 dSYM 文件——否则我将看不到文件名和行号。然而,这是一项非常乏味的工作:我不想每次从我的计算机“临时”安装应用程序时都将其存档。我宁愿将调试信息/符号与应用程序一起复制到设备上。然后,每当我拿回设备时,我都会打开它的崩溃日志,并且已经对崩溃进行了符号化 - 无需在我的计算机上保留 dSYM 文件(调试信息)。

有没有机会做到这一点?如果是,我应该遵循什么构建设置/程序?

为什么会有“在复制过程中去除调试符号”“去除链接产品”构建设置,即使禁用,仍然不会产生符号化的崩溃日志,除非我保留匹配的 dSYM 文件?

【问题讨论】:

    标签: ios xcode xcode7 crash-reports symbolicatecrash


    【解决方案1】:

    在没有 dSYM 文件的情况下从您的临时构建自动发送崩溃报告

    我想分享我的解决方案,该解决方案从我们的内部构建发送符号化的崩溃日志无需 dSYM 文件。因此,您无需每次在内部创建和部署过早的应用程序时都保存 dSYM 文件。并且您无需在仅调试构建时打扰诸如 HockeyApp 或 Crashlytics 之类的崩溃报告服务。

    我想出了一个崩溃报告管道,它可以自动将符号化的崩溃报告发布到 Slack 频道。下次启动应用程序时会发布此消息,因此发生崩溃后的下一次运行。所有这一切都在后台发生,而不会打扰已经烦恼的用户(最后他的应用程序崩溃了!)。

    应用崩溃时的样子

    崩溃报告

    链接指向崩溃报告本身,我的 iOS 应用程序在后台自动上传为文本文件以及提到的 Slack 消息:

    Build timestamp: Thu Oct  8 11:42:11 CEST 2015
    Git commit hash: master-d675928
    
    Incident Identifier: 4F9F2147-CEB3-426C-A122-CF215EB8DC16
    CrashReporter Key:   TODO
    Hardware Model:      iPad2,5
    Process:         Nuimo [4997]
    Path:            /private/var/mobile/Containers/Bundle/Application/9CE594BE-D9E7-4070-81FF-B1554C52CB2C/Nuimo.app/Nuimo
    Identifier:      com.senic.Nuimo
    Version:         1
    Code Type:       ARM
    Parent Process:  launchd [1]
    
    Date/Time:       2015-10-08 16:52:49 +0000
    OS Version:      iPhone OS 8.4.1 (12H321)
    Report Version:  104
    
    Exception Type:  SIGTRAP
    Exception Codes: #0 at 0x92c8b4
    Crashed Thread:  0
    
    Thread 0 Crashed:
    0   libswiftCore.dylib                  0x0092c8b4 _TTSf4s_s_d_d___TFSs18_fatalErrorMessageFTVSs12StaticStringS_S_Su_T_ + 72
    1   Nuimo                               0x000c80b4 _TFC5Nuimo33NuimoDiscoveryTableViewController11viewDidLoadfS0_FT_T_ + 608
    2   Nuimo                               0x000c8204 _TToFC5Nuimo33NuimoDiscoveryTableViewController11viewDidLoadfS0_FT_T_ + 56
    3   UIKit                               0x2d46f55d <redacted> + 600
    4   UIKit                               0x2d46f2cd <redacted> + 24
    5   UIKit                               0x2d9e97ad <redacted> + 2364
    6   UIKit                               0x2d9ea65d <redacted> + 256
    7   Nuimo                               0x000d2070 _TFC5Nuimo30NuimoDetailsPageViewController18showNuimoSelectionfS0_FT_T_ + 560
    8   Nuimo                               0x001750f4 _TPA21 + 32
    9   Nuimo                               0x001a0f80 _TTRXFo__dT__XFo_iT__iT__ + 16
    10  Nuimo                               0x0017133c _TPA__TTRXFo__dT__XFo_iT__iT__3 + 76
    11  Nuimo                               0x00121c64 _TFC5Nuimo6TabBar16setSelectedIndexfS0_FTGSqSi_8animatedSb_T_ + 1972
    12  Nuimo                               0x0011fec0 _TFC5Nuimo6TabBars13selectedIndexGSqSi_ + 64
    13  Nuimo                               0x00122e98 _TFC5Nuimo6TabBar12didTapButtonfS0_FCSo8UIButtonT_ + 132
    14  Nuimo                               0x00122f4c _TToFC5Nuimo6TabBar12didTapButtonfS0_FCSo8UIButtonT_ + 100
    15  UIKit                               0x2d4a082b <redacted> + 70
    16  UIKit                               0x2d4a07d1 <redacted> + 44
    17  UIKit                               0x2d48b375 <redacted> + 584
    18  UIKit                               0x2d4a8c9b <redacted> + 258
    19  UIKit                               0x2d49fe15 <redacted> + 316
    20  UIKit                               0x2d4997f1 <redacted> + 540
    21  UIKit                               0x2d46f9b5 <redacted> + 196
    22  UIKit                               0x2d6e60ff <redacted> + 14538
    23  UIKit                               0x2d46e3b7 <redacted> + 1350
    24  CoreFoundation                      0x29dd400f <redacted> + 14
    25  CoreFoundation                      0x29dd3423 <redacted> + 222
    26  CoreFoundation                      0x29dd1aa1 <redacted> + 768
    27  CoreFoundation                      0x29d1d6d1 CFRunLoopRunSpecific + 476
    28  CoreFoundation                      0x29d1d4e3 CFRunLoopRunInMode + 106
    29  GraphicsServices                    0x316b91a9 GSEventRunModal + 136
    30  UIKit                               0x2d4cf445 UIApplicationMain + 1440
    31  Nuimo                               0x0013e134 main + 164
    32  libdyld.dylib                       0x38b5baaf <redacted> + 2
    (Rest stripped...)
    

    那么我从崩溃报告中学到了什么?它向我展示了:

    1. 构建时间戳
    2. 构建的 git 提交哈希(非常方便地重现构建)
    3. 一些设备信息
    4. 错误堆栈跟踪——应用程序实际上在类NuimoDiscoveryTableViewController、方法viewDidLoad 中崩溃。不幸的是没有行号,但这比没有更重要!

    如何让您的临时构建发送一个符号化的崩溃报告

    如果您想在您的 iOS 应用中实现相同的管道,这里有一个操作指南:

    1。将 PLCrashReporter 包含到您的应用程序中

    PLCrashReporter is a free open-source crash reporting library 由 HockeyApp 等付费服务使用。它允许在设备上对错误堆栈跟踪进行符号化。

    假设您使用的是 CocoaPods,请在您的 Podfile 中包含:

    pod 'PLCrashReporter'
    

    如果你使用的是 Swift add these header files to your bridging header:

    #import "PLCrashReport.h"
    #import "PLCrashReporter.h"
    #import "PLCrashReportTextFormatter.h"
    

    2。在您的应用启动时启动您的自定义崩溃报告器

    在您的应用完成启动后运行崩溃报告器。这样,崩溃报告器可以首先发送上次应用程序运行的任何未决崩溃报告,并启用 PLCrashReporter 来符号化和存储崩溃报告以备将来失败。

    class AppDelegate: UIResponder, UIApplicationDelegate {
        func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
    #ifdef DEBUG
            CrashReporter.sharedReporter.applicationDidFinishLaunching()
            ...
    #end
        }
    

    如您所见,崩溃报告器仅针对 Debug 配置设置,因为明确不建议将其用于发布版本。不仅因为发生崩溃时符号化需要几秒钟的时间。这就是说,add a compiler flag for your debug builds

    3。实施您的自定义崩溃报告管道

    class CrashReporter {
        static let sharedReporter = CrashReporter()
    
        func applicationDidFinishLaunching() {
            let crashReporter = PLCrashReporter(configuration: PLCrashReporterConfig(signalHandlerType: PLCrashReporterSignalHandlerType.BSD, symbolicationStrategy: PLCrashReporterSymbolicationStrategy.All))
            if crashReporter.hasPendingCrashReport() {
                if let crashData = crashReporter.loadPendingCrashReportData(), let crashReport = try? PLCrashReport(data: crashData) {
                    let gitCommitHash = "Git commit hash: " + ((NSBundle.mainBundle().infoDictionary ?? [:])["GitCommitHash"] as? String ?? "(not set)")
                    let buildTimestamp = "Build timestamp: " + ((NSBundle.mainBundle().infoDictionary ?? [:])["BuildTimestamp"] as? String ?? "(not set)")
                    let message = PLCrashReportTextFormatter.stringValueForCrashReport(crashReport, withTextFormat: PLCrashReportTextFormatiOS)
                    sendCrashReport("\(buildTimestamp + "\n" +  gitCommitHash + "\n" + message)")
                }
                crashReporter.purgePendingCrashReport()
            }
            if !crashReporter.enableCrashReporter() {
                print("Cannot enable crash reporter")
            }
        }
    
        func sendCrashReport(message: String) {
            // Now send the crash report to Slack, Twitter, Facebook, via Email, ...
        }
    

    确保最后启用PLCrashReporter

    4。附加步骤:包括构建时间戳和 git 分支/标签以及用于构建的提交哈希

    我对@9​​87654336@ 的实现包括构建时间戳、git 分支/标签和提交哈希。这使我能够轻松地重现错误的代码库。以下是包含这些附加信息的方法。然而,没有必要让上面的示例实现运行。

    Project -> Build Phases 下添加一个新的 Run Script Phase 并粘贴这个写入构建时间和 git 详细信息的 bash 脚本进入你项目的Info.plist:

    echo "Writing build time and git commit hash to Info.plist"
    
    git_branch=$(git symbolic-ref --short -q HEAD)
    git_tag=$(git describe --tags --exact-match 2>/dev/null)
    git_branch_or_tag="${git_branch:-${git_tag}}"
    git_hash=$(git log -1 --format="%h")
    build_time=$(date)
    
    echo "${BUILT_PRODUCTS_DIR}/${EXECUTABLE_FOLDER_PATH}/Info.plist"
    echo "${git_branch_or_tag}-${git_hash}"
    
    info_plist="${BUILT_PRODUCTS_DIR}/${EXECUTABLE_FOLDER_PATH}/Info.plist"
    /usr/libexec/PlistBuddy -c "Set :GitCommitHash '${git_branch_or_tag}-${git_hash}'" "${info_plist}"
    /usr/libexec/PlistBuddy -c "Set :BuildTimestamp '${build_time}'" "${info_plist}"
    

    似乎有必要事先将这些 plist 键的两个空键值对添加到您的 Info.plist 中。添加一个名为BuildTimestamp 和另一个名为GitCommitHash。两种类型都是String

    5。让您的应用崩溃!

    现在在您的代码中放置一个assertionFailure() 并运行您的应用程序。确保在未连接 Xcode 时运行应用程序,否则 Xcode 将在崩溃报告器做出反应之前处理此错误。当您成功使您的应用崩溃时,无论是否将 Xcode 连接到您的应用,您都应该重新启动它,您应该会看到崩溃报告器生成崩溃报告消息,如本答案的介绍中所示。

    【讨论】:

      猜你喜欢
      • 2012-07-16
      • 1970-01-01
      • 2011-12-22
      • 2015-05-15
      • 1970-01-01
      • 1970-01-01
      • 2017-01-22
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多