/* Copyright (c) 1998, 1999, 2000, 2001, 2003, 2004 Benhur Stein This file is part of Pajé. Pajé is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Pajé is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with Pajé; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. */ #include "PajeTraceController.h" #include "PajeController.h" #include "../General/Protocols.h" #include "../General/Macros.h" #include "../General/PajeFilter.h" #include "../StorageController/Encapsulate.h" // number of chunks to keep simultaneously in memory // FIXME: should be configurable/more intelligent #define CHUNKS_TO_KEEP 10 @implementation PajeTraceController - (id)init { NSNotificationCenter *notificationCenter; self = [super init]; if (self == nil) { return nil; } Assign(components, [NSMutableArray array]); Assign(filters, [NSMutableDictionary dictionary]); Assign(tools, [NSMutableArray array]); notificationCenter = [NSNotificationCenter defaultCenter]; // accept notifications posted when we should read more of the trace file [notificationCenter addObserver:self selector:@selector(traceFault:) name:@"PajeTraceNotInMemoryNotification" object:nil]; return self; } - (void)dealloc { [[NSNotificationCenter defaultCenter] removeObserver:self]; [self destroyCheckPointDirectory]; Assign(checkPointDirectory, nil); Assign(filters, nil); Assign(tools, nil); Assign(checkPoints, nil); Assign(components, nil); [super dealloc]; } - (NSDictionary *)filters { return filters; } - (NSArray *)tools { return tools; } - (void)connectComponent:(id)c1 toComponent:(id)c2 { [c1 setOutputComponent:c2]; [c2 setInputComponent:c1]; } - (id)createComponentGraph { id decoder; // id virtualizer; id entityTypeSelector; id fieldFilter; id containerselector; id order; id statViewer; // id nodeGroup; id busyNode; id spacetime; id insetlimit; reader = (id)[NSClassFromString(@"PajeFileReader") componentWithController:self]; [components addObject:reader]; decoder = [NSClassFromString(@"PajeEventDecoder") componentWithController:self]; [components addObject:decoder]; simulator = (id)[NSClassFromString(@"PajeSimul") componentWithController:self]; [components addObject:simulator]; encapsulator = [NSClassFromString(@"Encapsulate") componentWithController:self]; [components addObject:encapsulator]; // virtualizer = [NSClassFromString(@"VirtualThread") componentWithController:self]; // [components addObject:virtualizer]; entityTypeSelector = [NSClassFromString(@"EntityTypeSelector") componentWithController:self]; fieldFilter = [NSClassFromString(@"FieldFilter") componentWithController:self]; [components addObject:entityTypeSelector]; containerselector = [NSClassFromString(@"ContainerSelector") componentWithController:self]; [components addObject:containerselector]; order = [NSClassFromString(@"Order") componentWithController:self]; [components addObject:order]; // nodeGroup = [NSClassFromString(@"NodeGroup") componentWithController:self]; // [components addObject:nodeGroup]; busyNode = [NSClassFromString(@"BusyNode") componentWithController:self]; [components addObject:busyNode]; insetlimit = [NSClassFromString(@"InsetLimit") componentWithController:self]; [components addObject:insetlimit]; spacetime = [NSClassFromString(@"STController") componentWithController:self]; [components addObject:spacetime]; statViewer = [NSClassFromString(@"StatViewer") componentWithController:self]; [components addObject:statViewer]; [self connectComponent:reader toComponent:decoder]; [self connectComponent:decoder toComponent:simulator]; [self connectComponent:simulator toComponent:encapsulator]; [self connectComponent:encapsulator toComponent:busyNode]; [self connectComponent:busyNode toComponent:fieldFilter]; [self connectComponent:fieldFilter toComponent:containerselector]; [self connectComponent:containerselector toComponent:order]; [self connectComponent:order toComponent:entityTypeSelector]; // [self connectComponent:busyNode toComponent:statViewer]; [self connectComponent:entityTypeSelector toComponent:insetlimit]; [self connectComponent:insetlimit toComponent:spacetime]; [self connectComponent:insetlimit toComponent:statViewer]; return reader; } // // message sent by NSApplication if the user double-clicks a trace file // - (BOOL)openFile:(NSString *)filename { NS_DURING [self createComponentGraph]; [reader setInputFilename:filename]; [self performSelector:@selector(readChunk:) withObject:self afterDelay:0.0]; [checkPoints release]; checkPoints = [[NSClassFromString(@"PSortedArray") alloc] initWithSelector:@selector(time)]; [self writeCheckPoint]; NS_VALUERETURN(YES, BOOL); NS_HANDLER if (NSRunAlertPanel([localException name], @"%@\n%@", @"Continue", @"Abort", nil, [localException reason], [[[localException userInfo] objectEnumerator] allObjects]) != NSAlertDefaultReturn) [[NSApplication sharedApplication] terminate:self]; NS_VALUERETURN(NO, BOOL); NS_ENDHANDLER } - (void)readChunk:(id)sender { int i; NSDate *start, *end; double t; NSAutoreleasePool *pool; if (![reader hasMoreData]) { return; } i = -(int)[simulator eventCount]; pool = [NSAutoreleasePool new]; start = [NSDate date]; NS_DURING NSDebugMLLog(@"tim", @"will read chunk starting at %@", [simulator currentTime]); [reader readNextChunk]; [simulator endOfChunk]; [self writeCheckPoint]; NS_HANDLER if (NSRunAlertPanel([localException name], @"%@\n%@", @"Continue", @"Abort", nil, [localException reason], [localException userInfo] //[[[localException userInfo] objectEnumerator] //allObjects] ) != NSAlertDefaultReturn) [[NSApplication sharedApplication] terminate:self]; NS_ENDHANDLER end = [NSDate date]; t = [end timeIntervalSinceDate:start]; i += [simulator eventCount]; NSLog(@"%@: %d events in %f seconds = %f e/s", [reader inputFilename], i, t, i/t); [pool release]; } - (void)createCheckPointDirectory { NSString *dirname; NSString *path; if (checkPointDirectory != nil) { return; } dirname = [NSString stringWithFormat:@"Paje-%@-%@", [[reader inputFilename] lastPathComponent], [[NSProcessInfo processInfo] globallyUniqueString]]; path = [NSTemporaryDirectory() stringByAppendingPathComponent:dirname]; checkPointDirectory = [path retain]; [[NSFileManager defaultManager] createDirectoryAtPath:checkPointDirectory attributes:nil]; } - (void)destroyCheckPointDirectory { [[NSFileManager defaultManager] removeFileAtPath:checkPointDirectory handler:nil]; } - (NSString *)checkPointDirectory { if (checkPointDirectory == nil) { [self createCheckPointDirectory]; } return checkPointDirectory; } - (void)writeCheckPoint { NSMutableData *d; NSArchiver *a; id comp_enum; id component; NSDate *time; NSString *fileName; NSString *filePath; PajeCheckPoint *checkPoint; time = [simulator currentTime]; if (time == nil) { return; } fileName = [NSString stringWithFormat:@"%@.ckp", [time description]]; filePath = [[self checkPointDirectory] stringByAppendingPathComponent:fileName]; checkPoint = [PajeCheckPoint checkPointWithTime:time fileName:filePath]; // If checkpoint has already been generated, do not write it again if ([checkPoints indexOfObject:checkPoint] != NSNotFound) { return; } NSDebugMLLog(@"tim", @"will write checkPoint %@", checkPoint); d = [[NSMutableData alloc] init]; a = [[NSArchiver alloc] initForWritingWithMutableData:d]; comp_enum = [components objectEnumerator]; while ((component = [comp_enum nextObject]) != nil) { if ([component respondsToSelector: @selector(encodeCheckPointWithCoder:)]) { NSDebugMLLog(@"tim", @"encoding component=%@", component); [component encodeCheckPointWithCoder:a]; } } [d writeToFile:filePath atomically:NO]; [a release]; [d release]; [checkPoints addObject:checkPoint]; } - (void)gotoCheckPoint:(PajeCheckPoint *)checkPoint { NSData *d; NSUnarchiver *a; NSEnumerator *comp_enum; id component; NSDebugMLLog(@"tim", @"will read checkPoint %@", checkPoint); d = [[NSData alloc] initWithContentsOfFile:[checkPoint fileName]]; a=[[NSUnarchiver alloc] initForReadingWithData:d]; comp_enum = [components objectEnumerator]; while ((component = [comp_enum nextObject]) != nil) { if ([component respondsToSelector: @selector(decodeCheckPointWithCoder:)]) { NSDebugMLLog(@"tim", @"decoding %@", component); [component decodeCheckPointWithCoder:a]; } } [a release]; [d release]; [encapsulator removeObjectsAfterTime:[checkPoint time]]; } - (void)traceFault:(NSNotification *)notification { NSDictionary *info; NSDate *startTime; NSDate *endTime; unsigned index; int index2; PajeCheckPoint *checkPoint; info = [notification userInfo]; startTime = [info objectForKey:@"StartTime"]; endTime = [info objectForKey:@"EndTime"]; if (startTime != nil) { index = [checkPoints indexOfLastObjectNotAfterValue: (id)startTime]; } else { index = NSNotFound; } if (index != NSNotFound) { // let's remove some old stuff // (should have some more intelligence in this!) index2 = index - CHUNKS_TO_KEEP; if (index2 > 0) { [encapsulator removeObjectsBeforeTime: [[checkPoints objectAtIndex: index2] time]]; } checkPoint = [checkPoints objectAtIndex:index]; if (([startTime isEarlierThanDate: [encapsulator startTimeInMemory]]) || ([[checkPoint time] isLaterThanDate:[simulator currentTime]])) [self gotoCheckPoint:checkPoint]; } while ([reader hasMoreData] && [[simulator currentTime] isEarlierThanDate:endTime]) { [self readChunk:self]; } } - (void)registerFilter:(PajeFilter *)filter { NSString *filterName; PajeFilter *f; filterName = [filter filterName]; while (YES) { f = [filters objectForKey:filterName]; if (f == nil) { break; } if (f == filter) { return; } filterName = [filterName stringByAppendingString:@"+"]; } [filters setObject:filter forKey:filterName]; [[PajeController controller] updateFiltersMenu]; } - (void)registerTool:(id)tool { [tools addObject:tool]; [[PajeController controller] updateToolsMenu]; } - (void)windowIsKey { [[PajeController controller] setCurrentTraceController:self]; } - (void)removeComponent:component { [[PajeController controller] closeTraceController:self]; } - (void)saveConfiguration { NSEnumerator *filterEnum; NSString *filterName; NSMutableDictionary *configuration; configuration = [NSMutableDictionary dictionaryWithCapacity:[filters count]]; filterEnum = [filters keyEnumerator]; while ((filterName = [filterEnum nextObject]) != nil) { PajeFilter *filter; NSDictionary *filterConfig; filter = [filters objectForKey:filterName]; filterConfig = [filter configuration]; if (filterConfig != nil) { [configuration setObject:filterConfig forKey:filterName]; } } [configuration writeToFile:configurationName atomically:YES]; } - (void)loadConfiguration:(NSString *)name { // FIXME: should load and connect filters; more info needed on config file NSDictionary *configuration; NSEnumerator *filterEnum; NSString *filterName; Assign(configurationName, name); configuration = [NSDictionary dictionaryWithContentsOfFile:configurationName]; filterEnum = [configuration keyEnumerator]; while ((filterName = [filterEnum nextObject]) != nil) { PajeFilter *filter; NSDictionary *filterConfig; filter = [filters objectForKey:filterName]; if (filter == nil) { // FIXME: shouldn't be done this way: reload all filters continue; } filterConfig = [configuration objectForKey:filterName]; if (filterConfig != nil) { [filter setConfiguration:filterConfig]; } } } - (void)setConfigurationName:(NSString *)name { Assign(configurationName, name); [self saveConfiguration]; } @end