123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807 |
- /*
- * Copyright (c) Facebook, Inc. and its affiliates.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
- #import "RCTProfile.h"
- #import <dlfcn.h>
- #import <mach/mach.h>
- #import <objc/message.h>
- #import <objc/runtime.h>
- #import <stdatomic.h>
- #import <UIKit/UIKit.h>
- #import "RCTAssert.h"
- #import "RCTBridge+Private.h"
- #import "RCTBridge.h"
- #import "RCTComponentData.h"
- #import "RCTDefines.h"
- #import "RCTLog.h"
- #import "RCTModuleData.h"
- #import "RCTUIManager.h"
- #import "RCTUIManagerUtils.h"
- #import "RCTUtils.h"
- NSString *const RCTProfileDidStartProfiling = @"RCTProfileDidStartProfiling";
- NSString *const RCTProfileDidEndProfiling = @"RCTProfileDidEndProfiling";
- const uint64_t RCTProfileTagAlways = 1L << 0;
- #if RCT_PROFILE
- #pragma mark - Constants
- static NSString *const kProfileTraceEvents = @"traceEvents";
- static NSString *const kProfileSamples = @"samples";
- static NSString *const kProfilePrefix = @"rct_profile_";
- #pragma mark - Variables
- static atomic_bool RCTProfileProfiling = ATOMIC_VAR_INIT(NO);
- static NSDictionary *RCTProfileInfo;
- static NSMutableDictionary *RCTProfileOngoingEvents;
- static NSTimeInterval RCTProfileStartTime;
- static NSUInteger RCTProfileEventID = 0;
- static CADisplayLink *RCTProfileDisplayLink;
- static __weak RCTBridge *_RCTProfilingBridge;
- static UIWindow *RCTProfileControlsWindow;
- #pragma mark - Macros
- #define RCTProfileAddEvent(type, props...) \
- [RCTProfileInfo[type] addObject:@{@"pid" : @([[NSProcessInfo processInfo] processIdentifier]), props}];
- #define CHECK(...) \
- if (!RCTProfileIsProfiling()) { \
- return __VA_ARGS__; \
- }
- #pragma mark - systrace glue code
- static RCTProfileCallbacks *callbacks;
- static char *systrace_buffer;
- static systrace_arg_t *newSystraceArgsFromDictionary(NSDictionary<NSString *, NSString *> *args)
- {
- if (args.count == 0) {
- return NULL;
- }
- systrace_arg_t *systrace_args = malloc(sizeof(systrace_arg_t) * args.count);
- if (systrace_args) {
- __block size_t i = 0;
- [args enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSString *value, __unused BOOL *stop) {
- systrace_args[i].key = [key UTF8String];
- systrace_args[i].key_len = [key length];
- systrace_args[i].value = [value UTF8String];
- systrace_args[i].value_len = [value length];
- i++;
- }];
- }
- return systrace_args;
- }
- void RCTProfileRegisterCallbacks(RCTProfileCallbacks *cb)
- {
- callbacks = cb;
- }
- #pragma mark - Private Helpers
- static RCTBridge *RCTProfilingBridge(void)
- {
- return _RCTProfilingBridge ?: [RCTBridge currentBridge];
- }
- static NSNumber *RCTProfileTimestamp(NSTimeInterval timestamp)
- {
- return @((timestamp - RCTProfileStartTime) * 1e6);
- }
- static NSString *RCTProfileMemory(vm_size_t memory)
- {
- double mem = ((double)memory) / 1024 / 1024;
- return [NSString stringWithFormat:@"%.2lfmb", mem];
- }
- static NSDictionary *RCTProfileGetMemoryUsage(void)
- {
- struct task_basic_info info;
- mach_msg_type_number_t size = sizeof(info);
- kern_return_t kerr = task_info(mach_task_self(), TASK_BASIC_INFO, (task_info_t)&info, &size);
- if (kerr == KERN_SUCCESS) {
- return @{
- @"suspend_count" : @(info.suspend_count),
- @"virtual_size" : RCTProfileMemory(info.virtual_size),
- @"resident_size" : RCTProfileMemory(info.resident_size),
- };
- } else {
- return @{};
- }
- }
- #pragma mark - Module hooks
- static const char *RCTProfileProxyClassName(Class class)
- {
- return [kProfilePrefix stringByAppendingString:NSStringFromClass(class)].UTF8String;
- }
- static dispatch_group_t RCTProfileGetUnhookGroup(void)
- {
- static dispatch_group_t unhookGroup;
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- unhookGroup = dispatch_group_create();
- });
- return unhookGroup;
- }
- // Used by RCTProfileTrampoline assembly file to call libc`malloc
- RCT_EXTERN void *RCTProfileMalloc(size_t size);
- void *RCTProfileMalloc(size_t size)
- {
- return malloc(size);
- }
- // Used by RCTProfileTrampoline assembly file to call libc`free
- RCT_EXTERN void RCTProfileFree(void *buf);
- void RCTProfileFree(void *buf)
- {
- free(buf);
- }
- RCT_EXTERN IMP RCTProfileGetImplementation(id obj, SEL cmd);
- IMP RCTProfileGetImplementation(id obj, SEL cmd)
- {
- return class_getMethodImplementation([obj class], cmd);
- }
- /**
- * For the profiling we have to execute some code before and after every
- * function being profiled, the only way of doing that with pure Objective-C is
- * by using `-forwardInvocation:`, which is slow and could skew the profile
- * results.
- *
- * The alternative in assembly is much simpler, we just need to store all the
- * state at the beginning of the function, start the profiler, restore all the
- * state, call the actual function we want to profile and stop the profiler.
- *
- * The implementation can be found in RCTProfileTrampoline-<arch>.s where arch
- * is one of: i386, x86_64, arm, arm64.
- */
- #if defined(__i386__) || defined(__x86_64__) || defined(__arm__) || defined(__arm64__)
- RCT_EXTERN void RCTProfileTrampoline(void);
- #else
- static void *RCTProfileTrampoline = NULL;
- #endif
- RCT_EXTERN void RCTProfileTrampolineStart(id, SEL);
- void RCTProfileTrampolineStart(id self, SEL cmd)
- {
- /**
- * This call might be during dealloc, so we shouldn't retain the object in the
- * block.
- */
- Class klass = [self class];
- RCT_PROFILE_BEGIN_EVENT(
- RCTProfileTagAlways, ([NSString stringWithFormat:@"-[%s %s]", class_getName(klass), sel_getName(cmd)]), nil);
- }
- RCT_EXTERN void RCTProfileTrampolineEnd(void);
- void RCTProfileTrampolineEnd(void)
- {
- RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"objc_call,modules,auto");
- }
- static UIView *(*originalCreateView)(RCTComponentData *, SEL, NSNumber *, NSNumber *);
- static UIView *RCTProfileCreateView(RCTComponentData *self, SEL _cmd, NSNumber *tag, NSNumber *rootTag)
- {
- UIView *view = originalCreateView(self, _cmd, tag, rootTag);
- RCTProfileHookInstance(view);
- return view;
- }
- static void RCTProfileHookUIManager(RCTUIManager *uiManager)
- {
- dispatch_async(dispatch_get_main_queue(), ^{
- for (id view in [uiManager valueForKey:@"viewRegistry"]) {
- RCTProfileHookInstance([uiManager viewForReactTag:view]);
- }
- Method createView = class_getInstanceMethod([RCTComponentData class], @selector(createViewWithTag:rootTag:));
- if (method_getImplementation(createView) != (IMP)RCTProfileCreateView) {
- originalCreateView = (typeof(originalCreateView))method_getImplementation(createView);
- method_setImplementation(createView, (IMP)RCTProfileCreateView);
- }
- });
- }
- void RCTProfileHookInstance(id instance)
- {
- Class moduleClass = object_getClass(instance);
- /**
- * We swizzle the instance -class method to return the original class, but
- * object_getClass will return the actual class.
- *
- * If they are different, it means that the object is returning the original
- * class, but it's actual class is the proxy subclass we created.
- */
- if ([instance class] != moduleClass) {
- return;
- }
- Class proxyClass = objc_allocateClassPair(moduleClass, RCTProfileProxyClassName(moduleClass), 0);
- if (!proxyClass) {
- proxyClass = objc_getClass(RCTProfileProxyClassName(moduleClass));
- if (proxyClass) {
- object_setClass(instance, proxyClass);
- }
- return;
- }
- unsigned int methodCount;
- Method *methods = class_copyMethodList(moduleClass, &methodCount);
- for (NSUInteger i = 0; i < methodCount; i++) {
- Method method = methods[i];
- SEL selector = method_getName(method);
- /**
- * Bail out on struct returns (except arm64) - we don't use it enough
- * to justify writing a stret version
- */
- #ifdef __arm64__
- BOOL returnsStruct = NO;
- #else
- const char *typeEncoding = method_getTypeEncoding(method);
- // bail out on structs and unions (since they might contain structs)
- BOOL returnsStruct = typeEncoding[0] == '{' || typeEncoding[0] == '(';
- #endif
- /**
- * Avoid hooking into NSObject methods, methods generated by React Native
- * and special methods that start `.` (e.g. .cxx_destruct)
- */
- if ([NSStringFromSelector(selector) hasPrefix:@"rct"] || [NSObject instancesRespondToSelector:selector] ||
- sel_getName(selector)[0] == '.' || returnsStruct) {
- continue;
- }
- const char *types = method_getTypeEncoding(method);
- class_addMethod(proxyClass, selector, (IMP)RCTProfileTrampoline, types);
- }
- free(methods);
- class_replaceMethod(
- object_getClass(proxyClass),
- @selector(initialize),
- imp_implementationWithBlock(^{
- }),
- "v@:");
- for (Class cls in @[ proxyClass, object_getClass(proxyClass) ]) {
- Method oldImp = class_getInstanceMethod(cls, @selector(class));
- class_replaceMethod(
- cls,
- @selector(class),
- imp_implementationWithBlock(^{
- return moduleClass;
- }),
- method_getTypeEncoding(oldImp));
- }
- objc_registerClassPair(proxyClass);
- object_setClass(instance, proxyClass);
- if (moduleClass == [RCTUIManager class]) {
- RCTProfileHookUIManager((RCTUIManager *)instance);
- }
- }
- void RCTProfileHookModules(RCTBridge *bridge)
- {
- _RCTProfilingBridge = bridge;
- #pragma clang diagnostic push
- #pragma clang diagnostic ignored "-Wtautological-pointer-compare"
- if (RCTProfileTrampoline == NULL) {
- return;
- }
- #pragma clang diagnostic pop
- RCT_PROFILE_BEGIN_EVENT(RCTProfileTagAlways, @"RCTProfileHookModules", nil);
- for (RCTModuleData *moduleData in [bridge valueForKey:@"moduleDataByID"]) {
- // Only hook modules with an instance, to prevent initializing everything
- if ([moduleData hasInstance]) {
- [bridge
- dispatchBlock:^{
- RCTProfileHookInstance(moduleData.instance);
- }
- queue:moduleData.methodQueue];
- }
- }
- RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"");
- }
- static void RCTProfileUnhookInstance(id instance)
- {
- if ([instance class] != object_getClass(instance)) {
- object_setClass(instance, [instance class]);
- }
- }
- void RCTProfileUnhookModules(RCTBridge *bridge)
- {
- _RCTProfilingBridge = nil;
- dispatch_group_enter(RCTProfileGetUnhookGroup());
- NSDictionary *moduleDataByID = [bridge valueForKey:@"moduleDataByID"];
- for (RCTModuleData *moduleData in moduleDataByID) {
- if ([moduleData hasInstance]) {
- RCTProfileUnhookInstance(moduleData.instance);
- }
- }
- if ([bridge moduleIsInitialized:[RCTUIManager class]]) {
- dispatch_async(dispatch_get_main_queue(), ^{
- for (id view in [bridge.uiManager valueForKey:@"viewRegistry"]) {
- RCTProfileUnhookInstance([bridge.uiManager viewForReactTag:view]);
- }
- dispatch_group_leave(RCTProfileGetUnhookGroup());
- });
- }
- }
- #pragma mark - Private ObjC class only used for the vSYNC CADisplayLink target
- @interface RCTProfile : NSObject
- @end
- @implementation RCTProfile
- + (void)vsync:(CADisplayLink *)displayLink
- {
- RCTProfileImmediateEvent(RCTProfileTagAlways, @"VSYNC", displayLink.timestamp, 'g');
- }
- + (void)reload
- {
- [RCTProfilingBridge() reloadWithReason:@"Profiling controls"];
- }
- + (void)toggle:(UIButton *)target
- {
- BOOL isProfiling = RCTProfileIsProfiling();
- // Start and Stop are switched here, since we're going to toggle isProfiling
- [target setTitle:isProfiling ? @"Start" : @"Stop" forState:UIControlStateNormal];
- if (isProfiling) {
- RCTProfileEnd(RCTProfilingBridge(), ^(NSString *result) {
- NSString *outFile = [NSTemporaryDirectory() stringByAppendingString:@"tmp_trace.json"];
- [result writeToFile:outFile atomically:YES encoding:NSUTF8StringEncoding error:nil];
- #if !TARGET_OS_TV
- UIActivityViewController *activityViewController =
- [[UIActivityViewController alloc] initWithActivityItems:@[ [NSURL fileURLWithPath:outFile] ]
- applicationActivities:nil];
- activityViewController.completionWithItemsHandler =
- ^(__unused UIActivityType activityType,
- __unused BOOL completed,
- __unused NSArray *items,
- __unused NSError *error) {
- RCTProfileControlsWindow.hidden = NO;
- };
- RCTProfileControlsWindow.hidden = YES;
- dispatch_async(dispatch_get_main_queue(), ^{
- [[[[RCTSharedApplication() delegate] window] rootViewController] presentViewController:activityViewController
- animated:YES
- completion:nil];
- });
- #endif
- });
- } else {
- RCTProfileInit(RCTProfilingBridge());
- }
- }
- + (void)drag:(UIPanGestureRecognizer *)gestureRecognizer
- {
- CGPoint translation = [gestureRecognizer translationInView:RCTProfileControlsWindow];
- RCTProfileControlsWindow.center =
- CGPointMake(RCTProfileControlsWindow.center.x + translation.x, RCTProfileControlsWindow.center.y + translation.y);
- [gestureRecognizer setTranslation:CGPointMake(0, 0) inView:RCTProfileControlsWindow];
- }
- @end
- #pragma mark - Public Functions
- dispatch_queue_t RCTProfileGetQueue(void)
- {
- static dispatch_queue_t queue;
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- queue = dispatch_queue_create("com.facebook.react.Profiler", DISPATCH_QUEUE_SERIAL);
- });
- return queue;
- }
- BOOL RCTProfileIsProfiling(void)
- {
- return atomic_load(&RCTProfileProfiling);
- }
- void RCTProfileInit(RCTBridge *bridge)
- {
- // TODO: enable assert JS thread from any file (and assert here)
- BOOL wasProfiling = atomic_fetch_or(&RCTProfileProfiling, 1);
- if (wasProfiling) {
- return;
- }
- if (callbacks != NULL) {
- systrace_buffer = callbacks->start();
- } else {
- NSTimeInterval time = CACurrentMediaTime();
- dispatch_async(RCTProfileGetQueue(), ^{
- RCTProfileStartTime = time;
- RCTProfileOngoingEvents = [NSMutableDictionary new];
- RCTProfileInfo = @{
- kProfileTraceEvents : [NSMutableArray new],
- kProfileSamples : [NSMutableArray new],
- };
- });
- }
- // Set up thread ordering
- dispatch_async(RCTProfileGetQueue(), ^{
- NSArray *orderedThreads =
- @[ @"JS async", @"RCTPerformanceLogger", @"com.facebook.react.JavaScript", @(RCTUIManagerQueueName), @"main" ];
- [orderedThreads enumerateObjectsUsingBlock:^(NSString *thread, NSUInteger idx, __unused BOOL *stop) {
- RCTProfileAddEvent(kProfileTraceEvents,
- @"ph"
- : @"M", // metadata event
- @"name"
- : @"thread_sort_index", @"tid"
- : thread, @"args"
- :
- @{@"sort_index" : @(-1000 + (NSInteger)idx)});
- }];
- });
- RCTProfileHookModules(bridge);
- RCTProfileDisplayLink = [CADisplayLink displayLinkWithTarget:[RCTProfile class] selector:@selector(vsync:)];
- [RCTProfileDisplayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
- [[NSNotificationCenter defaultCenter] postNotificationName:RCTProfileDidStartProfiling object:bridge];
- }
- void RCTProfileEnd(RCTBridge *bridge, void (^callback)(NSString *))
- {
- // assert JavaScript thread here again
- BOOL wasProfiling = atomic_fetch_and(&RCTProfileProfiling, 0);
- if (!wasProfiling) {
- return;
- }
- [[NSNotificationCenter defaultCenter] postNotificationName:RCTProfileDidEndProfiling object:bridge];
- [RCTProfileDisplayLink invalidate];
- RCTProfileDisplayLink = nil;
- RCTProfileUnhookModules(bridge);
- if (callbacks != NULL) {
- if (systrace_buffer) {
- callbacks->stop();
- callback(@(systrace_buffer));
- }
- } else {
- dispatch_async(RCTProfileGetQueue(), ^{
- NSString *log = RCTJSONStringify(RCTProfileInfo, NULL);
- RCTProfileEventID = 0;
- RCTProfileInfo = nil;
- RCTProfileOngoingEvents = nil;
- callback(log);
- });
- }
- }
- static NSMutableArray<NSArray *> *RCTProfileGetThreadEvents(NSThread *thread)
- {
- static NSString *const RCTProfileThreadEventsKey = @"RCTProfileThreadEventsKey";
- NSMutableArray<NSArray *> *threadEvents = thread.threadDictionary[RCTProfileThreadEventsKey];
- if (!threadEvents) {
- threadEvents = [NSMutableArray new];
- thread.threadDictionary[RCTProfileThreadEventsKey] = threadEvents;
- }
- return threadEvents;
- }
- void _RCTProfileBeginEvent(
- NSThread *calleeThread,
- NSTimeInterval time,
- uint64_t tag,
- NSString *name,
- NSDictionary<NSString *, NSString *> *args)
- {
- CHECK();
- if (callbacks != NULL) {
- systrace_arg_t *systraceArgs = newSystraceArgsFromDictionary(args);
- callbacks->begin_section(tag, name.UTF8String, args.count, systraceArgs);
- free(systraceArgs);
- return;
- }
- dispatch_async(RCTProfileGetQueue(), ^{
- NSMutableArray *events = RCTProfileGetThreadEvents(calleeThread);
- [events addObject:@[
- RCTProfileTimestamp(time),
- name,
- RCTNullIfNil(args),
- ]];
- });
- }
- void _RCTProfileEndEvent(
- NSThread *calleeThread,
- NSString *threadName,
- NSTimeInterval time,
- uint64_t tag,
- NSString *category)
- {
- CHECK();
- if (callbacks != NULL) {
- callbacks->end_section(tag, 0, nil);
- return;
- }
- dispatch_async(RCTProfileGetQueue(), ^{
- NSMutableArray<NSArray *> *events = RCTProfileGetThreadEvents(calleeThread);
- NSArray *event = events.lastObject;
- [events removeLastObject];
- if (!event) {
- return;
- }
- NSNumber *start = event[0];
- RCTProfileAddEvent(kProfileTraceEvents, @"tid"
- : threadName, @"name"
- : event[1], @"cat"
- : category, @"ph"
- : @"X", @"ts"
- : start, @"dur"
- : @(RCTProfileTimestamp(time).doubleValue - start.doubleValue), @"args"
- : event[2], );
- });
- }
- NSUInteger RCTProfileBeginAsyncEvent(uint64_t tag, NSString *name, NSDictionary<NSString *, NSString *> *args)
- {
- CHECK(0);
- static NSUInteger eventID = 0;
- NSTimeInterval time = CACurrentMediaTime();
- NSUInteger currentEventID = ++eventID;
- if (callbacks != NULL) {
- systrace_arg_t *systraceArgs = newSystraceArgsFromDictionary(args);
- callbacks->begin_async_section(tag, name.UTF8String, (int)(currentEventID % INT_MAX), args.count, systraceArgs);
- free(systraceArgs);
- } else {
- dispatch_async(RCTProfileGetQueue(), ^{
- RCTProfileOngoingEvents[@(currentEventID)] = @[
- RCTProfileTimestamp(time),
- name,
- RCTNullIfNil(args),
- ];
- });
- }
- return currentEventID;
- }
- void RCTProfileEndAsyncEvent(uint64_t tag, NSString *category, NSUInteger cookie, NSString *name, NSString *threadName)
- {
- CHECK();
- if (callbacks != NULL) {
- callbacks->end_async_section(tag, name.UTF8String, (int)(cookie % INT_MAX), 0, nil);
- return;
- }
- NSTimeInterval time = CACurrentMediaTime();
- dispatch_async(RCTProfileGetQueue(), ^{
- NSArray *event = RCTProfileOngoingEvents[@(cookie)];
- if (event) {
- NSNumber *endTimestamp = RCTProfileTimestamp(time);
- RCTProfileAddEvent(kProfileTraceEvents, @"tid"
- : threadName, @"name"
- : event[1], @"cat"
- : category, @"ph"
- : @"X", @"ts"
- : event[0], @"dur"
- : @(endTimestamp.doubleValue - [event[0] doubleValue]), @"args"
- : event[2], );
- [RCTProfileOngoingEvents removeObjectForKey:@(cookie)];
- }
- });
- }
- void RCTProfileImmediateEvent(uint64_t tag, NSString *name, NSTimeInterval time, char scope)
- {
- CHECK();
- if (callbacks != NULL) {
- callbacks->instant_section(tag, name.UTF8String, scope);
- return;
- }
- NSString *threadName = RCTCurrentThreadName();
- dispatch_async(RCTProfileGetQueue(), ^{
- RCTProfileAddEvent(kProfileTraceEvents, @"tid"
- : threadName, @"name"
- : name, @"ts"
- : RCTProfileTimestamp(time), @"scope"
- : @(scope), @"ph"
- : @"i", @"args"
- : RCTProfileGetMemoryUsage(), );
- });
- }
- NSUInteger _RCTProfileBeginFlowEvent(void)
- {
- static NSUInteger flowID = 0;
- CHECK(0);
- NSUInteger cookie = ++flowID;
- if (callbacks != NULL) {
- callbacks->begin_async_flow(1, "flow", (int)cookie);
- return cookie;
- }
- NSTimeInterval time = CACurrentMediaTime();
- NSString *threadName = RCTCurrentThreadName();
- dispatch_async(RCTProfileGetQueue(), ^{
- RCTProfileAddEvent(kProfileTraceEvents, @"tid"
- : threadName, @"name"
- : @"flow", @"id"
- : @(cookie), @"cat"
- : @"flow", @"ph"
- : @"s", @"ts"
- : RCTProfileTimestamp(time), );
- });
- return cookie;
- }
- void _RCTProfileEndFlowEvent(NSUInteger cookie)
- {
- CHECK();
- if (callbacks != NULL) {
- callbacks->end_async_flow(1, "flow", (int)cookie);
- return;
- }
- NSTimeInterval time = CACurrentMediaTime();
- NSString *threadName = RCTCurrentThreadName();
- dispatch_async(RCTProfileGetQueue(), ^{
- RCTProfileAddEvent(kProfileTraceEvents, @"tid"
- : threadName, @"name"
- : @"flow", @"id"
- : @(cookie), @"cat"
- : @"flow", @"ph"
- : @"f", @"ts"
- : RCTProfileTimestamp(time), );
- });
- }
- void RCTProfileSendResult(RCTBridge *bridge, NSString *route, NSData *data)
- {
- if (![bridge.bundleURL.scheme hasPrefix:@"http"]) {
- RCTLogWarn(
- @"Cannot upload profile information because you're not connected to the packager. The profiling data is still saved in the app container.");
- return;
- }
- NSURL *URL = [NSURL URLWithString:[@"/" stringByAppendingString:route] relativeToURL:bridge.bundleURL];
- NSMutableURLRequest *URLRequest = [NSMutableURLRequest requestWithURL:URL];
- URLRequest.HTTPMethod = @"POST";
- [URLRequest setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
- NSURLSessionTask *task = [[NSURLSession sharedSession]
- uploadTaskWithRequest:URLRequest
- fromData:data
- completionHandler:^(NSData *responseData, __unused NSURLResponse *response, NSError *error) {
- if (error) {
- RCTLogError(@"%@", error.localizedDescription);
- } else {
- NSString *message = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
- if (message.length) {
- #if !TARGET_OS_TV
- dispatch_async(dispatch_get_main_queue(), ^{
- UIAlertController *alertController =
- [UIAlertController alertControllerWithTitle:@"Profile"
- message:message
- preferredStyle:UIAlertControllerStyleAlert];
- [alertController addAction:[UIAlertAction actionWithTitle:@"OK"
- style:UIAlertActionStyleCancel
- handler:nil]];
- [RCTPresentedViewController() presentViewController:alertController animated:YES completion:nil];
- });
- #endif
- }
- }
- }];
- [task resume];
- }
- void RCTProfileShowControls(void)
- {
- static const CGFloat height = 30;
- static const CGFloat width = 60;
- UIWindow *window = [[UIWindow alloc] initWithFrame:CGRectMake(20, 80, width * 2, height)];
- window.windowLevel = UIWindowLevelAlert + 1000;
- window.hidden = NO;
- window.backgroundColor = [UIColor lightGrayColor];
- window.layer.borderColor = [UIColor grayColor].CGColor;
- window.layer.borderWidth = 1;
- window.alpha = 0.8;
- UIButton *startOrStop = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, width, height)];
- [startOrStop setTitle:RCTProfileIsProfiling() ? @"Stop" : @"Start" forState:UIControlStateNormal];
- [startOrStop addTarget:[RCTProfile class] action:@selector(toggle:) forControlEvents:UIControlEventTouchUpInside];
- startOrStop.titleLabel.font = [UIFont systemFontOfSize:12];
- UIButton *reload = [[UIButton alloc] initWithFrame:CGRectMake(width, 0, width, height)];
- [reload setTitle:@"Reload" forState:UIControlStateNormal];
- [reload addTarget:[RCTProfile class] action:@selector(reload) forControlEvents:UIControlEventTouchUpInside];
- reload.titleLabel.font = [UIFont systemFontOfSize:12];
- [window addSubview:startOrStop];
- [window addSubview:reload];
- UIPanGestureRecognizer *gestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:[RCTProfile class]
- action:@selector(drag:)];
- [window addGestureRecognizer:gestureRecognizer];
- RCTProfileControlsWindow = window;
- }
- void RCTProfileHideControls(void)
- {
- RCTProfileControlsWindow.hidden = YES;
- RCTProfileControlsWindow = nil;
- }
- #endif
|