How to pinpoint EXC_BREAKPOINT in an escaping closure?

Greetings everybody.

I've spent a few hours trying to get to the bottom of this and I'm all out of ideas. I have an application that interfaces with a native C library using a Swift wrapper. For architectural reasons, all calls to that library's code must be done in its worker threads.

To solve this, all of our swift wrapper functions create closures that are then passed to the library's worker system. Along the following lines:

public func doSomeWork(withConfig config: LibConfig, andResult result: @escaping ResultCallback) {
    let cb: @convention(c) (UnsafeMutableRawPointer?) -> Void = { slotPtr in
        // do some work
    }
  
    lib_run_work(cb)
}

This setup is used in 30+ places and none of them cause any issues, except for one of the closure callbacks. (slotPtr is used for reading and writing additional information for the command from a thread-safe array, this works fine in all other cases as well).

The one that causes the problem generates the following crash report via Crashlytics (unfortunately, I've never been able to replicate it locally so far to get a full crash report).

Crashed: Thread #1
EXC_BREAKPOINT 0x0000000100ff7878
Crashed: Thread

0  MyApp                        0x1f878 closure #1 in LibStack.doSomeWork(withConfig:andResult:) + 4330895480 (LibStack.swift:4330895480)
1  MyApp                         0x21761c timer_cb + 4332959260

Now, the closure that is referenced is ~30 LOC, none of which contain any obvious issues such as force-unwrapping optionals, though we're interacting with the C library and therefore unsafe memory directly.

The crash happens at 0x0000000100ff7878 (which is 4330895480) as listed in the last stack trace frame. But how do I figure out which line actually causes this?

I cannot simply symbolicate the crash (I assume) since this address is from the heap where the closure was allocated for that specific call. It is different in every occurrence (even with the same device/build).

Is there any possibility to narrow down or even pinpoint the command/line that causes the trap?

Accepted Reply

I recommend that you disable your third-party crash reporter and attempt to get an Apple crash report for this. When debugging gnarly problems, you really need a crash report that you can rely on [1].

But how do I figure out which line actually causes this?

By symbolicating your crash report.

I cannot simply symbolicate the crash (I assume) since this address is from the heap where the closure was allocated for that specific call.

Your logic is flawed here. A closure is allocated from the heap but all the code executed by the closure is in your executable’s __TEXT segment [2]. Thus, you should be able to symbolicate this. With an Apple crash report the standard symbolication methods should deal with this. If they don’t, I’m happy to help you out with that.

If your third-party crash report isn’t symbolicating properly, you need to escalate this with its vendor. Alternatively, you can manually symbolicate using the process described in the Symbolicate the Crash Report with the Command Line section of Adding Identifiable Symbol Names to a Crash Report. This assumes that your third-party crash report contains an equivalent to the Binary Images section of an Apple crash report.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

[1] For an explanation as to why you can’t rely on third-party crash reports, see Implementing Your Own Crash Reporter.

[2] Allocating code in the heap is not possible on iOS due to security restrictions.

  • Thanks a lot for clearing up the actual allocation of the closure!

    I have so far not received a crash report via Apple crash reports, as most likely none of the (very few) affected users opted in to share this information. But with this, I am confident that I can figure out what's going wrong once I get a hold of a proper Apple crash report.

Add a Comment

Replies

I recommend that you disable your third-party crash reporter and attempt to get an Apple crash report for this. When debugging gnarly problems, you really need a crash report that you can rely on [1].

But how do I figure out which line actually causes this?

By symbolicating your crash report.

I cannot simply symbolicate the crash (I assume) since this address is from the heap where the closure was allocated for that specific call.

Your logic is flawed here. A closure is allocated from the heap but all the code executed by the closure is in your executable’s __TEXT segment [2]. Thus, you should be able to symbolicate this. With an Apple crash report the standard symbolication methods should deal with this. If they don’t, I’m happy to help you out with that.

If your third-party crash report isn’t symbolicating properly, you need to escalate this with its vendor. Alternatively, you can manually symbolicate using the process described in the Symbolicate the Crash Report with the Command Line section of Adding Identifiable Symbol Names to a Crash Report. This assumes that your third-party crash report contains an equivalent to the Binary Images section of an Apple crash report.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

[1] For an explanation as to why you can’t rely on third-party crash reports, see Implementing Your Own Crash Reporter.

[2] Allocating code in the heap is not possible on iOS due to security restrictions.

  • Thanks a lot for clearing up the actual allocation of the closure!

    I have so far not received a crash report via Apple crash reports, as most likely none of the (very few) affected users opted in to share this information. But with this, I am confident that I can figure out what's going wrong once I get a hold of a proper Apple crash report.

Add a Comment