13

Example: When my method -fooBar gets called, I want it to log in the console which other method of which other class called it.

Right now, I only know how to log the method name of fooBar itself and it's class, with this:

_cmd

[self class]

Is this possible to figure out?

9 Answers9

35

In fully optimized code, there is no 100% surefire way to determine the caller to a certain method. The compiler may employ a tail call optimization whereas the compiler effectively re-uses the caller's stack frame for the callee.

To see an example of this, set a breakpoint on any given method using gdb and look at the backtrace. Note that you don't see objc_msgSend() before every method call. That is because objc_msgSend() does a tail call to each method's implementation.

While you could compile your application non-optimized, you would need non-optimized versions of all of the system libraries to avoid just this one problem.

And this is just but one problem; in effect, you are asking "how do I re-invent CrashTracer or gdb?". A very hard problem upon which careers are made. Unless you want "debugging tools" to be your career, I would recommend against going down this road.

What question are you really trying to answer?

bbum
  • 162,346
  • 23
  • 271
  • 359
  • 8
    @alexgray How is it an anti-answer? The answer is exactly precise in scope and scale of the problem and, given the acceptance and final question, hopefully led the OP down a path to success. – bbum May 04 '13 at 06:37
7

How about this:

NSString *sourceString = [[NSThread callStackSymbols] objectAtIndex:1];

NSCharacterSet *separatorSet = [NSCharacterSet characterSetWithCharactersInString:@" -[]+?.,"];
NSMutableArray *array = [NSMutableArray arrayWithArray:[sourceString  componentsSeparatedByCharactersInSet:separatorSet]];
[array removeObject:@""];

NSLog(@"Class caller = %@", [array objectAtIndex:3]);
NSLog(@"Method caller = %@", [array objectAtIndex:4]);

Credits to the original author, intropedro.

Community
  • 1
  • 1
Maxim Chetrusca
  • 3,262
  • 1
  • 32
  • 28
  • 4
    That won't work in fully optimized code as tail-call optimizations make frames disappear entirely from the stack. – bbum May 13 '14 at 17:28
3

It's not possible in the general case without actually walking the stack. There's not even a guarantee that another object send the message that called the method. For example, it could be called from a block in a signal handler.

Chuck
  • 234,037
  • 30
  • 302
  • 389
3
NSLog(@"Show stack trace: %@", [NSThread callStackSymbols]);
Dmitry A.
  • 588
  • 8
  • 13
2

See backtrace(3).

Peter Hosey
  • 95,783
  • 15
  • 211
  • 370
2

User the below method
Pass index for which you want to display method and pass -1 if you want to display full stack of method

+(void) methodAtIndex:(int)index{
    void* callstack[128];
    int frames = backtrace(callstack, 128);
    char** strs = backtrace_symbols(callstack, frames);

    if (index == -1) {
        for (int i = 0; i < frames; ++i) {
            printf("%s\n", strs[i]);
        }
    }
    else {
        if (index < frames) {
            printf("%s\n", strs[index]);
        }
    }
    free(strs);

}
Crazy Developer
  • 3,464
  • 3
  • 28
  • 62
  • I'm getting `error: warning: couldn't get cmd pointer (substituting NULL): no variable named '_cmd' found in this frame` – ReDetection Nov 04 '14 at 05:37
1

This information can be obtained using DTrace.

arul
  • 13,998
  • 1
  • 57
  • 77
1

Make a macro that adds the __FUNCTION__ to the function name to the function call. This macro will then call your function with an extra parameter of a char* to the target function.

Robert Deml
  • 12,390
  • 20
  • 65
  • 92
  • That assumes that you have control over the caller and can change the ABI between caller and callee by changing the argumentation, which is rarely the case. – bbum Nov 25 '09 at 01:08
0

I was trying to catch who, how and when changes window's size and did some handwork:

- (void)logWindowWidth:(NSString *)whoCalls {
   NSLog(@"%@", whoCalls);
   NSLog(@"self.window.size.width %f", self.window.size.width);
}

-(void)someMethod {
  [self logWindowWidth:@"someMethod - before"];
  ...
  [self logWindowWidth:@"someMethod - after"];
}

-(void)anotherMethod {
  [self logWindowWidth:@"anotherMethod - before"];
  ...
  [self logWindowWidth:@"anotherMethod - after"];
}
Denis
  • 940
  • 12
  • 16