【问题标题】:How can I find the address of a stack trace in LLDB for iOS如何在 LLDB for iOS 中找到堆栈跟踪的地址
【发布时间】:2013-08-09 09:18:22
【问题描述】:

当我收到崩溃报告时,我的代码的违规部分有时会如下所示,而不是显示实际的行号,即使崩溃报告是符号化的:

-[ViewController myMethod:] + 47  

为了调试它,我需要知道这代表我的代码的哪一行,以便我可以直观地检查它、设置断点等。

如上所示,使用LLDB获取方法的地址加上偏移量有什么好方法?

注意:这个问题不是how to read a crash report 的重复问题。我知道如何阅读崩溃报告。我非常具体地询问如何使用 LLDB 获取相应的行。其他答案中没有任何内容显示如何做到这一点。它们非常冗长,涉及到有关处理崩溃报告和一般调试的各种事情,但没有显示 LLDB 的具体步骤是什么。请不要复制此错误。

【问题讨论】:

  • 我即将给你一个糟糕的答案,但在紧张的情况下是可行的。将该数字转换为十进制,然后除以总线位。我称之为“我必须遍历的指令行数”。当然,还有另一个偏移量基于事物之间的确切存储内容。我有 2020 年,最终是 128 岁。所以基本上是“下降”。我发现了一个没有得到妥善保护的区域,它基本上帮助我解决了我的崩溃问题。猜测

标签: ios xcode lldb


【解决方案1】:

这是我发现有效的方法:

首先你需要找到方法本身的地址。

image lookup -v -F "-[ViewController myMethod:]"

在结果中你会看到很多信息,但范围部分会给你想要的

... range = [0x000708c0-0x00070c6c) ...

(其中 0x000708c0 是方法的地址)

现在添加给定的偏移量 47,只需使用 LLDB 为您计算:

(lldb) p/x 0x000708c0 + 47
(int) $0 = 0x000708ef

你得到你的答案,违规行在 0x000708ef

或者更好的是,根据 Jason Molenda 的回答,直接进入代码清单,它将显示行号:

(lldb) source list -a `0x000708c0 + 47`

编辑:根据 Jason Molenda 的回答进行了改进

【讨论】:

    【解决方案2】:

    [请注意,仅当您在 XCode 中保存您发布的所有构建的档案时,这才有效]

    你需要先收集的信息:

    • APPNAME:您的应用在存档目录中的简称(通常是 XCode 目标名称;当您在下面的 Finder 中查看存档目录时,您会立即看到它)。
    • CRASHING_FUNCTION_NAME:在无用的 Apple 回溯中显示的函数名称(在 OP 的示例中,-[ViewController myMethod:]
    • ARCH:崩溃设备的架构。最有可能的正确值是armv7arm64。如果您不知道,请尝试两者。

    好的,步骤如下:

    1. 在 XCode 中转到 Window...Organizer...Archives
    2. 右键单击崩溃用户所拥有的版本的存档,然后选择在 Finder 中显示
    3. 打开终端外壳并cd 到 Finder 中显示的目录
    4. 在 shell 中执行以下命令:

      lldb -arch ARCH Products/Applications/APPNAME.app/APPNAME
      
    5. 在 lldb 内部执行以下操作:

      (lldb) add-dsym dSYMs/APPNAME.app.dSYM/Contents/Resources/DWARF/APPNAME
      
      (lldb) disassemble --name CRASHING_FUNCTION_NAME
      
    6. 您现在看到一个带有符号的丰富反汇编,你瞧,每一行都显示与原始无用 Apple 回溯相同的十进制偏移量(在 OPs 示例中,无用偏移量是 47),如下所示:

      APPNAME[0xf4a7c] <+47>:  ldr    r0, [r0, r1]
      
    7. 如果反汇编有足够的符号来帮助您确定您所在的位置,您也许可以仅从这些信息中找出相应的源代码行。

    8. 如果没有,还有另一个很棒的技巧。传递崩溃行的地址:

      (lldb) image lookup -v --address 0xf4a7c
      
    9. 现在lldb 向您展示了丰富的信息集合---比 Apple 堆栈回溯显示的更丰富,即使它们确实包含行号,并且比 lldb source list 丰富得多---关于该地址的汇编指令的所有源代码行。密切关注SummaryLineEntry 部分。示例:

      Address: APPNAME[0x000f4a7c] (APPNAME.__TEXT.__text + 963740)
      Summary: APPNAME`myclass::myfunc(bool, bool) + 904 [inlined] std::__1::deque<mystruct, std::__1::allocator<mystruct> >::operator[](unsigned long) + 22 at myfile.cpp:37945
               APPNAME`myclass::myfunc(bool, bool) + 882 [inlined] myinlinefunc(int) + 14 at myfile.cpp:65498
               APPNAME`myclass::myfunc(bool, bool) + 868 at myfile.cpp:65498
      Module: file = "/Users/myuser/mydir/arch/Products/Applications/APPNAME.app/APPNAME", arch = "armv7"
      CompileUnit: id = {0x000483a4}, file = "/Users/myuser/mydir/myfile.cpp", language = "objective-c++"
      Function: id = {0x0045edde}, name = "myfunc", range = [0x000f46f4-0x000f572a)
      FuncType: id = {0x0045edde}, decl = myfile.cpp:65291, compiler_type = "void (_Bool, _Bool)"
      Blocks: id = {0x0045edde}, range = [0x000f46f4-0x000f572a)
              id = {0x0045f7d8}, ranges = [0x000f4936-0x000f51c0)[0x000f544c-0x000f5566)[0x000f5570-0x000f5698)
              id = {0x0046044c}, ranges = [0x000f49c6-0x000f49ce)[0x000f49d6-0x000f49d8)[0x000f4a2e-0x000f4a38)[0x000f4a58-0x000f4a82), name = "myinlinefunc", decl = myfile.cpp:37938, mangled = _Z11myinlinefunci, demangled = myinlinefunc(int)
              id = {0x00460460}, ranges = [0x000f4a58-0x000f4a64)[0x000f4a66-0x000f4a82), name = "operator[]", decl = deque:1675, mangled = _ZNSt3__15dequeI12mystructNS_9allocatorIS1_EEEixEm, demangled = std::__1::deque<mystruct, std::__1::allocator<mystruct> >::operator[](unsigned long)
      LineEntry: [0x000f4a7c-0x000f4a82): /Applications/Xcode7.3.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/deque:1678:14
      Symbol: id = {0x00000805}, range = [0x000f46f4-0x000f572a), name="myclass::myfunc(bool, bool)", mangled="_ZN7myclass7myfuncEbb"
      Variable: id = {0x00460459}, name = "myvar1", type = "int", location =     , decl = myfile.cpp:37938
      Variable: id = {0x0045f7dd}, name = "myvar2", type = "bool", location =  , decl = myfile.cpp:65583
      Variable: id = {0x0045edf2}, name = "this", type = "myclass *", location =  [sp+56], decl =
      Variable: id = {0x0045ee01}, name = "myvar3", type = "bool", location = , decl = myfile.cpp:65291
      Variable: id = {0x0045ee0e}, name = "myvar4", type = "bool", location = , decl = myfile.cpp:65292
      
    10. 在这个Summary 下的示例中,我们可以看到崩溃的行实际上是来自myclass::myfunc()myinlinefunc()std::deque::operator[] 的代码组合。这种混搭在优化代码中非常常见。这通常足以找到代码的违规源代码行。在LineEntry 下,我们可以看到构成该汇编程序行的最嵌套代码的行号,在本例中位于 STL std::deque 代码中,但在其他情况下,可能是您在代码中想要的确切行号。

    11. 现在剩下的唯一问题是:为什么在地球上 Apple 不只是在原始回溯中为我们执行此操作?他们显然拥有所有这些信息!为什么他们让我们跳过这样的圈子?他们在隐藏什么?

    【讨论】:

    • 非常感谢!
    • 这是非常有用的答案!谢谢!如何快速使用它? disassemble —name SwiftClass.foo() 说“无法找到名称为 ... 的符号”
    【解决方案3】:

    您的步骤 (image lookup + p/x addr + offset) 将为您提供您找到的原始地址。但最初的崩溃报告可能在方法 + 偏移量之前包含一个地址 --- 使用 target modules load 将二进制文件滑动到正确的地址同样容易。在崩溃报告的末尾应该有一个程序中存在的二进制图像列表,包括加载地址和 UUID。

    但更重要的是,虽然地址很好,但您真正追求的是源位置。在这种情况下,一旦您确定了该方法的正确地址(或通过target modules load 将其滑动到匹配地址),您就可以使用source list

    (lldb) so l -a `addr + offset`
    

    我在这里使用反引号表示法,它进行内联表达式评估。大多数带有地址的命令都有一个方便的快捷方式:如果省略空格,则可以编写不带反引号的表达式:

    (lldb) so l -a addr+offset
    

    您还可以将image lookup 与地址一起使用。如果您有调试信息,这将告诉您此时变量的当前位置。为什么这很有用?因为大多数崩溃报告都包含崩溃时的寄存器上下文,所以当前在寄存器中的所有变量都会提供给您(-v 是获取所有寄存器位置信息所必需的)。

    (lldb) im loo -v -a addr+offset
    

    最后——这是行不通的,因为你正在处理一个 Objective-C 方法名——但是使用一个简单的 C 函数名,只要你转换函数,你就可以内联进行偏移算术指针类型的名称(向函数指针添加偏移量是不合法的 C)。例如

    (lldb) so l -a (char*)main+10
    

    【讨论】:

    • 当我在 XCode 中运行应用程序并执行以下命令时:im loo -v -a $pc 我得到显示变量的输出,但无论如何我看不到将这些与其中的内容相匹配寄存器。它列出了他们的位置,但没有列出可能在寄存器中的位置。变量:id = {0x000000a4}, name = "self", type= "ViewController *const", location = DW_OP_fbreg(8), decl = ViewController.m:30 所以我似乎没有得到任何“注册位置信息” ”。我做错了吗?
    • 啊,是的,位置描述是 DWARF 表达语言,我忘了提。 “fbreg”的意思是“frame base reg”,lldb 给你一个“$fp”寄存器别名,这可能是正确的。如果您正在调试 32 位进程(arm、模拟器),您将执行 x/x $fp+8 - 如果您正在调试 64 位进程 (x86_64),您将执行 x/gx $fp+8 以查看值。 $fp+8 是堆栈内存的地址,x 命令(memory read 的别名)读取该地址的 4 字节(x)或 8 字节(gx)内存。
    • 由于图像查找不会告诉您哪些变量存储在寄存器中,那么我想我们应该从崩溃报告中删除有关使用寄存器上下文的注释。也许还有其他方法可以做到这一点,但这确实超出了问题的范围。您介意我编辑您的答案以删除该部分吗?
    • 另外,我不清楚如何使用目标模块加载来滑动我的二进制文件。一旦我在设备上运行该应用程序,就为时已晚,对吧?还是您以某种方式从命令行执行操作?我试图找到有关如何执行此操作的说明,但找不到。
    • 请不要编辑答案说image lookup 没有说明寄存器变量存在于什么位置。对于未优化的程序,它们往往在堆栈上有规范的位置(fbreg + offset 的东西)但在优化的程序中,它们只会存在于寄存器中。如果您正在调试实时进程(听起来像是),则无需通过 target modules load 设置二进制文件的加载地址 - lldb 将自动从动态链接加载器 (dyld) 获取正确的加载地址. ta mo loa 仅用于静态(未运行)崩溃跟踪分析。
    猜你喜欢
    • 2010-10-31
    • 1970-01-01
    • 1970-01-01
    • 2015-10-08
    • 2013-10-02
    • 2019-06-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多