// // PSDefines.m // Perforce // // Created by Adam Czubernat on 07.05.2013. // Copyright (c) 2013 Perforce Software, Inc. All rights reserved. // #import "PSDefines.h" #import // 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, (int64_t)(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 0) [PSInstancesDictionary setObject:@(count - 1) forKey:className]; else PSLog(@"PSInstanceDeallocated too many times: %@ %ld", className, count); } 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]; va_end(args); static FILE *logFile; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ if (isatty(STDERR_FILENO)) { logFile = stderr; // If connected to debug console print to stderr } else { NSURL *libraryURL = [[[NSFileManager defaultManager] URLsForDirectory:NSLibraryDirectory inDomains:NSUserDomainMask] firstObject]; NSURL *logsURL = [libraryURL URLByAppendingPathComponent:@"Logs" isDirectory:YES]; if (![logsURL checkResourceIsReachableAndReturnError:NULL]) { [[NSFileManager defaultManager] createDirectoryAtURL:logsURL withIntermediateDirectories:YES attributes:nil error:NULL]; } NSString *appName = [[NSBundle mainBundle] objectForInfoDictionaryKey:(id)kCFBundleExecutableKey]; NSString *logPath = [logsURL.path stringByAppendingFormat:@"/%@.log", appName]; // Set logging to file logFile = fopen(logPath.fileSystemRepresentation, "wb"); setbuf(logFile, NULL); } }); fprintf(logFile, "%s\n", [string UTF8String]); #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]; va_end(args); PSLog(@"%@ : %@", key, string); #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