2015-04-20 11:55:05 +00:00
/ * *
* Copyright ( c ) 2015 - present , Facebook , Inc .
* All rights reserved .
*
* This source code is licensed under the BSD - style license found in the
* LICENSE file in the root directory of this source tree . An additional grant
* of patent rights can be found in the PATENTS file in the same directory .
* /
# import "RCTProfile.h"
2015-10-20 11:11:38 +00:00
# import < dlfcn . h >
2015-11-04 17:00:01 +00:00
# import < libkern / OSAtomic . h >
2015-04-20 11:55:05 +00:00
# import < mach / mach . h >
2015-06-05 11:23:51 +00:00
# import < objc / message . h >
# import < objc / runtime . h >
2015-04-20 11:55:05 +00:00
# import < UIKit / UIKit . h >
2015-05-25 12:19:53 +00:00
# import "RCTAssert.h"
2015-06-05 11:23:51 +00:00
# import "RCTBridge.h"
2015-12-15 13:39:30 +00:00
# import "RCTBridge+Private.h"
2015-12-14 21:07:24 +00:00
# import "RCTComponentData.h"
2015-04-21 12:26:51 +00:00
# import "RCTDefines.h"
2015-09-11 13:35:25 +00:00
# import "RCTLog.h"
[ReactNative] Move module info from bridge to RCTModuleData
Summary:
@public
The info about bridge modules (such as id, name, queue, methods...) was spread
across arrays & dictionaries on the bridge, move it into a specific class.
It also removes a lot of information that was statically cached, and now have
the same lifecycle of the bridge.
Also moved RCTModuleMethod, RCTFrameUpdate and RCTBatchedBridge into it's own
files, for organization sake.
NOTE: This diff seems huge, but most of it was just moving code :)
Test Plan:
Tested UIExplorer & UIExplorer tests, Catalyst, MAdMan and Groups. Everything
looks fine.
2015-06-24 23:34:56 +00:00
# import "RCTModuleData.h"
2015-04-20 11:55:05 +00:00
# import "RCTUtils.h"
2015-12-14 21:07:24 +00:00
# import "RCTUIManager.h"
2016-02-12 11:49:51 +00:00
# import "RCTJSCExecutor.h"
2015-04-20 11:55:05 +00:00
2015-06-02 13:15:53 +00:00
NSString * const RCTProfileDidStartProfiling = @ "RCTProfileDidStartProfiling" ;
NSString * const RCTProfileDidEndProfiling = @ "RCTProfileDidEndProfiling" ;
2016-05-14 00:14:59 +00:00
const uint64_t RCTProfileTagAlways = 1 L < < 0 ;
2015-04-21 12:26:51 +00:00
# if RCT_DEV
2015-04-20 13:33:52 +00:00
2015-04-20 11:55:05 +00:00
# pragma mark - Constants
2016-01-07 18:21:39 +00:00
NSString * const RCTProfileTraceEvents = @ "traceEvents" ;
NSString * const RCTProfileSamples = @ "samples" ;
2015-06-05 11:23:51 +00:00
NSString * const RCTProfilePrefix = @ "rct_profile_" ;
2015-04-20 11:55:05 +00:00
# pragma mark - Variables
2015-11-07 21:46:35 +00:00
// This is actually a BOOL - but has to be compatible with OSAtomic
2015-11-04 17:00:01 +00:00
static volatile uint32_t RCTProfileProfiling ;
2015-11-07 21:46:35 +00:00
2015-09-23 18:59:44 +00:00
static NSDictionary * RCTProfileInfo ;
static NSMutableDictionary * RCTProfileOngoingEvents ;
static NSTimeInterval RCTProfileStartTime ;
static NSUInteger RCTProfileEventID = 0 ;
2015-11-23 17:48:43 +00:00
static CADisplayLink * RCTProfileDisplayLink ;
2016-03-11 14:20:24 +00:00
static __weak RCTBridge * _RCTProfilingBridge ;
static UIWindow * RCTProfileControlsWindow ;
2015-04-20 11:55:05 +00:00
# pragma mark - Macros
# define RCTProfileAddEvent ( type , props . . . ) \
[ RCTProfileInfo [ type ] addObject : @ { \
@ "pid" : @ ( [ [ NSProcessInfo processInfo ] processIdentifier ] ) , \
props \
} ] ;
# define CHECK ( . . . ) \
if ( ! RCTProfileIsProfiling ( ) ) { \
return __VA _ARGS __ ; \
}
2015-09-23 18:59:44 +00:00
# pragma mark - systrace glue code
static RCTProfileCallbacks * callbacks ;
static char * systrace_buffer ;
static systrace_arg _t * RCTProfileSystraceArgsFromNSDictionary ( NSDictionary * args )
{
if ( args . count = = 0 ) {
return NULL ;
}
systrace_arg _t * systrace_args = malloc ( sizeof ( systrace_arg _t ) * args . count ) ;
__block size_t i = 0 ;
[ args enumerateKeysAndObjectsUsingBlock : ^ ( id key , id value , __unused BOOL * stop ) {
const char * keyc = [ key description ] . UTF8String ;
systrace_args [ i ] . key = keyc ;
systrace_args [ i ] . key_len = ( int ) strlen ( keyc ) ;
2016-01-26 14:12:48 +00:00
const char * valuec = RCTJSONStringify ( value , NULL ) . UTF8String ;
2015-09-23 18:59:44 +00:00
systrace_args [ i ] . value = valuec ;
systrace_args [ i ] . value_len = ( int ) strlen ( valuec ) ;
i + + ;
} ] ;
return systrace_args ;
}
void RCTProfileRegisterCallbacks ( RCTProfileCallbacks * cb )
{
callbacks = cb ;
}
2015-04-22 14:03:55 +00:00
2015-04-20 11:55:05 +00:00
# pragma mark - Private Helpers
2016-03-11 14:20:24 +00:00
static RCTBridge * RCTProfilingBridge ( void )
{
return _RCTProfilingBridge ? : [ RCTBridge currentBridge ] ;
}
2015-08-20 06:36:11 +00:00
static NSNumber * RCTProfileTimestamp ( NSTimeInterval timestamp )
2015-04-20 11:55:05 +00:00
{
return @ ( ( timestamp - RCTProfileStartTime ) * 1 e6 ) ;
}
2015-08-20 06:36:11 +00:00
static NSString * RCTProfileMemory ( vm_size _t memory )
2015-04-20 11:55:05 +00:00
{
double mem = ( ( double ) memory ) / 1024 / 1024 ;
return [ NSString stringWithFormat : @ "%.2lfmb" , mem ] ;
}
2015-08-21 17:15:04 +00:00
static NSDictionary * RCTProfileGetMemoryUsage ( void )
2015-04-20 11:55:05 +00:00
{
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 ) ,
2015-08-21 17:15:04 +00:00
@ "virtual_size" : RCTProfileMemory ( info . virtual_size ) ,
@ "resident_size" : RCTProfileMemory ( info . resident_size ) ,
2015-04-20 11:55:05 +00:00
} ;
} else {
return @ { } ;
}
}
2015-08-20 06:36:11 +00:00
static NSDictionary * RCTProfileMergeArgs ( NSDictionary * args0 , NSDictionary * args1 )
{
args0 = RCTNilIfNull ( args0 ) ;
args1 = RCTNilIfNull ( args1 ) ;
if ( ! args0 && args1 ) {
args0 = args1 ;
} else if ( args0 && args1 ) {
NSMutableDictionary * d = [ args0 mutableCopy ] ;
[ d addEntriesFromDictionary : args1 ] ;
args0 = [ d copy ] ;
}
return RCTNullIfNil ( args0 ) ;
}
2015-06-05 11:23:51 +00:00
# pragma mark - Module hooks
static const char * RCTProfileProxyClassName ( Class class )
{
return [ RCTProfilePrefix stringByAppendingString : NSStringFromClass ( class ) ] . UTF8String ;
}
2015-10-02 11:16:04 +00:00
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 ;
}
2015-10-20 11:11:38 +00:00
RCT_EXTERN IMP RCTProfileGetImplementation ( id obj , SEL cmd ) ;
IMP RCTProfileGetImplementation ( id obj , SEL cmd )
2015-06-05 11:23:51 +00:00
{
2015-10-20 11:11:38 +00:00
return class_getMethodImplementation ( [ obj class ] , cmd ) ;
2015-06-05 11:23:51 +00:00
}
2015-10-20 11:11:38 +00:00
/ * *
* 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
2015-11-24 14:48:04 +00:00
* is one of : i386 , x86_64 , arm , arm64 .
2015-10-20 11:11:38 +00:00
* /
2015-11-24 14:48:04 +00:00
# if defined ( __i386 __ ) || \
2015-10-27 19:59:27 +00:00
defined ( __x86 _64 __ ) || \
defined ( __arm __ ) || \
defined ( __arm64 __ )
2015-10-20 11:11:38 +00:00
2015-10-27 19:59:27 +00:00
RCT_EXTERN void RCTProfileTrampoline ( void ) ;
# else
static void * RCTProfileTrampoline = NULL ;
# endif
2015-10-20 11:11:38 +00:00
RCT_EXTERN void RCTProfileTrampolineStart ( id , SEL ) ;
void RCTProfileTrampolineStart ( id self , SEL cmd )
{
2015-12-14 21:07:24 +00:00
/ * *
* This call might be during dealloc , so we shouldn ' t retain the object in the
* block .
* /
Class klass = [ self class ] ;
2016-05-14 00:14:59 +00:00
RCT_PROFILE _BEGIN _EVENT ( RCTProfileTagAlways , [ NSString stringWithFormat : @ "-[%s %s]" , class_getName ( klass ) , sel_getName ( cmd ) ] , nil ) ;
2015-10-20 11:11:38 +00:00
}
RCT_EXTERN void RCTProfileTrampolineEnd ( void ) ;
void RCTProfileTrampolineEnd ( void )
{
2016-05-14 00:14:59 +00:00
RCT_PROFILE _END _EVENT ( RCTProfileTagAlways , @ "objc_call,modules,auto" , nil ) ;
2015-06-05 11:23:51 +00:00
}
2016-05-04 13:54:12 +00:00
static UIView * (*originalCreateView)(RCTComponentData *, SEL, NSNumber *) ;
static UIView * RCTProfileCreateView ( RCTComponentData * self , SEL _cmd , NSNumber * tag )
{
UIView * view = originalCreateView ( self , _cmd , tag ) ;
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 : ) ) ;
if ( method_getImplementation ( createView ) ! = ( IMP ) RCTProfileCreateView ) {
originalCreateView = ( typeof ( originalCreateView ) ) method_getImplementation ( createView ) ;
method_setImplementation ( createView , ( IMP ) RCTProfileCreateView ) ;
}
} ) ;
}
void RCTProfileHookInstance ( id instance )
2015-12-14 21:07:24 +00:00
{
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 ) ;
2016-05-04 13:54:12 +00:00
if ( moduleClass = = [ RCTUIManager class ] ) {
RCTProfileHookUIManager ( ( RCTUIManager * ) instance ) ;
}
2015-12-14 21:07:24 +00:00
}
2015-07-22 17:54:45 +00:00
void RCTProfileHookModules ( RCTBridge * bridge )
2015-06-05 11:23:51 +00:00
{
2016-03-11 14:20:24 +00:00
_RCTProfilingBridge = bridge ;
2015-10-27 19:59:27 +00:00
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wtautological-pointer-compare"
2015-11-26 14:38:11 +00:00
if ( RCTProfileTrampoline = = NULL ) {
2015-10-20 11:11:38 +00:00
return ;
}
2015-10-27 19:59:27 +00:00
# pragma clang diagnostic pop
2015-10-20 11:11:38 +00:00
2016-05-14 00:15:05 +00:00
RCT_PROFILE _BEGIN _EVENT ( RCTProfileTagAlways , @ "RCTProfileHookModules" , nil ) ;
2015-08-07 13:42:34 +00:00
for ( RCTModuleData * moduleData in [ bridge valueForKey : @ "moduleDataByID" ] ) {
2016-05-04 13:54:12 +00:00
// Only hook modules with an instance , to prevent initializing everything
if ( [ moduleData hasInstance ] ) {
[ bridge dispatchBlock : ^ {
RCTProfileHookInstance ( moduleData . instance ) ;
} queue : moduleData . methodQueue ] ;
2015-12-14 21:07:24 +00:00
}
2016-05-04 13:54:12 +00:00
}
2016-05-14 00:15:05 +00:00
RCT_PROFILE _END _EVENT ( RCTProfileTagAlways , @ "" , nil ) ;
2015-12-14 21:07:24 +00:00
}
2015-06-05 11:23:51 +00:00
2015-12-14 21:07:24 +00:00
static void RCTProfileUnhookInstance ( id instance )
{
if ( [ instance class ] ! = object_getClass ( instance ) ) {
object_setClass ( instance , [ instance class ] ) ;
2015-06-15 14:53:45 +00:00
}
2015-06-05 11:23:51 +00:00
}
void RCTProfileUnhookModules ( RCTBridge * bridge )
{
2016-03-11 14:20:24 +00:00
_RCTProfilingBridge = nil ;
2015-10-02 11:16:04 +00:00
dispatch_group _enter ( RCTProfileGetUnhookGroup ( ) ) ;
2016-05-04 13:54:12 +00:00
NSDictionary * moduleDataByID = [ bridge valueForKey : @ "moduleDataByID" ] ;
for ( RCTModuleData * moduleData in moduleDataByID ) {
if ( [ moduleData hasInstance ] ) {
RCTProfileUnhookInstance ( moduleData . instance ) ;
}
2015-10-02 11:16:04 +00:00
}
2016-05-04 13:54:12 +00:00
if ( [ bridge moduleIsInitialized : [ RCTUIManager class ] ] ) {
dispatch_async ( dispatch_get _main _queue ( ) , ^ {
for ( id view in [ bridge . uiManager valueForKey : @ "viewRegistry" ] ) {
RCTProfileUnhookInstance ( view ) ;
}
2015-12-14 21:07:24 +00:00
2016-05-04 13:54:12 +00:00
dispatch_group _leave ( RCTProfileGetUnhookGroup ( ) ) ;
} ) ;
}
2015-06-05 11:23:51 +00:00
}
2015-11-23 17:48:43 +00:00
# pragma mark - Private ObjC class only used for the vSYNC CADisplayLink target
@ interface RCTProfile : NSObject
@ end
@ implementation RCTProfile
2015-12-09 17:53:00 +00:00
+ ( void ) vsync : ( CADisplayLink * ) displayLink
2015-11-23 17:48:43 +00:00
{
2016-05-14 00:14:59 +00:00
RCTProfileImmediateEvent ( RCTProfileTagAlways , @ "VSYNC" , displayLink . timestamp , ' g ' ) ;
2015-11-23 17:48:43 +00:00
}
2016-03-11 14:20:24 +00:00
+ ( void ) reload
{
2016-04-01 14:24:40 +00:00
[ [ NSNotificationCenter defaultCenter ] postNotificationName : RCTReloadNotification
object : NULL ] ;
2016-03-11 14:20:24 +00:00
}
+ ( 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 ] ;
UIActivityViewController * activityViewController = [ [ UIActivityViewController alloc ] initWithActivityItems : @ [ [ NSURL fileURLWithPath : outFile ] ]
applicationActivities : nil ] ;
activityViewController . completionHandler = ^ ( __unused NSString * activityType , __unused BOOL completed ) {
RCTProfileControlsWindow . hidden = NO ;
} ;
RCTProfileControlsWindow . hidden = YES ;
dispatch_async ( dispatch_get _main _queue ( ) , ^ {
[ [ [ [ [ UIApplication sharedApplication ] delegate ] window ] rootViewController ] presentViewController : activityViewController
animated : YES
completion : nil ] ;
} ) ;
} ) ;
} 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 ] ;
}
2015-11-23 17:48:43 +00:00
@ end
2015-06-05 11:23:51 +00:00
2015-04-20 11:55:05 +00:00
# pragma mark - Public Functions
2015-11-04 17:00:01 +00:00
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 ;
}
2015-04-20 11:55:05 +00:00
BOOL RCTProfileIsProfiling ( void )
{
2015-11-09 16:02:46 +00:00
return ( BOOL ) RCTProfileProfiling ;
2015-04-20 11:55:05 +00:00
}
2015-06-05 11:23:51 +00:00
void RCTProfileInit ( RCTBridge * bridge )
2015-04-20 11:55:05 +00:00
{
2015-11-04 17:00:01 +00:00
// TODO : enable assert JS thread from any file ( and assert here )
2015-11-09 16:02:46 +00:00
if ( RCTProfileIsProfiling ( ) ) {
return ;
}
OSAtomicOr32Barrier ( 1 , & RCTProfileProfiling ) ;
2015-06-05 11:23:51 +00:00
2015-09-23 18:59:44 +00:00
if ( callbacks ! = NULL ) {
size_t buffer_size = 1 < < 22 ;
systrace_buffer = calloc ( 1 , buffer_size ) ;
callbacks -> start ( ~ ( ( uint64_t ) 0 ) , systrace_buffer , buffer_size ) ;
} else {
2015-11-04 17:00:01 +00:00
NSTimeInterval time = CACurrentMediaTime ( ) ;
dispatch_async ( RCTProfileGetQueue ( ) , ^ {
RCTProfileStartTime = time ;
2015-09-23 18:59:44 +00:00
RCTProfileOngoingEvents = [ NSMutableDictionary new ] ;
RCTProfileInfo = @ {
RCTProfileTraceEvents : [ NSMutableArray new ] ,
RCTProfileSamples : [ NSMutableArray new ] ,
} ;
2015-11-04 17:00:01 +00:00
} ) ;
2015-09-23 18:59:44 +00:00
}
2015-06-02 13:15:53 +00:00
2016-02-12 11:49:51 +00:00
// Set up thread ordering
dispatch_async ( RCTProfileGetQueue ( ) , ^ {
2016-05-04 13:54:12 +00:00
NSArray * orderedThreads = @ [ @ "JS async" , @ "RCTPerformanceLogger" , RCTJSCThreadName , @ ( RCTUIManagerQueueName ) , @ "main" ] ;
2016-02-16 20:41:20 +00:00
[ orderedThreads enumerateObjectsUsingBlock : ^ ( NSString * thread , NSUInteger idx , __unused BOOL * stop ) {
2016-02-12 11:49:51 +00:00
RCTProfileAddEvent ( RCTProfileTraceEvents ,
@ "ph" : @ "M" , // metadata event
@ "name" : @ "thread_sort_index" ,
@ "tid" : thread ,
@ "args" : @ { @ "sort_index" : @ ( -1000 + ( NSInteger ) idx ) }
) ;
} ] ;
} ) ;
2015-11-04 17:00:01 +00:00
RCTProfileHookModules ( bridge ) ;
2015-11-23 17:48:43 +00:00
RCTProfileDisplayLink = [ CADisplayLink displayLinkWithTarget : [ RCTProfile class ]
selector : @ selector ( vsync : ) ] ;
[ RCTProfileDisplayLink addToRunLoop : [ NSRunLoop mainRunLoop ]
forMode : NSRunLoopCommonModes ] ;
2015-06-02 13:15:53 +00:00
[ [ NSNotificationCenter defaultCenter ] postNotificationName : RCTProfileDidStartProfiling
2015-12-15 13:42:45 +00:00
object : bridge ] ;
2015-04-20 11:55:05 +00:00
}
2015-11-04 17:00:01 +00:00
void RCTProfileEnd ( RCTBridge * bridge , void ( ^ callback ) ( NSString * ) )
2015-04-20 11:55:05 +00:00
{
2015-11-04 17:00:01 +00:00
// assert JavaScript thread here again
2015-11-03 15:22:28 +00:00
if ( ! RCTProfileIsProfiling ( ) ) {
2015-11-04 17:00:01 +00:00
return ;
2015-11-03 15:22:28 +00:00
}
2015-11-09 16:02:46 +00:00
OSAtomicAnd32Barrier ( 0 , & RCTProfileProfiling ) ;
2015-11-04 17:00:01 +00:00
2015-06-02 13:15:53 +00:00
[ [ NSNotificationCenter defaultCenter ] postNotificationName : RCTProfileDidEndProfiling
2015-12-15 13:42:45 +00:00
object : bridge ] ;
2015-06-02 13:15:53 +00:00
2015-11-23 17:48:43 +00:00
[ RCTProfileDisplayLink invalidate ] ;
RCTProfileDisplayLink = nil ;
2015-11-04 17:00:01 +00:00
RCTProfileUnhookModules ( bridge ) ;
2015-06-05 11:23:51 +00:00
2015-09-23 18:59:44 +00:00
if ( callbacks ! = NULL ) {
callbacks -> stop ( ) ;
2015-11-04 17:00:01 +00:00
callback ( @ ( systrace_buffer ) ) ;
2015-09-23 18:59:44 +00:00
} else {
2015-11-04 17:00:01 +00:00
dispatch_async ( RCTProfileGetQueue ( ) , ^ {
2015-09-23 18:59:44 +00:00
NSString * log = RCTJSONStringify ( RCTProfileInfo , NULL ) ;
RCTProfileEventID = 0 ;
RCTProfileInfo = nil ;
RCTProfileOngoingEvents = nil ;
2015-06-05 11:23:51 +00:00
2015-11-04 17:00:01 +00:00
callback ( log ) ;
} ) ;
2015-09-23 18:59:44 +00:00
}
2015-04-20 11:55:05 +00:00
}
2015-11-04 17:00:01 +00:00
static NSMutableArray < NSArray * > * RCTProfileGetThreadEvents ( NSThread * thread )
2015-04-20 11:55:05 +00:00
{
2015-08-20 06:36:11 +00:00
static NSString * const RCTProfileThreadEventsKey = @ "RCTProfileThreadEventsKey" ;
2015-11-03 22:45:46 +00:00
NSMutableArray < NSArray * > * threadEvents =
2015-11-04 17:00:01 +00:00
thread . threadDictionary [ RCTProfileThreadEventsKey ] ;
2015-08-20 06:36:11 +00:00
if ( ! threadEvents ) {
2015-11-02 15:58:47 +00:00
threadEvents = [ NSMutableArray new ] ;
2015-11-04 17:00:01 +00:00
thread . threadDictionary [ RCTProfileThreadEventsKey ] = threadEvents ;
2015-08-20 06:36:11 +00:00
}
return threadEvents ;
}
2015-11-04 17:00:01 +00:00
void _RCTProfileBeginEvent (
NSThread * calleeThread ,
NSTimeInterval time ,
uint64_t tag ,
NSString * name ,
NSDictionary * args
) {
2015-08-20 06:36:11 +00:00
CHECK ( ) ;
2015-09-23 18:59:44 +00:00
if ( callbacks ! = NULL ) {
callbacks -> begin_section ( tag , name . UTF8String , args . count , RCTProfileSystraceArgsFromNSDictionary ( args ) ) ;
return ;
}
2016-05-14 00:14:59 +00:00
dispatch_async ( RCTProfileGetQueue ( ) , ^ {
NSMutableArray * events = RCTProfileGetThreadEvents ( calleeThread ) ;
[ events addObject : @ [
RCTProfileTimestamp ( time ) ,
name ,
RCTNullIfNil ( args ) ,
] ] ;
} ) ;
2015-08-20 06:36:11 +00:00
}
2015-11-04 17:00:01 +00:00
void _RCTProfileEndEvent (
NSThread * calleeThread ,
NSString * threadName ,
NSTimeInterval time ,
2015-09-23 18:59:44 +00:00
uint64_t tag ,
2015-08-20 06:36:11 +00:00
NSString * category ,
NSDictionary * args
) {
CHECK ( ) ;
2015-09-23 18:59:44 +00:00
if ( callbacks ! = NULL ) {
callbacks -> end_section ( tag , args . count , RCTProfileSystraceArgsFromNSDictionary ( args ) ) ;
return ;
}
2016-05-14 00:14:59 +00:00
dispatch_async ( RCTProfileGetQueue ( ) , ^ {
NSMutableArray < NSArray * > * events = RCTProfileGetThreadEvents ( calleeThread ) ;
NSArray * event = events . lastObject ;
[ events removeLastObject ] ;
2015-08-20 06:36:11 +00:00
2016-05-14 00:14:59 +00:00
if ( ! event ) {
return ;
}
2015-08-20 06:36:11 +00:00
2016-05-14 00:14:59 +00:00
NSNumber * start = event [ 0 ] ;
RCTProfileAddEvent ( RCTProfileTraceEvents ,
@ "tid" : threadName ,
@ "name" : event [ 1 ] ,
@ "cat" : category ,
@ "ph" : @ "X" ,
@ "ts" : start ,
@ "dur" : @ ( RCTProfileTimestamp ( time ) . doubleValue - start . doubleValue ) ,
@ "args" : RCTProfileMergeArgs ( event [ 2 ] , args ) ,
) ;
} ) ;
2015-04-20 11:55:05 +00:00
}
2015-11-08 18:19:26 +00:00
NSUInteger RCTProfileBeginAsyncEvent (
2015-09-23 18:59:44 +00:00
uint64_t tag ,
2015-08-20 06:36:11 +00:00
NSString * name ,
NSDictionary * args
) {
CHECK ( 0 ) ;
2015-11-08 18:19:26 +00:00
static NSUInteger eventID = 0 ;
2015-08-20 06:36:11 +00:00
2015-11-04 17:00:01 +00:00
NSTimeInterval time = CACurrentMediaTime ( ) ;
2015-11-08 18:19:26 +00:00
NSUInteger currentEventID = + + eventID ;
2015-11-04 17:00:01 +00:00
2015-09-23 18:59:44 +00:00
if ( callbacks ! = NULL ) {
2015-11-08 18:19:26 +00:00
callbacks -> begin_async _section ( tag , name . UTF8String , ( int ) ( currentEventID % INT_MAX ) , args . count , RCTProfileSystraceArgsFromNSDictionary ( args ) ) ;
2015-09-23 18:59:44 +00:00
} else {
2015-11-04 17:00:01 +00:00
dispatch_async ( RCTProfileGetQueue ( ) , ^ {
RCTProfileOngoingEvents [ @ ( currentEventID ) ] = @ [
RCTProfileTimestamp ( time ) ,
2015-09-23 18:59:44 +00:00
name ,
RCTNullIfNil ( args ) ,
] ;
2015-11-04 17:00:01 +00:00
} ) ;
2015-09-23 18:59:44 +00:00
}
2015-08-20 06:36:11 +00:00
2015-11-04 17:00:01 +00:00
return currentEventID ;
2015-08-20 06:36:11 +00:00
}
void RCTProfileEndAsyncEvent (
2015-09-23 18:59:44 +00:00
uint64_t tag ,
2015-08-20 06:36:11 +00:00
NSString * category ,
2015-11-08 18:19:26 +00:00
NSUInteger cookie ,
2015-09-23 18:59:44 +00:00
NSString * name ,
2016-02-03 15:10:52 +00:00
NSString * threadName ,
2015-08-20 06:36:11 +00:00
NSDictionary * args
) {
2015-04-20 11:55:05 +00:00
CHECK ( ) ;
2015-09-23 18:59:44 +00:00
if ( callbacks ! = NULL ) {
2015-11-08 18:19:26 +00:00
callbacks -> end_async _section ( tag , name . UTF8String , ( int ) ( cookie % INT_MAX ) , args . count , RCTProfileSystraceArgsFromNSDictionary ( args ) ) ;
2015-09-23 18:59:44 +00:00
return ;
}
2015-11-04 17:00:01 +00:00
NSTimeInterval time = CACurrentMediaTime ( ) ;
dispatch_async ( RCTProfileGetQueue ( ) , ^ {
2015-08-20 06:36:11 +00:00
NSArray * event = RCTProfileOngoingEvents [ @ ( cookie ) ] ;
2015-11-04 17:00:01 +00:00
2015-08-20 06:36:11 +00:00
if ( event ) {
2015-11-04 17:00:01 +00:00
NSNumber * endTimestamp = RCTProfileTimestamp ( time ) ;
2015-04-22 14:03:55 +00:00
RCTProfileAddEvent ( RCTProfileTraceEvents ,
2015-11-04 17:00:01 +00:00
@ "tid" : threadName ,
2015-08-20 06:36:11 +00:00
@ "name" : event [ 1 ] ,
@ "cat" : category ,
2015-04-22 14:03:55 +00:00
@ "ph" : @ "X" ,
2015-08-20 06:36:11 +00:00
@ "ts" : event [ 0 ] ,
@ "dur" : @ ( endTimestamp . doubleValue - [ event [ 0 ] doubleValue ] ) ,
@ "args" : RCTProfileMergeArgs ( event [ 2 ] , args ) ,
2015-04-22 14:03:55 +00:00
) ;
2015-08-20 06:36:11 +00:00
[ RCTProfileOngoingEvents removeObjectForKey : @ ( cookie ) ] ;
2015-04-22 14:03:55 +00:00
}
2015-11-04 17:00:01 +00:00
} ) ;
2015-04-20 11:55:05 +00:00
}
2015-08-20 06:36:11 +00:00
void RCTProfileImmediateEvent (
2015-09-23 18:59:44 +00:00
uint64_t tag ,
2015-08-20 06:36:11 +00:00
NSString * name ,
2015-12-09 17:53:00 +00:00
NSTimeInterval time ,
2015-08-20 06:36:11 +00:00
char scope
) {
2015-04-20 11:55:05 +00:00
CHECK ( ) ;
2015-08-20 06:36:11 +00:00
2015-09-23 18:59:44 +00:00
if ( callbacks ! = NULL ) {
callbacks -> instant_section ( tag , name . UTF8String , scope ) ;
return ;
}
2015-11-04 17:00:01 +00:00
NSString * threadName = RCTCurrentThreadName ( ) ;
dispatch_async ( RCTProfileGetQueue ( ) , ^ {
2015-04-22 14:03:55 +00:00
RCTProfileAddEvent ( RCTProfileTraceEvents ,
2015-11-04 17:00:01 +00:00
@ "tid" : threadName ,
2015-04-22 14:03:55 +00:00
@ "name" : name ,
2015-11-04 17:00:01 +00:00
@ "ts" : RCTProfileTimestamp ( time ) ,
2015-08-20 06:36:11 +00:00
@ "scope" : @ ( scope ) ,
2015-04-22 14:03:55 +00:00
@ "ph" : @ "i" ,
2015-08-21 17:15:04 +00:00
@ "args" : RCTProfileGetMemoryUsage ( ) ,
2015-04-22 14:03:55 +00:00
) ;
2015-11-04 17:00:01 +00:00
} ) ;
2015-04-20 11:55:05 +00:00
}
2015-04-20 13:33:52 +00:00
2015-06-03 12:38:21 +00:00
NSNumber * _RCTProfileBeginFlowEvent ( void )
{
static NSUInteger flowID = 0 ;
CHECK ( @ 0 ) ;
2015-09-23 18:59:44 +00:00
2016-05-14 00:14:55 +00:00
unsigned int cookie = + + flowID ;
NSNumber * currentID = @ ( cookie ) ;
2015-09-23 18:59:44 +00:00
if ( callbacks ! = NULL ) {
2016-05-14 00:14:55 +00:00
callbacks -> begin_async _flow ( 1 , "flow" , cookie ) ;
return currentID ;
2015-09-23 18:59:44 +00:00
}
2015-11-04 17:00:01 +00:00
NSTimeInterval time = CACurrentMediaTime ( ) ;
NSString * threadName = RCTCurrentThreadName ( ) ;
dispatch_async ( RCTProfileGetQueue ( ) , ^ {
2015-10-02 11:16:04 +00:00
RCTProfileAddEvent ( RCTProfileTraceEvents ,
2015-11-04 17:00:01 +00:00
@ "tid" : threadName ,
2015-10-02 11:16:04 +00:00
@ "name" : @ "flow" ,
2015-11-04 17:00:01 +00:00
@ "id" : currentID ,
2015-10-02 11:16:04 +00:00
@ "cat" : @ "flow" ,
@ "ph" : @ "s" ,
2015-11-04 17:00:01 +00:00
@ "ts" : RCTProfileTimestamp ( time ) ,
2015-10-02 11:16:04 +00:00
) ;
2015-06-03 12:38:21 +00:00
2015-11-04 17:00:01 +00:00
} ) ;
return currentID ;
2015-06-03 12:38:21 +00:00
}
void _RCTProfileEndFlowEvent ( NSNumber * flowID )
{
CHECK ( ) ;
2015-09-23 18:59:44 +00:00
if ( callbacks ! = NULL ) {
2016-05-14 00:14:55 +00:00
callbacks -> end_async _flow ( 1 , "flow" , [ flowID integerValue ] ) ;
2015-09-23 18:59:44 +00:00
return ;
}
2015-11-04 17:00:01 +00:00
NSTimeInterval time = CACurrentMediaTime ( ) ;
NSString * threadName = RCTCurrentThreadName ( ) ;
dispatch_async ( RCTProfileGetQueue ( ) , ^ {
2015-10-02 11:16:04 +00:00
RCTProfileAddEvent ( RCTProfileTraceEvents ,
2015-11-04 17:00:01 +00:00
@ "tid" : threadName ,
2015-10-02 11:16:04 +00:00
@ "name" : @ "flow" ,
@ "id" : flowID ,
@ "cat" : @ "flow" ,
@ "ph" : @ "f" ,
2015-11-04 17:00:01 +00:00
@ "ts" : RCTProfileTimestamp ( time ) ,
2015-10-02 11:16:04 +00:00
) ;
2015-11-04 17:00:01 +00:00
} ) ;
2015-06-03 12:38:21 +00:00
}
2015-09-11 13:35:25 +00:00
void RCTProfileSendResult ( RCTBridge * bridge , NSString * route , NSData * data )
{
if ( ! [ bridge . bundleURL . scheme hasPrefix : @ "http" ] ) {
2015-12-15 11:57:26 +00:00
RCTLogWarn ( @ "Cannot upload profile information because you're not connected to the packager. The profiling data is still saved in the app container." ) ;
2015-09-11 13:35:25 +00:00
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 ) {
dispatch_async ( dispatch_get _main _queue ( ) , ^ {
[ [ [ UIAlertView alloc ] initWithTitle : @ "Profile"
message : message
delegate : nil
cancelButtonTitle : @ "OK"
otherButtonTitles : nil ] show ] ;
} ) ;
}
}
} ] ;
[ task resume ] ;
}
2016-03-11 14:20:24 +00:00
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 ;
}
2015-04-20 13:33:52 +00:00
# endif