//
// 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);
}