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"
|
|
|
|
|
|
|
|
#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-04-21 12:26:51 +00:00
|
|
|
#import "RCTDefines.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-06-02 13:15:53 +00:00
|
|
|
NSString *const RCTProfileDidStartProfiling = @"RCTProfileDidStartProfiling";
|
|
|
|
NSString *const RCTProfileDidEndProfiling = @"RCTProfileDidEndProfiling";
|
|
|
|
|
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 - Prototypes
|
|
|
|
|
|
|
|
NSNumber *RCTProfileTimestamp(NSTimeInterval);
|
|
|
|
NSString *RCTProfileMemory(vm_size_t);
|
|
|
|
NSDictionary *RCTProfileGetMemoryUsage(void);
|
|
|
|
|
|
|
|
#pragma mark - Constants
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
NSDictionary *RCTProfileInfo;
|
|
|
|
NSUInteger RCTProfileEventID = 0;
|
|
|
|
NSMutableDictionary *RCTProfileOngoingEvents;
|
|
|
|
NSTimeInterval RCTProfileStartTime;
|
2015-06-15 20:05:05 +00:00
|
|
|
NSRecursiveLock *_RCTProfileLock;
|
2015-04-20 11:55:05 +00:00
|
|
|
|
|
|
|
#pragma mark - Macros
|
|
|
|
|
|
|
|
#define RCTProfileAddEvent(type, props...) \
|
|
|
|
[RCTProfileInfo[type] addObject:@{ \
|
|
|
|
@"pid": @([[NSProcessInfo processInfo] processIdentifier]), \
|
2015-05-25 12:19:53 +00:00
|
|
|
@"tid": RCTCurrentThreadName(), \
|
2015-04-20 11:55:05 +00:00
|
|
|
props \
|
|
|
|
}];
|
|
|
|
|
|
|
|
#define CHECK(...) \
|
|
|
|
if (!RCTProfileIsProfiling()) { \
|
|
|
|
return __VA_ARGS__; \
|
|
|
|
}
|
|
|
|
|
2015-04-22 14:03:55 +00:00
|
|
|
#define RCTProfileLock(...) \
|
|
|
|
[_RCTProfileLock lock]; \
|
|
|
|
__VA_ARGS__ \
|
|
|
|
[_RCTProfileLock unlock]
|
|
|
|
|
2015-04-20 11:55:05 +00:00
|
|
|
#pragma mark - Private Helpers
|
|
|
|
|
|
|
|
NSNumber *RCTProfileTimestamp(NSTimeInterval timestamp)
|
|
|
|
{
|
|
|
|
return @((timestamp - RCTProfileStartTime) * 1e6);
|
|
|
|
}
|
|
|
|
|
|
|
|
NSString *RCTProfileMemory(vm_size_t memory)
|
|
|
|
{
|
|
|
|
double mem = ((double)memory) / 1024 / 1024;
|
|
|
|
return [NSString stringWithFormat:@"%.2lfmb", mem];
|
|
|
|
}
|
|
|
|
|
|
|
|
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 @{};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-05 11:23:51 +00:00
|
|
|
#pragma mark - Module hooks
|
|
|
|
|
|
|
|
static const char *RCTProfileProxyClassName(Class);
|
|
|
|
static const char *RCTProfileProxyClassName(Class class)
|
|
|
|
{
|
|
|
|
return [RCTProfilePrefix stringByAppendingString:NSStringFromClass(class)].UTF8String;
|
|
|
|
}
|
|
|
|
|
|
|
|
static SEL RCTProfileProxySelector(SEL);
|
|
|
|
static SEL RCTProfileProxySelector(SEL selector)
|
|
|
|
{
|
|
|
|
NSString *selectorName = NSStringFromSelector(selector);
|
|
|
|
return NSSelectorFromString([RCTProfilePrefix stringByAppendingString:selectorName]);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void RCTProfileForwardInvocation(NSObject *, SEL, NSInvocation *);
|
2015-06-15 14:53:45 +00:00
|
|
|
static void RCTProfileForwardInvocation(NSObject *self, __unused SEL cmd, NSInvocation *invocation)
|
2015-06-05 11:23:51 +00:00
|
|
|
{
|
|
|
|
NSString *name = [NSString stringWithFormat:@"-[%@ %@]", NSStringFromClass([self class]), NSStringFromSelector(invocation.selector)];
|
|
|
|
SEL newSel = RCTProfileProxySelector(invocation.selector);
|
|
|
|
|
2015-07-24 16:15:20 +00:00
|
|
|
if ([object_getClass(self) instancesRespondToSelector:newSel]) {
|
|
|
|
invocation.selector = newSel;
|
|
|
|
RCTProfileBeginEvent();
|
|
|
|
[invocation invoke];
|
|
|
|
RCTProfileEndEvent(name, @"objc_call,modules,auto", nil);
|
|
|
|
} else if ([self respondsToSelector:invocation.selector]) {
|
|
|
|
[invocation invoke];
|
|
|
|
} else {
|
|
|
|
// Use original selector to don't change error message
|
|
|
|
[self doesNotRecognizeSelector:invocation.selector];
|
|
|
|
}
|
2015-06-05 11:23:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static IMP RCTProfileMsgForward(NSObject *, SEL);
|
|
|
|
static IMP RCTProfileMsgForward(NSObject *self, SEL selector)
|
|
|
|
{
|
|
|
|
IMP imp = _objc_msgForward;
|
|
|
|
#if !defined(__arm64__)
|
|
|
|
NSMethodSignature *signature = [self methodSignatureForSelector:selector];
|
|
|
|
if (signature.methodReturnType[0] == _C_STRUCT_B && signature.methodReturnLength > 8) {
|
|
|
|
imp = _objc_msgForward_stret;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
return imp;
|
|
|
|
}
|
|
|
|
|
2015-07-22 17:54:45 +00:00
|
|
|
void RCTProfileHookModules(RCTBridge *bridge)
|
2015-06-05 11:23:51 +00:00
|
|
|
{
|
2015-08-07 13:42:34 +00:00
|
|
|
for (RCTModuleData *moduleData in [bridge valueForKey:@"moduleDataByID"]) {
|
[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
|
|
|
[moduleData dispatchBlock:^{
|
2015-07-27 15:51:28 +00:00
|
|
|
Class moduleClass = moduleData.moduleClass;
|
2015-06-05 11:23:51 +00:00
|
|
|
Class proxyClass = objc_allocateClassPair(moduleClass, RCTProfileProxyClassName(moduleClass), 0);
|
|
|
|
|
2015-07-22 17:54:45 +00:00
|
|
|
if (!proxyClass) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-06-05 11:23:51 +00:00
|
|
|
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);
|
|
|
|
if ([NSStringFromSelector(selector) hasPrefix:@"rct"] || [NSObject instancesRespondToSelector:selector]) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
IMP originalIMP = method_getImplementation(method);
|
|
|
|
const char *returnType = method_getTypeEncoding(method);
|
[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
|
|
|
class_addMethod(proxyClass, selector, RCTProfileMsgForward(moduleData.instance, selector), returnType);
|
2015-06-05 11:23:51 +00:00
|
|
|
class_addMethod(proxyClass, RCTProfileProxySelector(selector), originalIMP, returnType);
|
|
|
|
}
|
|
|
|
free(methods);
|
|
|
|
|
2015-06-22 20:29:39 +00:00
|
|
|
class_replaceMethod(object_getClass(proxyClass), @selector(initialize), imp_implementationWithBlock(^{}), "v@:");
|
|
|
|
|
2015-06-05 11:23:51 +00:00
|
|
|
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));
|
|
|
|
}
|
|
|
|
|
|
|
|
IMP originalFwd = class_replaceMethod(moduleClass, @selector(forwardInvocation:), (IMP)RCTProfileForwardInvocation, "v@:@");
|
|
|
|
if (originalFwd != NULL) {
|
|
|
|
class_addMethod(proxyClass, RCTProfileProxySelector(@selector(forwardInvocation:)), originalFwd, "v@:@");
|
|
|
|
}
|
|
|
|
|
|
|
|
objc_registerClassPair(proxyClass);
|
[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
|
|
|
object_setClass(moduleData.instance, proxyClass);
|
|
|
|
}];
|
2015-06-15 14:53:45 +00:00
|
|
|
}
|
2015-06-05 11:23:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void RCTProfileUnhookModules(RCTBridge *bridge)
|
|
|
|
{
|
2015-08-07 13:42:34 +00:00
|
|
|
for (RCTModuleData *moduleData in [bridge valueForKey:@"moduleDataByID"]) {
|
2015-07-24 16:15:20 +00:00
|
|
|
Class proxyClass = object_getClass(moduleData.instance);
|
2015-07-27 15:51:28 +00:00
|
|
|
if (moduleData.moduleClass != proxyClass) {
|
|
|
|
object_setClass(moduleData.instance, moduleData.moduleClass);
|
2015-07-24 16:15:20 +00:00
|
|
|
objc_disposeClassPair(proxyClass);
|
|
|
|
}
|
|
|
|
};
|
2015-06-05 11:23:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-04-20 11:55:05 +00:00
|
|
|
#pragma mark - Public Functions
|
|
|
|
|
|
|
|
BOOL RCTProfileIsProfiling(void)
|
|
|
|
{
|
2015-04-22 14:03:55 +00:00
|
|
|
RCTProfileLock(
|
|
|
|
BOOL profiling = RCTProfileInfo != nil;
|
|
|
|
);
|
|
|
|
return profiling;
|
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-06-05 11:23:51 +00:00
|
|
|
RCTProfileHookModules(bridge);
|
|
|
|
|
2015-04-22 14:03:55 +00:00
|
|
|
static dispatch_once_t onceToken;
|
|
|
|
dispatch_once(&onceToken, ^{
|
2015-06-15 20:05:05 +00:00
|
|
|
_RCTProfileLock = [[NSRecursiveLock alloc] init];
|
2015-04-22 14:03:55 +00:00
|
|
|
});
|
|
|
|
RCTProfileLock(
|
|
|
|
RCTProfileStartTime = CACurrentMediaTime();
|
|
|
|
RCTProfileOngoingEvents = [[NSMutableDictionary alloc] init];
|
|
|
|
RCTProfileInfo = @{
|
|
|
|
RCTProfileTraceEvents: [[NSMutableArray alloc] init],
|
|
|
|
RCTProfileSamples: [[NSMutableArray alloc] init],
|
|
|
|
};
|
|
|
|
);
|
2015-06-02 13:15:53 +00:00
|
|
|
|
|
|
|
[[NSNotificationCenter defaultCenter] postNotificationName:RCTProfileDidStartProfiling
|
|
|
|
object:nil];
|
2015-04-20 11:55:05 +00:00
|
|
|
}
|
|
|
|
|
2015-06-05 11:23:51 +00:00
|
|
|
NSString *RCTProfileEnd(RCTBridge *bridge)
|
2015-04-20 11:55:05 +00:00
|
|
|
{
|
2015-06-02 13:15:53 +00:00
|
|
|
[[NSNotificationCenter defaultCenter] postNotificationName:RCTProfileDidEndProfiling
|
|
|
|
object:nil];
|
|
|
|
|
2015-04-22 14:03:55 +00:00
|
|
|
RCTProfileLock(
|
|
|
|
NSString *log = RCTJSONStringify(RCTProfileInfo, NULL);
|
|
|
|
RCTProfileEventID = 0;
|
|
|
|
RCTProfileInfo = nil;
|
|
|
|
RCTProfileOngoingEvents = nil;
|
|
|
|
);
|
2015-06-05 11:23:51 +00:00
|
|
|
|
|
|
|
RCTProfileUnhookModules(bridge);
|
|
|
|
|
2015-04-20 11:55:05 +00:00
|
|
|
return log;
|
|
|
|
}
|
|
|
|
|
|
|
|
NSNumber *_RCTProfileBeginEvent(void)
|
|
|
|
{
|
|
|
|
CHECK(@0);
|
2015-04-22 14:03:55 +00:00
|
|
|
RCTProfileLock(
|
|
|
|
NSNumber *eventID = @(++RCTProfileEventID);
|
|
|
|
RCTProfileOngoingEvents[eventID] = RCTProfileTimestamp(CACurrentMediaTime());
|
|
|
|
);
|
2015-04-20 11:55:05 +00:00
|
|
|
return eventID;
|
|
|
|
}
|
|
|
|
|
|
|
|
void _RCTProfileEndEvent(NSNumber *eventID, NSString *name, NSString *categories, id args)
|
|
|
|
{
|
|
|
|
CHECK();
|
2015-04-22 14:03:55 +00:00
|
|
|
RCTProfileLock(
|
|
|
|
NSNumber *startTimestamp = RCTProfileOngoingEvents[eventID];
|
|
|
|
if (startTimestamp) {
|
|
|
|
NSNumber *endTimestamp = RCTProfileTimestamp(CACurrentMediaTime());
|
|
|
|
|
|
|
|
RCTProfileAddEvent(RCTProfileTraceEvents,
|
|
|
|
@"name": name,
|
|
|
|
@"cat": categories,
|
|
|
|
@"ph": @"X",
|
|
|
|
@"ts": startTimestamp,
|
|
|
|
@"dur": @(endTimestamp.doubleValue - startTimestamp.doubleValue),
|
|
|
|
@"args": args ?: @[],
|
|
|
|
);
|
|
|
|
[RCTProfileOngoingEvents removeObjectForKey:eventID];
|
|
|
|
}
|
2015-04-20 11:55:05 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
void RCTProfileImmediateEvent(NSString *name, NSTimeInterval timestamp, NSString *scope)
|
|
|
|
{
|
|
|
|
CHECK();
|
2015-04-22 14:03:55 +00:00
|
|
|
RCTProfileLock(
|
|
|
|
RCTProfileAddEvent(RCTProfileTraceEvents,
|
|
|
|
@"name": name,
|
|
|
|
@"ts": RCTProfileTimestamp(timestamp),
|
|
|
|
@"scope": scope,
|
|
|
|
@"ph": @"i",
|
|
|
|
@"args": RCTProfileGetMemoryUsage(),
|
|
|
|
);
|
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);
|
|
|
|
RCTProfileAddEvent(RCTProfileTraceEvents,
|
|
|
|
@"name": @"flow",
|
|
|
|
@"id": @(++flowID),
|
|
|
|
@"cat": @"flow",
|
|
|
|
@"ph": @"s",
|
|
|
|
@"ts": RCTProfileTimestamp(CACurrentMediaTime()),
|
|
|
|
);
|
|
|
|
|
|
|
|
return @(flowID);
|
|
|
|
}
|
|
|
|
|
|
|
|
void _RCTProfileEndFlowEvent(NSNumber *flowID)
|
|
|
|
{
|
|
|
|
CHECK();
|
|
|
|
RCTProfileAddEvent(RCTProfileTraceEvents,
|
|
|
|
@"name": @"flow",
|
|
|
|
@"id": flowID,
|
|
|
|
@"cat": @"flow",
|
|
|
|
@"ph": @"f",
|
|
|
|
@"ts": RCTProfileTimestamp(CACurrentMediaTime()),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2015-04-20 13:33:52 +00:00
|
|
|
#endif
|