Skip to content

Commit

Permalink
Add empty reasons for Flame Graph, Stack Chart, and Marker Table (Mer…
Browse files Browse the repository at this point in the history
  • Loading branch information
gregtatum authored Jul 18, 2019
2 parents fe7443a + c969337 commit f68bcc8
Show file tree
Hide file tree
Showing 14 changed files with 518 additions and 95 deletions.
61 changes: 61 additions & 0 deletions src/components/flame-graph/FlameGraphEmptyReasons.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// @flow

import React, { PureComponent } from 'react';

import EmptyReasons from '../shared/EmptyReasons';
import { selectedThreadSelectors } from '../../selectors/per-thread';
import { oneLine } from 'common-tags';
import explicitConnect, { type ConnectedProps } from '../../utils/connect';

import type { Thread } from '../../types/profile';
import type { State } from '../../types/store';

type StateProps = {|
threadName: string,
rangeFilteredThread: Thread,
thread: Thread,
|};

type Props = ConnectedProps<{||}, StateProps, {||}>;

/**
* This component attempts to tell why exactly a flame graph is empty with no samples
* and display a friendly message to the end user.
*/
class FlameGraphEmptyReasons extends PureComponent<Props> {
render() {
const { thread, rangeFilteredThread, threadName } = this.props;
let reason;

if (thread.samples.length === 0) {
reason = 'This thread has no samples.';
} else if (rangeFilteredThread.samples.length === 0) {
reason = 'Broaden the selected range to view samples.';
} else {
reason = oneLine`
Try broadening the selected range, removing search terms, or call tree transforms
to view samples.
`;
}

return (
<EmptyReasons
threadName={threadName}
reason={reason}
viewName="flame graph"
/>
);
}
}

export default explicitConnect<{||}, StateProps, {||}>({
mapStateToProps: (state: State) => ({
threadName: selectedThreadSelectors.getFriendlyThreadName(state),
thread: selectedThreadSelectors.getThread(state),
rangeFilteredThread: selectedThreadSelectors.getRangeFilteredThread(state),
}),
component: FlameGraphEmptyReasons,
});
14 changes: 13 additions & 1 deletion src/components/flame-graph/MaybeFlameGraph.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,17 @@
import * as React from 'react';
import explicitConnect from '../../utils/connect';
import { getInvertCallstack } from '../../selectors/url-state';
import { selectedThreadSelectors } from '../../selectors/per-thread';
import { changeInvertCallstack } from '../../actions/profile-view';
import FlameGraphEmptyReasons from './FlameGraphEmptyReasons';
import FlameGraph from './FlameGraph';

import type { ConnectedProps } from '../../utils/connect';

require('./MaybeFlameGraph.css');

type StateProps = {|
+maxStackDepth: number,
+invertCallstack: boolean,
|};
type DispatchProps = {|
Expand All @@ -27,7 +30,13 @@ class MaybeFlameGraph extends React.PureComponent<Props> {
};

render() {
if (this.props.invertCallstack) {
const { maxStackDepth, invertCallstack } = this.props;

if (maxStackDepth === 0) {
return <FlameGraphEmptyReasons />;
}

if (invertCallstack) {
return (
<div className="flameGraphDisabledMessage">
<h3>The Flame Graph is not available for inverted call stacks</h3>
Expand All @@ -51,6 +60,9 @@ export default explicitConnect<{||}, StateProps, DispatchProps>({
mapStateToProps: state => {
return {
invertCallstack: getInvertCallstack(state),
maxStackDepth: selectedThreadSelectors.getCallNodeMaxDepthForFlameGraph(
state
),
};
},
mapDispatchToProps: {
Expand Down
2 changes: 1 addition & 1 deletion src/components/marker-chart/MarkerChartEmptyReasons.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ class MarkerChartEmptyReasons extends PureComponent<Props> {
export default explicitConnect<{||}, StateProps, {||}>({
mapStateToProps: (state: State) => ({
threadName: selectedThreadSelectors.getFriendlyThreadName(state),
isMarkerChartEmptyInFullRange: selectedThreadSelectors.getIsMarkerChartEmptyInFullRange(
isMarkerChartEmptyInFullRange: selectedThreadSelectors.getAreMarkerPanelsEmptyInFullRange(
state
),
}),
Expand Down
47 changes: 47 additions & 0 deletions src/components/marker-table/MarkerTableEmptyReasons.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// @flow

import React, { PureComponent } from 'react';

import EmptyReasons from '../shared/EmptyReasons';
import { selectedThreadSelectors } from '../../selectors/per-thread';

import explicitConnect, { type ConnectedProps } from '../../utils/connect';

import type { State } from '../../types/store';

type StateProps = {|
+threadName: string,
+isMarkerTableEmptyInFullRange: boolean,
|};

type Props = ConnectedProps<{||}, StateProps, {||}>;
class MarkerTableEmptyReasons extends PureComponent<Props> {
render() {
const { isMarkerTableEmptyInFullRange, threadName } = this.props;

return (
<EmptyReasons
threadName={threadName}
reason={
isMarkerTableEmptyInFullRange
? 'This thread contains no markers for this table.'
: 'All markers were filtered out by the current selection or search term.'
}
viewName="marker table"
/>
);
}
}

export default explicitConnect<{||}, StateProps, {||}>({
mapStateToProps: (state: State) => ({
threadName: selectedThreadSelectors.getFriendlyThreadName(state),
isMarkerTableEmptyInFullRange: selectedThreadSelectors.getAreMarkerPanelsEmptyInFullRange(
state
),
}),
component: MarkerTableEmptyReasons,
});
37 changes: 21 additions & 16 deletions src/components/marker-table/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import memoize from 'memoize-immutable';

import explicitConnect from '../../utils/connect';
import TreeView from '../shared/TreeView';
import MarkerTableEmptyReasons from './MarkerTableEmptyReasons';
import {
getZeroAt,
getScrollToSelectionGeneration,
Expand Down Expand Up @@ -207,22 +208,26 @@ class MarkerTable extends PureComponent<Props> {
aria-labelledby="marker-table-tab-button"
>
<MarkerSettings />
<TreeView
maxNodeDepth={0}
tree={tree}
fixedColumns={this._fixedColumns}
mainColumn={this._mainColumn}
onSelectionChange={this._onSelectionChange}
onRightClickSelection={this._onRightClickSelection}
onExpandedNodesChange={this._onExpandedNodeIdsChange}
selectedNodeId={selectedMarker}
rightClickedNodeId={rightClickedMarker}
expandedNodeIds={this._expandedNodeIds}
ref={this._takeTreeViewRef}
contextMenuId="MarkerContextMenu"
rowHeight={16}
indentWidth={10}
/>
{markerIndexes.length === 0 ? (
<MarkerTableEmptyReasons />
) : (
<TreeView
maxNodeDepth={0}
tree={tree}
fixedColumns={this._fixedColumns}
mainColumn={this._mainColumn}
onSelectionChange={this._onSelectionChange}
onRightClickSelection={this._onRightClickSelection}
onExpandedNodesChange={this._onExpandedNodeIdsChange}
selectedNodeId={selectedMarker}
rightClickedNodeId={rightClickedMarker}
expandedNodeIds={this._expandedNodeIds}
ref={this._takeTreeViewRef}
contextMenuId="MarkerContextMenu"
rowHeight={16}
indentWidth={10}
/>
)}
</div>
);
}
Expand Down
61 changes: 61 additions & 0 deletions src/components/stack-chart/StackChartEmptyReasons.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// @flow

import React, { PureComponent } from 'react';

import EmptyReasons from '../shared/EmptyReasons';
import { selectedThreadSelectors } from '../../selectors/per-thread';
import { oneLine } from 'common-tags';
import explicitConnect, { type ConnectedProps } from '../../utils/connect';

import type { Thread } from '../../types/profile';
import type { State } from '../../types/store';

type StateProps = {|
threadName: string,
rangeFilteredThread: Thread,
thread: Thread,
|};

type Props = ConnectedProps<{||}, StateProps, {||}>;

/**
* This component attempts to tell why exactly a stack chart is empty with no samples
* and display a friendly message to the end user.
*/
class StackChartEmptyReasons extends PureComponent<Props> {
render() {
const { thread, rangeFilteredThread, threadName } = this.props;
let reason;

if (thread.samples.length === 0) {
reason = 'This thread has no samples.';
} else if (rangeFilteredThread.samples.length === 0) {
reason = 'Broaden the selected range to view samples.';
} else {
reason = oneLine`
Try broadening the selected range, removing search terms, or call tree transforms
to view samples.
`;
}

return (
<EmptyReasons
threadName={threadName}
reason={reason}
viewName="stack chart"
/>
);
}
}

export default explicitConnect<{||}, StateProps, {||}>({
mapStateToProps: (state: State) => ({
threadName: selectedThreadSelectors.getFriendlyThreadName(state),
thread: selectedThreadSelectors.getThread(state),
rangeFilteredThread: selectedThreadSelectors.getRangeFilteredThread(state),
}),
component: StackChartEmptyReasons,
});
81 changes: 43 additions & 38 deletions src/components/stack-chart/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
} from '../../selectors/profile';
import { selectedThreadSelectors } from '../../selectors/per-thread';
import { getSelectedThreadIndex } from '../../selectors/url-state';
import StackChartEmptyReasons from './StackChartEmptyReasons';
import ContextMenuTrigger from '../shared/ContextMenuTrigger';
import StackSettings from '../shared/StackSettings';
import TransformNavigator from '../shared/TransformNavigator';
Expand Down Expand Up @@ -148,44 +149,48 @@ class StackChartGraph extends React.PureComponent<Props> {
>
<StackSettings />
<TransformNavigator />
<ContextMenuTrigger
id="CallNodeContextMenu"
attributes={{
className: 'treeViewContextMenu',
}}
>
<div className="stackChartContent">
<StackChartCanvas
viewportProps={{
previewSelection,
timeRange,
maxViewportHeight,
viewportNeedsUpdate,
marginLeft: TIMELINE_MARGIN_LEFT,
marginRight: TIMELINE_MARGIN_RIGHT,
maximumZoom: this.getMaximumZoom(),
containerRef: this._takeViewportRef,
}}
chartProps={{
interval,
thread,
stackTimingByDepth,
// $FlowFixMe Error introduced by upgrading to v0.96.0. See issue #1936.
updatePreviewSelection,
rangeStart: timeRange.start,
rangeEnd: timeRange.end,
stackFrameHeight: STACK_FRAME_HEIGHT,
callNodeInfo,
categories,
selectedCallNodeIndex,
onSelectionChange: this._onSelectedCallNodeChange,
onRightClick: this._onRightClickedCallNodeChange,
shouldDisplayTooltips: this._shouldDisplayTooltips,
scrollToSelectionGeneration,
}}
/>
</div>
</ContextMenuTrigger>
{maxStackDepth === 0 ? (
<StackChartEmptyReasons />
) : (
<ContextMenuTrigger
id="CallNodeContextMenu"
attributes={{
className: 'treeViewContextMenu',
}}
>
<div className="stackChartContent">
<StackChartCanvas
viewportProps={{
previewSelection,
timeRange,
maxViewportHeight,
viewportNeedsUpdate,
marginLeft: TIMELINE_MARGIN_LEFT,
marginRight: TIMELINE_MARGIN_RIGHT,
maximumZoom: this.getMaximumZoom(),
containerRef: this._takeViewportRef,
}}
chartProps={{
interval,
thread,
stackTimingByDepth,
// $FlowFixMe Error introduced by upgrading to v0.96.0. See issue #1936.
updatePreviewSelection,
rangeStart: timeRange.start,
rangeEnd: timeRange.end,
stackFrameHeight: STACK_FRAME_HEIGHT,
callNodeInfo,
categories,
selectedCallNodeIndex,
onSelectionChange: this._onSelectedCallNodeChange,
onRightClick: this._onRightClickedCallNodeChange,
shouldDisplayTooltips: this._shouldDisplayTooltips,
scrollToSelectionGeneration,
}}
/>
</div>
</ContextMenuTrigger>
)}
</div>
);
}
Expand Down
4 changes: 2 additions & 2 deletions src/selectors/per-thread/markers.js
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,7 @@ export function getMarkerSelectorsPerThread(threadSelectors: *) {
/**
* Returns whether there's any marker besides network markers.
*/
const getIsMarkerChartEmptyInFullRange: Selector<boolean> = createSelector(
const getAreMarkerPanelsEmptyInFullRange: Selector<boolean> = createSelector(
getFullMarkerList,
markers => markers.every(marker => MarkerData.isNetworkMarker(marker))
);
Expand Down Expand Up @@ -421,7 +421,7 @@ export function getMarkerSelectorsPerThread(threadSelectors: *) {
getFullMarkerListIndexes,
getNetworkMarkerIndexes,
getSearchFilteredNetworkMarkerIndexes,
getIsMarkerChartEmptyInFullRange,
getAreMarkerPanelsEmptyInFullRange,
getMarkerChartMarkerIndexes,
getSearchFilteredMarkerChartMarkerIndexes,
getMarkerChartTiming,
Expand Down
Loading

0 comments on commit f68bcc8

Please sign in to comment.