Skip to content

Commit

Permalink
Change the UI to use sample counts, and add explicit support for trac…
Browse files Browse the repository at this point in the history
…ed profiles (PR #2558)
  • Loading branch information
gregtatum committed Jun 30, 2020
2 parents 55976bf + fe4e761 commit c4f68f4
Show file tree
Hide file tree
Showing 46 changed files with 2,959 additions and 1,635 deletions.
18 changes: 9 additions & 9 deletions res/css/style.css
Expand Up @@ -129,8 +129,8 @@ body,
background: #e5e5e5;
content: '';
}
.treeViewHeaderColumn.totalTime,
.treeViewHeaderColumn.selfTime {
.treeViewHeaderColumn.total,
.treeViewHeaderColumn.self {
text-align: right;
}
.treeViewRowColumn.treeViewFixedColumn {
Expand All @@ -139,20 +139,20 @@ body,
border-right: 1px solid var(--grey-30);
text-overflow: ellipsis;
}
.treeViewFixedColumn.totalTime {
.treeViewFixedColumn.total {
left: 0;
width: 70px;
}
.treeViewFixedColumn.totalTimePercent {
.treeViewFixedColumn.totalPercent {
left: 70px;
width: 50px;
border-right: none;
}
.treeViewFixedColumn.selfTime {
.treeViewFixedColumn.self {
left: 120px;
width: 70px;
}
.treeViewHeaderColumn.totalTime {
.treeViewHeaderColumn.total {
width: 120px;
}
.treeViewFixedColumn.icon {
Expand All @@ -162,9 +162,9 @@ body,
flex-flow: column nowrap;
align-items: center;
}
.treeViewRowColumn.totalTime,
.treeViewRowColumn.totalTimePercent,
.treeViewRowColumn.selfTime,
.treeViewRowColumn.total,
.treeViewRowColumn.totalPercent,
.treeViewRowColumn.self,
.treeViewRowColumn.timestamp {
padding-right: 5px;
text-align: right;
Expand Down
2 changes: 1 addition & 1 deletion src/app-logic/constants.js
Expand Up @@ -8,7 +8,7 @@
export const GECKO_PROFILE_VERSION = 19;

// The current version of the "processed" profile format.
export const PROCESSED_PROFILE_VERSION = 28;
export const PROCESSED_PROFILE_VERSION = 29;

// The following are the margin sizes for the left and right of the timeline. Independent
// components need to share these values.
Expand Down
2 changes: 1 addition & 1 deletion src/components/app/DetailsContainer.css
Expand Up @@ -5,7 +5,7 @@
}

.DetailsContainer .layout-pane:not(.layout-pane-primary) {
max-width: 300px;
max-width: 600px;
}

/* overriding defaults from splitter-layout */
Expand Down
132 changes: 98 additions & 34 deletions src/components/calltree/CallTree.js
Expand Up @@ -4,6 +4,8 @@
// @flow

import React, { PureComponent } from 'react';
import memoize from 'memoize-immutable';
import { oneLine } from 'common-tags';
import explicitConnect from '../../utils/connect';
import TreeView from '../shared/TreeView';
import CallTreeEmptyReasons from './CallTreeEmptyReasons';
Expand Down Expand Up @@ -32,11 +34,11 @@ import { assertExhaustiveCheck } from '../../utils/flow';
import type {
State,
ImplementationFilter,
CallTreeSummaryStrategy,
ThreadIndex,
CallNodeInfo,
IndexIntoCallNodeTable,
CallNodeDisplayData,
WeightType,
} from 'firefox-profiler/types';
import type { CallTree } from '../../profile-logic/call-tree';

Expand All @@ -57,7 +59,7 @@ type StateProps = {|
+invertCallstack: boolean,
+implementationFilter: ImplementationFilter,
+callNodeMaxDepth: number,
+callTreeSummaryStrategy: CallTreeSummaryStrategy,
+weightType: WeightType,
|};

type DispatchProps = {|
Expand All @@ -70,23 +72,102 @@ type DispatchProps = {|
type Props = ConnectedProps<{||}, StateProps, DispatchProps>;

class CallTreeComponent extends PureComponent<Props> {
_fixedColumnsTiming: Column[] = [
{ propName: 'totalTimePercent', title: '' },
{ propName: 'totalTime', title: 'Running Time (ms)' },
{ propName: 'selfTime', title: 'Self (ms)' },
{ propName: 'icon', title: '', component: Icon },
];
_fixedColumnsAllocations: Column[] = [
{ propName: 'totalTimePercent', title: '' },
{ propName: 'totalTime', title: 'Total Size (bytes)' },
{ propName: 'selfTime', title: 'Self (bytes)' },
{ propName: 'icon', title: '', component: Icon },
];
_mainColumn: Column = { propName: 'name', title: '' };
_appendageColumn: Column = { propName: 'lib', title: '' };
_treeView: TreeView<CallNodeDisplayData> | null = null;
_takeTreeViewRef = treeView => (this._treeView = treeView);

/**
* Call Trees can have different types of "weights" for the data. Choose the
* appropriate labels for the call tree based on this weight.
*/
_weightTypeToColumns = memoize(
(weightType: WeightType): Column[] => {
switch (weightType) {
case 'tracing-ms':
return [
{ propName: 'totalPercent', title: '' },
{
propName: 'total',
title: 'Running Time (ms)',
tooltip: oneLine`
The "total" running time includes a summary of all the time where this
function was observed to be on the stack. This includes the time where
the function was actually running, and the time spent in the callers from
this function.
`,
},
{
propName: 'self',
title: 'Self (ms)',
tooltip: oneLine`
The "self" time only includes the time where the function was
the leaf-most one on the stack. If this function called into other functions,
then the "other" functions' time is not included. The "self" time is useful
for understanding where time was actually spent in a program.
`,
},
{ propName: 'icon', title: '', component: Icon },
];
case 'samples':
return [
{ propName: 'totalPercent', title: '' },
{
propName: 'total',
title: 'Total (samples)',
tooltip: oneLine`
The "total" sample count includes a summary of every sample where this
function was observed to be on the stack. This includes the time where the
function was actually running, and the time spent in the callers from this
function.
`,
},
{
propName: 'self',
title: 'Self',
tooltip: oneLine`
The "self" sample count only includes the samples where the function was
the leaf-most one on the stack. If this function called into other functions,
then the "other" functions' counts are not included. The "self" count is useful
for understanding where time was actually spent in a program.
`,
},
{ propName: 'icon', title: '', component: Icon },
];
case 'bytes':
return [
{ propName: 'totalPercent', title: '' },
{
propName: 'total',
title: 'Total Size (bytes)',
tooltip: oneLine`
The "total size" includes a summary of all of the bytes allocated or
deallocated while this function was observed to be on the stack. This
includes both the bytes where the function was actually running, and the
bytes of the callers from this function.
`,
},
{
propName: 'self',
title: 'Self (bytes)',
tooltip: oneLine`
The "self" bytes includes the bytes allocated or deallocated while this
function was the leaf-most one on the stack. If this function called into
other functions, then the "other" functions' bytes are not included.
The "self" bytes are useful for understanding where memory was actually
allocated or deallocated in the program.
`,
},
{ propName: 'icon', title: '', component: Icon },
];
default:
throw assertExhaustiveCheck(weightType, 'Unhandled WeightType.');
}
},
// Use a Map cache, as the function only takes one argument, which is a simple string.
{ cache: new Map() }
);

componentDidMount() {
this.focus();
if (this.props.selectedCallNodeIndex === null) {
Expand Down Expand Up @@ -192,30 +273,15 @@ class CallTreeComponent extends PureComponent<Props> {
searchStringsRegExp,
disableOverscan,
callNodeMaxDepth,
callTreeSummaryStrategy,
weightType,
} = this.props;
if (tree.getRoots().length === 0) {
return <CallTreeEmptyReasons />;
}
let fixedColumns;
switch (callTreeSummaryStrategy) {
case 'timing':
fixedColumns = this._fixedColumnsTiming;
break;
case 'native-retained-allocations':
case 'native-allocations':
case 'native-deallocations-memory':
case 'native-deallocations-sites':
case 'js-allocations':
fixedColumns = this._fixedColumnsAllocations;
break;
default:
throw assertExhaustiveCheck(callTreeSummaryStrategy);
}
return (
<TreeView
tree={tree}
fixedColumns={fixedColumns}
fixedColumns={this._weightTypeToColumns(weightType)}
mainColumn={this._mainColumn}
appendageColumn={this._appendageColumn}
onSelectionChange={this._onSelectedCallNodeChange}
Expand Down Expand Up @@ -257,9 +323,7 @@ export default explicitConnect<{||}, StateProps, DispatchProps>({
invertCallstack: getInvertCallstack(state),
implementationFilter: getImplementationFilter(state),
callNodeMaxDepth: selectedThreadSelectors.getCallNodeMaxDepth(state),
callTreeSummaryStrategy: selectedThreadSelectors.getCallTreeSummaryStrategy(
state
),
weightType: selectedThreadSelectors.getWeightTypeForCallTree(state),
}),
mapDispatchToProps: {
changeSelectedCallNode,
Expand Down
34 changes: 31 additions & 3 deletions src/components/flame-graph/Canvas.js
Expand Up @@ -12,6 +12,10 @@ import {
import ChartCanvas from '../shared/chart/Canvas';
import TextMeasurement from '../../utils/text-measurement';
import { mapCategoryColorNameToStackChartStyles } from '../../utils/colors';
import {
formatCallNodeNumberWithUnit,
formatPercent,
} from '../../utils/format-numbers';
import { TooltipCallNode } from '../tooltip/CallNode';
import { getTimingsForCallNodeIndex } from '../../profile-logic/profile-data';
import MixedTupleMap from 'mixedtuplemap';
Expand All @@ -25,6 +29,9 @@ import type {
CallNodeInfo,
IndexIntoCallNodeTable,
CallTreeSummaryStrategy,
WeightType,
SamplesLikeTable,
TracedTiming,
} from 'firefox-profiler/types';

import type {
Expand All @@ -38,6 +45,7 @@ import type { Viewport } from '../shared/chart/Viewport';

export type OwnProps = {|
+thread: Thread,
+weightType: WeightType,
+pages: PageList | null,
+unfilteredThread: Thread,
+sampleIndexOffset: number,
Expand All @@ -55,6 +63,9 @@ export type OwnProps = {|
+interval: Milliseconds,
+isInverted: boolean,
+callTreeSummaryStrategy: CallTreeSummaryStrategy,
+samples: SamplesLikeTable,
+unfilteredSamples: SamplesLikeTable,
+tracedTiming: TracedTiming | null,
|};

type Props = {|
Expand Down Expand Up @@ -266,6 +277,10 @@ class FlameGraphCanvas extends React.PureComponent<Props> {
isInverted,
callTreeSummaryStrategy,
pages,
weightType,
samples,
unfilteredSamples,
tracedTiming,
} = this.props;

if (!shouldDisplayTooltips()) {
Expand All @@ -274,10 +289,20 @@ class FlameGraphCanvas extends React.PureComponent<Props> {

const stackTiming = flameGraphTiming[depth];
const callNodeIndex = stackTiming.callNode[flameGraphTimingIndex];
const duration =
const ratio =
stackTiming.end[flameGraphTimingIndex] -
stackTiming.start[flameGraphTimingIndex];

let percentage = formatPercent(ratio);
if (tracedTiming) {
const time = formatCallNodeNumberWithUnit(
'tracing-ms',
false,
tracedTiming.running[callNodeIndex]
);
percentage = `${time} (${percentage})`;
}

const shouldComputeTimings =
// This is currently too slow for JS Tracer threads.
!thread.isJsTracer &&
Expand All @@ -291,12 +316,13 @@ class FlameGraphCanvas extends React.PureComponent<Props> {
// doesn't over-render.
<TooltipCallNode
thread={thread}
weightType={weightType}
pages={pages}
interval={interval}
callNodeIndex={callNodeIndex}
callNodeInfo={callNodeInfo}
categories={categories}
durationText={`${(100 * duration).toFixed(2)}%`}
durationText={percentage}
callTree={callTree}
callTreeSummaryStrategy={callTreeSummaryStrategy}
timings={
Expand All @@ -309,7 +335,9 @@ class FlameGraphCanvas extends React.PureComponent<Props> {
thread,
unfilteredThread,
sampleIndexOffset,
categories
categories,
samples,
unfilteredSamples
)
: undefined
}
Expand Down

0 comments on commit c4f68f4

Please sign in to comment.