atos 和 dwarfdump 不会象征我的地址

Posted

技术标签:

【中文标题】atos 和 dwarfdump 不会象征我的地址【英文标题】:atos and dwarfdump won't symbolicate my address 【发布时间】:2012-04-20 08:30:16 【问题描述】:

我通过 AirBrake.io 收到了一份没有符号化的崩溃报告。由于崩溃报告的格式与 Apple 崩溃日志的格式不完全相同,因此我不能像往常一样将其放在 XCode 上,因此我从 XCode 存档中获取了完全相同的构建,试图在命令行上对其进行符号化。结果如下:

$ atos -o kidsapp.app/kidsapp 0x0002fc4c
0x0002fc4c (in kidsapp)

我绝对确定我使用的版本与崩溃报告的来源相同。所以我也尝试了 dwarfdump:

$ dwarfdump --lookup 0x0002fc4c --arch armv7 kidsapp.app.dSYM
----------------------------------------------------------------------
 File: kidsapp.app.dSYM/Contents/Resources/DWARF/kidsapp (armv7)
----------------------------------------------------------------------
Looking up address: 0x000000000002fc4c in .debug_info... not found.
Looking up address: 0x000000000002fc4c in .debug_frame... not found.

也没有结果。除了使用错误的 dSYM 文件之外,还有什么我可能做错的吗?我知道它是正确的,因为这是 AirBrake 的崩溃报告中提到的版本,它在我的 XCode 存档中。

欢迎任何想法/提示!

【问题讨论】:

【参考方案1】:

我用下面的算术算出来了:

slide + stack address - load address = symbol address

stack address 是我从堆栈转储崩溃报告(不是 .crash 文件,只是堆栈转储)中获得的十六进制值。

slide 是运行 otool -arch armv7 -l APP_BINARY_PATH 时 LC_SEGMENT cmd 的 vmaddr。我的通常最终是 0x00001000。

load address 是复杂的部分。它实际上是主线程的最底部堆栈地址与运行dwarfdump --arch armv7 --all DSYM_BINARY_PATH 时包含符号的二进制部分的第一个地址之间的差异。这只是main 函数的符号地址。因此,如果您最底部的崩溃地址是 0x8000,而您的主函数的符号地址是 0x2000,那么您的 load address 是 0x6000。

现在有了所有这些部分,我可以计算符号地址并将其放入 atos 或 dwarfdump:dwarfdump --lookup SYM_ADDR --arch armv7 APP_BINARY_PATH

转储示例(您可以看到load address 是0x00003af4):

----------------------------------------------------------------------

文件:/Users/user/Desktop/MyApp.xcarchive/dSYMs/MyApp.app.dSYM/Contents/Resources/DWARF/MyApp (armv7)

----------------------------------------------------------------------

0x00000024: [0x00003af4 - 0x00003b4e) 主要

0x00000098: [0x00003b50 - 0x00003d8c) -[MyAppDelegate 应用程序:didFinishLaunchingWithOptions:]

...转储的其余部分

最难的部分是意识到我包含的 2 个静态库之一在链接到我的应用程序的二进制文件之前已经剥离了它们的符号!这留下了巨大的符号地址差距,所以我最终只得到了 dSYM 中所需符号的三分之二。

确保在您的静态库 xcode 项目中将以下标志设置为 NO,以便当您链接它时,您可以将符号拉入应用程序的二进制文件(以后可以剥离):COPY_PHASE_STRIP, @ 987654339@,和STRIP_INSTALLED_PRODUCT

现在你可能会问,“如果堆栈转储不包含主函数,因为它不在主线程上,所以我无法获取主函数的堆栈地址,我该怎么办?”。对此我会回答,“我没有一个该死的线索!”。只需交叉手指,希望您可以获得包含符号地址的堆栈跟踪,或使用模仿 Apple 崩溃日志的崩溃报告系统,如 PLCrashReporter。

[2013 年 5 月 26 日编辑] -

我注意到load address 确实是 mach-o 二进制文件的地址。虽然我上面描述的通常可以工作 - 它实际上并不正确。这可以通过崩溃报告获得,但是这个答案的重点是在您没有崩溃报告时提供崩溃的符号。当我想表示符号时,找出load address 的最佳方法是确保我用stack addresses 记录load address

我亲自创建了一个系统,用于记录崩溃(不是崩溃报告)并将它们发送到 S3 存储桶,以便稍后检索它们以进行调试。当我启动我的应用程序时,我缓存了slideload addressmain function address,以便在我的应用程序崩溃时使用,然后我发送stack addresses

注意:dyld 函数使用#include <mach-o/dyld.h>

slide = _dyld_get_image_vmaddr_slide(0)返回的地址

load address = _dyld_get_image_header(0)返回的地址

main function address = [NSThread callStackReturnAddresses] 中的最后一个地址 在主线程上调用

在崩溃时,我肯定会记录 [NSThread callStackReturnAddresses][NSThread callStackSymbols] 以及可以通过此方法检索的架构:

- (NSString*) arch

    NSString* arch =
#ifdef _ARM_ARCH_7
        @"armv7";
#elif defined (_ARM_ARCH_6)
        @"armv6";
#else
        nil;
#endif

    return arch;

虽然我还不知道如何区分 armv7 和 armv7s。

所以这可能会在未来有所帮助。我计划把我学到的所有东西都变成一个简单的崩溃工具——比 natos 工具(可能是 natos v2)更好。

我已更新 natos 以支持手动提供 load address:https://github.com/NSProgrammer/natos

【讨论】:

我创建了一个命令行工具来完成所有繁重的工作。只需要 1) xcarchive 路径,2) 主函数的堆栈地址符号,3) 所需符号的堆栈地址,4) 感兴趣的架构。它位于 github:github.com/nob1984/natos 不错的文章,但遗憾的是其中有一个重大错误:加载地址不是你写的。加载地址可以在崩溃报告的二进制图片部分找到,是对应二进制地址范围的起始地址。 我使用了你的工具,但我怎样才能获得 _mainFunctionStackAddress?我通过dwarfdump我的dsym文件找到一个地址并使用它,但是他给我的结果和atos解释的一样,我仍然找不到崩溃点的正确位置。 @Zhou,该工具已更新为仅接受加载地址,而不是根据主函数堆栈地址进行计算。 @NSProgrammer,我尝试使用 natos,但它没有给我正确的功能。【参考方案2】:

首先检查 dSYM 是否真的适合该应用程序:

dwarfdump --uuid kidsapp.app/kidsapp
dwarfdump --uuid kidsapp.app.dSYM

两者都应该返回相同的结果。

接下来检查dSYM是否有任何有效内容

dwarfdump --all kidsapp.app.dSYM

这应该至少提供一些信息,而不是not found

我猜 dSYM 已损坏。通常,您可能希望使用崩溃报告器,它为您提供包含所有线程和最后异常回溯信息的完整崩溃报告。我建议使用基于 PLCrashReporter 的东西,例如QuincyKit(开源 SDK + 服务器 + mac 上的符号)或 HockeyApp(开源 SDK + 付费服务 + 服务器端符号)(注意:我都是开发人员之一!)

【讨论】:

我遇到了同样的问题。我报告了一个提供堆栈跟踪的崩溃,但在我的存档的 dSYM 中找不到我项目中最终在堆栈中的符号。 UUID 匹配,但符号全部关闭。这怎么可能,我该如何解决?苹果是否会在发布到应用商店之前以某种方式修改二进制文件,从而破坏与我的 dSYM 的对齐? 您必须考虑二进制文件的幻灯片和应用程序的起始地址。您不能只使用堆栈跟踪中的内存地址。只需使用 Xcode 中的 symbolicatecrash 脚本即可满足您的所有需求。 但是如果我只有符号(我应该说堆栈转储,而不是跟踪),它纯粹给我十六进制值,我该如何考虑“幻灯片”? 使用 atos 的地址 = slide + stack address - binary load address。您从 dsym 或二进制文件中获得带有 dwarfdump 的幻灯片,检查 symbolicatecrash 脚本是如何完成的。您从崩溃报告中的二进制图像部分获得的二进制加载地址。 我回答了我的具体子问题。【参考方案3】:

对于某些时间没有像这样的加载地址值的人:

Jan 14 11:02:39 Dennins-iPhone AppName[584] <Critical>: Stack Trace: (
    0   CoreFoundation                      0x2c3084b7 <redacted> + 150
    1   libobjc.A.dylib                     0x39abec8b objc_exception_throw + 38
    2   CoreFoundation                      0x2c21cc35 CFRunLoopRemoveTimer + 0
    3   AppName                             0x0005a7db AppName + 272347  

我创建了一个简单的 bash 来帮助我调试:

#! /bin/bash
read -p "[Path] [App Name] [Stack Address] [Relative Address] " path appName runtimeAddress relativeAddress
loadAddress=`echo "obase=16;ibase=10;$((runtimeAddress-relativeAddress))" | bc`
atos -o $path/Payload/$appName.app/$appName -l $loadAddress $runtimeAddress -arch armv7

它只是读取应用程序的路径、应用程序名称、运行时地址以及“+”信号后的值(十进制值),然后找到加载地址的值来运行 atos 命令。

【讨论】:

非常感谢!我有没有加载地址的堆栈跟踪,您的脚本是从运行时和相对地址派生加载地址的唯一方法【参考方案4】:

所以我的情况: 我从 NSException.callStackSymbols 中接收到崩溃字符串。

崩溃堆栈跟踪如下所示:

2   AppName                               0x00000001006c75b4 AppName + 2356660\r3   AppName                               0x00000001004f5cfc AppName + 449788\r4   UIKit                               0x000000018c0a8968 \u003credacted\u003e + 108\r5   UIKit      0x000000018c0a9328 \u003credacted\u003e + 28\r6   UIKit                               0x000000018beea250 \u003credacted\u003e + 1320\r7   UIKit                               0x000000018beede98 \u003credacted\u003e + 188\r8   UIKit                               0x000000018bcb5820 \u003credacted\u003e + 116\r9   UIKit                               0x000000018bbdec88 \u003credacted\u003e + 760\r10  UIKit                               0x000000018bbde610 \u003credacted\u003e + 312\r11  UIKit                               0x000000018bbde31c \u003credacted\u003e + 296\r12  UIKit                               0x000000018bbde3bc \u003credacted\u003e + 456\r13  QuartzCore                          0x0000000185b93b7c \u003credacted\u003e + 284\r14  libdispatch.dylib                   0x00000001811a8a2c \u003credacted\u003e + 16\r15  libdispatch.dylib                   0x00000001811b5654 \u003credacted\u003e + 1012\r16  CoreFoundation                      0x0000000181851650 \u003credacted\u003e + 12\r17  CoreFoundation                      0x000000018184f1a8 \u003credacted\u003e + 2272\r18  CoreFoundation                      0x000000018176f488 CFRunLoopRunSpecific + 552\r19  GraphicsServices                    0x0000000183735020 GSEventRunModal + 100\r20  UIKit         0x000000018bc09cc0 UIApplicationMain + 236\r21  AppName                               0x000000010048f714 AppName + 30484\r22  libdyld.dylib 0x000000018120dfc0 \u003credacted\u003e + 4

不要包含位码,因为 Apple 可以重新编译您的代码,并且您在 Organizer 中存档的 dsym 文件将不匹配。

运行这样的 bash 脚本:

#!/bin/bash
appName=AppName
runtimeAddress=0x00000001006c75b4
relativeAddress=2356660
loadAddress=`echo "obase=16;ibase=10;$((runtimeAddress-relativeAddress))" | bc`
atos -o $appName.app/$appName -l $loadAddress $runtimeAddress -arch arm64

假设您已经创建了一个 test.sh (touch test.sh)。如果由于权限问题而无法运行 sctipt (./test.sh):调用 chmod +x test.sh 现在 ./test.sh 应该可以工作了。并生成结果。

也可以使用 AppName.app.dSYM 文件来表示崩溃文件:

#!/bin/bash
appName=AppName
runtimeAddress=0x00000001006c75b4
relativeAddress=2356660
loadAddress=`echo "obase=16;ibase=10;$((runtimeAddress-relativeAddress))" | bc`
atos -o $appName.app.dSYM/Contents/Resources/DWARF/$appName -l $loadAddress $runtimeAddress -arch arm64

【讨论】:

【参考方案5】:

我认为这篇文章可能会对您有所帮助,https://***.com/a/12559150/1773317。 乔的提交解决了我的问题。

原因是我的 .app 和 .dSYM 文件无法被 Spotlight 索引,所以我的 XCode 无法正确表示崩溃信息。

【讨论】:

【参考方案6】:

您可以尝试使用我为符号化编写的脚本,使用 atos 命令:

https://github.com/IdoTene/MacosSymbolicateCrash

【讨论】:

以上是关于atos 和 dwarfdump 不会象征我的地址的主要内容,如果未能解决你的问题,请参考以下文章

Symbolicating Crash Reports With atos

用 atos 进行符号化

象征性的崩溃报告

Xcode 5 中的象征性崩溃

iOS崩溃日志分析-b

为啥 Ruby 象征着我的哈希键?