NSTask troubles
NSTask troubles
- Subject: NSTask troubles
- From: John Stiles <email@hidden>
- Date: Thu, 12 Jul 2007 14:56:12 -0700
I'm attempting to use NSTask to implement a debugging stack-crawl
feature in our app. It needs to drive the developer tool "/usr/bin/
atos" (which turns addresses inside an application into symbols).
If I go to Terminal and type:
$ /usr/bin/atos -o /Applications/MyApp.app/Contents/MacOS/MyApp
I now get an interactive session where I can run queries like:
0x56780
and get responses back like:
+[MyAppController myMethod:]
atos stays open, so I can make multiple requests to the atos process:
0xabcd0
+[MyAppController anotherMethod:]
So far, so good. That's exactly what I want to do, but I need to do
it with NSTask. So I've written this code:
NSTask * atosTask = [[NSTask alloc] init];
NS_DURING
[atosTask setLaunchPath:@"/usr/bin/atos"];
[atosTask setArguments:[NSArray arrayWithObjects:
@"-o",
[[NSFileManager defaultManager]
stringWithFileSystemRepresentation:imagePath length:strlen(imagePath)],
NULL]];
[atosTask setStandardInput:[NSPipe pipe]];
[atosTask setStandardOutput:[NSPipe pipe]];
[atosTask launch];
NS_HANDLER
// fail gracefully
return;
NS_ENDHANDLER
// For each symbol I need to process, do the following:
char atosResult[8192];
if( !WriteLine( [[[atosTask standardInput] fileHandleForWriting]
fileDescriptor], FormatString( "0x%llx\n", (uint64)(pc) ) ) ||
!ReadLine( [[[atosTask standardOutput] fileHandleForReading]
fileDescriptor], atosResult, sizeof(atosResult) ) )
{
// fail gracefully
}
WriteLine and ReadLine are simple wrappers around write() and read().
WriteLine blats the whole string out in one write() call. ReadLine
gets one character at a time via read() until it finds a '\n' character.
This code works great on various Intel machines that I have at my
disposal to test with. However, I cannot get it to work on a G4 Mac
Mini at all! I've verified that the Mac Mini works perfectly if I
open up Terminal and issue the commands manually. However, when
NSTask is used to open up the atos process, it just hangs on ReadLine
waiting for characters. My process is hung at read(), and the atos
process is hung on fgetc() as if it is waiting for input.
I've actually implemented this code twice now—the first time, I used
popen along with the "r+" access mode, and I got the exact same
results. On all our Intel machines, the popen implementation also
worked perfectly—however, on the G4 Mac Mini, it hung in the same
way. My app was stuck on fgets() and atos was stuck on fgetc(). I
started thinking, "maybe NSTask will work better," but it looks like
they both have the same problem.
I did find one corny workaround—if I don't use atos' interactive
mode, and specify the addresses on the command line, it works. i.e.
it is as if I'm doing this:
$ /usr/bin/atos -o /Applications/MyApp.app/Contents/MacOS/MyApp 0x56780
+[MyAppController myMethod:]
$ /usr/bin/atos -o /Applications/MyApp.app/Contents/MacOS/MyApp 0xabcd0
+[MyAppController anotherMethod:]
This, however, is much much slower than the method shown above, since
each instantiation of atos needs to initialize itself and this takes
a very long time if your app is large. So it's not a really usable
solution.
I've used NSTask in the exact same way, to solve a near-identical
problem—passing symbols through /usr/bin/c++filt. Interestingly, in
limited testing, this usage did work fine on the Mac Mini. Not sure
why one would be good and the other would be bad.
For the curious, here are the implementations of WriteLine and
ReadLine. I don't think there are any problems here but I'm sure if I
don't post it, somebody will ask :) (Though if someone knows a better
way to do ReadLine, I'd like to know.)
bool WriteLine( int fd, const char * line )
{
int length = strlen(line);
if( length != write( fd, line, length ) )
{
printf( "WriteLine: *** unable to write to file descriptor\n" );
return false;
}
return true;
}
bool ReadLine( int fd, char * line, int lineMaxLength )
{
// NOTE: I'm pretty sure this could be a lot better.
bool success = true;
for( char * end = line + lineMaxLength - 2; line < end; )
{
// Read one character.
char c;
for( ;; )
{
if( 1 == read( fd, &c, 1 ) && c != '\0' )
break;
if( errno == EINTR )
continue;
printf( "ReadLine: *** unable to read from file descriptor\n" );
success = false;
c = '\n';
break;
}
// If it's an end-of-line, we are done.
if( c == '\n' )
break;
// Tack on the character and read again.
*line++ = c;
}
*line++ = '\n';
*line++ = '\0';
return success;
}
_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden