// // PSDefines.m // Perforce // // Created by Adam Czubernat on 07.05.2013. // Copyright (c) 2013 Perforce Software, Inc. All rights reserved. // #import "PSDefines.h" // Constant strings NSString * const PSException = @"Perforce Exception"; NSString * const PSDomain = @"com.perforce"; // Queue definitions const char * PSDispatchGlobalQueueLabel = "com.perforce.globalQueue"; dispatch_queue_t PSDispatchGlobalQueue = NULL; static dispatch_once_t PSDispatchOnceToken; // Queue invocations count static int PSDispatchGlobalQueueWaiting = 0; static int PSDispatchMainQueueWaiting = 0; void PSDispatchInBackgroundThreadAndWait(dispatch_block_t block); // Logging NSString * const PSDebugLogNotification = @"PSDebugLogNotification"; static NSMutableArray *_PSLogBuffer; static int _PSLogBufferSize = 128; static int _PSLogBufferPointer; static NSMutableDictionary *_PSLogStorage; void _PSDebugLogSave(NSString *path, NSString *header); // Crash handling private void _PSDebugCrash(NSString *description, NSArray *callstack); void _PSDebugExceptionCallback(NSException *exception); void _PSDebugSignalCallback(int signal); #pragma mark - GCD void PSDispatchInBackgroundThread(dispatch_block_t block) { dispatch_once(&PSDispatchOnceToken, ^{ PSDispatchGlobalQueue = dispatch_queue_create(PSDispatchGlobalQueueLabel, NULL); }); dispatch_async(PSDispatchGlobalQueue, block); } void PSDispatchInMainThread(dispatch_block_t block) { PSDispatchMainQueueWaiting++; if (dispatch_get_current_queue() == dispatch_get_main_queue()) { // PSLogf(@"Already in main queue: performing block in-place"); block(); // Already in main queue: performing block in-place } else { if (PSDispatchGlobalQueueWaiting > 0) PSLogf(@"Possible deadlock"); dispatch_sync(dispatch_get_main_queue(), block); } PSDispatchMainQueueWaiting--; } extern void PSDispatchAfter(NSTimeInterval interval, dispatch_block_t block) { dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, interval * NSEC_PER_SEC); dispatch_after(time, dispatch_get_main_queue(), block); } void PSDispatchInBackgroundThreadAndWait(dispatch_block_t block) { PSDispatchGlobalQueueWaiting++; if (dispatch_get_current_queue() == PSDispatchGlobalQueue) { PSLogf(@"Already in background queue: performing block in-place"); block(); } else { dispatch_once(&PSDispatchOnceToken, ^{ PSDispatchGlobalQueue = dispatch_queue_create(PSDispatchGlobalQueueLabel, NULL); }); if (PSDispatchMainQueueWaiting > 0) PSLogf(@"Possible deadlock"); dispatch_sync(PSDispatchGlobalQueue, block); } PSDispatchGlobalQueueWaiting--; } PSTimeStamp PSTimeStampMake(void) { return CACurrentMediaTime(); } NSTimeInterval PSTimeInterval(PSTimeStamp timestamp) { return CACurrentMediaTime() - timestamp; } #pragma mark - Runtime NSDictionary * PSRuntimeIvarsForClass(Class class) { NSMutableDictionary *ivarsDict = [NSMutableDictionary dictionary]; unsigned int count; Ivar *ivars = class_copyIvarList(class, &count); for(int i=0; i<count; i++) { Ivar ivar = ivars[i]; const char *name = ivar_getName(ivar); const char *typeEncoding = ivar_getTypeEncoding(ivar); [ivarsDict setObject:[NSString stringWithFormat:@"%s", typeEncoding] forKey:[NSString stringWithFormat:@"%s", name]]; } free(ivars); return ivarsDict; } void * PSRuntimeAddressOfInstanceVariable(id object, char * variableName) { Ivar instanceVar = class_getInstanceVariable([object class], variableName); return (__bridge void *)object + ivar_getOffset(instanceVar); } #pragma mark - Debug NSMutableDictionary *PSInstancesDictionary; NSInteger PSInstanceCount(Class classObject) { NSNumber *count = [PSInstancesDictionary objectForKey:NSStringFromClass(classObject)]; return count.integerValue; } void PSInstanceCreated(Class classObject) { if (!PSInstancesDictionary) PSInstancesDictionary = [NSMutableDictionary dictionaryWithCapacity:1024]; NSString *className = NSStringFromClass(classObject); NSInteger count = [[PSInstancesDictionary objectForKey:className] integerValue]; [PSInstancesDictionary setObject:@(count + 1) forKey:className]; } void PSInstanceDeallocated(Class classObject) { NSString *className = NSStringFromClass(classObject); NSInteger count = [[PSInstancesDictionary objectForKey:className] integerValue]; if (count <= 0) PSLog(@"PSInstanceDeallocated too many times: %@ %ld", className, count); [PSInstancesDictionary setObject:@(count - 1) forKey:className]; } void PSInstanceLog(void) { printf("Printing living instances : \n\n"); [PSInstancesDictionary enumerateKeysAndObjectsUsingBlock: ^(NSString *key, NSNumber *count, BOOL *stop) { printf("%6ld | %s\n", count.integerValue, key.UTF8String); }]; printf("\n"); } #pragma mark - Logs void PSLog(NSString *format, ...) { va_list args; va_start(args, format); NSString *string = [[NSString alloc] initWithFormat:format arguments:args]; printf("%s\n", string.UTF8String); va_end(args); #ifdef PS_LOGGING if (!_PSLogBuffer) _PSLogBuffer = [NSMutableArray arrayWithCapacity:_PSLogBufferSize]; @synchronized (_PSLogBuffer) { if (_PSLogBuffer.count < _PSLogBufferSize) [_PSLogBuffer insertObject:string atIndex:_PSLogBufferPointer]; else [_PSLogBuffer replaceObjectAtIndex:_PSLogBufferPointer withObject:string]; _PSLogBufferPointer = (_PSLogBufferPointer + 1) % _PSLogBufferSize; dispatch_async(dispatch_get_main_queue(), ^{ [[NSNotificationCenter defaultCenter] postNotificationName:PSDebugLogNotification object:string]; }); } #endif } void PSLogStore(NSString *key, NSString *format, ...) { va_list args; va_start(args, format); NSString *string = [[NSString alloc] initWithFormat:format arguments:args]; printf("%s : %s\n", key.UTF8String, string.UTF8String); va_end(args); #ifdef PS_LOGGING dispatch_async(dispatch_get_main_queue(), ^{ if (!_PSLogStorage) _PSLogStorage = [NSMutableDictionary dictionary]; [_PSLogStorage setObject:string forKey:key]; }); #endif } NSArray * PSDebugLogDump(void) { NSMutableArray *log = [NSMutableArray arrayWithCapacity:_PSLogBufferSize]; NSInteger size = _PSLogBuffer.count; NSInteger start = _PSLogBufferPointer; if (size != _PSLogBufferSize) start = 0; for (NSInteger i=0; i<size; i++) { NSString *entry = [_PSLogBuffer objectAtIndex:(start+i) % size]; [log addObject:entry]; } return log; } void PSDebugLogSave(void) { _PSDebugLogSave(@"P4Log ", nil); } void PSDebugLogClear(void) { _PSLogStorage = nil; [[NSNotificationCenter defaultCenter] postNotificationName:PSDebugLogNotification object:nil]; } void _PSDebugLogSave(NSString *name, NSString *header) { NSMutableString *log = [NSMutableString string]; if (header) [log appendString:header]; [log appendString:@"\n----------------- Log info -----------------\n\n"]; [_PSLogStorage enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSString *value, BOOL *stop) { [log appendFormat:@"%@ : %@\n", key, value]; }]; [log appendString:@"\n----------------- Instances -----------------\n\n"]; [PSInstancesDictionary enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSString *value, BOOL *stop) { [log appendFormat:@"%@ : %@\n", key, value]; }]; [log appendString:@"\n\n----------------- Messages -----------------\n\n"]; [log appendString:[PSDebugLogDump() componentsJoinedByString:@"\n"]]; [log appendString:@"\n\n--------------------------------------------\n\n"]; NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; [formatter setDateFormat:@"yyyy-MM-dd' 'HH.mm"]; NSString *path = [NSString stringWithFormat:@"%@/Desktop/%@%@.log", NSHomeDirectory(), name, [formatter stringFromDate:[NSDate date]]]; [log writeToFile:path atomically:YES encoding:NSUTF8StringEncoding error:NULL]; } #pragma mark - Crash handling void PSDebugInstallCrashHandler(void) { NSSetUncaughtExceptionHandler(&_PSDebugExceptionCallback); signal(SIGABRT, _PSDebugSignalCallback); signal(SIGSEGV, _PSDebugSignalCallback); signal(SIGBUS, _PSDebugSignalCallback); } void _PSDebugCrash(NSString *description, NSArray *callstack) { NSMutableString *log = [NSMutableString string]; [log appendString: @"\n---------- File Manager Crash Log ----------\n\n"]; [log appendFormat:@"%@\n\nStack trace: %@\n", description, callstack]; _PSDebugLogSave(@"P4Crash ", log); PSLog(@"%@", log); // Remove crash handlers NSSetUncaughtExceptionHandler(NULL); signal(SIGABRT, SIG_DFL); signal(SIGSEGV, SIG_DFL); signal(SIGBUS, SIG_DFL); } void _PSDebugExceptionCallback(NSException *exception) { NSString *desc = [NSString stringWithFormat: @"Application crashed with uncaught exception:\n" @"%@", exception.description]; _PSDebugCrash(desc, exception.callStackSymbols); } void _PSDebugSignalCallback(int signal) { char *name = NULL; if (signal == SIGABRT) name = "SIGABRT"; else if (signal == SIGSEGV) name = "EXC_BAD_ACCESS"; else if (signal == SIGBUS) name = "SIGBUS"; NSArray *callStack = [NSThread callStackSymbols]; NSString *desc = [NSString stringWithFormat: @"Application crashed receiving signal %d %s", signal, name]; _PSDebugCrash(desc, callStack); }