2015-10-23 17:53:42 +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 "RCTDefines.h"
|
|
|
|
|
|
|
|
#if RCT_DEV
|
|
|
|
|
|
|
|
#import <dlfcn.h>
|
|
|
|
|
|
|
|
#import <mach/mach.h>
|
|
|
|
|
|
|
|
#import "RCTBridge.h"
|
Add RCTDevSettings module
Summary:
This decouples non-UI logic from RCTDevMenu into a new module RCTDevSettings.
**Motivation**: This allows developers to change dev settings without depending on the built-in dev menu, e.g. if they want to introduce their own UI, or have other devtools logic that doesn't depend on an action sheet.
It also introduces the RCTDevSettingsDataSource protocol for storing dev tools preferences. This could allow a developer to implement alternative behaviors, e.g. loading the settings from some other config, changing settings based on the user, deciding not to persist some settings, or something else.
The included data source implementation, RCTDevSettingsUserDefaultsDataSource, uses NSUserDefaults and is backwards compatible with the older implementation, so **no workflows or dependent code will break, and old saved settings will persist.**
The RCTDevMenu interface has not changed and is therefore also backwards-compatible, though
some methods are now deprecated.
In order to ensure that RCTDevSettings
Closes https://github.com/facebook/react-native/pull/11613
Reviewed By: mmmulani
Differential Revision: D4571773
Pulled By: javache
fbshipit-source-id: 25555d0a6eaa81f694343e079ed02439e5845fbc
2017-02-24 14:50:29 +00:00
|
|
|
#import "RCTDevSettings.h"
|
2015-10-23 17:53:42 +00:00
|
|
|
#import "RCTFPSGraph.h"
|
|
|
|
#import "RCTInvalidating.h"
|
|
|
|
#import "RCTJavaScriptExecutor.h"
|
2015-10-30 23:51:48 +00:00
|
|
|
#import "RCTPerformanceLogger.h"
|
2015-10-23 17:53:42 +00:00
|
|
|
#import "RCTRootView.h"
|
|
|
|
#import "RCTUIManager.h"
|
2017-01-23 18:07:46 +00:00
|
|
|
#import "RCTBridge+Private.h"
|
2017-04-01 10:16:52 +00:00
|
|
|
#import "RCTUtils.h"
|
2015-10-23 17:53:42 +00:00
|
|
|
|
2017-02-27 21:25:47 +00:00
|
|
|
#if __has_include("RCTDevMenu.h")
|
|
|
|
#import "RCTDevMenu.h"
|
|
|
|
#endif
|
|
|
|
|
2015-10-30 23:51:48 +00:00
|
|
|
static NSString *const RCTPerfMonitorCellIdentifier = @"RCTPerfMonitorCellIdentifier";
|
|
|
|
|
|
|
|
static CGFloat const RCTPerfMonitorBarHeight = 50;
|
|
|
|
static CGFloat const RCTPerfMonitorExpandHeight = 250;
|
2015-10-23 17:53:42 +00:00
|
|
|
|
|
|
|
typedef BOOL (*RCTJSCSetOptionType)(const char *);
|
|
|
|
|
|
|
|
static BOOL RCTJSCSetOption(const char *option)
|
|
|
|
{
|
|
|
|
static RCTJSCSetOptionType setOption;
|
|
|
|
static dispatch_once_t onceToken;
|
|
|
|
|
|
|
|
dispatch_once(&onceToken, ^{
|
2015-10-24 21:01:11 +00:00
|
|
|
/**
|
|
|
|
* JSC private C++ static method to toggle options at runtime
|
|
|
|
*
|
|
|
|
* JSC::Options::setOptions - JavaScriptCore/runtime/Options.h
|
|
|
|
*/
|
2015-10-23 17:53:42 +00:00
|
|
|
setOption = dlsym(RTLD_DEFAULT, "_ZN3JSC7Options9setOptionEPKc");
|
2015-10-24 21:01:11 +00:00
|
|
|
|
|
|
|
if (RCT_DEBUG && setOption == NULL) {
|
|
|
|
RCTLogWarn(@"The symbol used to enable JSC runtime options is not available in this iOS version");
|
|
|
|
}
|
2015-10-23 17:53:42 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2015-10-30 23:51:48 +00:00
|
|
|
@interface RCTPerfMonitor : NSObject <RCTBridgeModule, RCTInvalidating, UITableViewDataSource, UITableViewDelegate>
|
2015-10-23 17:53:42 +00:00
|
|
|
|
2017-02-27 21:25:47 +00:00
|
|
|
#if __has_include("RCTDevMenu.h")
|
2015-10-23 17:53:42 +00:00
|
|
|
@property (nonatomic, strong, readonly) RCTDevMenuItem *devMenuItem;
|
2017-02-27 21:25:47 +00:00
|
|
|
#endif
|
2015-10-23 17:53:42 +00:00
|
|
|
@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;
|
2015-10-30 23:51:48 +00:00
|
|
|
@property (nonatomic, strong, readonly) UITableView *metrics;
|
2015-10-23 17:53:42 +00:00
|
|
|
@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 {
|
2017-02-27 21:25:47 +00:00
|
|
|
#if __has_include("RCTDevMenu.h")
|
2015-10-23 17:53:42 +00:00
|
|
|
RCTDevMenuItem *_devMenuItem;
|
2017-02-27 21:25:47 +00:00
|
|
|
#endif
|
2015-10-23 17:53:42 +00:00
|
|
|
UIPanGestureRecognizer *_gestureRecognizer;
|
|
|
|
UIView *_container;
|
|
|
|
UILabel *_memory;
|
|
|
|
UILabel *_heap;
|
|
|
|
UILabel *_views;
|
|
|
|
UILabel *_uiGraphLabel;
|
|
|
|
UILabel *_jsGraphLabel;
|
2015-10-30 23:51:48 +00:00
|
|
|
UITableView *_metrics;
|
2015-10-23 17:53:42 +00:00
|
|
|
|
|
|
|
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;
|
2015-10-30 23:51:48 +00:00
|
|
|
|
|
|
|
CGRect _storedMonitorFrame;
|
|
|
|
|
|
|
|
NSArray *_perfLoggerMarks;
|
2015-10-23 17:53:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@synthesize bridge = _bridge;
|
|
|
|
|
|
|
|
RCT_EXPORT_MODULE()
|
|
|
|
|
2016-05-03 16:08:03 +00:00
|
|
|
- (instancetype)init
|
|
|
|
{
|
|
|
|
// We're only overriding this to ensure the module gets created at startup
|
2016-05-04 14:06:09 +00:00
|
|
|
// TODO (t11106126): Remove once we have more declarative control over module setup.
|
2016-05-03 16:08:03 +00:00
|
|
|
return [super init];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (dispatch_queue_t)methodQueue
|
|
|
|
{
|
|
|
|
return dispatch_get_main_queue();
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)setBridge:(RCTBridge *)bridge
|
|
|
|
{
|
|
|
|
_bridge = bridge;
|
|
|
|
|
2017-02-27 21:25:47 +00:00
|
|
|
#if __has_include("RCTDevMenu.h")
|
2017-02-09 20:33:13 +00:00
|
|
|
[_bridge.devMenu addItem:self.devMenuItem];
|
2017-02-27 21:25:47 +00:00
|
|
|
#endif
|
2016-05-03 16:08:03 +00:00
|
|
|
}
|
|
|
|
|
2015-10-23 17:53:42 +00:00
|
|
|
- (void)invalidate
|
|
|
|
{
|
|
|
|
[self hide];
|
|
|
|
}
|
|
|
|
|
2017-02-27 21:25:47 +00:00
|
|
|
#if __has_include("RCTDevMenu.h")
|
2015-10-23 17:53:42 +00:00
|
|
|
- (RCTDevMenuItem *)devMenuItem
|
|
|
|
{
|
|
|
|
if (!_devMenuItem) {
|
|
|
|
__weak __typeof__(self) weakSelf = self;
|
Add RCTDevSettings module
Summary:
This decouples non-UI logic from RCTDevMenu into a new module RCTDevSettings.
**Motivation**: This allows developers to change dev settings without depending on the built-in dev menu, e.g. if they want to introduce their own UI, or have other devtools logic that doesn't depend on an action sheet.
It also introduces the RCTDevSettingsDataSource protocol for storing dev tools preferences. This could allow a developer to implement alternative behaviors, e.g. loading the settings from some other config, changing settings based on the user, deciding not to persist some settings, or something else.
The included data source implementation, RCTDevSettingsUserDefaultsDataSource, uses NSUserDefaults and is backwards compatible with the older implementation, so **no workflows or dependent code will break, and old saved settings will persist.**
The RCTDevMenu interface has not changed and is therefore also backwards-compatible, though
some methods are now deprecated.
In order to ensure that RCTDevSettings
Closes https://github.com/facebook/react-native/pull/11613
Reviewed By: mmmulani
Differential Revision: D4571773
Pulled By: javache
fbshipit-source-id: 25555d0a6eaa81f694343e079ed02439e5845fbc
2017-02-24 14:50:29 +00:00
|
|
|
__weak RCTDevSettings *devSettings = self.bridge.devSettings;
|
2015-10-23 17:53:42 +00:00
|
|
|
_devMenuItem =
|
Add RCTDevSettings module
Summary:
This decouples non-UI logic from RCTDevMenu into a new module RCTDevSettings.
**Motivation**: This allows developers to change dev settings without depending on the built-in dev menu, e.g. if they want to introduce their own UI, or have other devtools logic that doesn't depend on an action sheet.
It also introduces the RCTDevSettingsDataSource protocol for storing dev tools preferences. This could allow a developer to implement alternative behaviors, e.g. loading the settings from some other config, changing settings based on the user, deciding not to persist some settings, or something else.
The included data source implementation, RCTDevSettingsUserDefaultsDataSource, uses NSUserDefaults and is backwards compatible with the older implementation, so **no workflows or dependent code will break, and old saved settings will persist.**
The RCTDevMenu interface has not changed and is therefore also backwards-compatible, though
some methods are now deprecated.
In order to ensure that RCTDevSettings
Closes https://github.com/facebook/react-native/pull/11613
Reviewed By: mmmulani
Differential Revision: D4571773
Pulled By: javache
fbshipit-source-id: 25555d0a6eaa81f694343e079ed02439e5845fbc
2017-02-24 14:50:29 +00:00
|
|
|
[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;
|
|
|
|
}
|
|
|
|
}];
|
2015-10-23 17:53:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return _devMenuItem;
|
|
|
|
}
|
2017-02-27 21:25:47 +00:00
|
|
|
#endif
|
2015-10-23 17:53:42 +00:00
|
|
|
|
|
|
|
- (UIPanGestureRecognizer *)gestureRecognizer
|
|
|
|
{
|
|
|
|
if (!_gestureRecognizer) {
|
|
|
|
_gestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self
|
|
|
|
action:@selector(gesture:)];
|
|
|
|
}
|
|
|
|
|
|
|
|
return _gestureRecognizer;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (UIView *)container
|
|
|
|
{
|
|
|
|
if (!_container) {
|
2015-10-30 23:51:48 +00:00
|
|
|
_container = [[UIView alloc] initWithFrame:CGRectMake(10, 25, 180, RCTPerfMonitorBarHeight)];
|
2015-10-23 17:53:42 +00:00
|
|
|
_container.backgroundColor = UIColor.whiteColor;
|
|
|
|
_container.layer.borderWidth = 2;
|
2015-10-30 23:51:48 +00:00
|
|
|
_container.layer.borderColor = [UIColor lightGrayColor].CGColor;
|
2015-10-23 17:53:42 +00:00
|
|
|
[_container addGestureRecognizer:self.gestureRecognizer];
|
2015-10-30 23:51:48 +00:00
|
|
|
[_container addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self
|
|
|
|
action:@selector(tap)]];
|
2015-10-23 17:53:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return _container;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (UILabel *)memory
|
|
|
|
{
|
|
|
|
if (!_memory) {
|
2015-10-30 23:51:48 +00:00
|
|
|
_memory = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 44, RCTPerfMonitorBarHeight)];
|
2015-10-23 17:53:42 +00:00
|
|
|
_memory.font = [UIFont systemFontOfSize:12];
|
|
|
|
_memory.numberOfLines = 3;
|
|
|
|
_memory.textAlignment = NSTextAlignmentCenter;
|
|
|
|
}
|
|
|
|
|
|
|
|
return _memory;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (UILabel *)heap
|
|
|
|
{
|
|
|
|
if (!_heap) {
|
2015-10-30 23:51:48 +00:00
|
|
|
_heap = [[UILabel alloc] initWithFrame:CGRectMake(44, 0, 44, RCTPerfMonitorBarHeight)];
|
2015-10-23 17:53:42 +00:00
|
|
|
_heap.font = [UIFont systemFontOfSize:12];
|
|
|
|
_heap.numberOfLines = 3;
|
|
|
|
_heap.textAlignment = NSTextAlignmentCenter;
|
|
|
|
}
|
|
|
|
|
|
|
|
return _heap;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (UILabel *)views
|
|
|
|
{
|
|
|
|
if (!_views) {
|
2015-10-30 23:51:48 +00:00
|
|
|
_views = [[UILabel alloc] initWithFrame:CGRectMake(88, 0, 44, RCTPerfMonitorBarHeight)];
|
2015-10-23 17:53:42 +00:00
|
|
|
_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;
|
|
|
|
}
|
|
|
|
|
2015-10-30 23:51:48 +00:00
|
|
|
- (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;
|
|
|
|
}
|
|
|
|
|
2015-10-23 17:53:42 +00:00
|
|
|
- (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];
|
|
|
|
|
2017-04-01 10:16:52 +00:00
|
|
|
UIWindow *window = RCTSharedApplication().delegate.window;
|
2015-10-23 17:53:42 +00:00
|
|
|
[window addSubview:self.container];
|
|
|
|
|
|
|
|
|
|
|
|
_uiDisplayLink = [CADisplayLink displayLinkWithTarget:self
|
|
|
|
selector:@selector(threadUpdate:)];
|
|
|
|
[_uiDisplayLink addToRunLoop:[NSRunLoop mainRunLoop]
|
|
|
|
forMode:NSRunLoopCommonModes];
|
|
|
|
|
2017-02-09 20:33:13 +00:00
|
|
|
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];
|
2015-10-23 17:53:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (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
|
|
|
|
) {
|
2016-07-07 19:36:56 +00:00
|
|
|
write(self->_stderr, buffer, size);
|
2015-10-23 17:53:42 +00:00
|
|
|
|
|
|
|
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, ^{
|
2017-02-09 20:33:13 +00:00
|
|
|
NSString *pattern = @"\\[GC: [\\d\\.]+ \\wb => (Eden|Full)Collection, (?:Skipped copying|Did copy), ([\\d\\.]+) \\wb, [\\d.]+ \\ws\\]";
|
2015-10-23 17:53:42 +00:00
|
|
|
GCRegex = [NSRegularExpression regularExpressionWithPattern:pattern
|
|
|
|
options:0
|
|
|
|
error:nil];
|
|
|
|
});
|
|
|
|
|
|
|
|
if (_remaining) {
|
|
|
|
log = [_remaining stringByAppendingString:log];
|
|
|
|
_remaining = nil;
|
|
|
|
}
|
|
|
|
|
2015-11-03 22:45:46 +00:00
|
|
|
NSArray<NSString *> *lines = [log componentsSeparatedByString:@"\n"];
|
2015-10-23 17:53:42 +00:00
|
|
|
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
|
|
|
|
{
|
2015-11-14 18:25:00 +00:00
|
|
|
NSDictionary<NSNumber *, UIView *> *views = [_bridge.uiManager valueForKey:@"viewRegistry"];
|
2015-10-23 17:53:42 +00:00
|
|
|
NSUInteger viewCount = views.count;
|
|
|
|
NSUInteger visibleViewCount = 0;
|
2015-11-14 18:25:00 +00:00
|
|
|
for (UIView *view in views.allValues) {
|
2015-10-23 17:53:42 +00:00
|
|
|
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];
|
|
|
|
}
|
|
|
|
|
2015-10-30 23:51:48 +00:00
|
|
|
- (void)tap
|
|
|
|
{
|
|
|
|
if (CGRectIsEmpty(_storedMonitorFrame)) {
|
|
|
|
_storedMonitorFrame = CGRectMake(0, 20, self.container.window.frame.size.width, RCTPerfMonitorExpandHeight);
|
|
|
|
[self.container addSubview:self.metrics];
|
|
|
|
[self loadPerformanceLoggerData];
|
|
|
|
}
|
|
|
|
|
|
|
|
[UIView animateWithDuration:.25 animations:^{
|
|
|
|
CGRect tmp = self.container.frame;
|
2016-07-07 19:36:56 +00:00
|
|
|
self.container.frame = self->_storedMonitorFrame;
|
|
|
|
self->_storedMonitorFrame = tmp;
|
2015-10-30 23:51:48 +00:00
|
|
|
}];
|
|
|
|
}
|
|
|
|
|
2015-10-23 17:53:42 +00:00
|
|
|
- (void)threadUpdate:(CADisplayLink *)displayLink
|
|
|
|
{
|
|
|
|
RCTFPSGraph *graph = displayLink == _jsDisplayLink ? _jsGraph : _uiGraph;
|
|
|
|
[graph onTick:displayLink.timestamp];
|
|
|
|
}
|
|
|
|
|
2015-10-30 23:51:48 +00:00
|
|
|
- (void)loadPerformanceLoggerData
|
|
|
|
{
|
|
|
|
NSUInteger i = 0;
|
2016-02-26 12:53:38 +00:00
|
|
|
NSMutableArray<NSString *> *data = [NSMutableArray new];
|
2016-07-07 14:20:03 +00:00
|
|
|
RCTPerformanceLogger *performanceLogger = [_bridge performanceLogger];
|
|
|
|
NSArray<NSNumber *> *values = [performanceLogger valuesForTags];
|
|
|
|
for (NSString *label in [performanceLogger labelsForTags]) {
|
2016-02-26 12:53:38 +00:00
|
|
|
long long value = values[i+1].longLongValue - values[i].longLongValue;
|
2016-03-17 14:26:54 +00:00
|
|
|
NSString *unit = @"ms";
|
|
|
|
if ([label hasSuffix:@"Size"]) {
|
|
|
|
unit = @"b";
|
|
|
|
} else if ([label hasSuffix:@"Count"]) {
|
|
|
|
unit = @"";
|
|
|
|
}
|
2016-02-26 12:53:38 +00:00
|
|
|
[data addObject:[NSString stringWithFormat:@"%@: %lld%@", label, value, unit]];
|
2015-10-30 23:51:48 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2015-10-23 17:53:42 +00:00
|
|
|
@end
|
|
|
|
|
|
|
|
#endif
|