Jay
Jay

Reputation: 20136

Capturing all methods/message calls on an object

How do I put a "hook" on an object so I can see what messages are being sent to it? (ie do an NSLog() every time a message is sent to an object).

I think recall seeing this done before but I forget how. I am thinking it might help me track down why part of my code is not working.

Upvotes: 6

Views: 2287

Answers (4)

smokris
smokris

Reputation: 11840

Following up on Louis Gerbarg's comment from last year, it's also useful to be able to easily filter by class name.

Try the following D script:

 #!/usr/sbin/dtrace -s
 #pragma D option quiet
 objc$target:::entry
 /strstr(probemod,$$1) != NULL/
 {
     printf("%s %s\n", probemod, probefunc);
 }

Save it, chmod a+x objc-calls.d, and then do sudo objc-calls.d -c /Your/Binary NSObject to see just the invocations related to NSObject (and its categories).

Upvotes: 1

mfazekas
mfazekas

Reputation: 5699

You can also use objective-c forwarding. Basically you can create a proxy object that logs the methods then forwards the call to the original. See my blog post for more details.

@interface LoggerProxy : NSObject
{
    id original;
}

- (id)initWithOriginal:(id) value;

@end
@implementation LoggerProxy

- (id) initWithOriginal:(id)value
{
    if (self = [super init]) {
        original = value;
    }
    return self;
}

- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel
{
    NSMethodSignature *sig = [super methodSignatureForSelector:sel];
    if(!sig)
    {
        sig = [original methodSignatureForSelector:sel];
    }
    return sig;
}

- (void)forwardInvocation:(NSInvocation *)inv
{
    NSLog(@"[%@ %@] %@ %@", original, inv,[inv methodSignature],
         NSStringFromSelector([inv selector]));
    [inv invokeWithTarget:original];
}

@end

Upvotes: 18

Louis Gerbarg
Louis Gerbarg

Reputation: 43452

The best way to do this is with dtrace or an instruments script. Using dtrace you can do the following:

Write the following script as objc-calls.d

#pragma D option quiet
objc$target:::entry
{
   printf("%s %s\n", probemod, probefunc);
}

Then run the app using the script:

setenv DYLD_SHARED_REGION avoid
sudo dtrace -s objc-calls.d -c /Path/To/Your/App/Binary

You can also build a custom Instrument using a similiar dtrace probe.

Upvotes: 8

teabot
teabot

Reputation: 15444

You could use a NSProxy object and override the forwardInvocation: method.

Upvotes: 0

Related Questions