Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I use stack content in an LLDB breakpoint condition?

Tags:

xcode

lldb

The problem:

I've got a situation where we have a media playback during launch, and objc_exception_throw() hits about 5 times during that period, but is always caught, and it's way south of the media player object.

I'm tired of either (a) having to manually continue n times, or (b) having to leave breakpoints disabled until after the playback is complete.

What I've tried:

  • making the breakpoint ignore the first five hits (problem: it's not always exactly five times)
  • creating my own symbolic breakpoint using my target as the module (problem: nothing changed)

What I'd like to do:

One solution that comes to mind is to evaluate the stack when the breakpoint hits, and continue if a particular method or function is listed therein. But I have no idea how to do this.

Other ideas welcome as well.

like image 359
MikeyWard Avatar asked Jun 04 '13 16:06

MikeyWard


1 Answers

You do it using Python.

The following defines an ignore list and a function you can attach as a command to a breakpoint.

The function grabs the names of functions in the backtrace and set-intersects those names with the ignore list. If any names match, it continues running the process. This effectively skips dropping into the debugger for unwanted stacks.

(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

I tried it against the following file, and it successfully skips the first throw inside recurse_then_throw_and_catch and drops into the debugger during the throw inside 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();
}

I imagine you could add this function to your .lldbinit and then connect it to breakpoints as needed from the console. (I don't think you can set a script command from within Xcode.)

like image 94
Jeremy W. Sherman Avatar answered Sep 28 '22 09:09

Jeremy W. Sherman