initial commit taken from gitlab.lrz.de

This commit is contained in:
privatereese
2018-08-24 18:09:42 +02:00
parent ae54ed4c48
commit fc05486403
28494 changed files with 2159823 additions and 0 deletions

27
node_modules/react-native/React/Profiler/RCTFPSGraph.h generated vendored Normal file
View File

@@ -0,0 +1,27 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <UIKit/UIKit.h>
#import <React/RCTDefines.h>
#if RCT_DEV
@interface RCTFPSGraph : UIView
@property (nonatomic, assign, readonly) NSUInteger FPS;
@property (nonatomic, assign, readonly) NSUInteger maxFPS;
@property (nonatomic, assign, readonly) NSUInteger minFPS;
- (instancetype)initWithFrame:(CGRect)frame
color:(UIColor *)color NS_DESIGNATED_INITIALIZER;
- (void)onTick:(NSTimeInterval)timestamp;
@end
#endif

124
node_modules/react-native/React/Profiler/RCTFPSGraph.m generated vendored Normal file
View File

@@ -0,0 +1,124 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RCTFPSGraph.h"
#import "RCTAssert.h"
#if RCT_DEV
@interface RCTFPSGraph()
@property (nonatomic, strong, readonly) CAShapeLayer *graph;
@property (nonatomic, strong, readonly) UILabel *label;
@end
@implementation RCTFPSGraph
{
CAShapeLayer *_graph;
UILabel *_label;
CGFloat *_frames;
UIColor *_color;
NSTimeInterval _prevTime;
NSUInteger _frameCount;
NSUInteger _FPS;
NSUInteger _maxFPS;
NSUInteger _minFPS;
NSUInteger _length;
NSUInteger _height;
}
- (instancetype)initWithFrame:(CGRect)frame color:(UIColor *)color
{
if ((self = [super initWithFrame:frame])) {
_frameCount = -1;
_prevTime = -1;
_maxFPS = 0;
_minFPS = 60;
_length = (NSUInteger)floor(frame.size.width);
_height = (NSUInteger)floor(frame.size.height);
_frames = calloc(sizeof(CGFloat), _length);
_color = color;
[self.layer addSublayer:self.graph];
[self addSubview:self.label];
}
return self;
}
- (void)dealloc
{
free(_frames);
}
RCT_NOT_IMPLEMENTED(- (instancetype)initWithFrame:(CGRect)frame)
RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder)
- (CAShapeLayer *)graph
{
if (!_graph) {
_graph = [CAShapeLayer new];
_graph.frame = self.bounds;
_graph.backgroundColor = [_color colorWithAlphaComponent:0.2].CGColor;
_graph.fillColor = _color.CGColor;
}
return _graph;
}
- (UILabel *)label
{
if (!_label) {
_label = [[UILabel alloc] initWithFrame:self.bounds];
_label.font = [UIFont boldSystemFontOfSize:13];
_label.textAlignment = NSTextAlignmentCenter;
}
return _label;
}
- (void)onTick:(NSTimeInterval)timestamp
{
_frameCount++;
if (_prevTime == -1) {
_prevTime = timestamp;
} else if (timestamp - _prevTime >= 1) {
_FPS = round(_frameCount / (timestamp - _prevTime));
_minFPS = MIN(_minFPS, _FPS);
_maxFPS = MAX(_maxFPS, _FPS);
dispatch_async(dispatch_get_main_queue(), ^{
self->_label.text = [NSString stringWithFormat:@"%lu", (unsigned long)self->_FPS];
});
CGFloat scale = 60.0 / _height;
for (NSUInteger i = 0; i < _length - 1; i++) {
_frames[i] = _frames[i + 1];
}
_frames[_length - 1] = _FPS / scale;
CGMutablePathRef path = CGPathCreateMutable();
CGPathMoveToPoint(path, NULL, 0, _height);
for (NSUInteger i = 0; i < _length; i++) {
CGPathAddLineToPoint(path, NULL, i, _height - _frames[i]);
}
CGPathAddLineToPoint(path, NULL, _length - 1, _height);
_graph.path = path;
CGPathRelease(path);
_prevTime = timestamp;
_frameCount = 0;
}
}
@end
#endif

16
node_modules/react-native/React/Profiler/RCTMacros.h generated vendored Normal file
View File

@@ -0,0 +1,16 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#define _CONCAT(A, B) A##B
#define CONCAT(A, B) _CONCAT(A, B)
#if !defined(PIC_MODIFIER)
#define PIC_MODIFIER
#endif
#define SYMBOL_NAME(name) CONCAT(__USER_LABEL_PREFIX__, name)
#define SYMBOL_NAME_PIC(name) CONCAT(SYMBOL_NAME(name), PIC_MODIFIER)

View File

@@ -0,0 +1,578 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RCTDefines.h"
#if RCT_DEV
#import <dlfcn.h>
#import <mach/mach.h>
#import "RCTBridge.h"
#import "RCTDevSettings.h"
#import "RCTFPSGraph.h"
#import "RCTInvalidating.h"
#import "RCTJavaScriptExecutor.h"
#import "RCTPerformanceLogger.h"
#import "RCTRootView.h"
#import "RCTUIManager.h"
#import "RCTBridge+Private.h"
#import "RCTUtils.h"
#if __has_include("RCTDevMenu.h")
#import "RCTDevMenu.h"
#endif
static NSString *const RCTPerfMonitorCellIdentifier = @"RCTPerfMonitorCellIdentifier";
static CGFloat const RCTPerfMonitorBarHeight = 50;
static CGFloat const RCTPerfMonitorExpandHeight = 250;
typedef BOOL (*RCTJSCSetOptionType)(const char *);
static BOOL RCTJSCSetOption(const char *option)
{
static RCTJSCSetOptionType setOption;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
/**
* JSC private C++ static method to toggle options at runtime
*
* JSC::Options::setOptions - JavaScriptCore/runtime/Options.h
*/
setOption = dlsym(RTLD_DEFAULT, "_ZN3JSC7Options9setOptionEPKc");
if (RCT_DEBUG && setOption == NULL) {
RCTLogWarn(@"The symbol used to enable JSC runtime options is not available in this iOS version");
}
});
if (setOption) {
return setOption(option);
} else {
return NO;
}
}
static vm_size_t RCTGetResidentMemorySize(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 0;
}
return info.resident_size;
}
@interface RCTPerfMonitor : NSObject <RCTBridgeModule, RCTInvalidating, UITableViewDataSource, UITableViewDelegate>
#if __has_include("RCTDevMenu.h")
@property (nonatomic, strong, readonly) RCTDevMenuItem *devMenuItem;
#endif
@property (nonatomic, strong, readonly) UIPanGestureRecognizer *gestureRecognizer;
@property (nonatomic, strong, readonly) UIView *container;
@property (nonatomic, strong, readonly) UILabel *memory;
@property (nonatomic, strong, readonly) UILabel *heap;
@property (nonatomic, strong, readonly) UILabel *views;
@property (nonatomic, strong, readonly) UITableView *metrics;
@property (nonatomic, strong, readonly) RCTFPSGraph *jsGraph;
@property (nonatomic, strong, readonly) RCTFPSGraph *uiGraph;
@property (nonatomic, strong, readonly) UILabel *jsGraphLabel;
@property (nonatomic, strong, readonly) UILabel *uiGraphLabel;
@end
@implementation RCTPerfMonitor {
#if __has_include("RCTDevMenu.h")
RCTDevMenuItem *_devMenuItem;
#endif
UIPanGestureRecognizer *_gestureRecognizer;
UIView *_container;
UILabel *_memory;
UILabel *_heap;
UILabel *_views;
UILabel *_uiGraphLabel;
UILabel *_jsGraphLabel;
UITableView *_metrics;
RCTFPSGraph *_uiGraph;
RCTFPSGraph *_jsGraph;
CADisplayLink *_uiDisplayLink;
CADisplayLink *_jsDisplayLink;
NSUInteger _heapSize;
dispatch_queue_t _queue;
dispatch_io_t _io;
int _stderr;
int _pipe[2];
NSString *_remaining;
CGRect _storedMonitorFrame;
NSArray *_perfLoggerMarks;
}
@synthesize bridge = _bridge;
RCT_EXPORT_MODULE()
+ (BOOL)requiresMainQueueSetup
{
return YES;
}
- (dispatch_queue_t)methodQueue
{
return dispatch_get_main_queue();
}
- (void)setBridge:(RCTBridge *)bridge
{
_bridge = bridge;
#if __has_include("RCTDevMenu.h")
[_bridge.devMenu addItem:self.devMenuItem];
#endif
}
- (void)invalidate
{
[self hide];
}
#if __has_include("RCTDevMenu.h")
- (RCTDevMenuItem *)devMenuItem
{
if (!_devMenuItem) {
__weak __typeof__(self) weakSelf = self;
__weak RCTDevSettings *devSettings = self.bridge.devSettings;
_devMenuItem =
[RCTDevMenuItem buttonItemWithTitleBlock:^NSString *{
return (devSettings.isPerfMonitorShown) ?
@"Hide Perf Monitor" :
@"Show Perf Monitor";
} handler:^{
if (devSettings.isPerfMonitorShown) {
[weakSelf hide];
devSettings.isPerfMonitorShown = NO;
} else {
[weakSelf show];
devSettings.isPerfMonitorShown = YES;
}
}];
}
return _devMenuItem;
}
#endif
- (UIPanGestureRecognizer *)gestureRecognizer
{
if (!_gestureRecognizer) {
_gestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self
action:@selector(gesture:)];
}
return _gestureRecognizer;
}
- (UIView *)container
{
if (!_container) {
_container = [[UIView alloc] initWithFrame:CGRectMake(10, 25, 180, RCTPerfMonitorBarHeight)];
_container.backgroundColor = UIColor.whiteColor;
_container.layer.borderWidth = 2;
_container.layer.borderColor = [UIColor lightGrayColor].CGColor;
[_container addGestureRecognizer:self.gestureRecognizer];
[_container addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self
action:@selector(tap)]];
}
return _container;
}
- (UILabel *)memory
{
if (!_memory) {
_memory = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 44, RCTPerfMonitorBarHeight)];
_memory.font = [UIFont systemFontOfSize:12];
_memory.numberOfLines = 3;
_memory.textAlignment = NSTextAlignmentCenter;
}
return _memory;
}
- (UILabel *)heap
{
if (!_heap) {
_heap = [[UILabel alloc] initWithFrame:CGRectMake(44, 0, 44, RCTPerfMonitorBarHeight)];
_heap.font = [UIFont systemFontOfSize:12];
_heap.numberOfLines = 3;
_heap.textAlignment = NSTextAlignmentCenter;
}
return _heap;
}
- (UILabel *)views
{
if (!_views) {
_views = [[UILabel alloc] initWithFrame:CGRectMake(88, 0, 44, RCTPerfMonitorBarHeight)];
_views.font = [UIFont systemFontOfSize:12];
_views.numberOfLines = 3;
_views.textAlignment = NSTextAlignmentCenter;
}
return _views;
}
- (RCTFPSGraph *)uiGraph
{
if (!_uiGraph) {
_uiGraph = [[RCTFPSGraph alloc] initWithFrame:CGRectMake(134, 14, 40, 30)
color:[UIColor lightGrayColor]];
}
return _uiGraph;
}
- (RCTFPSGraph *)jsGraph
{
if (!_jsGraph) {
_jsGraph = [[RCTFPSGraph alloc] initWithFrame:CGRectMake(178, 14, 40, 30)
color:[UIColor lightGrayColor]];
}
return _jsGraph;
}
- (UILabel *)uiGraphLabel
{
if (!_uiGraphLabel) {
_uiGraphLabel = [[UILabel alloc] initWithFrame:CGRectMake(134, 3, 40, 10)];
_uiGraphLabel.font = [UIFont systemFontOfSize:11];
_uiGraphLabel.textAlignment = NSTextAlignmentCenter;
_uiGraphLabel.text = @"UI";
}
return _uiGraphLabel;
}
- (UILabel *)jsGraphLabel
{
if (!_jsGraphLabel) {
_jsGraphLabel = [[UILabel alloc] initWithFrame:CGRectMake(178, 3, 38, 10)];
_jsGraphLabel.font = [UIFont systemFontOfSize:11];
_jsGraphLabel.textAlignment = NSTextAlignmentCenter;
_jsGraphLabel.text = @"JS";
}
return _jsGraphLabel;
}
- (UITableView *)metrics
{
if (!_metrics) {
_metrics = [[UITableView alloc] initWithFrame:CGRectMake(
0,
RCTPerfMonitorBarHeight,
self.container.frame.size.width,
self.container.frame.size.height - RCTPerfMonitorBarHeight
)];
_metrics.dataSource = self;
_metrics.delegate = self;
_metrics.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
[_metrics registerClass:[UITableViewCell class] forCellReuseIdentifier:RCTPerfMonitorCellIdentifier];
}
return _metrics;
}
- (void)show
{
if (_container) {
return;
}
[self.container addSubview:self.memory];
[self.container addSubview:self.heap];
[self.container addSubview:self.views];
[self.container addSubview:self.uiGraph];
[self.container addSubview:self.uiGraphLabel];
[self redirectLogs];
RCTJSCSetOption("logGC=1");
[self updateStats];
UIWindow *window = RCTSharedApplication().delegate.window;
[window addSubview:self.container];
_uiDisplayLink = [CADisplayLink displayLinkWithTarget:self
selector:@selector(threadUpdate:)];
[_uiDisplayLink addToRunLoop:[NSRunLoop mainRunLoop]
forMode:NSRunLoopCommonModes];
self.container.frame = (CGRect) {
self.container.frame.origin, {
self.container.frame.size.width + 44,
self.container.frame.size.height
}
};
[self.container addSubview:self.jsGraph];
[self.container addSubview:self.jsGraphLabel];
[_bridge dispatchBlock:^{
self->_jsDisplayLink = [CADisplayLink displayLinkWithTarget:self
selector:@selector(threadUpdate:)];
[self->_jsDisplayLink addToRunLoop:[NSRunLoop currentRunLoop]
forMode:NSRunLoopCommonModes];
} queue:RCTJSThread];
}
- (void)hide
{
if (!_container) {
return;
}
[self.container removeFromSuperview];
_container = nil;
_jsGraph = nil;
_uiGraph = nil;
RCTJSCSetOption("logGC=0");
[self stopLogs];
[_uiDisplayLink invalidate];
[_jsDisplayLink invalidate];
_uiDisplayLink = nil;
_jsDisplayLink = nil;
}
- (void)redirectLogs
{
_stderr = dup(STDERR_FILENO);
if (pipe(_pipe) != 0) {
return;
}
dup2(_pipe[1], STDERR_FILENO);
close(_pipe[1]);
__weak __typeof__(self) weakSelf = self;
_queue = dispatch_queue_create("com.facebook.react.RCTPerfMonitor", DISPATCH_QUEUE_SERIAL);
_io = dispatch_io_create(
DISPATCH_IO_STREAM,
_pipe[0],
_queue,
^(__unused int error) {});
dispatch_io_set_low_water(_io, 20);
dispatch_io_read(
_io,
0,
SIZE_MAX,
_queue,
^(__unused bool done, dispatch_data_t data, __unused int error) {
if (!data) {
return;
}
dispatch_data_apply(
data,
^bool(
__unused dispatch_data_t region,
__unused size_t offset,
const void *buffer,
size_t size
) {
write(self->_stderr, buffer, size);
NSString *log = [[NSString alloc] initWithBytes:buffer
length:size
encoding:NSUTF8StringEncoding];
[weakSelf parse:log];
return true;
});
});
}
- (void)stopLogs
{
dup2(_stderr, STDERR_FILENO);
dispatch_io_close(_io, 0);
}
- (void)parse:(NSString *)log
{
static NSRegularExpression *GCRegex;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSString *pattern = @"\\[GC: [\\d\\.]+ \\wb => (Eden|Full)Collection, (?:Skipped copying|Did copy), ([\\d\\.]+) \\wb, [\\d.]+ \\ws\\]";
GCRegex = [NSRegularExpression regularExpressionWithPattern:pattern
options:0
error:nil];
});
if (_remaining) {
log = [_remaining stringByAppendingString:log];
_remaining = nil;
}
NSArray<NSString *> *lines = [log componentsSeparatedByString:@"\n"];
if (lines.count == 1) { // no newlines
_remaining = log;
return;
}
for (NSString *line in lines) {
NSTextCheckingResult *match = [GCRegex firstMatchInString:line options:0 range:NSMakeRange(0, line.length)];
if (match) {
NSString *heapSizeStr = [line substringWithRange:[match rangeAtIndex:2]];
_heapSize = [heapSizeStr integerValue];
}
}
}
- (void)updateStats
{
NSDictionary<NSNumber *, UIView *> *views = [_bridge.uiManager valueForKey:@"viewRegistry"];
NSUInteger viewCount = views.count;
NSUInteger visibleViewCount = 0;
for (UIView *view in views.allValues) {
if (view.window || view.superview.window) {
visibleViewCount++;
}
}
double mem = (double)RCTGetResidentMemorySize() / 1024 / 1024;
self.memory.text =[NSString stringWithFormat:@"RAM\n%.2lf\nMB", mem];
self.heap.text = [NSString stringWithFormat:@"JSC\n%.2lf\nMB", (double)_heapSize / 1024];
self.views.text = [NSString stringWithFormat:@"Views\n%lu\n%lu", (unsigned long)visibleViewCount, (unsigned long)viewCount];
__weak __typeof__(self) weakSelf = self;
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->_container.superview) {
[strongSelf updateStats];
}
});
}
- (void)gesture:(UIPanGestureRecognizer *)gestureRecognizer
{
CGPoint translation = [gestureRecognizer translationInView:self.container.superview];
self.container.center = CGPointMake(
self.container.center.x + translation.x,
self.container.center.y + translation.y
);
[gestureRecognizer setTranslation:CGPointMake(0, 0)
inView:self.container.superview];
}
- (void)tap
{
[self loadPerformanceLoggerData];
if (CGRectIsEmpty(_storedMonitorFrame)) {
_storedMonitorFrame = CGRectMake(0, 20, self.container.window.frame.size.width, RCTPerfMonitorExpandHeight);
[self.container addSubview:self.metrics];
} else {
[_metrics reloadData];
}
[UIView animateWithDuration:.25 animations:^{
CGRect tmp = self.container.frame;
self.container.frame = self->_storedMonitorFrame;
self->_storedMonitorFrame = tmp;
}];
}
- (void)threadUpdate:(CADisplayLink *)displayLink
{
RCTFPSGraph *graph = displayLink == _jsDisplayLink ? _jsGraph : _uiGraph;
[graph onTick:displayLink.timestamp];
}
- (void)loadPerformanceLoggerData
{
NSUInteger i = 0;
NSMutableArray<NSString *> *data = [NSMutableArray new];
RCTPerformanceLogger *performanceLogger = [_bridge performanceLogger];
NSArray<NSNumber *> *values = [performanceLogger valuesForTags];
for (NSString *label in [performanceLogger labelsForTags]) {
long long value = values[i+1].longLongValue - values[i].longLongValue;
NSString *unit = @"ms";
if ([label hasSuffix:@"Size"]) {
unit = @"b";
} else if ([label hasSuffix:@"Count"]) {
unit = @"";
}
[data addObject:[NSString stringWithFormat:@"%@: %lld%@", label, value, unit]];
i += 2;
}
_perfLoggerMarks = [data copy];
}
#pragma mark - UITableViewDataSource
- (NSInteger)numberOfSectionsInTableView:(__unused UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(__unused UITableView *)tableView
numberOfRowsInSection:(__unused NSInteger)section
{
return _perfLoggerMarks.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:RCTPerfMonitorCellIdentifier
forIndexPath:indexPath];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:RCTPerfMonitorCellIdentifier];
}
cell.textLabel.text = _perfLoggerMarks[indexPath.row];
cell.textLabel.font = [UIFont systemFontOfSize:12];
return cell;
}
#pragma mark - UITableViewDelegate
- (CGFloat)tableView:(__unused UITableView *)tableView
heightForRowAtIndexPath:(__unused NSIndexPath *)indexPath
{
return 20;
}
@end
#endif

233
node_modules/react-native/React/Profiler/RCTProfile.h generated vendored Normal file
View File

@@ -0,0 +1,233 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <Foundation/Foundation.h>
#import <React/RCTAssert.h>
#import <React/RCTDefines.h>
/**
* RCTProfile
*
* This file provides a set of functions and macros for performance profiling
*
* NOTE: This API is a work in progress, please consider carefully before
* using it.
*/
RCT_EXTERN NSString *const RCTProfileDidStartProfiling;
RCT_EXTERN NSString *const RCTProfileDidEndProfiling;
RCT_EXTERN const uint64_t RCTProfileTagAlways;
#if RCT_PROFILE
@class RCTBridge;
#define RCTProfileBeginFlowEvent() \
_Pragma("clang diagnostic push") \
_Pragma("clang diagnostic ignored \"-Wshadow\"") \
NSUInteger __rct_profile_flow_id = _RCTProfileBeginFlowEvent(); \
_Pragma("clang diagnostic pop")
#define RCTProfileEndFlowEvent() \
_RCTProfileEndFlowEvent(__rct_profile_flow_id)
RCT_EXTERN dispatch_queue_t RCTProfileGetQueue(void);
RCT_EXTERN NSUInteger _RCTProfileBeginFlowEvent(void);
RCT_EXTERN void _RCTProfileEndFlowEvent(NSUInteger);
/**
* Returns YES if the profiling information is currently being collected
*/
RCT_EXTERN BOOL RCTProfileIsProfiling(void);
/**
* Start collecting profiling information
*/
RCT_EXTERN void RCTProfileInit(RCTBridge *);
/**
* Stop profiling and return a JSON string of the collected data - The data
* returned is compliant with google's trace event format - the format used
* as input to trace-viewer
*/
RCT_EXTERN void RCTProfileEnd(RCTBridge *, void (^)(NSString *));
/**
* Collects the initial event information for the event and returns a reference ID
*/
RCT_EXTERN void _RCTProfileBeginEvent(NSThread *calleeThread,
NSTimeInterval time,
uint64_t tag,
NSString *name,
NSDictionary<NSString *, NSString *> *args);
#define RCT_PROFILE_BEGIN_EVENT(tag, name, args) \
do { \
if (RCTProfileIsProfiling()) { \
NSThread *__calleeThread = [NSThread currentThread]; \
NSTimeInterval __time = CACurrentMediaTime(); \
_RCTProfileBeginEvent(__calleeThread, __time, tag, name, args); \
} \
} while(0)
/**
* The ID returned by BeginEvent should then be passed into EndEvent, with the
* rest of the event information. Just at this point the event will actually be
* registered
*/
RCT_EXTERN void _RCTProfileEndEvent(NSThread *calleeThread,
NSString *threadName,
NSTimeInterval time,
uint64_t tag,
NSString *category);
#define RCT_PROFILE_END_EVENT(tag, category) \
do { \
if (RCTProfileIsProfiling()) { \
NSThread *__calleeThread = [NSThread currentThread]; \
NSString *__threadName = RCTCurrentThreadName(); \
NSTimeInterval __time = CACurrentMediaTime(); \
_RCTProfileEndEvent(__calleeThread, __threadName, __time, tag, category); \
} \
} while(0)
/**
* Collects the initial event information for the event and returns a reference ID
*/
RCT_EXTERN NSUInteger RCTProfileBeginAsyncEvent(uint64_t tag,
NSString *name,
NSDictionary<NSString *, NSString *> *args);
/**
* The ID returned by BeginEvent should then be passed into EndEvent, with the
* rest of the event information. Just at this point the event will actually be
* registered
*/
RCT_EXTERN void RCTProfileEndAsyncEvent(uint64_t tag,
NSString *category,
NSUInteger cookie,
NSString *name,
NSString *threadName);
/**
* An event that doesn't have a duration (i.e. Notification, VSync, etc)
*/
RCT_EXTERN void RCTProfileImmediateEvent(uint64_t tag,
NSString *name,
NSTimeInterval time,
char scope);
/**
* Helper to profile the duration of the execution of a block. This method uses
* self and _cmd to name this event for simplicity sake.
*
* NOTE: The block can't expect any argument
*
* DEPRECATED: this approach breaks debugging and stepping through instrumented block functions
*/
#define RCTProfileBlock(block, tag, category, arguments) \
^{ \
RCT_PROFILE_BEGIN_EVENT(tag, @(__PRETTY_FUNCTION__), nil); \
block(); \
RCT_PROFILE_END_EVENT(tag, category, arguments); \
}
/**
* Hook into a bridge instance to log all bridge module's method calls
*/
RCT_EXTERN void RCTProfileHookModules(RCTBridge *);
/**
* Unhook from a given bridge instance's modules
*/
RCT_EXTERN void RCTProfileUnhookModules(RCTBridge *);
/**
* Hook into all of a module's methods
*/
RCT_EXTERN void RCTProfileHookInstance(id instance);
/**
* Send systrace or cpu profiling information to the packager
* to present to the user
*/
RCT_EXTERN void RCTProfileSendResult(RCTBridge *bridge, NSString *route, NSData *profileData);
/**
* Systrace gluecode
*
* allow to use systrace to back RCTProfile
*/
typedef struct {
const char *key;
int key_len;
const char *value;
int value_len;
} systrace_arg_t;
typedef struct {
char *(*start)(void);
void (*stop)(void);
void (*begin_section)(uint64_t tag, const char *name, size_t numArgs, systrace_arg_t *args);
void (*end_section)(uint64_t tag, size_t numArgs, systrace_arg_t *args);
void (*begin_async_section)(uint64_t tag, const char *name, int cookie, size_t numArgs, systrace_arg_t *args);
void (*end_async_section)(uint64_t tag, const char *name, int cookie, size_t numArgs, systrace_arg_t *args);
void (*instant_section)(uint64_t tag, const char *name, char scope);
void (*begin_async_flow)(uint64_t tag, const char *name, int cookie);
void (*end_async_flow)(uint64_t tag, const char *name, int cookie);
} RCTProfileCallbacks;
RCT_EXTERN void RCTProfileRegisterCallbacks(RCTProfileCallbacks *);
/**
* Systrace control window
*/
RCT_EXTERN void RCTProfileShowControls(void);
RCT_EXTERN void RCTProfileHideControls(void);
#else
#define RCTProfileBeginFlowEvent()
#define _RCTProfileBeginFlowEvent() @0
#define RCTProfileEndFlowEvent()
#define _RCTProfileEndFlowEvent(...)
#define RCTProfileIsProfiling(...) NO
#define RCTProfileInit(...)
#define RCTProfileEnd(...) @""
#define _RCTProfileBeginEvent(...)
#define _RCTProfileEndEvent(...)
#define RCT_PROFILE_BEGIN_EVENT(...)
#define RCT_PROFILE_END_EVENT(...)
#define RCTProfileBeginAsyncEvent(...) 0
#define RCTProfileEndAsyncEvent(...)
#define RCTProfileImmediateEvent(...)
#define RCTProfileBlock(block, ...) block
#define RCTProfileHookModules(...)
#define RCTProfileHookInstance(...)
#define RCTProfileUnhookModules(...)
#define RCTProfileSendResult(...)
#define RCTProfileShowControls(...)
#define RCTProfileHideControls(...)
#endif

828
node_modules/react-native/React/Profiler/RCTProfile.m generated vendored Normal file
View File

@@ -0,0 +1,828 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* 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);
__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 *);
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)
{
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(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() reload];
}
+ (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

View File

@@ -0,0 +1,96 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "RCTDefines.h"
#include "RCTMacros.h"
#if RCT_PROFILE && defined(__arm__)
.thumb
.thumb_func
.globl SYMBOL_NAME(RCTProfileTrampoline)
SYMBOL_NAME(RCTProfileTrampoline):
/**
* The explanation here is shorter, refer to the x86_64 implementation to a
* richer explanation
*/
/**
* Save the parameter registers (r0-r3), r7 (frame pointer) and lr (link
* register (contains the address of the caller of RCTProfileTrampoline)
*/
push {r0-r3, r7, lr}
/**
* Allocate memory to store values across function calls: 12-bytes are
* allocated to store 3 values: the previous value of the callee saved
* register used to save the pointer to the allocated memory, the caller of
* RCTProfileTrampoline and the address of the actual function we want to
* profile
*/
mov r0, #0xc
bl SYMBOL_NAME(RCTProfileMalloc)
/**
* r4 is the callee saved register we'll use to refer to the allocated memory,
* store its initial value, so we can restore it later
*/
str r4, [r0]
mov r4, r0
/**
* void RCTProfileGetImplementation(id object, SEL selector) in RCTProfile.m
*
* Load the first 2 argumenters (self and _cmd) used to call
* RCTProfileTrampoline from the stack and put them on the appropriate registers.
*/
ldr r0, [sp]
ldr r1, [sp, #0x4]
bl SYMBOL_NAME(RCTProfileGetImplementation)
// store the actual function address in the allocated memory
str r0, [r4, #0x4]
/**
* void RCTProfileGetImplementation(id object, SEL selector) in RCTProfile.m
*
* Load the first 2 arguments again to start the profiler
*/
ldr r0, [sp]
ldr r1, [sp, #0x4]
bl SYMBOL_NAME(RCTProfileTrampolineStart)
/**
* Restore the state to call the actual function we want to profile: pop
* all the registers
*/
pop {r0-r3, r7, lr}
// store lr (the caller) since it'll be overridden by `blx` (call)
str lr, [r4, #0x8]
ldr r12, [r4, #0x4] // load the function address
blx r12 // call it
push {r0} // save return value
// void RCTProfileTrampolineEnd(void) in RCTProfile.m - just ends this profile
bl SYMBOL_NAME(RCTProfileTrampolineEnd)
/**
* Save the value we still need from the allocated memory (caller address),
* restore r4 and free the allocated memory (put its address in r0)
*/
mov r0, r4
ldr r1, [r4, #0x8]
ldr r4, [r4]
push {r1} // save the caller on the stack
bl SYMBOL_NAME(RCTProfileFree)
pop {lr} // pop the caller
pop {r0} // pop the return value
bx lr // jump to the calleer
trap
#endif

View File

@@ -0,0 +1,125 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "RCTDefines.h"
#include "RCTMacros.h"
#if RCT_PROFILE && defined(__arm64__)
.align 5
.globl SYMBOL_NAME(RCTProfileTrampoline)
SYMBOL_NAME(RCTProfileTrampoline):
/**
* The explanation here is shorter, refer to the x86_64 implementation to a
* richer explanation
*/
// Basic prolog: save the frame pointer and the link register (caller address)
stp fp, lr, [sp, #-16]!
mov fp, sp
/**
* Store the value of all the parameter registers (x0-x8, q0-q7) so we can
* restore everything to the initial state at the time of the actual function
* call
*/
sub sp, sp, #(10*8 + 8*16)
stp q0, q1, [sp, #(0*16)]
stp q2, q3, [sp, #(2*16)]
stp q4, q5, [sp, #(4*16)]
stp q6, q7, [sp, #(6*16)]
stp x0, x1, [sp, #(8*16+0*8)]
stp x2, x3, [sp, #(8*16+2*8)]
stp x4, x5, [sp, #(8*16+4*8)]
stp x6, x7, [sp, #(8*16+6*8)]
str x8, [sp, #(8*16+8*8)]
/**
* Allocate 16-bytes for the values that have to be preserved across the call
* to the actual function, since the stack has to be in the exact initial
* state. During its lifetimewe use it to store the initial value of the
* callee saved registers we use to point the memory, the actual address of
* the implementation and the caller address.
*/
mov x0, #0x10
bl SYMBOL_NAME(RCTProfileMalloc)
// store the initial value of r19, the callee saved register we'll use
str x19, [x0]
mov x19, x0
/**
* void RCTProfileGetImplementation(id object, SEL selector)
*
* Load the 2 first arguments from the stack, they are the same used to call
* this function
*/
ldp x0, x1, [sp, #(8*16+0*8)]
bl SYMBOL_NAME(RCTProfileGetImplementation)
str x0, [x19, #0x8] // store the actual function address
/**
* void RCTProfileTrampolineStart(id, SEL) in RCTProfile.m
*
* start the profile, it takes the same first 2 arguments as above.
*/
ldp x0, x1, [sp, #(8*16+0*8)]
bl SYMBOL_NAME(RCTProfileTrampolineStart)
// Restore all the parameter registers to the initial state.
ldp q0, q1, [sp, #(0*16)]
ldp q2, q3, [sp, #(2*16)]
ldp q4, q5, [sp, #(4*16)]
ldp q6, q7, [sp, #(6*16)]
ldp x0, x1, [sp, #(8*16+0*8)]
ldp x2, x3, [sp, #(8*16+2*8)]
ldp x4, x5, [sp, #(8*16+4*8)]
ldp x6, x7, [sp, #(8*16+6*8)]
ldr x8, [sp, #(8*16+8*8)]
// Restore the stack pointer, frame pointer and link register
mov sp, fp
ldp fp, lr, [sp], #16
ldr x9, [x19, #0x8] // Load the function
str lr, [x19, #0x8] // store the address of the caller
blr x9 // call the actual function
/**
* allocate 32-bytes on the stack, for the 2 return values + the caller
* address that has to preserved across the call to `free`
*/
sub sp, sp, #0x20
str q0, [sp, #0x0] // 16-byte return value
str x0, [sp, #0x10] // 8-byte return value
// void RCTProfileTrampolineEnd(void) in RCTProfile.m - just ends this profile
bl SYMBOL_NAME(RCTProfileTrampolineEnd)
/**
* restore the callee saved registers, move the values we still need to the
* stack and free the allocated memory
*/
mov x0, x19 // move the address of the memory to x0, first argument
ldr x10, [x19, #0x8] // load the caller address
ldr x19, [x19] // restore x19
str x10, [sp, #0x18] // store x10 on the stack space allocated above
bl SYMBOL_NAME(RCTProfileFree)
// Load both return values and link register from the stack
ldr q0, [sp, #0x0]
ldr x0, [sp, #0x10]
ldr lr, [sp, #0x18]
// restore the stack pointer
add sp, sp, #0x20
// jump to the calleer, without a link
br lr
#endif

View File

@@ -0,0 +1,97 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "RCTDefines.h"
#include "RCTMacros.h"
#if RCT_PROFILE && defined(__i386__)
.globl SYMBOL_NAME(RCTProfileTrampoline)
SYMBOL_NAME(RCTProfileTrampoline):
/**
* The x86 version is much simpler, since all the arguments are passed in the
* stack, so we just have to preserve the stack pointer (%esp) and the callee
* saved register used to keep the memory allocated
*
* The explanation here is also shorter, refer to the x86_64 implementation to
* a richer explanation
*/
/**
* Allocate memory to save the caller of RCTProfileTrampoline (used afterwards
* to return at the end of the function) and the initial value for the callee
* saved register (%edi) that will be used to point to the memory allocated.
*/
subl $0x8, %esp // stack padding (16-byte alignment for function calls)
pushl $0xc // allocate 12-bytes
calll SYMBOL_NAME(RCTProfileMalloc)
addl $0xc, %esp // restore stack (8-byte padding + 4-byte argument)
/**
* actually store the values in the memory allocated
*/
movl %edi, 0x0(%eax) // previous value of edi
popl 0x4(%eax) // caller of RCTProfileTrampoline
// save the pointer to the allocated memory in %edi
movl %eax, %edi
/**
* void RCTProfileGetImplementation(id object, SEL selector) in RCTProfile.m
*
* Get the address of the actual C function we have to profile
*/
calll SYMBOL_NAME(RCTProfileGetImplementation)
movl %eax, 0x8(%edi) // Save it in the allocated memory
/**
* void RCTProfileTrampolineStart(id, SEL) in RCTProfile.m
*
* start profile - the arguments are already in the right position in the
* stack since it takes the same first 2 arguments as the any ObjC function -
* "self" and "_cmd"
*/
calll SYMBOL_NAME(RCTProfileTrampolineStart)
/**
* Call the actual function and save it's return value, since it should be the
* return value of RCTProfileTrampoline
*/
calll *0x8(%edi)
pushl %eax
// Align stack and end profile
subl $0xc, %esp
calll SYMBOL_NAME(RCTProfileTrampolineEnd)
addl $0xc, %esp // restore the stack
/**
* Move the values from the allocated memory to the stack, restore the
* value of %edi, and prepare to free the allocated memory.
*/
pushl 0x4(%edi) // caller of RCTProfileTrampoline
subl $0x4, %esp // Stack padding
pushl %edi // push the memory address
movl 0x0(%edi), %edi // restore the value of %edi
/**
* Actually free the memory used to store the values across function calls,
* the stack has already been padded and the first and only argument, the
* memory address, is already in the bottom of the stack.
*/
calll SYMBOL_NAME(RCTProfileFree)
addl $0x8, %esp
/**
* pop the caller address to %ecx and the actual function return value to
* %eax, so it's the return value of RCTProfileTrampoline
*/
popl %ecx
popl %eax
jmpl *%ecx
#endif

View File

@@ -0,0 +1,190 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "RCTDefines.h"
#include "RCTMacros.h"
#if RCT_PROFILE && defined(__x86_64__)
.globl SYMBOL_NAME(RCTProfileTrampoline)
SYMBOL_NAME(RCTProfileTrampoline):
/**
* Saves all the state so we can restore it before calling the functions being
* profiled. Registers have the same value at the point of the function call,
* the only thing we can change is the return value, so we return to
* `RCTProfileTrampoline` rather than to its caller.
*
* Save all the parameters registers (%rdi, %rsi, %rdx, %rcx, %r8, %r9), they
* have the 6 first arguments of the function call, and %rax which in special
* cases might be a pointer used for struct returns.
*
* We have to save %r12 since its value should be preserved across function
* calls and we'll use it to keep the stack pointer
*/
subq $0x80+8, %rsp // 8 x 16-bytes xmm registers + 8-bytes alignment
movdqa %xmm0, 0x70(%rsp)
movdqa %xmm1, 0x60(%rsp)
movdqa %xmm2, 0x50(%rsp)
movdqa %xmm3, 0x40(%rsp)
movdqa %xmm4, 0x30(%rsp)
movdqa %xmm5, 0x20(%rsp)
movdqa %xmm6, 0x10(%rsp)
movdqa %xmm7, 0x00(%rsp)
pushq %rdi
pushq %rsi
pushq %rdx
pushq %rcx
pushq %r8
pushq %r9
pushq %rax
pushq %r12
/**
* Store the stack pointer in the callee saved register %r12 and align the
* stack - it has to 16-byte aligned at the point of the function call
*/
movq %rsp, %r12
andq $-0x10, %rsp
/**
* void RCTProfileGetImplementation(id object, SEL selector)
*
* This is a C function defined in `RCTProfile.m`, the object and the selector
* already have to be on %rdi and %rsi respectively, as in any ObjC call.
*/
callq SYMBOL_NAME_PIC(RCTProfileGetImplementation)
// Restore/unalign the stack pointer, so we can access the registers we stored
movq %r12, %rsp
/**
* pop %r12 before pushing %rax, which contains the address of the actual
* function we have to call, than we keep %r12 at the bottom of the stack to
* reference the stack pointer
*/
popq %r12
pushq %rax
pushq %r12
// align stack
movq %rsp, %r12
andq $-0x10, %rsp
/**
* Allocate memory to save parent before start profiling: the address is put
* at the bottom of the stack at the function call, so ret can actually return
* to the caller. In this case it has the address of RCTProfileTrampoline's
* caller where we'll have to return to after we're finished.
*
* We can't store it on the stack or in any register, since we have to be in
* the exact same state we were at the moment we were called, so the solution
* is to allocate a tiny bit of memory to save this address
*/
// allocate 16 bytes
movq $0x10, %rdi
callq SYMBOL_NAME_PIC(RCTProfileMalloc)
// store the initial value of calle saved registers %r13 and %r14
movq %r13, 0x0(%rax)
movq %r14, 0x8(%rax)
// mov the pointers we need to the callee saved registers
movq 0xd8(%rsp), %r13 // caller of RCTProfileTrampoline (0xd8 is stack top)
movq %rax, %r14 // allocated memory's address
/**
* Move self and cmd back to the registers and call start profile: it uses
* the object and the selector to label the call in the profile.
*/
movq 0x40(%r12), %rdi // object
movq 0x38(%r12), %rsi // selector
// void RCTProfileTrampolineStart(id, SEL) in RCTProfile.m
callq SYMBOL_NAME_PIC(RCTProfileTrampolineStart)
// unalign the stack and restore %r12
movq %r12, %rsp
popq %r12
// Restore registers for actual function call
popq %r11
popq %rax
popq %r9
popq %r8
popq %rcx
popq %rdx
popq %rsi
popq %rdi
movdqa 0x00(%rsp), %xmm7
movdqa 0x10(%rsp), %xmm6
movdqa 0x20(%rsp), %xmm5
movdqa 0x30(%rsp), %xmm4
movdqa 0x40(%rsp), %xmm3
movdqa 0x50(%rsp), %xmm2
movdqa 0x60(%rsp), %xmm1
movdqa 0x70(%rsp), %xmm0
addq $0x80+8, %rsp
/**
* delete parent caller (saved in %r13) `call` will add the new address so
* we return to RCTProfileTrampoline rather than to its caller
*/
addq $0x8, %rsp
// call the actual function and save the return value
callq *%r11
pushq %rax
pushq %rdx
subq $0x20, %rsp // 2 16-bytes xmm register
movdqa %xmm0, 0x00(%rsp)
movdqa %xmm1, 0x10(%rsp)
// void RCTProfileTrampolineEnd(void) in RCTProfile.m - just ends this profile
callq SYMBOL_NAME_PIC(RCTProfileTrampolineEnd)
/**
* Restore the initial value of the callee saved registers, saved in the
* memory allocated.
*/
movq %r13, %rcx
movq %r14, %rdi
movq 0x0(%r14), %r13
movq 0x8(%r14), %r14
/**
* save caller address and actual function return (previously in the allocated
* memory) and align the stack
*/
pushq %rcx
pushq %r12
movq %rsp, %r12
andq $-0x10, %rsp
// Free the memory allocated to stash callee saved registers
callq SYMBOL_NAME_PIC(RCTProfileFree)
// unalign stack and restore %r12
movq %r12, %rsp
popq %r12
/**
* pop the caller address to %rcx and the actual function return value(s)
* so it's the return value of RCTProfileTrampoline
*/
popq %rcx
movdqa 0x00(%rsp), %xmm0
movdqa 0x10(%rsp), %xmm1
addq $0x20, %rsp
popq %rdx
popq %rax
// jump to caller
jmpq *%rcx
#endif