简体   繁体   中英

Ignore certain exceptions when using Xcode's All Exceptions breakpoint

I have an All Exceptions breakpoint configured in Xcode:

在Xcode断点痛苦中配置的异常断点的屏幕截图,配置为在抛出异常时发出声音

Sometimes Xcode will stop on a line like:

[managedObjectContext save:&error];

with the following backtrace:

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

but the program continues on as if nothing happened if you click Continue.

How can I ignore these "normal" exceptions, but still have the debugger stop on exceptions in my own code?

(I understand that this happens because Core Data internally throws and catches exceptions, and that Xcode is simply honoring my request to pause the program whenever an exception is thrown. However, I want to ignore these so I can get back to debugging my own code!)

Moderators: this is similar to "Xcode 4 exception breakpoint filtering" , but I think that question takes too long to get around to the point and doesn't have any useful answers. Can they be linked?

For Core Data exceptions, what I typically do is remove the "All Exceptions" breakpoint from Xcode and instead:

  1. Add a Symbolic Breakpoint on objc_exception_throw
  2. Set a Condition on the Breakpoint to (BOOL)(! (BOOL)[[(NSException *)$x0 className] hasPrefix:@"_NSCoreData"])

The configured breakpoint should look something like this: 配置断点

This will ignore any private Core Data exceptions (as determined by the class name being prefixed by _NSCoreData ) that are used for control flow. Note that the appropriate register is going to be dependent on the target device / simulator that you are running in. Take a look at this table for reference.

Note that this technique can be adapted easily to other conditionals. The tricky part was in crafting the BOOL and NSException casts to get lldb happy with the condition.

I wrote an lldb script that lets you selectively ignore Objective-C exceptions with a much simpler syntax, and it handles both OS X, iOS Simulator, and both 32bit and 64bit ARM.

Installation

  1. Put this script in ~/Library/lldb/ignore_specified_objc_exceptions.py or somewhere useful.
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. Add the following to ~/.lldbinit :

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

    replacing ~/Library/lldb/ignore_specified_objc_exceptions.py with the correct path if you saved it somewhere else.

Usage

  • In Xcode, add a breakpoint to catch all Objective-C exceptions
  • Edit the breakpoint and add a Debugger Command with the following command: ignore_specified_objc_exceptions name:NSAccessibilityException className:NSSomeException
  • This will ignore exceptions where NSException -name matches NSAccessibilityException OR -className matches NSSomeException

It should look something like this:

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

In your case, you would use ignore_specified_objc_exceptions className:_NSCoreData

See http://chen.do/blog/2013/09/30/selectively-ignoring-objective-c-exceptions-in-xcode/ for the script and more details.

Here is an alternative quick answer for when you have a block of code eg a 3rd part library that throws multiple exceptions that you want to ignore:

  1. Set two breakpoints, one before and one after the exception throwing block of code you want to ignore.
  2. Run the program, until it stops at an exception, and type 'breakpoint list' into the debugger console, and find the number of the 'all exceptions' break point, it should look like this:

2: names = {'objc_exception_throw', '__cxa_throw'}, locations = 2 Options: disabled 2.1: where = libobjc.A.dylib objc_exception_throw, address = 0x00007fff8f8da6b3, unresolved, hit count = 0 2.2: where = libc++abi.dylib __cxa_throw, address = 0x00007fff8d19fab7, unresolved, hit count = 0

  1. This means it is breakpoint 2. Now in xcode, edit the first breakpoint (before the exception throwing code) and change the action to 'debugger command' and type in 'breakpoint disable 2' (and set 'automatically continue...' checkbox ).

  2. Do the same for the break point after the offending line and have the command 'breakpoint enable 2'.

The all breakpoints exception will now turn on and off so it's only active when you need it.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM