It's normally easy to log all messages sent to a class with DTrace, for example by using trace_msg_send.sh
script from here:
#!/usr/sbin/dtrace -s
#pragma D option quiet
unsigned long long indention;
int indentation_amount;
BEGIN {
indentation_amount = 4;
}
objc$target:$1::entry
{
method = (string)&probefunc[1];
type = probefunc[0];
class = probemod;
printf("%*s%s %c[%s %s]\n", indention * indentation_amount, "", "->", type, class, method);
indention++;
}
objc$target:$1::return
{
indention--;
method = (string)&probefunc[1];
type = probefunc[0];
class = probemod;
printf("%*s%s %c[%s %s]\n", indention * indentation_amount, "", "<-", type, class, method);
}
Given the code:
$ cat > test.m
#include <stdio.h>
#import <Foundation/Foundation.h>
@interface TestClass : NSObject
+ (int)randomNum;
@end
@implementation TestClass
+ (int)randomNum {
return 4; // chosen by fair dice roll.
// guaranteed to be random.
}
@end
int main(void) {
printf("num: %d\n", [TestClass randomNum]);
return 0;
}
^D
you would get the following output by using the DTrace script:
$ gcc test.m -lobjc -o test
$ ./test
num: 4
$ sudo ./trace_msg_send.d -c ./test TestClass
num: 4
-> +[TestClass randomNum]
<- +[TestClass randomNum]
However if we strip the binary:
$ strip test
$ sudo ./trace_msg_send.d -c ./test TestClass
dtrace: failed to compile script ./trace_msg_send.d: line 11: probe description objc49043:TestClass::entry does not match any probes
the script no longer works.
Is there a way of telling DTrace, where the stripped classes/message implementations are located?
Maybe using the info available from class-dump
, like in the answer for Import class-dump info into GDB.
I have created a script that tries to compare the classname at runtime, however it doesn't seem to work:
#!/usr/sbin/dtrace -s
#pragma D option quiet
unsigned long long indention;
int indentation_amount;
BEGIN {
indentation_amount = 4;
}
objc$target:::entry
{
// Assumes 64 bit executable
isaptr = *(uint64_t *)copyin(arg0, 8);
class_rw = *(uint64_t *)copyin(isaptr + 4*8, 8);
class_ro = *(uint64_t *)copyin(class_rw + 8, 8);
classnameptr = *(uint64_t *)copyin(class_ro + 4*4 + 8, 8);
classname = copyinstr(classnameptr);
type = classname == $$1? (string)&probefunc[0]: "";
method = classname == $$1? (string)&probefunc[1]: "";
arrow = classname == $$1? "->": "";
space = classname == $$1? " ": "";
bracket1 = classname == $$1? "[": "";
bracket2 = classname == $$1? "]": "";
current_indention = classname == $$1? indention: 0;
indention = classname == $$1? indention + 1: indention;
newline = classname == $$1? "\n": "";
classname = classname == $$1? classname: "";
printf("%*s%s%s%.1s%s%s%s%s%s%s", current_indention * indentation_amount, "", arrow, space, type, bracket1, classname, space, method, bracket2, newline);
}
Running sudo ./trace_msg_send2.d -c ./test TestClass
prints:
num: 4
dtrace: error on enabled probe ID 179 (ID 13922: objc3517:NSObject:+load:entry): invalid address (0x7fff8204e03e) in action #5 at DIF offset 12
dtrace: error on enabled probe ID 186 (ID 13915: objc3517:__IncompleteProtocol:+load:entry): invalid address (0x7fff8204e01f) in action #5 at DIF offset 12
dtrace: error on enabled probe ID 195 (ID 13906: objc3517:Protocol:+load:entry): invalid address (0x7fff8204e034) in action #5 at DIF offset 12
-> +[TestClass initialize]
where -> -[TestClass randomNum:]
is missing.