ios using the principle of RunLoop to monitor Caton

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

  1. 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.
  2. 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!

 

Guess you like

Origin www.cnblogs.com/qiyiyifan/p/11089735.html