1 2 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])); } }
|