Setting conditional breakpoint on Cocoa method?

  • Once in a blue moon, I get a console message that a nil string was
    passed to [NSConcreteAttributedString initWithString:] I'd like to
    find out where this is coming from by setting a breakpoint there, but
    only for a nil string. Adding the breakpoint as a symbol to the
    symbolic breakpoints works, but I can't set the condition (e.g.
    aString == nil), since the parameter's name isn't available.

    What can I do, since this method is called hundreds of times in the
    normal course of things, so just having an unconditional breakpoint
    isn't very useful. Can I specify a register somehow?

    tia,

    Graham
  • There's probably a better way, but you could create an if statement
    containing a log message (or something) that has a breakpoint (e.g.:

    if (aString == nil)
    {
    NSLog(@"blah") <-- Breakpoint here
    }

    )

    Kinda stupid solution, though.

    Alex

    On Jul 28, 2008, at 12:02 AM, Graham Cox wrote:

    > Once in a blue moon, I get a console message that a nil string was
    > passed to [NSConcreteAttributedString initWithString:] I'd like to
    > find out where this is coming from by setting a breakpoint there,
    > but only for a nil string. Adding the breakpoint as a symbol to the
    > symbolic breakpoints works, but I can't set the condition (e.g.
    > aString == nil), since the parameter's name isn't available.
    >
    > What can I do, since this method is called hundreds of times in the
    > normal course of things, so just having an unconditional breakpoint
    > isn't very useful. Can I specify a register somehow?
    >
    > tia,
    >
    > Graham
  • On Mon, Jul 28, 2008 at 8:02 AM, Graham Cox <graham.cox...> wrote:
    > Once in a blue moon, I get a console message that a nil string was passed to
    > [NSConcreteAttributedString initWithString:] I'd like to find out where this
    > is coming from by setting a breakpoint there, but only for a nil string.
    > Adding the breakpoint as a symbol to the symbolic breakpoints works, but I
    > can't set the condition (e.g. aString == nil), since the parameter's name
    > isn't available.
    >
    > What can I do, since this method is called hundreds of times in the normal
    > course of things, so just having an unconditional breakpoint isn't very
    > useful. Can I specify a register somehow?
    >

    Take a look at the debugging magic page -
    http://developer.apple.com/technotes/tn2004/tn2124.html
    I believe you want *(int*)($ebp+16)
  • Ah thanks, that's very helpful.

    One thing that I'm a bit unsure about. If I enter a symbolic
    breakpoint, will it break on the first instruction of that method or
    further along? It matters because that doc states:

    "If you've stopped at the first instruction of a routine, the first
    parameter is at 4 bytes above the stack pointer, the second parameter
    is 8 bytes above the stack pointer, and so on. The GDB syntax is *(int
    *)($esp+4), *(int *)($esp+8), and so on.

    If you've stopped elsewhere in the routine (after the frame has been
    created), the first parameter is at 8 bytes above the frame pointer,
    the second parameter is 12 bytes above the frame pointer, and so on.
    The GDB syntax is *(int *)($ebp+8), *(int *)($ebp+12), and so on."

    Assuming the first case, wouldn't the offset be 12, not 16? (two
    implied parameters then the first visible one). The second case would
    be 16 of course - so I need to know where a symbolic breakpoint
    actually stops.

    cheers, Graham

    On 28 Jul 2008, at 6:47 pm, Jonathan del Strother wrote:

    > On Mon, Jul 28, 2008 at 8:02 AM, Graham Cox <graham.cox...>
    > wrote:
    >> Once in a blue moon, I get a console message that a nil string was
    >> passed to
    >> [NSConcreteAttributedString initWithString:] I'd like to find out
    >> where this
    >> is coming from by setting a breakpoint there, but only for a nil
    >> string.
    >> Adding the breakpoint as a symbol to the symbolic breakpoints
    >> works, but I
    >> can't set the condition (e.g. aString == nil), since the
    >> parameter's name
    >> isn't available.
    >>
    >> What can I do, since this method is called hundreds of times in the
    >> normal
    >> course of things, so just having an unconditional breakpoint isn't
    >> very
    >> useful. Can I specify a register somehow?
    >>
    >
    > Take a look at the debugging magic page -
    > http://developer.apple.com/technotes/tn2004/tn2124.html
    > I believe you want *(int*)($ebp+16)
  • I need to break on a Cocoa method that I don't have the source to. If
    I had the source, I could just set a breakpoint by clicking in the
    margin in the usual way.

    Thanks anyway,

    Graham

    On 28 Jul 2008, at 6:11 pm, Alex Heinz wrote:

    > There's probably a better way, but you could create an if statement
    > containing a log message (or something) that has a breakpoint (e.g.:
    >
    > if (aString == nil)
    > {
    > NSLog(@"blah") <-- Breakpoint here
    > }
    >
    > )
  • On Jul 28, 2008, at 7:30 AM, Graham Cox wrote:

    > One thing that I'm a bit unsure about. If I enter a symbolic
    > breakpoint, will it break on the first instruction of that method
    > or further along?

    The breakpoint is after the local frame has been set up:

    $ gdb /Applications/TextEdit.app/Contents/MacOS/TextEdit
    [...]
    (gdb) run
    Starting program: /Applications/TextEdit.app/Contents/MacOS/TextEdit
    [...]
    ^C
    Program received signal SIGINT, Interrupt.
    0x90009cd7 in mach_msg_trap ()
    (gdb) break -[NSConcreteAttributedString initWithString:]
    Breakpoint 1 at 0x927fb612
    (gdb) info break
    Num Type          Disp Enb Address    What
    1  breakpoint    keep y  0x927fb612 <-[NSConcreteAttributedString
    initWithString:]+5>

    Notice the "+5" in the breakpoint description.  If you were to hit
    that breakpoint and disassemble the current function, you'd see it's
    past the point where the old frame pointer is pushed and the current
    stack pointer is copied to be the new frame pointer.

    Cheers,
    Ken
  • Thanks Ken and Jonathan - with your help I got the debugger to reveal
    the source of the bug and fixed it. (And learned a useful new weapon
    in the process).

    cheers, Graham

    On 29 Jul 2008, at 9:38 am, Ken Thomases wrote:

    > On Jul 28, 2008, at 7:30 AM, Graham Cox wrote:
    >
    >> One thing that I'm a bit unsure about. If I enter a symbolic
    >> breakpoint, will it break on the first instruction of that method
    >> or further along?
    >
    > The breakpoint is after the local frame has been set up:
    >
    > $ gdb /Applications/TextEdit.app/Contents/MacOS/TextEdit
    > [...]
    > (gdb) run
    > Starting program: /Applications/TextEdit.app/Contents/MacOS/TextEdit
    > [...]
    > ^C
    > Program received signal SIGINT, Interrupt.
    > 0x90009cd7 in mach_msg_trap ()
    > (gdb) break -[NSConcreteAttributedString initWithString:]
    > Breakpoint 1 at 0x927fb612
    > (gdb) info break
    > Num Type          Disp Enb Address    What
    > 1  breakpoint    keep y  0x927fb612 <-[NSConcreteAttributedString
    > initWithString:]+5>
    >
    > Notice the "+5" in the breakpoint description.  If you were to hit
    > that breakpoint and disassemble the current function, you'd see it's
    > past the point where the old frame pointer is pushed and the current
    > stack pointer is copied to be the new frame pointer.
    >
    > Cheers,
    > Ken
  • Hi Graham,

    On Mon, Jul 28, 2008 at 5:02 PM, Graham Cox <graham.cox...> wrote:

    > Once in a blue moon, I get a console message that a nil string was passed
    > to [NSConcreteAttributedString initWithString:] I'd like to find out where
    > this is coming from by setting a breakpoint there, but only for a nil
    > string. Adding the breakpoint as a symbol to the symbolic breakpoints works,
    > but I can't set the condition (e.g. aString == nil), since the parameter's
    > name isn't available.
    >
    > What can I do, since this method is called hundreds of times in the normal
    > course of things, so just having an unconditional breakpoint isn't very
    > useful. Can I specify a register somehow?

    I realise that you used GDB to solve this problem but another way that you
    can solve these kinds of problems is to use method_exchangeImplementations.
    It's slightly tricky with NSConcreteAttributedString because it's not
    publicly declared anywhere but I believe it's still possible (albeit with a
    couple of compiler warnings):

    #include <Foundation/Foundation.h>

    #include <objc/runtime.h>

    @class NSConcreteAttributedString;

    @implementation NSAttributedString (Override)

    - (id)myInitWithString:(NSString *)string

    {

      if (!string)

        NSLog (@"nil string");

      return [self myInitWithString:string];

    }

    @end

    int main ()

    {

      [[NSAutoreleasePool alloc] init];

      Method m1 = class_getInstanceMethod ([NSConcreteAttributedString class],

          @selector (myInitWithString:));

      Method m2 = class_getInstanceMethod ([NSConcreteAttributedString class],

          @selector (initWithString:));

      method_exchangeImplementations (m1, m2);

      [[NSAttributedString alloc] initWithString:nil];

      return 0;

    }

    Another thing that you could have done is have a look at the disassembly for
    -[NSConcreteAttributedString initWithString:]. The test for a nil parameter
    is obviously going to be early on and the call to a function called
    _nilArgumentWarning suggests a likely place for a breakpoint.

    -- Chris