当我收到崩溃报告时,我的代码中有问题的部分有时看起来像这样,而不是向我显示实际的行号,即使崩溃报告是象征性的:
-[ViewController myMethod:] + 47
为了调试这个,我需要知道我代码的代码行,以便我可以直观地检查它,设置一个断点等。
如上所示,使用LLDB获取方法地址加偏移量的好方法是什么?
注意:此问题与how to read a crash report不重复。我知道如何阅读崩溃报告。我非常具体地询问如何使用LLDB获取相应的行。其他答案中没有任何内容显示如何做到这一点。它们非常冗长,并且涉及一般处理崩溃报告和调试的各种事情,但没有说明LLDB的具体步骤是什么。请不要复制此错误。
答案 0 :(得分:7)
您找到的步骤(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)。 e.g。
(lldb) so l -a (char*)main+10
答案 1 :(得分:6)
我发现这是有效的:
首先,您需要找到方法本身的地址。
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 :(得分:5)
[请注意,只有在您发布的所有版本的XCode中保存档案时才会有效]
首先需要收集的信息:
-[ViewController myMethod:]
)armv7
或arm64
。如果您不知道,请尝试两者。好的以下是步骤:
cd
到Finder 在shell中执行以下命令:
lldb -arch ARCH Products/Applications/APPNAME.app/APPNAME
执行以下操作:
(lldb) add-dsym dSYMs/APPNAME.app.dSYM/Contents/Resources/DWARF/APPNAME
(lldb) disassemble --name CRASHING_FUNCTION_NAME
你现在看到带有符号的丰富反汇编,并且看,每行显示与原始无用Apple回溯相同的十进制偏移(在OP示例中,无用的偏移为47
),如:
APPNAME[0xf4a7c] <+47>: ldr r0, [r0, r1]
如果反汇编有足够的符号来帮助您弄清楚自己的位置,您可以从这些信息中找出相应的源代码行。
如果没有,还有另一个很棒的伎俩。传递崩溃线的地址:
(lldb) image lookup -v --address 0xf4a7c
现在lldb
向您展示了丰富的信息集合--- 更丰富,而不是Apple堆栈回溯显示的信息,即使它们包含行号,也更丰富比lldb source list
---关于在该地址处对汇编指令做出贡献的所有源代码行。密切关注Summary
和LineEntry
部分。例如:
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
在Summary
下的此示例中,我们可以看到崩溃的行实际上是来自myclass::myfunc()
,myinlinefunc()
和std::deque::operator[]
的代码组合。对于优化的代码,这种混合是非常常见的。这通常足以找到代码中有问题的源代码行。在LineEntry
下,我们看到有助于该汇编程序行的最嵌套代码的行号,在本例中是在STL std::deque
代码中,但在其他情况下可能是您想要的确切行号在你的代码中。
现在唯一剩下的问题是:为什么在地球上并不是Apple在原始回溯中为我们做这件事?他们自己显然拥有所有这些信息!他们为什么让我们跳过这样的箍?他们藏着什么?