使用Xcode的All Exceptions断点时忽略某些异常


78

我在Xcode中配置了一个All Exceptions断点:

Xcode断点痛苦中配置的异常断点的屏幕快照,配置为在引发异常时发出声音

有时Xcode会停在以下行中:

[managedObjectContext save:&error];

具有以下回溯:

回溯显示NSPersistentStoreCoordinator在保存调用中抛出异常:

但如果您单击“继续”,程序将继续运行,好像什么都没发生。

如何忽略这些“常规”异常,但仍然让调试器停止自己代码中的异常?

(我知道发生这种情况是因为Core Data在内部抛出并捕获了异常,并且Xcode只是在每次抛出异常时都接受我的暂停程序的请求。但是,我想忽略这些,以便回到调试自己的代码上!)

主持人:这类似于“ Xcode 4异常断点过滤”,但是我认为这个问题花很长时间才能解决问题,并且没有任何有用的答案。它们可以链接吗?


您能否更清楚地了解“某些例外”?
Jesse Rusak 2013年

啊,对不起!在我准备好之前发布堆栈溢出(不小心在标签字段中按Enter。)我将进行编辑。=(
Phil Calvin

这似乎与另一个问题完全相同。关闭它并在另一个上赏金怎么样?如果您不清楚,也可以建议对另一个进行清理。
杰西·鲁萨克

Answers:


41

我编写了一个lldb脚本,该脚本使您可以使用更简单的语法选择性地忽略Objective-C异常,它可以处理OS X,iOS Simulator以及32位和64位ARM。

安装

  1. 将此脚本~/Library/lldb/ignore_specified_objc_exceptions.py放在有用的地方。
import lldb
import re
import shlex

# This script allows Xcode to selectively ignore Obj-C exceptions
# based on any selector on the NSException instance

def getRegister(target):
    if target.triple.startswith('x86_64'):
        return "rdi"
    elif target.triple.startswith('i386'):
        return "eax"
    elif target.triple.startswith('arm64'):
        return "x0"
    else:
        return "r0"

def callMethodOnException(frame, register, method):
    return frame.EvaluateExpression("(NSString *)[(NSException *)${0} {1}]".format(register, method)).GetObjectDescription()

def filterException(debugger, user_input, result, unused):
    target = debugger.GetSelectedTarget()
    frame = target.GetProcess().GetSelectedThread().GetFrameAtIndex(0)

    if frame.symbol.name != 'objc_exception_throw':
        # We can't handle anything except objc_exception_throw
        return None

    filters = shlex.split(user_input)

    register = getRegister(target)


    for filter in filters:
        method, regexp_str = filter.split(":", 1)
        value = callMethodOnException(frame, register, method)

        if value is None:
            output = "Unable to grab exception from register {0} with method {1}; skipping...".format(register, method)
            result.PutCString(output)
            result.flush()
            continue

        regexp = re.compile(regexp_str)

        if regexp.match(value):
            output = "Skipping exception because exception's {0} ({1}) matches {2}".format(method, value, regexp_str)
            result.PutCString(output)
            result.flush()

            # If we tell the debugger to continue before this script finishes,
            # Xcode gets into a weird state where it won't refuse to quit LLDB,
            # so we set async so the script terminates and hands control back to Xcode
            debugger.SetAsync(True)
            debugger.HandleCommand("continue")
            return None

    return None

def __lldb_init_module(debugger, unused):
    debugger.HandleCommand('command script add --function ignore_specified_objc_exceptions.filterException ignore_specified_objc_exceptions')
  1. 将以下内容添加到~/.lldbinit

    command script import ~/Library/lldb/ignore_specified_objc_exceptions.py

    ~/Library/lldb/ignore_specified_objc_exceptions.py如果您将其保存在其他位置,请替换为正确的路径。

用法

  • 在Xcode中,添加一个断点以捕获所有Objective-C异常
  • 编辑断点并使用以下命令添加调试器命令: ignore_specified_objc_exceptions name:NSAccessibilityException className:NSSomeException
  • 这将忽略NSException -name匹配NSAccessibilityException-className匹配的异常NSSomeException

它看起来应该像这样:

屏幕截图显示了按照说明在Xcode中设置的断点

在您的情况下,您将使用 ignore_specified_objc_exceptions className:_NSCoreData

有关脚本和更多详细信息,请参见http://chen.do/blog/2013/09/30/selectively-ignoring-objective-c-exceptions-in-xcode/


这对我来说真的很好。您是否愿意将您的脚本和安装说明直接贡献给Stack Overflow(从而将它们许可为cc-wiki?)。如果是,我将接受此答案!
菲尔·加尔文

我应该在最后一个响应中标记@chendo。
菲尔·加尔文

@PhilCalvin是否可以将它作为MIT许可?
chendo

1
效果很好。再次强调一下-您需要将断点设置为“ Objective-C”,因为这里还会引发C ++异常。
Matej Bukovinski 2014年

1
在Xcode 5.1中工作得很好。一个重要的细节:您必须选择Objective-C作为异常类型(如说明中所述。)
Phil Calvin 2014年

87

对于核心数据异常,我通常要做的是从Xcode中删除“所有异常”断点,而改为:

  1. 在上添加符号断点 objc_exception_throw
  2. 在断点上设置一个条件为 (BOOL)(! (BOOL)[[(NSException *)$x0 className] hasPrefix:@"_NSCoreData"])

配置的断点应如下所示: 配置断点

这将忽略_NSCoreData用于控制流的任何私有Core Data异常(由以前缀的类名确定)。请注意,适当的寄存器将取决于您要在其中运行的目标设备/模拟器。请查看此表以作参考。

注意,此技术可以轻松地适应其他条件。棘手的部分是精心设计BOOL和NSException强制转换,以使lldb对条件感到满意。


非常有用,特别是在多个线程上使用Core Data时,这些错误异常的发生率更高!非常感谢你!
bmasters

1
我在设备上尝试此$r0(BOOL)(! (BOOL)[[(NSException *)$r0 className] hasPrefix:@”_NSCoreData”])。但是,这不起作用。我在控制台中得到以下内容。Stopped due to an error evaluating condition of breakpoint 1.1: "(BOOL)(! (BOOL)[[(NSException *)$r0 className] hasPrefix:@‚Äù_NSCoreData‚Äù])" error: unexpected '@' in program error: 1 errors parsing expression
lammert

@lammert,您可能希望将示例中复制的引号替换为实际引号。您复制的内容包括美化的引号。
杰里米·富

我只是在Xcode 4.6.3(4H1503)中尝试过,它不喜欢currentName选择器。更改它[(NSException *)$eax name]为我工作。
亚当·夏普

4
iPhone6上具有iOS 8.2的Xcode 6.2需要我将更$r0改为$x0(在此处定义:sealiesoftware.com/blog/archive/2013/09/12/…)。因此,情况变为:(BOOL)(! (BOOL)[[(NSException *)$x0 className] hasPrefix:@"_NSCoreData"])
Wim Fikkert

16

这是当您有代码块(例如,第3部分库)引发多个要忽略的异常时的替代快速解答:

  1. 设置两个断点,在要忽略的异常抛出代码块之前设置一个,在断点之后设置一个。
  2. 运行程序,直到它停止出现异常为止,然后在调试器控制台中键入“ breakpoint list”,并找到“所有异常”断点的编号,它应如下所示:

2:名称= {'objc_exception_throw','__cxa_throw'},位置= 2选项:禁用2.1:其中= libobjc.A.dylib objc_exception_throw, address = 0x00007fff8f8da6b3, unresolved, hit count = 0 2.2: where = libc++abi.dylib__cxa_throw,地址= 0x00007fff8d19fab7,未解析,命中计数= 0

  1. 这意味着它是断点2。现在在xcode中,编辑第一个断点(在引发异常之前),并将操作更改为“ debugger command”,然后键入“ breakpoint disable 2”(并设置“ automatically Continue ...”复选框, )。

  2. 对违规行之后的断点执行相同的操作,并使用命令“ breakpoint enable 2”。

现在,所有断点异常都将打开和关闭,因此仅在需要时才处于活动状态。


谢谢!正是我想要的。虽然我找不到异常ID,但只是蛮力强行执行了。您可以更详细地描述该部分吗?我的意思是确切地在Xcode中粘贴“断点列表”的位置以查看断点ID /位置?
vir我们

惊人!像魅力一样工作!
谢尔盖·格里斯乔夫

1
极好-此解决方案比其他一些答案更简单明了,并且效果很好。
ajgryc

辉煌!这是SO答案的瑰宝,应该被接受。
jb
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.