Is it possible to get the result of objc method in dtrace? - objective-c

I wrote an action that fires when any objc method return (objc:::return).
For now, I need to get the return value. Is it possible?

In summary: no, you can’t get the return value of an Objective-C method in a DTrace probe.
Bill Bumgarner has a post on tracing messages to nil in which he says the following:
Aside: objc_msgSend()‘s return cannot be traced via dtrace at this time. Not surprising given that the function doesn’t actually return — as far as dtrace is concerned, it is nothing but preamble.
The blog post is rather old (January 2008) and it uses the pid provider, not the objc provider. That said, it is still valid as of Mac OS X v10.7.1 and it also applies to the objc provider.
Amusingly, it might seem that it sometimes works but it depends on when DTrace reads the RAX register. Since objc_msgSend() doesn’t return, DTrace ends up using values in RAX that have been stored by code that is not necessarily the return of the method being traced.
Consider the following code:
NSNumber *n = [NSNumber numberWithInt:1234];
printf("n has address %p\n", n);
and the following probe:
objc$target:NSPlaceholderNumber:-initWithInt?:return
{
printf("Returning from -[NSPlaceholderNumber initWithInt:]\n");
printf("\treturn value = 0x%p\n", (void *)arg1);
}
When run using DTrace, I get the following output:
n has address 0x4d283
Returning from -[NSPlaceholderNumber initWithInt:]
return value = 0x4d283
so it seems that the probe was able to capture the return value of -initWithInt:. That’s only luck though, probably caused by a function (e.g. CFNumberCreate() or CFMakeCollectable()) called by -initWithInt: and which ended up placing the expected value in RAX.
Now consider the following code:
char *c = "hello";
NSData *data = [NSData dataWithBytes:c length:strlen(c)];
NSString *s = [[NSString alloc] initWithData:data
encoding:NSASCIIStringEncoding];
printf("s has address %p\n", s);
and the following probe:
objc$target:NSPlaceholderString:-initWithData?encoding?:return
{
printf("Returning from -[NSPlaceholderString initWithData:encoding:]\n");
printf("\treturn value = 0x%p\n", (void *)arg1);
}
When run using DTrace, I get the following output:
s has address 0x7fcd92414ea0
Returning from -[NSPlaceholderString initWithData:encoding:]
return value = 0x600
As you can see, the addresses (i.e., the return values) don’t match. In fact, 0x600 is the value of kCFStringEncodingASCII which is the Core Foundation counterpart of NSASCIIStringEncoding. At some point either the method or a function called by the method moved 0x600 to RAX and that’s the value that DTrace wrongly considered to be the return value.

Related

managing C-strings from blocks, NSAlert, OS X

I’ve written a piece of test code "AlertTest" to make sure I’m implementing NSAlert object the proper way. It consists of a button which triggers [doSomething: cstr] printing a C-string, and a method posting an alert, which passes the same string to the same method, but from within the completion handler.
This is the debugger console printout:
-from [buttonPressed]
2015-01-09 18:28:09.832 AlertTest[1260:40881] Application must quit
Application must quit
-from [mPostError:cstr]
2015-01-09 18:28:12.276 AlertTest[1260:40881] #Èø_ˇ
#\351\277_\377
I’m a bit confused on what I have to do to properly pass a string from within the completion handler. No compiler errors reported. I must be missing something very obvious. Thanks in advance. Here’s the code:
//___________________________________________________________
- (IBAction)buttonPressed:(NSButton *)sender {
char cstr[256];
strcpy(cstr, "Application must quit");
[self mDoSomething:cstr];
[self mPostError:cstr];
}
//___________________________________________________________
-(void)mDoSomething: (char*)cstr
{
NSLog(#"%s",cstr);
printf("%s\n", cstr);
}
//___________________________________________________________
-(void) mPostError: (char *)cstr
{
char cMessage[64] = "Critical Error";
NSString *message = [NSString stringWithCString:cMessage encoding:NSASCIIStringEncoding];
NSString *infoText = [NSString stringWithCString:cstr encoding:NSASCIIStringEncoding];
NSAlert* mAlert = [[NSAlert alloc] init];
[mAlert setMessageText:message];
[mAlert setInformativeText:infoText];
[mAlert setAlertStyle: NSCriticalAlertStyle];
[mAlert beginSheetModalForWindow:self.window completionHandler:^(NSModalResponse returnCode) {
[self mDoSomething: cstr]; //this is the suspect line!
}];
}
Your problem is nothing to do with C-strings but with variable lifetime.
Your execution flow goes as follows:
buttonPressed: is called. This creates a local variable cstr and calls mPostError:
mPostError: is called. This calls beginSheetModalForWindow:completionHandler: passing it a block. That block references cstr so a copy is made of the value in cstr, and that value is the address of buttonPressed:'s local variable of the same name.
mPostError: returns which gets us back to...
buttonPressed: which in turn returns destroying its local variable cstr. The block passed to beginSheetModalForWindow:completionHandler: now has the address pointing into released memory.
The user dismisses the alert and the block is called. That block calls mDoSomething: passing it the, now invalid, address.
mDoSomething: calls printf which tries to interpret the memory at the passed address as a C-string, but by now that memory has been re-used for something else and it produces the nonsense you see.
Passing a C-string is not a problem, but whatever value (a char * value in this case) is passed to a block must be valid at the time the block is executed.
You can "fix" the issue in your example by making cstr a global variable:
char cstr[256]; // global
- (IBAction)buttonPressed:(NSButton *)sender {
strcpy(cstr, "Application must quit");
Now cstr always exists and the address the block copies is therefore always valid.
Of course you've now also permanently used up 256 bytes of memory and if this string is only required for a short period that is a (small) waste.
In your real code this may not be an issue - the C strings may be managed already so they live as long as required. If not and using C strings is a requirement then you may need to look at dynamically allocating them (see malloc and friends) and releasing them when no longer needed (see free).
HTH

why does this code give EXC_BAD_ACCESS (using IMP)

This code gives me EXC_BAD_ACCESS, why?
NSMutableDictionary *d = [[NSMutableDictionary alloc] init];
IMP imp= [d methodForSelector:#selector(setObject:forKey:) ];
imp(d, #selector( setObject:forKey:), #"obj", #"key");
I'm just starting using IMP, firs try.. no luck. Not sure why I get the error, also.. in the past, when I got EXC_BAD_ACCESS, the message was printed at the console, this time the error line is highlighted.
Some notes:
ARC is enabled, XCode 4.3.2, the project uses Objective-C++ as de default language/compiler,this code is at the very beginning of the project
thanks guys
You need to cast the function pointer properly or ARC doesn't know what it's supposed to be doing. IMP is a generic function pointer that takes an id, a selector and a variable number of other, undefined arguments and returns an id. The method implementation you're trying to call takes an id, a selector followed by exactly two id parameters and has a void return type. You can fix it by changing to the following code:
NSMutableDictionary* dict = [[NSMutableDictionary alloc] init];
void (*imp)(id, SEL, id, id) = (void(*)(id,SEL,id,id))[dict methodForSelector:#selector(setObject:forKey:)];
if( imp ) imp(dict, #selector(setObject:forKey:), #"obj", #"key");
You should always check that you actually got a function pointer back before you dereference it, as that would also crash. The code above will work even in an ARC environment. Also, even when you're not using ARC, you should always cast your function pointers to the actual prototype rather than IMP. You should never use IMP. Other places that would cause major issues are if the method returns a struct or if the method takes floating point parameters, etc.
Good habit: always cast your function pointers or make typedefs for them if you find the function pointer syntax jarring.
The problem is that IMP has a return type of "id" which ARC will attempt to manage. You need to cast your function pointer to have a return type of void (consistent with the method you are calling):
NSMutableDictionary *d = [[NSMutableDictionary alloc] init];
IMP imp= [d methodForSelector: #selector(setObject:forKey:)];
void (*func)(__strong id,SEL,...) = (void (*)(__strong id, SEL, ...))imp;
func( d, #selector(setObject:forKey:), #"obj", #"key" );

how to handle an data from server without knowing the object type (aka, could be a dictionary or array)

so i make a lot of server calls for my app. what is returned can depend on the result of the server operation.
say i make an api call to "foo" which will return either a hash map/nsdictionary if successful or a bool (or a 0 for false, meaning it did not execute).
with my code, i typecast it to i believe it should be assuming it was a successful operation. i will check to see if i get back something else then i expected, say a BOOL false.
NSString *mapContext = (NSString *) [call xmlrpcCall:#"load_map" withObjects: [NSArray arrayWithObjects:dataCenter.state,nil]];
NSLog(#"mapContext in loadStateMap: %#", mapContext);
if ([mapContext isKindOfClass:[NSDictionary class]])
{
if ([mapContext objectForKey:#"faultCode"])
{
NSLog(#"mapContext: %#", mapContext);
[self defaultAlert:mapContext titleMsg:#"load_map"];
}
}
here i ask the server to load a map. if successfull, it will return a string. if it fails, it will return a dictionary with a fault code and a fault message. since mapContext is instantiated as a string, when i check to see if its a dictionary and check for a key fault code, xcode gives me a warning that mapContext may not respond to "objectForKey". i understand completely why i get the warning, but is there a way i can prevent the warning? it never breaks the app but its annoying to see 30+ warnings about this issue.
Use id, this is what it is for and that is why so many abstracted foundation classes use them (NSArray anyone).
//problem solved!
id mapContext = [call xmlrpcCall:#"load_map" withObjects: [NSArray arrayWithObjects:dataCenter.state,nil]];

& with variable [duplicate]

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Why passing &error instead of error in Cocoa programming?
I have a question for which I cannot seem to find an answer...
I am using the SBJsonParser and there is a line of code I find puzzling:
NSError *error;
self.jsonData = [jsonParser objectWithString:responseString error:&error];
What is the & in front of the error parameter? (&error)?
In Objective-C, just like in C, & is the "address-of operator" and it returns the address of its argument. To find out more about it, I recommend you read this short chapter from The C Book.
Here's an example of how the operator is used, to get a better idea:
#include <stdio.h>
// define a function that takes a pointer to an integer as argument
void change_value_of_int(int* int_to_change) {
// change the value to which the argument points
*int_to_change = 5;
}
int main() {
// create a stack variable
int test_int = 10;
// pass the address of test_int to the function defined earlier
change_value_of_int(&test_int);
// now the value of test_int is 5
printf("%d\n", test_int);
return 0;
}
Note that the change_value_of_int() function expects the first parameter to be a pointer to an int, not an int, so you can't call it with change_value_of_int(test_int). You must send it the address of the test_int variable, not the variable itself (because if you send a copy of the variable, it can't change it).
Same thing with the NSError* example. jsonParser expects the address of a NSError*, not a NSError*, therefore the method is defined as:
- (id)objectWithString:(NSString*)jsonrep error:(NSError**)error;
Take a look at the header file and at the implementation to see how it is used. The value of your error (*error = the value of the thing pointed to by the error argument) becomes the return value of [errorTrace lastObject].
It's the address of operator, found in C, C++ and Objective-C.
In your example, &error yields a NSError ** (that is, a pointer to a pointer).
This is common in C (and, by extension, Objective-C): pass-by-reference is simulated with pointers, which means you have to pass the address of the object you want to modify (in this case, another pointer) to a function.
& is the address operator.
real quick lesson, all objects in Objective-C are pointers, which makes thing easy, because you know the level of indifference of everything, id is a special case, which a pointer to any object, without specifying class.
pointer to a pointer is most often used when returning an error, that is passed as a parameter to a method.
-(void)doSomething:(NSError**)err
{
//we have an error, return it.
*err = [NSError errorWithDomain:#"custom Domain" code:42 userInfo:nil];
}
then use the method with:
NSError * err = nil;
[self doSomething:&err];
if(err)
{
NSLog(#"we have an error in domain: %#",[err domain]);
}
which outputs:
we have an error in domain: custom
Domain
this is especially useful when you are returning something, as you can only return one value in C. This is exceptionally common is vanilla C and C++, but not used that much in Objective-C.

Why doesn't this crash?

I'm attempting to narrow down a bug to a minimum reproducible case and found something odd.
Consider this code:
static NSString *staticString = nil;
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
if (staticString == nil) {
staticString = [[NSArray arrayWithObjects:#"1", #"2", #"3", nil] componentsJoinedByString:#","];
}
[pool drain];
NSLog(#"static: %#", staticString);
return 0;
}
I'm expecting this code to crash. Instead, it logs:
2011-01-18 14:41:06.311 EmptyFoundation[61419:a0f] static: static:
However, if I change the NSLog() to:
NSLog(#"static: %s", [staticString UTF8String]);
Then it does crash.
edit a bit more info:
After draining the pool:
NSLog(#"static: %#", staticString); //this logs "static: static: "
NSLog(#"static: %#", [staticString description]); //this crashes
So apparently invoking a method on the string is good enough to get it to crash. In that case, why doesn't logging the string directly cause it to crash? Shouldn't NSLog() be invoking the -description method?
Where is the second "static: " coming from? Why isn't this crashing?
Results:
Both Kevin Ballard and Graham Lee are correct. Graham's correct in realizing that NSLog() is not invoking -description (as I was erroneously assuming), and Kevin is almost definitely correct that this is a weird stack-related issue with copying a format string and a va_list around.
NSLogging and NSString does not invoke -description. Graham elegantly showed this, and if you trace through the Core Foundation sources that do the logging, you'll see that this is the case. Any backtrace originating inside NSLog shows that it invokes NSLogv => _CFLogvEx => _CFStringCreateWithFormatAndArgumentsAux => _CFStringAppendFormatAndArgumentsAux. _CFStringAppendFormatAndArgumentsAux() (line 5365) is where all of the magic is going on. You can see that it's manually going through to find all the % substitutions. It only ends up invoking the description copy function if the type of the substitution is a CFFormatObjectType, the description function is non-nil, and the substitution hasn't already been handled by another type. Since we've shown that the description is not getting copied, it's reasonable to assume that an NSString gets handled earlier (in which case it's probably going to be doing a raw byte copy), which leads us to believe...
There's a stack error going on here, as Kevin surmises. Somehow the pointer that was pointing to the autoreleased string is getting substituted to a different object, which happens to be an NSString. So, it doesn't crash. Weird. However, if we change the type of the static variable to something else, like an NSArray, then the -description method does get called, and the program does crash as expected.
How truly and utterly strange. Points go to Kevin for being the most correct about the root cause of the behavior, and kudos to Graham for correcting my fallacious thinking. I wish I could accept two answers...
My best guess for what you're seeing is that NSLog() copies the format string (probably as a mutable copy), and then parses the arguments. Since you've dealloc'd staticString, it just so happens that the copy of the format string is being placed into the same location. This is causing you to see the "static: static: " output that you described. Of course, this behavior is undefined - there's no guarantee it will always use the same memory location for this.
On the other hand, your NSLog(#"static: %s", [staticString UTF8String]) is accessing staticString before the format string copy happens, which means it's accessing garbage memory.
Your assumption that NSLog() calls -description on an NSString instance is faulty. I just added this category:
#implementation NSString (GLDescription)
- (NSString *)description {
NSLog(#"-description called on %#", self);
return self;
}
#end
It doesn't cause a stack overflow, because it doesn't get called recursively. Not only that, but if I insert that category into the code in your question, I find this output:
2011-01-18 23:04:11.653 LogString[3769:a0f] -description called on 1
2011-01-18 23:04:11.656 LogString[3769:a0f] -description called on 2
2011-01-18 23:04:11.657 LogString[3769:a0f] -description called on 3
2011-01-18 23:04:11.658 LogString[3769:a0f] static: static:
so we conclude that NSLog() doesn't call -description on an NSString it comes across in its args. Why you get the static string twice is likely a quirk of the data on the stack when you erroneously access the released staticString variable.
Accessing dealocated memory does not necessarily cause a crash. The behavior is undefined. You are expecting too much!
Maybe it has something to do with the #"static:" being stored in the same memory location as staticString. staticString will be deallocated and it stores the #"static: %#" in that recycled mem location, so then the staticString pointer is on "static: %#" so it ends up static: static:.
This is a case of "Use after free()". What happens is "undefined behavior". Your example is really no different than:
char *stringPtr = NULL;
stringPtr = malloc(1024); // Example code, assumes this returns non-NULL.
strcpy(stringPtr, "Zippers!");
free(stringPtr);
printf("Pants: %s\n", stringPtr);
What happens at the printf line? Who knows. Anything from Pants: Zippers! to Pants: (...garbage...) Core Dump.
All the Objective-C specific stuff is actually irrelevant- it's the fact that you're using a pointer to memory which is no longer valid is the only thing that matters. You're better off throwing darts at the wall than trying to explain "why" it's not crashing and printing static: static. For performance reasons, most malloc implementations don't bother "reaping" free()'d allocations until they have to. IMHO, this is probably why your example isn't crashing in the way you were expecting it to.
If you really want to see this particular program crash, you can do one of the following:
Set the environment variable CFZombieLevel to 17 (scribble + don't free).
Set the environment variable NSZombieEnabled to YES.
Set the environment variable DYLD_INSERT_LIBRARIES to /usr/lib/libgmalloc.dylib (see man libgmalloc).