From 05a1319058166db67f1a573217ae58b4e496fa68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hanno=20J=2E=20G=C3=B6decke?= Date: Mon, 6 Jun 2022 18:30:21 +0200 Subject: [PATCH] feat(ios): implemented CPU tracking --- README.md | 1 - ios/PerformanceStats/PerformanceStats.mm | 80 ++++++++++++++++++++++-- 2 files changed, 74 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 72da15f..46adfda 100644 --- a/README.md +++ b/README.md @@ -107,7 +107,6 @@ No additional steps for android are required, except when using the new react na ## Caveats -- 🤖 Currently android only, iOS support will be added shortly - By default, no CPU usage will be tracked, as tracking adds a little overhead. Pass `true` to the `.start` function to track CPU as well. - The FPS reported may not match 100% from what you see in the "Perf Monitor". However, the values are really close to each other and convey the same information. - Technically, this is due to the fact that we only "refresh" the FPS every 500ms in the native impl (so does the "Perf Monitor"). And the `react-native-performance-stats` may started tracking at another point in time than the "Perf Monitor", thus they refresh at different times. diff --git a/ios/PerformanceStats/PerformanceStats.mm b/ios/PerformanceStats/PerformanceStats.mm index ac84119..c574f23 100644 --- a/ios/PerformanceStats/PerformanceStats.mm +++ b/ios/PerformanceStats/PerformanceStats.mm @@ -12,7 +12,7 @@ // NOTICE: Mainly copied from here: https://github.com/facebook/react-native/blob/main/React/CoreModules/RCTPerfMonitor.mm - +#pragma Resource usage methods static vm_size_t RCTGetResidentMemorySize(void) { vm_size_t memoryUsageInByte = 0; @@ -25,6 +25,71 @@ static vm_size_t RCTGetResidentMemorySize(void) return memoryUsageInByte; } +// https://stackoverflow.com/a/8382889/3668241 +float cpu_usage() +{ + kern_return_t kr; + task_info_data_t tinfo; + mach_msg_type_number_t task_info_count; + + task_info_count = TASK_INFO_MAX; + kr = task_info(mach_task_self(), TASK_BASIC_INFO, (task_info_t)tinfo, &task_info_count); + if (kr != KERN_SUCCESS) { + return -1; + } + + task_basic_info_t basic_info; + thread_array_t thread_list; + mach_msg_type_number_t thread_count; + + thread_info_data_t thinfo; + mach_msg_type_number_t thread_info_count; + + thread_basic_info_t basic_info_th; + uint32_t stat_thread = 0; // Mach threads + + basic_info = (task_basic_info_t)tinfo; + + // get threads in the task + kr = task_threads(mach_task_self(), &thread_list, &thread_count); + if (kr != KERN_SUCCESS) { + return -1; + } + if (thread_count > 0) + stat_thread += thread_count; + + long tot_sec = 0; + long tot_usec = 0; + float tot_cpu = 0; + int j; + + for (j = 0; j < (int)thread_count; j++) + { + thread_info_count = THREAD_INFO_MAX; + kr = thread_info(thread_list[j], THREAD_BASIC_INFO, + (thread_info_t)thinfo, &thread_info_count); + if (kr != KERN_SUCCESS) { + return -1; + } + + basic_info_th = (thread_basic_info_t)thinfo; + + if (!(basic_info_th->flags & TH_FLAGS_IDLE)) { + tot_sec = tot_sec + basic_info_th->user_time.seconds + basic_info_th->system_time.seconds; + tot_usec = tot_usec + basic_info_th->user_time.microseconds + basic_info_th->system_time.microseconds; + tot_cpu = tot_cpu + basic_info_th->cpu_usage / (float)TH_USAGE_SCALE * 100.0; + } + + } // for each thread + + kr = vm_deallocate(mach_task_self(), (vm_offset_t)thread_list, thread_count * sizeof(thread_t)); + assert(kr == KERN_SUCCESS); + + return tot_cpu; +} + +#pragma Module implementation + @implementation PerformanceStats { bool _isRunning; @@ -35,8 +100,6 @@ @implementation PerformanceStats { CADisplayLink *_jsDisplayLink; } -//@synthesize bridge = _bridge; - RCT_EXPORT_MODULE(PerformanceStats) + (BOOL)requiresMainQueueSetup @@ -54,7 +117,7 @@ - (dispatch_queue_t)methodQueue return @[ @"performanceStatsUpdate" ]; } -- (void)updateStats +- (void)updateStats:(bool)withCPU { // View count NSDictionary *views = [self.bridge.uiManager valueForKey:@"viewRegistry"]; @@ -68,10 +131,15 @@ - (void)updateStats // Memory double mem = (double)RCTGetResidentMemorySize() / 1024 / 1024; + float cpu = 0; + if (withCPU) { + cpu = cpu_usage(); + } [self sendEventWithName:@"performanceStatsUpdate" body:@{ @"jsFps": [NSNumber numberWithUnsignedInteger:_jsFPSTracker.FPS], @"uiFps": [NSNumber numberWithUnsignedInteger:_uiFPSTracker.FPS], + @"usedCpu": [NSNumber numberWithFloat:cpu], @"usedRam": [NSNumber numberWithDouble:mem], @"viewCount": [NSNumber numberWithUnsignedInteger:viewCount], @"visibleViewCount": [NSNumber numberWithUnsignedInteger:visibleViewCount] @@ -81,7 +149,7 @@ - (void)updateStats dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ __strong __typeof__(weakSelf) strongSelf = weakSelf; if (strongSelf && strongSelf->_isRunning) { - [strongSelf updateStats]; + [strongSelf updateStats:withCPU]; } }); } @@ -98,7 +166,7 @@ - (void)threadUpdate:(CADisplayLink *)displayLink _uiFPSTracker= [[FPSTracker alloc] init]; _jsFPSTracker= [[FPSTracker alloc] init]; - [self updateStats]; + [self updateStats:withCpu]; // Get FPS for UI Thread _uiDisplayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(threadUpdate:)];