iOS App卡頓監控(Freezing/Lag)

iOS App卡頓監控(Freezing/Lag)

筆記(寫在前面):
關於應用的性能監控,須要從多方面進行綜合考慮,此處僅從其中一個方面,進行學習研究。

如何判斷主線程卡頓:

監測NSRunLoop耗時狀況。服務器

NSRunLoop的調用主要在kCFRunLoopBeforeSourceskCFRunLoopBeforeWaiting之間,以及kCFRunLoopAfterWaiting以後。所以,如果發現這個兩個時間內耗時過長,就能夠斷定此時主線程出現卡頓狀況。async

1、監控NSRunLoop狀態變化

使用CFRunLoopObserverRef,實時得到這些狀態值的變化,以下:函數

/// RunLoop狀態觀察回調
static void runLoopObserverCallBack(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info)
{
    <#MyClass#> *object = (__bridge <#MyClass#>*)info;
    // 記錄狀態值
    object->activity = activity;
}
/// 註冊RunLoop狀態觀察
- (void)registerRunLoopObserver {
    CFRunLoopObserverContext context = {0,(__bridge void*)self,NULL,NULL};
    CFRunLoopObserverRef observer = CFRunLoopObserverCreate(kCFAllocatorDefault,
                                                            kCFRunLoopAllActivities,
                                                            YES,
                                                            0,
                                                            &runLoopObserverCallBack,
                                                            &context);
    CFRunLoopAddObserver(CFRunLoopGetMain(), observer, kCFRunLoopCommonModes);
}

2、RunLoop耗時計算

另外開啓一個線程,實時計算兩個狀態區域之間的耗時,是否達到閾值。

dispatch_semaphore_t讓子線程更及時地獲知主線程NSRunLoop狀態變化oop

卡頓覆蓋範圍:屢次連續小卡頓單次長時間卡頓性能

添加計算邏輯,以下:學習

/// RunLoop狀態觀察回調
static void runLoopObserverCallBack(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info)
{
    <#MyClass#> *object = (__bridge <#MyClass#>*)info;
    // 記錄狀態值
    object->activity = activity;
    
    // 發送信號
    dispatch_semaphore_t semaphore = object->semaphore;
    dispatch_semaphore_signal(semaphore);
}
/// 註冊RunLoop狀態觀察,並計算是否卡頓
- (void) registerRunLoopObserver {
    CFRunLoopObserverContext context = {0,(__bridge void*)self,NULL,NULL};
    CFRunLoopObserverRef observer = CFRunLoopObserverCreate(kCFAllocatorDefault,
                                                            kCFRunLoopAllActivities,
                                                            YES,
                                                            0,
                                                            &runLoopObserverCallBack,
                                                            &context);
    CFRunLoopAddObserver(CFRunLoopGetMain(), observer, kCFRunLoopCommonModes);
    
    // 建立信號
    semaphore = dispatch_semaphore_create(0);
    
    // 在子線程監控時長
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        while (YES) {
            // 假定連續5次超時50ms認爲卡頓(固然也包含了單次超時250ms)
            long st = dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, 50*NSEC_PER_MSEC));
            if (st != 0) {
                if (activity==kCFRunLoopBeforeSources || activity==kCFRunLoopAfterWaiting) {
                    if (++timeoutCount < 5) {
                        continue;
                    }
                    // 發現卡頓
                    NSLog(@"卡、卡、卡、頓、頓、了");
                }
            }
            timeoutCount = 0;
        }
    });
}

3、記錄卡頓的函數調用

目睹卡頓現場,記錄此時的調用函數信息,做爲卡頓證據。

此處,使用第三方Crash收集組件PLCrashReporter,它不只能夠收集Crash信息,也可用於實時獲取各線程的調用堆棧,使用示例以下:測試

PLCrashReporterConfig *config = [[PLCrashReporterConfig alloc] initWithSignalHandlerType:PLCrashReporterSignalHandlerTypeBSD
                                                                   symbolicationStrategy:PLCrashReporterSymbolicationStrategyAll];
PLCrashReporter *crashReporter = [[PLCrashReporter alloc] initWithConfiguration:config];
NSData *data = [crashReporter generateLiveReport];
PLCrashReport *reporter = [[PLCrashReport alloc] initWithData:data error:NULL];
NSString *report = [PLCrashReportTextFormatter stringValueForCrashReport:reporter
                                                          withTextFormat:PLCrashReportTextFormatiOS];
NSLog(@"------------\n%@\n------------", report);

特別注意:優化

PLCrashReporter雖然能提供較爲準確的堆棧信息,用於定位問題,特別是使用符號化策略 PLCrashReporterSymbolicationStrategyAll時,可以對堆棧信息進行符號化,但會消耗大量資源,須要佔用較多時間,致使卡死現象(自測時,耗時超過7s,層屢次到10s以上)。

不使用符號化策略PLCrashReporterSymbolicationStrategyNone,測試時,平均耗時也接近3s。spa

所以,加入該信息採集,須要特別注意,建議僅在開發調試階段使用。線程

爲了投入線上使用,還須要再想一想如何解決該問題。

4、上報服務器

檢測到卡頓,獲取到調用堆棧信息,客戶端再根據實際狀況進行必定程度的過濾處理,將有價值的信息上報服務器。

後續對服務器收集到的數據進行分析,定位須要優化的功能邏輯。

相關文章
相關標籤/搜索