| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
 100
 101
 102
 103
 104
 105
 106
 107
 108
 109
 110
 111
 112
 113
 114
 115
 116
 117
 118
 119
 120
 121
 122
 123
 124
 125
 126
 127
 128
 129
 130
 131
 132
 133
 134
 135
 136
 137
 138
 139
 140
 141
 142
 143
 144
 145
 146
 147
 148
 149
 150
 151
 152
 153
 154
 155
 156
 157
 158
 159
 160
 161
 162
 163
 
 | #import <Foundation/Foundation.h>#import <UIKit/UIKit.h>
 #import <execinfo.h>
 #import <sys/time.h>
 
 
 typedef enum {
 eRunloopDefaultMode,
 eRunloopTrackingMode
 } RunloopMode;
 
 
 static CFRunLoopActivity g_runLoopActivity;
 static RunloopMode g_runLoopMode;
 static BOOL g_bRun = NO;
 static struct timeval g_tvRun;
 
 
 @interface HangMonitor : NSObject
 @property (nonatomic, assign) CFRunLoopObserverRef runLoopBeginObserver;
 @property (nonatomic, assign) CFRunLoopObserverRef runLoopEndObserver;
 @property (nonatomic, strong) dispatch_semaphore_t semaphore;
 @property (nonatomic, assign) NSTimeInterval timeoutInterval;
 - (void)addRunLoopObserver;
 - (void)startMonitor;
 - (void)logStackTrace;
 - (void)reportHang;
 @end
 
 @implementation HangMonitor
 
 
 + (instancetype)sharedInstance {
 static HangMonitor *instance;
 static dispatch_once_t onceToken;
 dispatch_once(&onceToken, ^{
 instance = [[HangMonitor alloc] init];
 });
 return instance;
 }
 
 
 - (instancetype)init {
 self = [super init];
 if (self) {
 _timeoutInterval = 8.0;
 _semaphore = dispatch_semaphore_create(0);
 [self addRunLoopObserver];
 [self startMonitor];
 }
 return self;
 }
 
 
 - (void)addRunLoopObserver {
 NSRunLoop *curRunLoop = [NSRunLoop currentRunLoop];
 
 
 CFRunLoopObserverContext context = {0, (__bridge void *) self, NULL, NULL, NULL};
 CFRunLoopObserverRef beginObserver = CFRunLoopObserverCreate(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, LONG_MIN, &myRunLoopBeginCallback, &context);
 CFRetain(beginObserver);
 self.runLoopBeginObserver = beginObserver;
 
 
 CFRunLoopObserverRef endObserver = CFRunLoopObserverCreate(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, LONG_MAX, &myRunLoopEndCallback, &context);
 CFRetain(endObserver);
 self.runLoopEndObserver = endObserver;
 
 
 CFRunLoopRef runloop = [curRunLoop getCFRunLoop];
 CFRunLoopAddObserver(runloop, beginObserver, kCFRunLoopCommonModes);
 CFRunLoopAddObserver(runloop, endObserver, kCFRunLoopCommonModes);
 }
 
 
 void myRunLoopBeginCallback(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info) {
 HangMonitor *monitor = (__bridge HangMonitor *)info;
 g_runLoopActivity = activity;
 g_runLoopMode = eRunloopDefaultMode;
 switch (activity) {
 case kCFRunLoopEntry:
 g_bRun = YES;
 break;
 case kCFRunLoopBeforeTimers:
 case kCFRunLoopBeforeSources:
 case kCFRunLoopAfterWaiting:
 if (g_bRun == NO) {
 gettimeofday(&g_tvRun, NULL);
 }
 g_bRun = YES;
 break;
 case kCFRunLoopAllActivities:
 break;
 default:
 break;
 }
 dispatch_semaphore_signal(monitor.semaphore);
 }
 
 
 void myRunLoopEndCallback(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info) {
 HangMonitor *monitor = (__bridge HangMonitor *)info;
 g_runLoopActivity = activity;
 g_runLoopMode = eRunloopDefaultMode;
 switch (activity) {
 case kCFRunLoopBeforeWaiting:
 gettimeofday(&g_tvRun, NULL);
 g_bRun = NO;
 break;
 case kCFRunLoopExit:
 g_bRun = NO;
 break;
 case kCFRunLoopAllActivities:
 break;
 default:
 break;
 }
 dispatch_semaphore_signal(monitor.semaphore);
 }
 
 
 - (void)startMonitor {
 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
 while (YES) {
 long result = dispatch_semaphore_wait(self.semaphore, dispatch_time(DISPATCH_TIME_NOW, self.timeoutInterval * NSEC_PER_SEC));
 if (result != 0) {
 if (g_runLoopActivity == kCFRunLoopBeforeSources || g_runLoopActivity == kCFRunLoopAfterWaiting) {
 [self logStackTrace];
 [self reportHang];
 }
 }
 }
 });
 }
 
 
 - (void)logStackTrace {
 void *callstack[128];
 int frames = backtrace(callstack, 128);
 char **strs = backtrace_symbols(callstack, frames);
 NSMutableString *stackTrace = [NSMutableString stringWithString:@"\n"];
 for (int i = 0; i < frames; i++) {
 [stackTrace appendFormat:@"%s\n", strs[i]];
 }
 free(strs);
 NSLog(@"%@", stackTrace);
 }
 
 
 - (void)reportHang {
 
 NSLog(@"检测到卡死崩溃,进行上报");
 }
 
 @end
 
 
 int main(int argc, char * argv[]) {
 @autoreleasepool {
 HangMonitor *monitor = [HangMonitor sharedInstance];
 return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
 }
 }
 
 |