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



