Skip to content

Commit de53ef1

Browse files
tadeuzagalloFacebook Github Bot 0
authored andcommitted
Add systrace controls window
Summary:Add a couple functions to show and hide a small window with buttons to start/stop systrace and reload the current bridge. After stop profiling, the results will be saved to a temporary file, and a share sheet will show up with the file attached so that you can send it to your computer whatever way is more convenient. Depends on D2700069 Reviewed By: jspahrsummers Differential Revision: D2811560 fb-gh-sync-id: 5e91ece3a7ea748d4cb5fbc612a9b76ab80fc8f3 shipit-source-id: 5e91ece3a7ea748d4cb5fbc612a9b76ab80fc8f3
1 parent fd816b1 commit de53ef1

2 files changed

Lines changed: 108 additions & 0 deletions

File tree

React/Profiler/RCTProfile.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,12 @@ typedef struct {
185185

186186
RCT_EXTERN void RCTProfileRegisterCallbacks(RCTProfileCallbacks *);
187187

188+
/**
189+
* Systrace control window
190+
*/
191+
RCT_EXTERN void RCTProfileShowControls(void);
192+
RCT_EXTERN void RCTProfileHideControls(void);
193+
188194
#else
189195

190196
#define RCTProfileBeginFlowEvent()
@@ -215,4 +221,7 @@ RCT_EXTERN void RCTProfileRegisterCallbacks(RCTProfileCallbacks *);
215221

216222
#define RCTProfileSendResult(...)
217223

224+
#define RCTProfileShowControls(...)
225+
#define RCTProfileHideControls(...)
226+
218227
#endif

React/Profiler/RCTProfile.m

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@
5050
static NSTimeInterval RCTProfileStartTime;
5151
static NSUInteger RCTProfileEventID = 0;
5252
static CADisplayLink *RCTProfileDisplayLink;
53+
static __weak RCTBridge *_RCTProfilingBridge;
54+
static UIWindow *RCTProfileControlsWindow;
5355

5456
#pragma mark - Macros
5557

@@ -97,6 +99,11 @@ void RCTProfileRegisterCallbacks(RCTProfileCallbacks *cb)
9799

98100
#pragma mark - Private Helpers
99101

102+
static RCTBridge *RCTProfilingBridge(void)
103+
{
104+
return _RCTProfilingBridge ?: [RCTBridge currentBridge];
105+
}
106+
100107
static NSNumber *RCTProfileTimestamp(NSTimeInterval timestamp)
101108
{
102109
return @((timestamp - RCTProfileStartTime) * 1e6);
@@ -288,6 +295,8 @@ static void RCTProfileHookInstance(id instance)
288295

289296
void RCTProfileHookModules(RCTBridge *bridge)
290297
{
298+
_RCTProfilingBridge = bridge;
299+
291300
#pragma clang diagnostic push
292301
#pragma clang diagnostic ignored "-Wtautological-pointer-compare"
293302
if (RCTProfileTrampoline == NULL) {
@@ -324,6 +333,8 @@ static void RCTProfileUnhookInstance(id instance)
324333

325334
void RCTProfileUnhookModules(RCTBridge *bridge)
326335
{
336+
_RCTProfilingBridge = nil;
337+
327338
dispatch_group_enter(RCTProfileGetUnhookGroup());
328339

329340
for (RCTModuleData *moduleData in [bridge valueForKey:@"moduleDataByID"]) {
@@ -351,6 +362,54 @@ + (void)vsync:(CADisplayLink *)displayLink
351362
RCTProfileImmediateEvent(0, @"VSYNC", displayLink.timestamp, 'g');
352363
}
353364

365+
+ (void)reload
366+
{
367+
[RCTProfilingBridge() reload];
368+
}
369+
370+
+ (void)toggle:(UIButton *)target
371+
{
372+
BOOL isProfiling = RCTProfileIsProfiling();
373+
374+
// Start and Stop are switched here, since we're going to toggle isProfiling
375+
[target setTitle:isProfiling ? @"Start" : @"Stop"
376+
forState:UIControlStateNormal];
377+
378+
if (isProfiling) {
379+
RCTProfileEnd(RCTProfilingBridge(), ^(NSString *result) {
380+
NSString *outFile = [NSTemporaryDirectory() stringByAppendingString:@"tmp_trace.json"];
381+
[result writeToFile:outFile
382+
atomically:YES
383+
encoding:NSUTF8StringEncoding
384+
error:nil];
385+
UIActivityViewController *activityViewController = [[UIActivityViewController alloc] initWithActivityItems:@[[NSURL fileURLWithPath:outFile]]
386+
applicationActivities:nil];
387+
activityViewController.completionHandler = ^(__unused NSString *activityType, __unused BOOL completed) {
388+
RCTProfileControlsWindow.hidden = NO;
389+
};
390+
RCTProfileControlsWindow.hidden = YES;
391+
dispatch_async(dispatch_get_main_queue(), ^{
392+
[[[[[UIApplication sharedApplication] delegate] window] rootViewController] presentViewController:activityViewController
393+
animated:YES
394+
completion:nil];
395+
});
396+
});
397+
} else {
398+
RCTProfileInit(RCTProfilingBridge());
399+
}
400+
}
401+
402+
+ (void)drag:(UIPanGestureRecognizer *)gestureRecognizer
403+
{
404+
CGPoint translation = [gestureRecognizer translationInView:RCTProfileControlsWindow];
405+
RCTProfileControlsWindow.center = CGPointMake(
406+
RCTProfileControlsWindow.center.x + translation.x,
407+
RCTProfileControlsWindow.center.y + translation.y
408+
);
409+
[gestureRecognizer setTranslation:CGPointMake(0, 0)
410+
inView:RCTProfileControlsWindow];
411+
}
412+
354413
@end
355414

356415
#pragma mark - Public Functions
@@ -713,4 +772,44 @@ void RCTProfileSendResult(RCTBridge *bridge, NSString *route, NSData *data)
713772
[task resume];
714773
}
715774

775+
void RCTProfileShowControls(void)
776+
{
777+
static const CGFloat height = 30;
778+
static const CGFloat width = 60;
779+
780+
UIWindow *window = [[UIWindow alloc] initWithFrame:CGRectMake(20, 80, width * 2, height)];
781+
window.windowLevel = UIWindowLevelAlert + 1000;
782+
window.hidden = NO;
783+
window.backgroundColor = [UIColor lightGrayColor];
784+
window.layer.borderColor = [UIColor grayColor].CGColor;
785+
window.layer.borderWidth = 1;
786+
window.alpha = 0.8;
787+
788+
UIButton *startOrStop = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, width, height)];
789+
[startOrStop setTitle:RCTProfileIsProfiling() ? @"Stop" : @"Start"
790+
forState:UIControlStateNormal];
791+
[startOrStop addTarget:[RCTProfile class] action:@selector(toggle:) forControlEvents:UIControlEventTouchUpInside];
792+
startOrStop.titleLabel.font = [UIFont systemFontOfSize:12];
793+
794+
UIButton *reload = [[UIButton alloc] initWithFrame:CGRectMake(width, 0, width, height)];
795+
[reload setTitle:@"Reload" forState:UIControlStateNormal];
796+
[reload addTarget:[RCTProfile class] action:@selector(reload) forControlEvents:UIControlEventTouchUpInside];
797+
reload.titleLabel.font = [UIFont systemFontOfSize:12];
798+
799+
[window addSubview:startOrStop];
800+
[window addSubview:reload];
801+
802+
UIPanGestureRecognizer *gestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:[RCTProfile class]
803+
action:@selector(drag:)];
804+
[window addGestureRecognizer:gestureRecognizer];
805+
806+
RCTProfileControlsWindow = window;
807+
}
808+
809+
void RCTProfileHideControls(void)
810+
{
811+
RCTProfileControlsWindow.hidden = YES;
812+
RCTProfileControlsWindow = nil;
813+
}
814+
716815
#endif

0 commit comments

Comments
 (0)