When analyzing an Objective-C application with Dtrace, one big challenge is how to introspect any objective-c objects that are passed as parameters into the various trace points.

While you can use the Objective-C provider (documented on the dtrace man page) to trace particular methods of specific classes, it doesn’t really help for actually introspecting an instance or class.

By “introspecting”, I mean “printing out the damned class name”, for example.

Full monty on the click through.

Useful example: internally to the appkit, there is a function called forwardMethod() that is used by the responder chain to forward events from one responder to another. It shows up in lots of backtraces and, sometimes, it is really nice to know exactly what/when/why/how/who is being diddled by said function.

Like just about everything else that deals with messaging some object, forwardMethod() takes an object as the first parameter and a selector as the second parameter (poke at it w/gdb for a bit to see what I mean). Given what it logs w/the script below, there is very likely a third argument that is event itself. Ignored for this example’s purpose.

D, the programming language used to write dtrace probes, is a very C like language. It does pointers. And pointer de-referencing. Relatively straightforward.

Almost.

A dtrace probe runs in the kernel. Thus, it doesn’t have direct access to the inferior’s memory. While printing the class name in C would is easy — printf("%s\n", obj->isa->name); — doing the same in D requires that the various bits o’ memory be copied over from the inferior into the kernel.

And, when copied, the memory shows up in a buffer — a buffer that contains a pointer and, thus, requires a bit o’ double de-referencing magic. Old school Mac programmers should feel right at home about now.

Anyway, what follows is a probe that will print the name of the class of the object to which forwardMethod() is about to, well, forward a method and the name of the method that is about to be forwarded (Huge thanks to Daniel Delwood — who commented on this entry with something very very useful — for boatloads of help on this):

This obviously assumes a 32 bit process. Even with 64 bit types, this won’t work on a 64 bit process because the innards of Objective-C are opaque (to eliminate binary compatibility issues in the future, not for obfuscation reasons in the present).

You can run this against TextEdit easily enough. Yes, the environment variable is still necessary. Note that this actually launches the inferior (TextEdit) and, thus, traces it from the beginning.