• Open Menu Close Menu
  • Apple
  • Shopping Bag
  • Apple
  • Mac
  • iPad
  • iPhone
  • Watch
  • TV
  • Music
  • Support
  • Search apple.com
  • Shopping Bag
 

Lists

Open Menu Close Menu
  • Terms and Conditions
  • Lists hosted on this site
  • Email the Postmaster
  • Tips for posting to public mailing lists
NSTask troubles
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

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


  • Follow-Ups:
    • Re: NSTask troubles
      • From: Alastair Houghton <email@hidden>
    • Re: NSTask troubles
      • From: Dave Camp <email@hidden>
  • Prev by Date: Re: Bonjour Rendezvous
  • Next by Date: Re: Bonjour Rendezvous
  • Previous by thread: Re: Bonjour Rendezvous
  • Next by thread: Re: NSTask troubles
  • Index(es):
    • Date
    • Thread