224 lines
		
	
	
		
			8.6 KiB
		
	
	
	
		
			Mathematica
		
	
	
	
		
		
			
		
	
	
			224 lines
		
	
	
		
			8.6 KiB
		
	
	
	
		
			Mathematica
		
	
	
	
| 
								 | 
							
								#import "ThinkingExceptionHandler.h"
							 | 
						||
| 
								 | 
							
								#include <libkern/OSAtomic.h>
							 | 
						||
| 
								 | 
							
								#include <stdatomic.h>
							 | 
						||
| 
								 | 
							
								#import "TDLogging.h"
							 | 
						||
| 
								 | 
							
								#import "TDAutoTrackManager.h"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#if __has_include(<ThinkingDataCore/TDCorePresetDisableConfig.h>)
							 | 
						||
| 
								 | 
							
								#import <ThinkingDataCore/TDCorePresetDisableConfig.h>
							 | 
						||
| 
								 | 
							
								#else
							 | 
						||
| 
								 | 
							
								#import "TDCorePresetDisableConfig.h"
							 | 
						||
| 
								 | 
							
								#endif
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static NSString * const TD_CRASH_REASON = @"#app_crashed_reason";
							 | 
						||
| 
								 | 
							
								static NSUInteger const TD_PROPERTY_CRASH_LENGTH_LIMIT = 8191*2;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static NSString * const TDUncaughtExceptionHandlerSignalExceptionName = @"UncaughtExceptionHandlerSignalExceptionName";
							 | 
						||
| 
								 | 
							
								static NSString * const TDUncaughtExceptionHandlerSignalKey = @"UncaughtExceptionHandlerSignalKey";
							 | 
						||
| 
								 | 
							
								static int TDSignals[] = {SIGILL, SIGABRT, SIGBUS, SIGSEGV, SIGFPE, SIGPIPE, SIGTRAP};
							 | 
						||
| 
								 | 
							
								static volatile atomic_int_fast32_t TDExceptionCount = 0;
							 | 
						||
| 
								 | 
							
								static const atomic_int_fast32_t TDExceptionMaximum = 9;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								@interface ThinkingExceptionHandler ()
							 | 
						||
| 
								 | 
							
								@property (nonatomic) NSUncaughtExceptionHandler *td_lastExceptionHandler;
							 | 
						||
| 
								 | 
							
								@property (nonatomic, unsafe_unretained) struct sigaction *td_signalHandlers;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								@end
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								@implementation ThinkingExceptionHandler
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								+ (void)start {
							 | 
						||
| 
								 | 
							
								    [self sharedHandler];
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								+ (instancetype)sharedHandler {
							 | 
						||
| 
								 | 
							
								    static ThinkingExceptionHandler *gSharedHandler = nil;
							 | 
						||
| 
								 | 
							
								    static dispatch_once_t onceToken;
							 | 
						||
| 
								 | 
							
								    dispatch_once(&onceToken, ^{
							 | 
						||
| 
								 | 
							
								        gSharedHandler = [[ThinkingExceptionHandler alloc] init];
							 | 
						||
| 
								 | 
							
								    });
							 | 
						||
| 
								 | 
							
								    return gSharedHandler;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								- (instancetype)init {
							 | 
						||
| 
								 | 
							
								    self = [super init];
							 | 
						||
| 
								 | 
							
								    if (self) {
							 | 
						||
| 
								 | 
							
								        _td_signalHandlers = calloc(NSIG, sizeof(struct sigaction));
							 | 
						||
| 
								 | 
							
								        [self setupHandlers];
							 | 
						||
| 
								 | 
							
								        
							 | 
						||
| 
								 | 
							
								#pragma clang diagnostic push
							 | 
						||
| 
								 | 
							
								#pragma clang diagnostic ignored "-Wundeclared-selector"
							 | 
						||
| 
								 | 
							
								        // start APMStuck
							 | 
						||
| 
								 | 
							
								        Class cls = NSClassFromString(@"TAAPMStuckMonitor");
							 | 
						||
| 
								 | 
							
								        if (cls && [cls respondsToSelector:@selector(shareInstance)]) {
							 | 
						||
| 
								 | 
							
								            id ins = [cls performSelector:@selector(shareInstance)];
							 | 
						||
| 
								 | 
							
								            if (ins && [ins respondsToSelector:@selector(beginMonitor)]) {
							 | 
						||
| 
								 | 
							
								                [ins performSelector:@selector(beginMonitor)];
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								#pragma clang diagnostic push
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    return self;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								- (void)setupHandlers {
							 | 
						||
| 
								 | 
							
								    _td_lastExceptionHandler = NSGetUncaughtExceptionHandler();
							 | 
						||
| 
								 | 
							
								    NSSetUncaughtExceptionHandler(&TDHandleException);
							 | 
						||
| 
								 | 
							
								    
							 | 
						||
| 
								 | 
							
								    struct sigaction action;
							 | 
						||
| 
								 | 
							
								    sigemptyset(&action.sa_mask);
							 | 
						||
| 
								 | 
							
								    action.sa_flags = SA_SIGINFO;
							 | 
						||
| 
								 | 
							
								    action.sa_sigaction = &TDSignalHandler;
							 | 
						||
| 
								 | 
							
								    for (int i = 0; i < sizeof(TDSignals) / sizeof(int); i++) {
							 | 
						||
| 
								 | 
							
								        struct sigaction prev_action;
							 | 
						||
| 
								 | 
							
								        int err = sigaction(TDSignals[i], &action, &prev_action);
							 | 
						||
| 
								 | 
							
								        if (err == 0) {
							 | 
						||
| 
								 | 
							
								            memcpy(_td_signalHandlers + TDSignals[i], &prev_action, sizeof(prev_action));
							 | 
						||
| 
								 | 
							
								        } else {
							 | 
						||
| 
								 | 
							
								            TDLogError(@"Error Signal: %d", TDSignals[i]);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static void TDHandleException(NSException *exception) {
							 | 
						||
| 
								 | 
							
								    ThinkingExceptionHandler *handler = [ThinkingExceptionHandler sharedHandler];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    atomic_int_fast32_t exceptionCount = atomic_fetch_add_explicit(&TDExceptionCount, 1, memory_order_relaxed);
							 | 
						||
| 
								 | 
							
								    if (exceptionCount <= TDExceptionMaximum) {
							 | 
						||
| 
								 | 
							
								        [handler td_handleUncaughtException:exception];
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    if (handler.td_lastExceptionHandler) {
							 | 
						||
| 
								 | 
							
								        handler.td_lastExceptionHandler(exception);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static void TDSignalHandler(int signalNumber, struct __siginfo *info, void *context) {
							 | 
						||
| 
								 | 
							
								    ThinkingExceptionHandler *handler = [ThinkingExceptionHandler sharedHandler];
							 | 
						||
| 
								 | 
							
								    NSMutableDictionary *crashInfo;
							 | 
						||
| 
								 | 
							
								    NSString *reason;
							 | 
						||
| 
								 | 
							
								    NSException *exception;
							 | 
						||
| 
								 | 
							
								    
							 | 
						||
| 
								 | 
							
								    atomic_int_fast32_t exceptionCount = atomic_fetch_add_explicit(&TDExceptionCount, 1, memory_order_relaxed);
							 | 
						||
| 
								 | 
							
								    if (exceptionCount <= TDExceptionMaximum) {
							 | 
						||
| 
								 | 
							
								        [crashInfo setObject:[NSNumber numberWithInt:signalNumber] forKey:TDUncaughtExceptionHandlerSignalKey];
							 | 
						||
| 
								 | 
							
								        reason = [NSString stringWithFormat:@"Signal %d was raised.", signalNumber];
							 | 
						||
| 
								 | 
							
								        exception = [NSException exceptionWithName:TDUncaughtExceptionHandlerSignalExceptionName reason:reason userInfo:crashInfo];
							 | 
						||
| 
								 | 
							
								        [handler td_handleUncaughtException:exception];
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    
							 | 
						||
| 
								 | 
							
								    struct sigaction prev_action = handler.td_signalHandlers[signalNumber];
							 | 
						||
| 
								 | 
							
								    if (prev_action.sa_handler == SIG_DFL) {
							 | 
						||
| 
								 | 
							
								        signal(signalNumber, SIG_DFL);
							 | 
						||
| 
								 | 
							
								        raise(signalNumber);
							 | 
						||
| 
								 | 
							
								        return;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    if (prev_action.sa_flags & SA_SIGINFO) {
							 | 
						||
| 
								 | 
							
								        if (prev_action.sa_sigaction) {
							 | 
						||
| 
								 | 
							
								            prev_action.sa_sigaction(signalNumber, info, context);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    } else if (prev_action.sa_handler) {
							 | 
						||
| 
								 | 
							
								        prev_action.sa_handler(signalNumber);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								- (void)td_handleUncaughtException:(NSException *)exception {
							 | 
						||
| 
								 | 
							
								    NSDictionary *crashInfo = [ThinkingExceptionHandler crashInfoWithException:exception];
							 | 
						||
| 
								 | 
							
								    TDAutoTrackEvent *crashEvent = [[TDAutoTrackEvent alloc] initWithName:TD_APP_CRASH_EVENT];
							 | 
						||
| 
								 | 
							
								    [[TDAutoTrackManager sharedManager] trackWithEvent:crashEvent withProperties:crashInfo];
							 | 
						||
| 
								 | 
							
								    
							 | 
						||
| 
								 | 
							
								    TDAutoTrackEvent *appEndEvent = [[TDAutoTrackEvent alloc] initWithName:TD_APP_END_EVENT];
							 | 
						||
| 
								 | 
							
								    [[TDAutoTrackManager sharedManager] trackWithEvent:appEndEvent withProperties:nil];
							 | 
						||
| 
								 | 
							
								    
							 | 
						||
| 
								 | 
							
								    dispatch_sync([ThinkingAnalyticsSDK sharedTrackQueue], ^{});
							 | 
						||
| 
								 | 
							
								    dispatch_sync([ThinkingAnalyticsSDK sharedNetworkQueue], ^{});
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    NSSetUncaughtExceptionHandler(NULL);
							 | 
						||
| 
								 | 
							
								    for (int i = 0; i < sizeof(TDSignals) / sizeof(int); i++) {
							 | 
						||
| 
								 | 
							
								        signal(TDSignals[i], SIG_DFL);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								+ (void)trackCrashWithMessage:(NSString *)message {
							 | 
						||
| 
								 | 
							
								    NSDictionary *crashInfo = [ThinkingExceptionHandler crashInfoWithMessage:message];
							 | 
						||
| 
								 | 
							
								    TDAutoTrackEvent *crashEvent = [[TDAutoTrackEvent alloc] initWithName:TD_APP_CRASH_EVENT];
							 | 
						||
| 
								 | 
							
								    [[TDAutoTrackManager sharedManager] trackWithEvent:crashEvent withProperties:crashInfo];
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								+ (NSMutableDictionary *)crashInfoWithMessage:(NSString *)message {
							 | 
						||
| 
								 | 
							
								    NSMutableDictionary *properties = [[NSMutableDictionary alloc] init];
							 | 
						||
| 
								 | 
							
								    
							 | 
						||
| 
								 | 
							
								    if ([TDCorePresetDisableConfig disableAppCrashedReason]) {
							 | 
						||
| 
								 | 
							
								        return properties;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    
							 | 
						||
| 
								 | 
							
								    NSString *crashStr = message;
							 | 
						||
| 
								 | 
							
								    @try {
							 | 
						||
| 
								 | 
							
								        crashStr = [crashStr stringByReplacingOccurrencesOfString:@"\n" withString:@"<br>"];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        NSUInteger strLength = [((NSString *)crashStr) lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
							 | 
						||
| 
								 | 
							
								        NSUInteger strMaxLength = TD_PROPERTY_CRASH_LENGTH_LIMIT;
							 | 
						||
| 
								 | 
							
								        if (strLength > strMaxLength) {
							 | 
						||
| 
								 | 
							
								            crashStr = [NSMutableString stringWithString:[ThinkingExceptionHandler limitString:crashStr withLength:strMaxLength - 1]];
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        [properties setValue:crashStr forKey:TD_CRASH_REASON];
							 | 
						||
| 
								 | 
							
								    } @catch(NSException *exception) {
							 | 
						||
| 
								 | 
							
								        TDLogError(@"%@ error: %@", self, exception);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    return properties;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								+ (NSMutableDictionary *)crashInfoWithException:(NSException *)exception {
							 | 
						||
| 
								 | 
							
								    NSMutableDictionary *properties = [[NSMutableDictionary alloc] init];
							 | 
						||
| 
								 | 
							
								    
							 | 
						||
| 
								 | 
							
								    
							 | 
						||
| 
								 | 
							
								    if ([TDCorePresetDisableConfig disableAppCrashedReason]) {
							 | 
						||
| 
								 | 
							
								        return properties;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    
							 | 
						||
| 
								 | 
							
								    NSString *crashStr;
							 | 
						||
| 
								 | 
							
								    @try {
							 | 
						||
| 
								 | 
							
								        if ([exception callStackSymbols]) {
							 | 
						||
| 
								 | 
							
								            crashStr = [NSString stringWithFormat:@"Exception Reason:%@\nException Stack:%@", [exception reason], [exception callStackSymbols]];
							 | 
						||
| 
								 | 
							
								        } else {
							 | 
						||
| 
								 | 
							
								            NSString *exceptionStack = [[NSThread callStackSymbols] componentsJoinedByString:@"\n"];
							 | 
						||
| 
								 | 
							
								            crashStr = [NSString stringWithFormat:@"%@ %@", [exception reason], exceptionStack];
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        crashStr = [crashStr stringByReplacingOccurrencesOfString:@"\n" withString:@"<br>"];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        NSUInteger strLength = [((NSString *)crashStr) lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
							 | 
						||
| 
								 | 
							
								        NSUInteger strMaxLength = TD_PROPERTY_CRASH_LENGTH_LIMIT;
							 | 
						||
| 
								 | 
							
								        if (strLength > strMaxLength) {
							 | 
						||
| 
								 | 
							
								            crashStr = [NSMutableString stringWithString:[ThinkingExceptionHandler limitString:crashStr withLength:strMaxLength - 1]];
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        [properties setValue:crashStr forKey:TD_CRASH_REASON];
							 | 
						||
| 
								 | 
							
								    } @catch(NSException *exception) {
							 | 
						||
| 
								 | 
							
								        TDLogError(@"%@ error: %@", self, exception);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    return properties;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								+ (NSString *)limitString:(NSString *)originalString withLength:(NSInteger)length {
							 | 
						||
| 
								 | 
							
								    NSStringEncoding encoding = CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingUTF8);
							 | 
						||
| 
								 | 
							
								    NSData *originalData = [originalString dataUsingEncoding:encoding];
							 | 
						||
| 
								 | 
							
								    NSData *subData = [originalData subdataWithRange:NSMakeRange(0, length)];
							 | 
						||
| 
								 | 
							
								    NSString *limitString = [[NSString alloc] initWithData:subData encoding:encoding];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    NSInteger index = 1;
							 | 
						||
| 
								 | 
							
								    while (index <= 3 && !limitString) {
							 | 
						||
| 
								 | 
							
								        if (length > index) {
							 | 
						||
| 
								 | 
							
								            subData = [originalData subdataWithRange:NSMakeRange(0, length - index)];
							 | 
						||
| 
								 | 
							
								            limitString = [[NSString alloc] initWithData:subData encoding:encoding];
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        index ++;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (!limitString) {
							 | 
						||
| 
								 | 
							
								        return originalString;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    return limitString;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								@end
							 |