diff --git a/Libraries/WebSocket/RCTWebSocketExecutor.m b/Libraries/WebSocket/RCTWebSocketExecutor.m index bd9600d3c..22a63e83b 100644 --- a/Libraries/WebSocket/RCTWebSocketExecutor.m +++ b/Libraries/WebSocket/RCTWebSocketExecutor.m @@ -18,6 +18,7 @@ #import "RCTSparseArray.h" #import "RCTUtils.h" #import "RCTSRWebSocket.h" +#import "RCTProfile.h" typedef void (^RCTWSMessageCallback)(NSError *error, NSDictionary *reply); @@ -109,11 +110,19 @@ RCT_EXPORT_MODULE() - (void)webSocket:(RCTSRWebSocket *)webSocket didReceiveMessage:(id)message { NSError *error = nil; - NSDictionary *reply = RCTJSONParse(message, &error); - NSNumber *messageID = reply[@"replyID"]; - RCTWSMessageCallback callback = _callbacks[messageID]; - if (callback) { - callback(error, reply); + NSDictionary *parsedMessage = RCTJSONParse(message, &error); + + if ([parsedMessage objectForKey:@"method"]) { + NSString *methodName = parsedMessage[@"method"]; + if ([methodName isEqual:@"requestMetrics"]) { + [self sendUsageMetrics]; + } + } else if ([parsedMessage objectForKey:@"replyID"]) { + NSNumber *messageID = parsedMessage[@"replyID"]; + RCTWSMessageCallback callback = _callbacks[messageID]; + if (callback) { + callback(error, parsedMessage); + } } } @@ -181,6 +190,21 @@ RCT_EXPORT_MODULE() }]; } +- (void)sendUsageMetrics +{ + NSDictionary *memoryUsage = RCTProfileGetMemoryUsage(YES); + NSNumber *cpuUsage = RCTProfileGetCPUUsage(); + + NSDictionary *message = @{ + @"method": @"usageMetrics", + @"memoryUsage": memoryUsage, + @"deviceCPUUsage": cpuUsage + }; + + // TODO: handle errors + [self sendMessage:message waitForReply:^(NSError *socketError, NSDictionary *reply) {}]; +} + - (void)injectJSONText:(NSString *)script asGlobalObjectNamed:(NSString *)objectName callback:(RCTJavaScriptCompleteBlock)onComplete { dispatch_async(_jsQueue, ^{ diff --git a/React/Base/RCTProfile.h b/React/Base/RCTProfile.h index 794f4a910..96ede77db 100644 --- a/React/Base/RCTProfile.h +++ b/React/Base/RCTProfile.h @@ -73,7 +73,24 @@ RCT_EXTERN void RCTProfileEndEvent(uint64_t tag, NSDictionary *args); /** - * Collects the initial event information for the event and returns a reference ID + * Exposes memory usage metrics + */ + +NSDictionary *RCTProfileGetMemoryUsage(BOOL); + +/** + * Exposes device cpu usage metrics - Note this does not include JS Runtime CPU usage + */ + +NSNumber *RCTProfileGetCPUUsage(void); + +/** + * This pair of macros implicitly handle the event ID when beginning and ending + * an event, for both simplicity and performance reasons, this method is preferred + * + * NOTE: The EndEvent call has to be either, in the same scope of BeginEvent, + * or in a sub-scope, otherwise the ID stored by BeginEvent won't be accessible + * for EndEvent, in this case you may want to use the actual C functions. */ RCT_EXTERN int RCTProfileBeginAsyncEvent(uint64_t tag, NSString *name, @@ -139,6 +156,9 @@ RCT_EXTERN void RCTProfileUnhookModules(RCTBridge *); #define RCTProfileImmediateEvent(...) +#define RCTProfileGetMemoryUsage(...) +#define RCTProfileGetCPUUsage(...) + #define RCTProfileBlock(block, ...) block #define RCTProfileHookModules(...) diff --git a/React/Base/RCTProfile.m b/React/Base/RCTProfile.m index 85d5a799a..4e21eae2f 100644 --- a/React/Base/RCTProfile.m +++ b/React/Base/RCTProfile.m @@ -72,7 +72,7 @@ static NSString *RCTProfileMemory(vm_size_t memory) return [NSString stringWithFormat:@"%.2lfmb", mem]; } -static NSDictionary *RCTProfileGetMemoryUsage(void) +NSDictionary *RCTProfileGetMemoryUsage(BOOL raw) { struct task_basic_info info; mach_msg_type_number_t size = sizeof(info); @@ -81,14 +81,76 @@ static NSDictionary *RCTProfileGetMemoryUsage(void) (task_info_t)&info, &size); if( kerr == KERN_SUCCESS ) { + vm_size_t vs = info.virtual_size; + vm_size_t rs = info.resident_size; return @{ @"suspend_count": @(info.suspend_count), - @"virtual_size": RCTProfileMemory(info.virtual_size), - @"resident_size": RCTProfileMemory(info.resident_size), + @"virtual_size": raw ? @(vs) : RCTProfileMemory(vs), + @"resident_size": raw ? @(rs) : RCTProfileMemory(rs), }; } else { return @{}; } + +} + +NSNumber *RCTProfileGetCPUUsage(void) +{ + 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 nil; + } + + 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; + + // get threads in the task + kr = task_threads(mach_task_self(), &thread_list, &thread_count); + if (kr != KERN_SUCCESS) { + return nil; + } + + long tot_sec = 0; + long tot_usec = 0; + float tot_cpu = 0; + unsigned j; + + for (j = 0; j < 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 nil; + } + + 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->system_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)); + + if( kr == KERN_SUCCESS ) { + return @(tot_cpu); + } else { + return nil; + + } } static NSDictionary *RCTProfileMergeArgs(NSDictionary *args0, NSDictionary *args1) @@ -369,7 +431,7 @@ void RCTProfileImmediateEvent( @"ts": RCTProfileTimestamp(CACurrentMediaTime()), @"scope": @(scope), @"ph": @"i", - @"args": RCTProfileGetMemoryUsage(), + @"args": RCTProfileGetMemoryUsage(NO), ); ); } diff --git a/packager/debugger.html b/packager/debugger.html index 24f8aea55..f152d9e17 100644 --- a/packager/debugger.html +++ b/packager/debugger.html @@ -14,8 +14,197 @@ React Native Debugger - + + +