如何在LLDB断点条件中使用堆栈内容?

10
问题:

我有一个情况,启动时需要媒体播放,而在那段时间内 objc_exception_throw() 会被触发大约5次,但总是被捕获,并且这远离了媒体播放器对象。

我厌倦了要么(a)手动继续n次,或者(b)在播放完成之前禁用断点。

我尝试过的:
  • 使断点忽略前五个击中(问题:不总是恰好五次)
  • 使用我的目标作为模块创建自己的符号断点(问题:没有任何更改)
我想要做的事情:

一个解决方案是在断点被触发时评估堆栈,如果特定的方法或函数在其中列出,则继续执行。但我不知道如何实现这一点。

欢迎其他想法。

2个回答

17
你可以使用Python来完成这个任务。
以下是一个忽略列表和一个可附加为断点命令的函数定义。
该函数获取回溯中的函数名称,并将这些名称与忽略列表进行交集设置。如果有任何名称匹配,则继续运行进程。这有效地跳过了不需要调试的堆栈。
(lldb) b objc_exception_throw
Breakpoint 1: where = libobjc.A.dylib`objc_exception_throw, address = 0x00000000000113c5
(lldb) script
Python Interactive Interpreter. To exit, type 'quit()', 'exit()' or Ctrl-D.
>>> ignored_functions = ['recurse_then_throw_and_catch']
def continue_ignored(frame, bp_loc, dict):
    global ignored_functions
    names = set([frame.GetFunctionName() for frame in frame.GetThread()])
    all_ignored = set(ignored_functions)
    ignored_here = all_ignored.intersection(names)
    if len(ignored_here) > 0:
        frame.GetThread().GetProcess().Continue()

quit()

(lldb) br comm add -F continue_ignored 1
(lldb) r

我对以下文件进行了测试,它成功跳过了recurse_then_throw_and_catch中的第一个抛出,并在throw_for_real中抛出时进入了调试器。

#import <Foundation/Foundation.h>

void
f(int n)
{
    if (n <= 0) @throw [NSException exceptionWithName:@"plugh" reason:@"foo" userInfo:nil];

    f(n - 1);
}

void
recurse_then_throw_and_catch(void)
{
    @try {
        f(5);
    } @catch (NSException *e) {
        NSLog(@"Don't care: %@", e);
    }
}

void
throw_for_real(void)
{
    f(2);
}

int
main(void)
{
    recurse_then_throw_and_catch();
    throw_for_real();
}

我想你可以将这个函数添加到你的.lldbinit中,然后根据需要从控制台连接到断点。(我认为你不能在Xcode内部设置脚本命令。)


该死,伙计。我所期望的最佳答案。 - MikeyWard
不错的回答。唯一的注意事项是,如果堆栈中出现任何ignored_functions,它将会continue,而不仅仅是检查“上面”的objc_exception_throw帧。 - Jason Molenda
@JasonMolenda 完全正确。根据“当断点命中时评估堆栈,并在其中列出特定方法或函数”的要求,我认为想要的是“堆栈上的任何位置”。可以简单地适应为执行if frame.GetThread().GetFrameAtIndex(1).GetFunctionName() in ignored_functions:。关键的洞察力是使用Python函数作为断点命令。 - Jeremy W. Sherman
1
请注意,从Xcode 5的lldb开始,lldb会检查Python断点命令的返回值,如果它们返回False布尔值,则lldb将从断点继续执行。这可能比手动调用“continue”更容易。这也意味着,如果您同时触发了两个断点(在线程化代码中确实会发生),只有当两个断点都想要继续时,lldb才会继续执行。 - Jim Ingham

4
break command add -s python -o "return any('xyz' in f.name for f in frame.thread)"

如果Python断点命令返回False,lldb会继续执行。所以这句话的意思是:如果堆栈中的任何一个帧(frame)的名称中包含字符串'xyz',则返回True(停止)。否则,如果没有帧的名称包含该字符串,则此any表达式将返回False(继续执行)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接