Skip to content

Commit

Permalink
Add support for RN bridge data recording
Browse files Browse the repository at this point in the history
Update RN sampler to be more precise in its data collection.
Remove legacy RN support (< 0.51)
Stop collecting network requests if network recording is disabled in profiler configuration
Fix horizontal scroller appearing where it shouldn’t
Fix many small issues
  • Loading branch information
LeoNatan committed Oct 31, 2018
1 parent 0ff8894 commit 99b7a94
Show file tree
Hide file tree
Showing 150 changed files with 893 additions and 219 deletions.
Expand Up @@ -20,8 +20,22 @@ <h1>
<h3>
<a name="user-content-discussion" class="anchor" href="#discussion"><span class="octicon octicon-link"></span></a>Discussion</h3>
<p>Use the information captured by this instrument to inspect the data passed in your app's React Native bridge. The more data passed, the more processing needed in native and the JavaScript thread, and thus can lead to your app being less responsive.</p>
<p>If the <strong>Record bridge data</strong> option is enabled, recorded bridge data can be accessed from the navigation bar.</p>
<p>For an in-depth look at profiling options, see <a href="ProfilingOptions.html">Profiling Options</a>.</p>
<h3>
<a name="user-content-detail-pane" class="anchor" href="#detail-pane"><span class="octicon octicon-link"></span></a>Detail Pane</h3>
<p>The detail pane includes your app's React Native bridge data at the time of the sample; N➔JS (native to JavaScript) and JS➔N (JavaScript to native) are displayed in columns of delta as well as total.</p>
<p><a href="Resources/Instrument_RNBridgeData_DetailPane.png" target="_blank"><img src="Resources/Instrument_RNBridgeData_DetailPane.png" alt="React Native Bridge Data Detail Pane" title="React Native Bridge Data Detail Pane" style="max-width:100%;" /></a></p>
<p>The detail pane includes your app's React Native bridge data at the time of the sample; N ➔ JS (native to JavaScript) and JS ➔ N (JavaScript to native) are displayed in columns of delta as well as total.</p>
<p>If the <strong>Record bridge data</strong> option was enabled during recording, you can select to view <strong>Samples</strong> or <strong>Bridge Data</strong> in the navigation bar.</p>
<h4>
<a name="user-content-samples" class="anchor" href="#samples"><span class="octicon octicon-link"></span></a>Samples</h4>
<p><a href="Resources/Instrument_RNBridgeData_DetailPane.png" target="_blank"><img src="Resources/Instrument_RNBridgeData_DetailPane.png" alt="CPU Usage Detail Pane" title="Bridge Data Detail Pane" style="max-width:100%;" /></a></p>
<h4>
<a name="user-content-bridge-data" class="anchor" href="#bridge-data"><span class="octicon octicon-link"></span></a>Bridge Data</h4>
<p><a href="Resources/Instrument_RNBridgeData_DetailPane_BridgeData.png" target="_blank"><img src="Resources/Instrument_RNBridgeData_DetailPane_BridgeData.png" alt="CPU Usage Detail Pane" title="Bridge Data Detail Pane" style="max-width:100%;" /></a></p>
<h3>
<a name="user-content-inspector" class="anchor" href="#inspector"><span class="octicon octicon-link"></span></a>Inspector</h3>
<h4>
<a name="user-content-bridge-data-1" class="anchor" href="#bridge-data-1"><span class="octicon octicon-link"></span></a>Bridge Data</h4>
<p>If the <strong>Record bridge data</strong> option was enabled during recording, the inspector pane shows information about React Native bridge data packets.</p>
<p><a href="Resources/Instrument_RNBridgeData_InspectorPane_BridgeData.png" target="_blank"><img src="Resources/Instrument_RNBridgeData_InspectorPane_BridgeData.png" alt="CPU Usage Inspector Pane" title="Bridge Data Inspector Pane" style="max-width:100%;" /></a></p>
</body></html>
Expand Up @@ -25,7 +25,7 @@ <h3>
<h3>
<a name="user-content-detail-pane" class="anchor" href="#detail-pane"><span class="octicon octicon-link"></span></a>Detail Pane</h3>
<p>The detail pane includes your app's JavaScript thread CPU usage at the time of the sample.</p>
<p><a href="Resources/Instrument_RNJavaScriptThread_DetailPane.png" target="_blank"><img src="Resources/Instrument_RNJavaScriptThread_DetailPane.png" alt="JavaScript Thread Detail Pane" title="JavaScript Thread Detail Pane" style="max-width:100%;" /></a></p>
<p><a href="Resources/Instrument_RNJSThread_DetailPane.png" target="_blank"><img src="Resources/Instrument_RNJSThread_DetailPane.png" alt="JavaScript Thread Detail Pane" title="JavaScript Thread Detail Pane" style="max-width:100%;" /></a></p>
<p>Samples will be highlighted in red (warning 3) when:</p>
<ul>
<li>The JavaScript thread CPU load is above 90%</li>
Expand Down
Expand Up @@ -44,9 +44,5 @@ <h3>
<h3>
<a name="user-content-react-native" class="anchor" href="#react-native"><span class="octicon octicon-link"></span></a>React Native</h3>
<p>The <strong>Profile React Native (if available)</strong> options controls the React Native profiling systems in Detox Instruments and its Profiler framework. These systems provide information such as JavaScript thread performance, bridge calls and bridge data, which can be very helpful when debugging apps with React Native usage.</p>
<h3>
<a name="user-content-javascript" class="anchor" href="#javascript"><span class="octicon octicon-link"></span></a>JavaScript</h3>
<p><strong>These options are disabled and will be available in a future release.</strong></p>
<p>The <strong>Collect JavaScript stack traces</strong> option enables the recording of the stack trace of the JavaScript virtual machine. This provides code symbol information for additional debugging purposes. This is a relatively inexpensive operation, but depending on the sampling frequency, may have a slight performance cost.</p>
<p>The <strong>Symbolicate JavaScript stack traces</strong> option enables the runtime symbolication of symbols collected in JavaScript stack traces, using Source Maps generated by the React Native bundler/packager. This further assists development by creating human-readable symbols. This is a slightly expensive operation, and depending on the sampling frequency, may have a performance impact on the app.</p>
<p>The <strong>Record bridge data</strong> option controls whether React Native bridge data is recorded during profiling. Depending on your app's activity, this can take a small-to-moderate toll on performance. If bridge data recording is not necessary, you can turn this option off to save performance.</p>
</body></html>
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Diff not rendered.
Binary file not shown.
12 changes: 12 additions & 0 deletions DetoxInstruments/DetoxInstruments.xcodeproj/project.pbxproj
Expand Up @@ -26,6 +26,8 @@
3905F4C11EF83FF0003E08C1 /* DTXLineLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 3905F4C01EF83FF0003E08C1 /* DTXLineLayer.m */; };
390688C12117781900D15C86 /* DTXDebugMenuGenerator.xib in Resources */ = {isa = PBXBuildFile; fileRef = 390688C02117781900D15C86 /* DTXDebugMenuGenerator.xib */; };
390688DB21177D5000D15C86 /* DTXDebugMenuGenerator.m in Sources */ = {isa = PBXBuildFile; fileRef = 390688DA21177D5000D15C86 /* DTXDebugMenuGenerator.m */; };
390BEDD821874EC500F0C6EB /* DTXRNBridgeDataDataDataProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = 390BEDD721874EC500F0C6EB /* DTXRNBridgeDataDataDataProvider.m */; };
390BEE0021875E0A00F0C6EB /* DTXRNBridgeDataDataInspectorDataProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = 390BEDFF21875E0A00F0C6EB /* DTXRNBridgeDataDataInspectorDataProvider.m */; };
3916501A213292D50016A468 /* DTXDocument.m in Sources */ = {isa = PBXBuildFile; fileRef = 39165019213292D50016A468 /* DTXDocument.m */; };
3922AE131F2BA72C0034B1FE /* DTXSocketConnection.m in Sources */ = {isa = PBXBuildFile; fileRef = 39C72E071F28843D00FD470E /* DTXSocketConnection.m */; };
3925A2631F1142EC008343ED /* DTXStackTraceCellView.m in Sources */ = {isa = PBXBuildFile; fileRef = 3925A2621F1142EC008343ED /* DTXStackTraceCellView.m */; };
Expand Down Expand Up @@ -485,6 +487,10 @@
390688C02117781900D15C86 /* DTXDebugMenuGenerator.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = DTXDebugMenuGenerator.xib; sourceTree = "<group>"; };
390688D921177D5000D15C86 /* DTXDebugMenuGenerator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DTXDebugMenuGenerator.h; sourceTree = "<group>"; };
390688DA21177D5000D15C86 /* DTXDebugMenuGenerator.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DTXDebugMenuGenerator.m; sourceTree = "<group>"; };
390BEDD621874EC500F0C6EB /* DTXRNBridgeDataDataDataProvider.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DTXRNBridgeDataDataDataProvider.h; sourceTree = "<group>"; };
390BEDD721874EC500F0C6EB /* DTXRNBridgeDataDataDataProvider.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DTXRNBridgeDataDataDataProvider.m; sourceTree = "<group>"; };
390BEDFE21875E0A00F0C6EB /* DTXRNBridgeDataDataInspectorDataProvider.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DTXRNBridgeDataDataInspectorDataProvider.h; sourceTree = "<group>"; };
390BEDFF21875E0A00F0C6EB /* DTXRNBridgeDataDataInspectorDataProvider.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DTXRNBridgeDataDataInspectorDataProvider.m; sourceTree = "<group>"; };
39165018213292D50016A468 /* DTXDocument.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DTXDocument.h; sourceTree = "<group>"; };
39165019213292D50016A468 /* DTXDocument.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DTXDocument.m; sourceTree = "<group>"; };
391958DC20A1E1B200BD9C75 /* DTXApplicationDelegate+DocumentationGeneration.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "DTXApplicationDelegate+DocumentationGeneration.h"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1036,6 +1042,8 @@
39EE1F571F3E4411002CB6C1 /* DTXRNBridgeCallsDataProvider.m */,
39EE1F591F3E4729002CB6C1 /* DTXRNBridgeDataDataProvider.h */,
39EE1F5A1F3E4729002CB6C1 /* DTXRNBridgeDataDataProvider.m */,
390BEDD621874EC500F0C6EB /* DTXRNBridgeDataDataDataProvider.h */,
390BEDD721874EC500F0C6EB /* DTXRNBridgeDataDataDataProvider.m */,
);
path = ReactNative;
sourceTree = "<group>";
Expand All @@ -1051,6 +1059,8 @@
39EE1F541F3E439D002CB6C1 /* DTXRNBridgeCallsInspectorDataProvider.m */,
39EE1F5C1F3E489F002CB6C1 /* DTXRNBridgeDataInspectorDataProvider.h */,
39EE1F5D1F3E489F002CB6C1 /* DTXRNBridgeDataInspectorDataProvider.m */,
390BEDFE21875E0A00F0C6EB /* DTXRNBridgeDataDataInspectorDataProvider.h */,
390BEDFF21875E0A00F0C6EB /* DTXRNBridgeDataDataInspectorDataProvider.m */,
);
path = ReactNative;
sourceTree = "<group>";
Expand Down Expand Up @@ -1973,6 +1983,7 @@
39C141621F3C55B3008CF5B9 /* DTXTintedImageView.m in Sources */,
39DE8E7A2091EDFD00ED16B4 /* DTXCookiesViewController.m in Sources */,
39E578EA20DA66AD0034560C /* DTXIntervalSamplePlotController.m in Sources */,
390BEE0021875E0A00F0C6EB /* DTXRNBridgeDataDataInspectorDataProvider.m in Sources */,
39BEE1EE207A47B3003AF93E /* aestab.c in Sources */,
393E76B1212995CE00E5623F /* DTXTouchBarGraphHostingView.m in Sources */,
D8243F941F5401530005CF4B /* DTXFilterField.m in Sources */,
Expand Down Expand Up @@ -2064,6 +2075,7 @@
39D02CE11EF1C98F00BAD7AF /* DTXInspectorContentTableDataSource.m in Sources */,
39782CDD1FC590DB0034D70A /* DTXSample+Additions.m in Sources */,
39F6F6D11F0E407900C7F411 /* DTXRecording+UIExtensions.m in Sources */,
390BEDD821874EC500F0C6EB /* DTXRNBridgeDataDataDataProvider.m in Sources */,
39C6A9151EE3A313000E2EE1 /* DTXMemoryUsagePlotController.m in Sources */,
39782CDC1FC590DB0034D70A /* NSManagedObject+Additions.m in Sources */,
395F01C61EE81DA200695236 /* DTXTimelineIndicatorView.m in Sources */,
Expand Down
@@ -0,0 +1,15 @@
{
"images" : [
{
"idiom" : "mac",
"filename" : "bridge_data-dark.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
},
"properties" : {
"template-rendering-intent" : "template"
}
}
Binary file not shown.
Expand Up @@ -25,15 +25,12 @@ - (void)viewDidLoad

- (NSString *)displayName
{
return NSLocalizedString(@"Samples", @"");
return self.detailDataProvider.displayName;
}

- (NSImage *)smallDisplayIcon
{
NSImage* image = [NSImage imageNamed:@"samples"];
image.size = NSMakeSize(16, 16);

return image;
return self.detailDataProvider.displayIcon;
}

- (void)setDetailDataProvider:(DTXDetailDataProvider *)detailDataProvider
Expand Down
Expand Up @@ -72,12 +72,15 @@ - (instancetype)initWithDocument:(DTXRecordingDocument*)document plotController:

- (NSString *)displayName
{
return self.plotController.displayName;
return NSLocalizedString(@"Samples", @"");;
}

- (NSImage *)displayIcon
{
return self.plotController.displayIcon;
NSImage* image = [NSImage imageNamed:@"samples"];
image.size = NSMakeSize(16, 16);

return image;
}

- (void)setManagedOutlineView:(NSOutlineView *)outlineView
Expand All @@ -102,6 +105,8 @@ - (void)setManagedOutlineView:(NSOutlineView *)outlineView

if(_managedOutlineView == nil)
{
[_rootGroupProxy unloadData];

return;
}

Expand Down
Expand Up @@ -29,6 +29,7 @@
- (instancetype)initWithOutlineView:(NSOutlineView*)outlineView isRoot:(BOOL)root managedObjectContext:(NSManagedObjectContext*)managedObjectContext;
- (BOOL)isDataLoaded;
- (void)reloadData;
- (void)unloadData;

- (void)handleSampleInserts:(NSArray*)inserts updates:(NSArray*)updates shouldReloadProxy:(BOOL*)reloadProxy;

Expand Down
Expand Up @@ -63,6 +63,13 @@ - (BOOL)isDataLoaded
return _isDataLoaded;
}

- (void)unloadData
{
_isDataLoaded = NO;

_fetchedResultsController = nil;
}

- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller
{
_updates = [NSMutableArray new];
Expand Down
Expand Up @@ -22,16 +22,16 @@ + (Class)inspectorDataProviderClass

- (void)setManagedOutlineView:(NSOutlineView *)managedOutlineView
{
[super setManagedOutlineView:managedOutlineView];
[self _enableOutlineRespectIfNeededForOutlineView:managedOutlineView];

[self _enableOutlineRespectIfNeeded];
[super setManagedOutlineView:managedOutlineView];
}

- (void)_enableOutlineRespectIfNeeded
- (void)_enableOutlineRespectIfNeededForOutlineView:(NSOutlineView*)outlineView
{
if([self.managedOutlineView respondsToSelector:@selector(setRespectsOutlineCellFraming:)])
if([outlineView respondsToSelector:@selector(setRespectsOutlineCellFraming:)])
{
[(DTXDetailOutlineView*)self.managedOutlineView setRespectsOutlineCellFraming:self.document.documentState >= DTXRecordingDocumentStateLiveRecordingFinished];
[(DTXDetailOutlineView*)outlineView setRespectsOutlineCellFraming:self.document.documentState >= DTXRecordingDocumentStateLiveRecordingFinished];
}
}

Expand Down
Expand Up @@ -24,19 +24,19 @@ + (Class)inspectorDataProviderClass
- (NSArray<DTXColumnInformation *> *)columns
{
DTXColumnInformation* reads = [DTXColumnInformation new];
reads.title = NSLocalizedString(@"N->JS (Total)", @"");
reads.title = NSLocalizedString(@"NJS (Total)", @"");
reads.minWidth = 80;

DTXColumnInformation* writes = [DTXColumnInformation new];
writes.title = NSLocalizedString(@"JS->N (Total)", @"");
writes.title = NSLocalizedString(@"JSN (Total)", @"");
writes.minWidth = 80;

DTXColumnInformation* readsDelta = [DTXColumnInformation new];
readsDelta.title = NSLocalizedString(@"N->JS (Delta)", @"");
readsDelta.title = NSLocalizedString(@"NJS (Delta)", @"");
readsDelta.minWidth = 80;

DTXColumnInformation* writesDelta = [DTXColumnInformation new];
writesDelta.title = NSLocalizedString(@"JS->N (Delta)", @"");
writesDelta.title = NSLocalizedString(@"JSN (Delta)", @"");
writesDelta.minWidth = 80;

return @[readsDelta, writesDelta, reads, writes];
Expand Down
@@ -0,0 +1,13 @@
//
// DTXRNBridgeDataDataDataProvider.h
// DetoxInstruments
//
// Created by Leo Natan (Wix) on 10/29/18.
// Copyright © 2018 Wix. All rights reserved.
//

#import "DTXDetailDataProvider.h"

@interface DTXRNBridgeDataDataDataProvider : DTXDetailDataProvider

@end
@@ -0,0 +1,95 @@
//
// DTXRNBridgeDataDataDataProvider.m
// DetoxInstruments
//
// Created by Leo Natan (Wix) on 10/29/18.
// Copyright © 2018 Wix. All rights reserved.
//

#import "DTXRNBridgeDataDataDataProvider.h"
#import "DTXRNBridgeDataDataInspectorDataProvider.h"

@implementation DTXRNBridgeDataDataDataProvider

+ (Class)inspectorDataProviderClass
{
return [DTXRNBridgeDataDataInspectorDataProvider class];
}

- (NSString *)displayName
{
return NSLocalizedString(@"Bridge Data", @"");
}

- (NSImage *)displayIcon
{
NSImage* image = [NSImage imageNamed:@"bridge_data"];
image.size = NSMakeSize(16, 16);

return image;
}

- (NSArray<NSNumber *> *)sampleTypes
{
return @[@(DTXSampleTypeReactNativeBridgeDataType)];
}

- (NSArray<DTXColumnInformation *> *)columns
{
DTXColumnInformation* type = [DTXColumnInformation new];
type.title = NSLocalizedString(@"Type", @"");
type.minWidth = 45;

DTXColumnInformation* function = [DTXColumnInformation new];
function.title = NSLocalizedString(@"Function", @"");
function.minWidth = 200;

DTXColumnInformation* arguments = [DTXColumnInformation new];
arguments.title = NSLocalizedString(@"Data", @"");
arguments.automaticallyGrowsWithTable = YES;

return @[type, function, arguments];
}

- (NSString*)_dataFromSample:(DTXReactNativeDataSample*)sample
{
NSMutableString* str = [NSMutableString new];

if(sample.data.arguments.count > 0)
{
[str appendFormat:@"%@: ", NSLocalizedString(@"Arguments", @"")];
[str appendString:[sample.data.arguments componentsJoinedByString:@", "]];
}

if(sample.data.returnValue != nil)
{
if(str.length != 0)
{
[str appendString:@" "];
}

[str appendFormat:@"%@: ", NSLocalizedString(@"Return Value", @"")];
[str appendString:sample.data.returnValue];
}

return str;
}

- (NSString*)formattedStringValueForItem:(id)item column:(NSUInteger)column;
{
DTXReactNativeDataSample* sample = (DTXReactNativeDataSample*)item;

switch(column)
{
case 0:
return sample.isFromNative ? @"N → JS" : @"JS → N";
case 1:
return sample.function;
case 2:
return [self _dataFromSample:sample];
default:
return @"";
}
}

@end
Expand Up @@ -24,19 +24,19 @@ + (Class)inspectorDataProviderClass
- (NSArray<DTXColumnInformation *> *)columns
{
DTXColumnInformation* reads = [DTXColumnInformation new];
reads.title = NSLocalizedString(@"N->JS (Total)", @"");
reads.title = NSLocalizedString(@"NJS (Total)", @"");
reads.minWidth = 80;

DTXColumnInformation* writes = [DTXColumnInformation new];
writes.title = NSLocalizedString(@"JS->N (Total)", @"");
writes.title = NSLocalizedString(@"JSN (Total)", @"");
writes.minWidth = 80;

DTXColumnInformation* readsDelta = [DTXColumnInformation new];
readsDelta.title = NSLocalizedString(@"N->JS (Delta)", @"");
readsDelta.title = NSLocalizedString(@"NJS (Delta)", @"");
readsDelta.minWidth = 80;

DTXColumnInformation* writesDelta = [DTXColumnInformation new];
writesDelta.title = NSLocalizedString(@"JS->N (Delta)", @"");
writesDelta.title = NSLocalizedString(@"JSN (Delta)", @"");
writesDelta.minWidth = 80;

return @[readsDelta, writesDelta, reads, writes];
Expand Down

0 comments on commit 99b7a94

Please sign in to comment.