NSTask crashing after 7 to 15 runs

Discussion in 'Mac Programming' started by MrFusion, Feb 4, 2009.

  1. macrumors 6502a

    Joined:
    Jun 8, 2005
    Messages:
    583
    Location:
    West-Europe
    #1
    I have a strange problem, and I was wondering if someone else has run into this as well.

    I have a class that executes an NSTask. Each time I want to run an NSTask, I create a new instance of that class. Everything works as it is suppose to do. The output is as expected. There is no memory leak that I am aware of. I have stepped through the routines in question many times, the flow is what it should be.

    An new instance of this class is created through some interface button, and each time I start it once or twice, it runs fine.

    But the thing is, If I click the button too many times (between 7 and 15) after each other, the program will crash.

    Did anyone experience something similar?
     
  2. macrumors 6502

    Joined:
    Jul 25, 2006
    Messages:
    294
    #2
    Although I haven't tried reproducing your problem, a backtrace might immediately indicate a problem. Could you provide one?
     
  3. macrumors 6502a

    Joined:
    Jun 8, 2005
    Messages:
    583
    Location:
    West-Europe
    #3
    What is a backtrace, and how do you get one? Is that the upper left part in the debug window? thanks.
     
  4. Moderator emeritus

    kainjow

    Joined:
    Jun 15, 2000
    Messages:
    7,743
    #4
    It's a good chance it's a memory related problem and those are impossible to solve unless there is code, so maybe post some relevant code.
     
  5. macrumors 6502

    Joined:
    Jul 25, 2006
    Messages:
    294
    #5
    This is pretty basic knowledge a programmer needs for debugging...

    If the app crashes in gdb, type 'bt' to show the entire stack at the point of the crash.

    If it's not in gdb, you should see the crash reporter dialog come up. Click 'report' and show us the part after where it says "Thread X Crahsed:" followed by a series of numbered lines.
     
  6. macrumors 68020

    Krevnik

    Joined:
    Sep 8, 2003
    Messages:
    2,375
    #6
    It could also be a timing issue, but again, impossible to solve unless there is code.

    And a huge point to the original poster: Almost never do two programmers hit the exact same issues, especially if the problem is in the code written by one of the programmers. This is why when people ask for help, they tend to post a code snippet of some kind about the problem area.

    As for the backtrace, another term for the same thing is call stack. Basically what is being asked for is "What was the order of the method calls on this?"
     
  7. macrumors 6502a

    Joined:
    Jun 8, 2005
    Messages:
    583
    Location:
    West-Europe
    #7
    code

    here is the code.

    Code:
    
    #import "MyTask.h"
    
    //see Apple code example moriaty
    
    @implementation MyTask
    -(id)initForController:(id) control {
    	self = [super init];
    	if (self != nil) {
    		controller = control;
    		    [[NSNotificationCenter defaultCenter] addObserver:self
    													 selector:@selector(checkTaskStatus:)
    														 name:NSTaskDidTerminateNotification
    													   object:nil];		
    	}
    	return self;
    }
    
    #pragma mark current state
    - (void)checkTaskStatus:(NSNotification *)aNotification {
        if(![myTask isRunning]) {
    		int status = [[aNotification object] terminationStatus];
    		if (status)
    			NSLog(@"Task succeeded.");
    		else
    			NSLog(@"Task stopped.");
    	}
    }
    
    #pragma mark task
    -(BOOL) startTask:(NSString *) command inWorkingDirectory:(NSURL *) url withOptions:(NSArray *) options {
    	NSMutableArray *allOptions = [[NSMutableArray alloc] initWithCapacity:2+[options count]];
    	[allOptions addObject:command];
    	[allOptions addObject:url];	
    	[allOptions addObjectsFromArray:options];
    	BOOL succes = [self startTask:allOptions];
    	[allOptions release];
    	return succes;
    }
    -(BOOL) startTask:(NSArray *) options{
    	if ([options count] < 2)
    		return NO;
    	
    	//options:
    	//0, NSString = shell command
    	//1, NSURL = working directory
    	//2 - count-2 = arguments
    	NSString *command = [options objectAtIndex:0];
    	NSString *workingDirectory = [[options objectAtIndex:1] path];
    
    	BOOL succes = NO;
    	//check if file is valid path
    	BOOL isDir = NO;
    	if ([[NSFileManager defaultManager] fileExistsAtPath:workingDirectory isDirectory:&isDir] & (isDir)) {
    		[controller processStarted];
    		//create task
    		myTask = [[NSTask alloc] init];
    		[myTask setCurrentDirectoryPath:workingDirectory];
    		[myTask setLaunchPath:command]; 
    		//set arguments
    		NSArray *args = [options subarrayWithRange:NSMakeRange(2, [options count]-2)];
    		[myTask setArguments:args];
    		//pipes -> log
    		NSPipe *outputPipe = [NSPipe pipe];
    		[myTask setStandardOutput:outputPipe];
    		[myTask setStandardError:outputPipe];
    		NSPipe *inputPipe = [NSPipe pipe];
    		[myTask setStandardInput:inputPipe];
    		//pipes -> filehandles
    		readHandle = [outputPipe fileHandleForReading];
    		writeHandle = [inputPipe fileHandleForWriting];
    		
    		//observe change to output of task, so that they can be collected
    		[[NSNotificationCenter defaultCenter] addObserver:self 
    												 selector:@selector(readFromTask:) 
    													 name:NSFileHandleReadCompletionNotification 
    												   object:readHandle];
    		// We tell the file handle to go ahead and read in the background asynchronously, and notify
    		// us via the callback registered above when we signed up as an observer.  The file handle will
    		// send a NSFileHandleReadCompletionNotification when it has data that is available.
    		[readHandle readInBackgroundAndNotify];
    		
    		// launch the task asynchronously
    		[myTask launch];    
    		
    		succes = YES;
    	}
    	return succes;
    }
    
    -(BOOL) stopTask {
    	if ([myTask isRunning]) {
    		[[NSNotificationCenter defaultCenter] removeObserver:self name:NSFileHandleReadCompletionNotification object:readHandle];
    		[myTask terminate];
    		
    		NSData *data = nil;	
    		while ((data = [readHandle availableData]) && [data length])  {
    			[controller appendOutput: [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease]];
    		}
    	}
    	[myTask release];
    	[controller processFinished];
    	controller = nil;
    	return YES;
    }
    
    #pragma mark input/output
    -(void) readFromTask: (NSNotification *)aNotification{
    	NSData *data = [[aNotification userInfo] objectForKey:NSFileHandleNotificationDataItem];
    	if ([data length]) {
    		// Send the data on to the controller; we can't just use +stringWithUTF8String: here because -[data bytes] is not necessarily a properly terminated string.
    		// -initWithData:encoding: on the other hand checks -[data length]
    		[controller appendOutput: [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease]];
    		// we need to schedule the file handle go read more data in the background again.
    		[[aNotification object] readInBackgroundAndNotify];  
    	} else {
    		[self stopTask]; //no more data
    	}
    }
    
    -(void) writeToTask:(NSString *) output{
    	[writeHandle writeData:[output dataUsingEncoding:NSUTF8StringEncoding]];
    }
    
    #pragma mark cleanup
    -(void) dealloc {
    	[[NSNotificationCenter defaultCenter] removeObserver:self name:NSTaskDidTerminateNotification object:nil];
    	
    	[super dealloc];
    }
    
    
     
  8. macrumors 6502a

    Joined:
    Jun 8, 2005
    Messages:
    583
    Location:
    West-Europe
    #8
    Maybe that is basic knowledge for a programmer, but cs is not my background. 80% self taught, the other 20% is introduction to programming (using pascal).

    I am looking around in the xCode menu's, but I can't find a 'report'.

    --edit--
    Maybe I can't find backtrace, but I did find instruments. I haven't used that tool before.
    I found one memory leak, which is not in the code I posted above. It is in a different class, and was a retain function instead of an alloc. That is why I missed it before. I fixed it, but the program still crashes.
     
  9. Moderator emeritus

    kainjow

    Joined:
    Jun 15, 2000
    Messages:
    7,743
    #9
    You might be able to get a stack trace by first showing the debugger (Run > Debugger) and then test your code by running the app via Run > Debug.
     
  10. macrumors 6502a

    Joined:
    Jun 8, 2005
    Messages:
    583
    Location:
    West-Europe
    #10
    or just cmd-Y to start debugging. Yep, add some breakpoints to see at which one it crashes, etc. That I know. Which button to click to get a stack report I don't know. I appreciate everyones help and patience. This thread is getting irritating for me, because it shows my ignorance about something basic about xCode and I can't get past it. I hate feeling stupid.

    Anyway, here is a screenshot.
     
  11. macrumors 6502

    Joined:
    Jul 25, 2006
    Messages:
    294
    #11
    Try removing your object as an observer of the NSFileHandleReadCompletionNotification notification in -dealloc. You remove it for the other notification you registered for, but perhaps there's a chance you're not removing the notification before deallocate as you may have expected.

    It's definitely crashing because a notification is getting sent to a deallocated object. The question is which one... It's possible to see what selector is getting called on the object, but that's too much to explain here...

    Just try what I said above and see if that fixes the crash.
     
  12. macrumors 6502a

    Joined:
    Jun 8, 2005
    Messages:
    583
    Location:
    West-Europe
    #12
    yihaa...
    Thanks.
    Moving both to the dealloc solves it.
    I can see the notificationpost in the screenshot, but how did can you tell that it's being sent to a deallocated object?
    I really thought it would be deeper down, beyond my control. Or that not all notifications would be received by my code (as in sending mouse events to a view, where some might be dropped). That's why I didn't post any code in the first place.
     
  13. macrumors 6502

    Joined:
    Jul 25, 2006
    Messages:
    294
    #13
    Another Objective-C debugging tip: A crash in objc_msgSend 99 times out of 100 is due to a message being sent to a deallocated object. It happens a lot with delegates too.
     
  14. macrumors regular

    Joined:
    Jun 10, 2008
    Messages:
    102
    #14
    Not to derail the thread, but I've been writing software for decades, and I've never even heard the term "backtrace". In my world, what you're describing has always been called a stack trace.
     
  15. Moderator emeritus

    kainjow

    Joined:
    Jun 15, 2000
    Messages:
    7,743
    #15
    I think learning to debug code properly takes a long time. I'm no way an expert (don't really know gdb at all, only a few commands). It takes patience and persistence. The main problem I come across is debugging other people's code. Once you write a lot of code you learn where potential problems may come from, so you prepare your code in advance to avoid these situations. But not everyone does that so when you're stuck in situations like me fixing other's code, it can be annoying :)
     
  16. macrumors 68020

    Krevnik

    Joined:
    Sep 8, 2003
    Messages:
    2,375
    #16
    Backtrace is a fairly GDB-centric term. It is the only place I have seen it used.
     
  17. macrumors 6502a

    Joined:
    Jun 8, 2005
    Messages:
    583
    Location:
    West-Europe
    #17
    Ok. I will keep this one in mind.
     

Share This Page