First, the problem for several reasons Caton
- Complex UI, photo-text drawing excessive;
- For network synchronization request on the main thread;
- In the main thread to do a lot of IO operations;
- Excessive operation, CPU continued high occupancy;
- Master thread deadlock and grab the lock.
Second, the idea of monitoring Caton
- Monitoring FPS: FPS is the number of frames displayed in one second, i.e. within one second the number of screen changes. If you follow the cartoon, the cartoon FPS is 24, is not up to 60 full frames. That is, for animation, the time when 24 60 Although there is no smooth, but it has been consistent, so we can not say when the 24 even stuck. Thus, simply by monitoring the FPS it is difficult to determine whether there will be Caton problem, so I decisively abandoned to monitor Caton by monitoring the FPS program.
- RunLoop: to judge by the state monitoring RunLoop of whether there will be Caton. RunLoop principle here is not to say any more, says the main method, first clear the loop of six state
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) { kCFRunLoopEntry, // into the Loop kCFRunLoopBeforeTimers, // trigger Timer callback kCFRunLoopBeforeSources, // trigger Source0 callback kCFRunLoopBeforeWaiting, // wait mach_port news kCFRunLoopAfterWaiting), // receiving mach_port message kCFRunLoopExit, // Exit Loop kCFRunLoopAllActivities // Loop all state changes }
We need to monitor the status of two: two values after the loop defined state before entering sleep and wake RunLoop, respectively kCFRunLoopBeforeSources and kCFRunLoopAfterWaiting, that is to trigger a callback Source0 and receive messages mach_port two states.
Third, how to check Caton
Said first rough steps:
- Create a CFRunLoopObserverContext observer;
- We will create a good observer runLoopObserver added under common thread RunLoop main mode of observation;
- Create a child thread dedicated to continuous condition monitoring RunLoop main thread;
- Once entering the state kCFRunLoopBeforeSources state before sleep, or wake up after kCFRunLoopAfterWaiting, within the time set threshold has not changed, it can be judged Caton;
- a stack dump information to further analyze the execution time of which method concrete is too long;
On the code:
// // SMLagMonitor.h // // Created by DaiMing on 16/3/28. // #import <Foundation/Foundation.h> @interface SMLagMonitor : NSObject + (instancetype)shareInstance; - ( void ) beginMonitor; // start monitoring Caton - ( void ) endMonitor; // stop monitoring Caton @end
// // SMLagMonitor.m // // Created by DaiMing on 16/3/28. // #import "SMLagMonitor.h" #import "SMCallStack.h" #import "SMCPUMonitor.h" @interface SMLagMonitor() { int timeoutCount; CFRunLoopObserverRef runLoopObserver; @public dispatch_semaphore_t dispatchSemaphore; CFRunLoopActivity runLoopActivity; } @property (nonatomic, strong) NSTimer *cpuMonitorTimer; @end @implementation SMLagMonitor #pragma mark - Interface + (instancetype)shareInstance { static id instance = nil; static dispatch_once_t dispatchOnce; dispatch_once(&dispatchOnce, ^{ instance = [[self alloc] init]; }); return instance; } - ( void ) {beginMonitor // monitoring CPU consumption self.cpuMonitorTimer = [the NSTimer scheduledTimerWithTimeInterval: . 3 target:self selector:@selector(updateCPUInfo) userInfo:nil repeats:YES]; // monitoring Caton IF (runLoopObserver) { return ; } dispatchSemaphore = dispatch_semaphore_create ( 0 ); // the Dispatch Semaphore ensure synchronization // Create a viewer CFRunLoopObserverContext context = { 0 , (__ Bridge void * ) Self, NULL, NULL}; runLoopObserver = CFRunLoopObserverCreate(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0 , & runLoopObserverCallBack, & context); // add the viewer to observe the common mode in the main thread runloop CFRunLoopAddObserver (CFRunLoopGetMain (), runLoopObserver, kCFRunLoopCommonModes); // create a child thread monitor dispatch_async (dispatch_get_global_queue ( 0 , 0 ), ^ { // child thread open a continuous loop to monitor the while (YES) { Long semaphoreWait = dispatch_semaphore_wait (dispatchSemaphore, dispatch_time (DISPATCH_TIME_NOW, 20 * NSEC_PER_MSEC) ); IF (semaphoreWait =! 0 ) { IF (! runLoopObserver) { timeoutCount = 0; dispatchSemaphore = 0; runLoopActivity = 0; return; } // status of two runloop, BeforeSources AfterWaiting two states and can be detected if the time interval Caton IF (runLoopActivity == == kCFRunLoopBeforeSources || runLoopActivity kCFRunLoopAfterWaiting) { // stack information reported by the server code will be placed here / / appears three results // IF (++ timeoutCount <. 3) { // Continue; // } NSLog ( @ " Monitor Trigger " ); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ // [SMCallStack callStackWithType:SMCallStackTypeAll]; }); } //end activity }// end semaphore wait timeoutCount = 0; }// end while }); } - ( void ) {endMonitor [self.cpuMonitorTimer invalidate]; if (!runLoopObserver) { return; } CFRunLoopRemoveObserver(CFRunLoopGetMain(), runLoopObserver, kCFRunLoopCommonModes); CFRelease(runLoopObserver); runLoopObserver = NULL; } #pragma mark - Private static void runLoopObserverCallBack(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info){ SMLagMonitor *lagMonitor = (__bridge SMLagMonitor*)info; lagMonitor->runLoopActivity = activity; dispatch_semaphore_t semaphore = lagMonitor->dispatchSemaphore; dispatch_semaphore_signal(semaphore); } - (void)updateCPUInfo { thread_act_array_t threads; mach_msg_type_number_t threadCount = 0; const task_t thisTask = mach_task_self(); kern_return_t kr = task_threads(thisTask, &threads, &threadCount); if (kr != KERN_SUCCESS) { return; } for (int i = 0; i < threadCount; i++) { thread_info_data_t threadInfo; thread_basic_info_t threadBaseInfo; mach_msg_type_number_t threadInfoCount = THREAD_INFO_MAX; if (thread_info((thread_act_t)threads[i], THREAD_BASIC_INFO, (thread_info_t)threadInfo, &threadInfoCount) == KERN_SUCCESS) { threadBaseInfo = (thread_basic_info_t)threadInfo; if (!(threadBaseInfo->flags & TH_FLAGS_IDLE)) { cpuUsage integer_t = threadBaseInfo-> cpu_usage / 10 ; IF (cpuUsage> 70 ) { // Cup consumption is greater than when printing and recording stack 70 NSString * RESTR = smStackOfThread (Threads [I]); // record database // [[[ shareInstance SMLagDB] increaseWithStackString: RESTR] subscribeNext: ^ (ID X) {}]; NSLog ( @ " the CPU Useage overload Thread Stack: \ @ n-% " , RESTR); } } } } } @end
Use direct write on the inside APP didFinishLaunchingWithOptions method:
[[SMLagMonitor shareInstance] beginMonitor];
Get!