简体   繁体   English

直接从Xcode安装时,iOS应用程序能否包含其调试符号(dSYM文件)?

[英]Can an iOS app include its debug symbols (dSYM file) when installed directly from Xcode?

We have a couple of iOS devices inhouse that I regularly update with an iOS app that I develop. 我们内部有几个iOS设备,我会定期使用自己开发的iOS应用程序进行更新。 I just connect the device to my developer machine and run the app from Xcode ( Cmd + R ). 我只是将设备连接到我的开发人员机器,然后从Xcode( Cmd + R )运行该应用程序。 This way the app gets installed on the connected device. 这样,该应用程序便会安装在连接的设备上。

Later, when the app crashes on any of these devices I'd like to get it back, connect it to Xcode again, open the crash log and see the backtrace symbolicated. 稍后,当应用在这些设备中的任何一个上崩溃时,我想找回它,再次将其连接到Xcode,打开崩溃日志,并以符号表示回溯。 Thus reading the file name and line numbers of the call stack where the crash occurred. 因此,读取发生崩溃的调用堆栈的文件名和行号。

As I know so far, I therefor need to preserve the dSYM file – otherwise I wouldn't see the file names and line numbers. 到目前为止,我需要保留dSYM文件-否则我将看不到文件名和行号。 This however is a very tedious job: I don't want to archive the app every time I install it "ad hoc" from my computer. 但是,这是一项非常繁琐的工作:我不想每次从计算机“临时”安装该应用程序时都将其存档。 I rather would like the debug information/symbols to be copied along with the app onto the device. 我希望将调试信息/符号与应用程序一起复制到设备上。 Then, whenever I get the device back, I open its crashlog and have the crash already symbolicated - without having to preserve the dSYM file (debug information) on my computer. 然后,每当我取回设备时,都打开它的崩溃日志,并已将崩溃标记为符号-无需在计算机上保留dSYM文件(调试信息)。

Is there any chance to achieve that? 有机会实现这一目标吗? If yes, what build settings / procedure should I follow? 如果是,我应该遵循什么构建设置/步骤?

Why are there "Strip Debug Symbols During Copy" and "Strip Linked Product" build settings that, even when disabled, still don't yield symbolicated crashlogs unless I preserve the matching dSYM file? 为什么有“复制期间的条带调试符号”“条带链接的产品”构建设置,即使被禁用,除非我保留匹配的dSYM文件,否则它们仍然不会产生带符号的崩溃日志?

Automatically send a crash report from your adhoc builds without dSYM files 在没有dSYM文件的情况下从您的即席构建自动发送崩溃报告

I want to share my solution that sends symbolicated crash logs from our inhouse builds without the need for a dSYM file . 我想分享我的解决方案,该解决方案从我们的内部版本发送带符号的崩溃日志, 而无需dSYM文件 So you don't need to save the dSYM file every time you create and deploy your premature app inhouse. 因此,您无需在内部每次创建和部署过早的应用程序时都保存dSYM文件。 And you don't need to bother a crash report service such as HockeyApp or Crashlytics when in comes to debug builds only. 而且,仅在进行调试构建时,您就不必打扰诸如HockeyApp或Crashlytics之类的崩溃报告服务。

I came up with a crash reporting pipeline that automatically posts the symbolicated crash report to a Slack channel. 我想出了一个崩溃报告管道,该管道会自动将符号化的崩溃报告发布到Slack频道。 This message is posted the next time the app is started, thus the next run after the crash occurred. 该消息将在下次启动应用程序时发布,从而在崩溃发生后的下一次运行中发布。 All this happens in background without bothering the already annoyed user (at the end his app crashed!). 所有这些都在后台发生,而不会打扰已经烦恼的用户(最终,他的应用程序崩溃了!)。

How it looks like when the app has crashed 应用程序崩溃时的外观

崩溃报告消息发送到Slack

The crash report 崩溃报告

The link points to the crash report itself that my iOS app automatically uploaded in background as a text file along with the mentioned Slack message: 该链接指向崩溃报告本身,我的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...)

So what did I learn from the crash report? 那么我从崩溃报告中学到了什么? It showed me: 它告诉我:

  1. the build time stamp 构建时间戳
  2. the git commit hash of the build (quiet handy to reproduce the build) 生成的git commit哈希(重现生成时非常方便)
  3. some device information 一些设备信息
  4. the error stack trace – the app actually crashed in class NuimoDiscoveryTableViewController , method viewDidLoad . 错误堆栈跟踪-应用程序实际上在类NuimoDiscoveryTableViewController viewDidLoad方法中崩溃了。 Unfortunately no line number, but that's more than nothing! 不幸的是,没有行号,但这不算什么!

How to make your adhoc builds send a symbolicated crash report 如何使自组织构建发送符号化的崩溃报告

Here's a how-to-guide in case you want to implement the same pipeline into your iOS app: 如果您想在iOS应用程序中实现相同的管道,这是一种方法指南:

1. Include PLCrashReporter into your application 1.将PLCrashReporter包含到您的应用程序中

PLCrashReporter is a free open-source crash reporting library that is used by paid services such as HockeyApp. PLCrashReporter是一个免费的开源崩溃报告库 ,供HockeyApp等付费服务使用。 It allows for on-device symbolication of the error stack trace. 它允许错误堆栈跟踪的设备上符号化。

Assuming that you are using CocoaPods, include in your Podfile : 假设您正在使用CocoaPods,请在您的Podfile包括:

pod 'PLCrashReporter'

If you are using Swift add these header files to your bridging header : 如果您使用的是Swift, 请将这些头文件添加到桥接头中

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

2. Start your custom crash reporter when your app has launched 2.启动应用程序后启动自定义崩溃报告器

Run your crash reporter when your app has finished launching. 应用启动完成后,运行崩溃报告器。 This way the crash reporter can first send any pending crash report from the last application run and enable PLCrashReporter to symbolicate and store crash reports for future failures. 这样,崩溃报告器可以首先从上次运行的应用程序发送任何未决的崩溃报告,并使PLCrashReporter可以符号化和存储崩溃报告, PLCrashReporter将来发生故障。

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

As you can see, the crash reporter is only set up for the Debug configuration as it's explicitly not recommended for release builds. 如您所见,崩溃报告器仅针对Debug配置设置,因为明确不建议将其用于发行版。 Not only because the symbolication takes a few seconds to happen when a crash occurs. 不仅因为崩溃发生时象征符号需要花费几秒钟的时间。 This said, add a compiler flag for your debug builds . 这就是说, 为调试build添加一个编译器标志

3. Implement your custom crash report pipeline 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, ...
    }

Make sure to enable the PLCrashReporter at the end! 确保最后启用PLCrashReporter

4. Additional step: Include a build timestamp and the git branch/tag and the commit hash used for the build 4.附加步骤:包括构建时间戳记和git分支/标记以及用于构建的提交哈希

My implementation of CrashReporter includes the build time stamp and the git branch/tag and commit hash. 我对CrashReporter实现包括构建时间戳和git分支/标记以及提交哈希。 That allows me to easily reproduce the buggy code base. 这使我可以轻松地重现错误的代码库。 Here's how to include these additional information. 这是包括这些附加信息的方法。 It's however not necessary to make the sample implementation above running. 但是,不必在运行时进行示例实现。

Under Project -> Build Phases add a new Run Script Phase and paste this bash script that writes the build time and the git details into your project's Info.plist : 在“ 项目” ->“ 构建阶段”下,添加一个新的“运行脚本阶段”,然后将此bash脚本粘贴,将构建时间和git详细信息写入项目的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}"

It seems to be necessary to add two empty key value pairs for these plist keys into your Info.plist beforehand. 似乎有必要将这些plist键的两个空键值对事先添加到Info.plist中。 Add one named BuildTimestamp and another one named GitCommitHash . 添加一个名为BuildTimestamp和另一个名为GitCommitHash Both of type String . 两者均为String类型。

5. Make your app crash! 5.让您的应用崩溃!

Now place an assertionFailure() in your code and run your app. 现在,在您的代码中放置一个assertionFailure()并运行您的应用程序。 Make sure to run the app when Xcode is not connected, otherwise Xcode will handle this error before the crash reporter can react. 确保未连接Xcode时运行该应用程序,否则Xcode将在崩溃报告器做出反应之前处理此错误。 When you successfully crashed your app, restart it with or without Xcode connected to your app, and you should see your crash reporter generating a crash report message as shown in the introduction of this answer. 成功使应用程序崩溃后,无论是否将Xcode连接到应用程序,都可以重新启动它,并且应该看到崩溃报告程序生成崩溃报告消息,如此答案的介绍中所示。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM