Skip to content

Commit e413415

Browse files
authored
Source view for marker stacks (#5633)
2 parents 2690409 + 10bd60e commit e413415

File tree

22 files changed

+387
-76
lines changed

22 files changed

+387
-76
lines changed

src/actions/profile-view.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1917,7 +1917,7 @@ export function changeTableViewOptions(
19171917

19181918
export function updateBottomBoxContentsAndMaybeOpen(
19191919
currentTab: TabSlug,
1920-
{ libIndex, sourceIndex, nativeSymbols }: BottomBoxInfo
1920+
{ libIndex, sourceIndex, nativeSymbols, lineNumber }: BottomBoxInfo
19211921
): Action {
19221922
// TODO: If the set has more than one element, pick the native symbol with
19231923
// the highest total sample count
@@ -1932,6 +1932,7 @@ export function updateBottomBoxContentsAndMaybeOpen(
19321932
currentTab,
19331933
shouldOpenBottomBox: sourceIndex !== null || nativeSymbol !== null,
19341934
shouldOpenAssemblyView: sourceIndex === null && nativeSymbol !== null,
1935+
lineNumber,
19351936
};
19361937
}
19371938

src/components/app/AssemblyViewToggleButton.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import React from 'react';
66
import classNames from 'classnames';
77

88
import { getAssemblyViewIsOpen } from 'firefox-profiler/selectors/url-state';
9+
import { getIsAssemblyViewAvailable } from 'firefox-profiler/selectors/code';
910
import {
1011
openAssemblyView,
1112
closeAssemblyView,
@@ -18,6 +19,7 @@ import { Localized } from '@fluent/react';
1819

1920
type StateProps = {
2021
readonly assemblyViewIsOpen: boolean;
22+
readonly isAssemblyViewAvailable: boolean;
2123
};
2224

2325
type DispatchProps = {
@@ -37,7 +39,7 @@ class AssemblyViewToggleButtonImpl extends React.PureComponent<Props> {
3739
};
3840

3941
override render() {
40-
const { assemblyViewIsOpen } = this.props;
42+
const { assemblyViewIsOpen, isAssemblyViewAvailable } = this.props;
4143

4244
return assemblyViewIsOpen ? (
4345
<Localized id="AssemblyView--hide-button" attrs={{ title: true }}>
@@ -51,6 +53,7 @@ class AssemblyViewToggleButtonImpl extends React.PureComponent<Props> {
5153
title="Hide the assembly view"
5254
type="button"
5355
onClick={this._onClick}
56+
disabled={!isAssemblyViewAvailable}
5457
/>
5558
</Localized>
5659
) : (
@@ -64,6 +67,7 @@ class AssemblyViewToggleButtonImpl extends React.PureComponent<Props> {
6467
title="Show the assembly view"
6568
type="button"
6669
onClick={this._onClick}
70+
disabled={!isAssemblyViewAvailable}
6771
/>
6872
</Localized>
6973
);
@@ -77,6 +81,7 @@ export const AssemblyViewToggleButton = explicitConnect<
7781
>({
7882
mapStateToProps: (state) => ({
7983
assemblyViewIsOpen: getAssemblyViewIsOpen(state),
84+
isAssemblyViewAvailable: getIsAssemblyViewAvailable(state),
8085
}),
8186
mapDispatchToProps: {
8287
openAssemblyView,

src/components/app/BottomBox.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { CodeLoadingOverlay } from './CodeLoadingOverlay';
1414
import { CodeErrorOverlay } from './CodeErrorOverlay';
1515
import {
1616
getSourceViewScrollGeneration,
17+
getSourceViewLineNumber,
1718
getAssemblyViewIsOpen,
1819
getAssemblyViewNativeSymbol,
1920
getAssemblyViewScrollGeneration,
@@ -54,6 +55,7 @@ type StateProps = {
5455
readonly sourceViewFile: string | null;
5556
readonly sourceViewCode: SourceCodeStatus | void;
5657
readonly sourceViewScrollGeneration: number;
58+
readonly sourceViewLineNumber?: number;
5759
readonly globalLineTimings: LineTimings;
5860
readonly selectedCallNodeLineTimings: LineTimings;
5961
readonly assemblyViewIsOpen: boolean;
@@ -160,6 +162,7 @@ class BottomBoxImpl extends React.PureComponent<Props> {
160162
globalLineTimings,
161163
disableOverscan,
162164
sourceViewScrollGeneration,
165+
sourceViewLineNumber,
163166
selectedCallNodeLineTimings,
164167
assemblyViewIsOpen,
165168
assemblyViewScrollGeneration,
@@ -231,7 +234,9 @@ class BottomBoxImpl extends React.PureComponent<Props> {
231234
sourceCode={sourceCode}
232235
filePath={path}
233236
scrollToHotSpotGeneration={sourceViewScrollGeneration}
237+
scrollToLineNumber={sourceViewLineNumber}
234238
hotSpotTimings={selectedCallNodeLineTimings}
239+
highlightedLine={sourceViewLineNumber}
235240
ref={this._sourceView}
236241
/>
237242
) : null}
@@ -300,6 +305,7 @@ export const BottomBox = explicitConnect<{}, StateProps, DispatchProps>({
300305
selectedCallNodeLineTimings:
301306
selectedNodeSelectors.getSourceViewLineTimings(state),
302307
sourceViewScrollGeneration: getSourceViewScrollGeneration(state),
308+
sourceViewLineNumber: getSourceViewLineNumber(state),
303309
assemblyViewNativeSymbol: getAssemblyViewNativeSymbol(state),
304310
assemblyViewCode: getAssemblyViewCode(state),
305311
globalAddressTimings:

src/components/marker-chart/Canvas.tsx

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import type {
1717
changeRightClickedMarker,
1818
changeMouseTimePosition,
1919
changeSelectedMarker,
20+
updateBottomBoxContentsAndMaybeOpen,
2021
} from 'firefox-profiler/actions/profile-view';
2122

2223
type UpdatePreviewSelection = typeof updatePreviewSelection;
@@ -35,7 +36,10 @@ import type {
3536
MarkerIndex,
3637
MarkerSchemaByName,
3738
GraphColor,
39+
Thread,
40+
IndexIntoStackTable,
3841
} from 'firefox-profiler/types';
42+
import type { TabSlug } from 'firefox-profiler/app-logic/tabs-handling';
3943
import { getStartEndRangeForMarker } from 'firefox-profiler/utils';
4044
import {
4145
getStrokeColor,
@@ -45,6 +49,7 @@ import {
4549
isValidGraphColor,
4650
} from 'firefox-profiler/profile-logic/graph-color';
4751
import { getSchemaFromMarker } from 'firefox-profiler/profile-logic/marker-schema';
52+
import { getBottomBoxInfoForStackFrame } from 'firefox-profiler/profile-logic/profile-data';
4853

4954
import type {
5055
ChartCanvasScale,
@@ -81,6 +86,9 @@ type OwnProps = {
8186
readonly selectedMarkerIndex: MarkerIndex | null;
8287
readonly rightClickedMarkerIndex: MarkerIndex | null;
8388
readonly shouldDisplayTooltips: () => boolean;
89+
readonly thread: Thread;
90+
readonly updateBottomBoxContentsAndMaybeOpen: typeof updateBottomBoxContentsAndMaybeOpen;
91+
readonly selectedTab: TabSlug;
8492
};
8593

8694
type Props = OwnProps & {
@@ -869,18 +877,45 @@ class MarkerChartCanvasImpl extends React.PureComponent<Props> {
869877
changeRightClickedMarker(threadsKey, markerIndex);
870878
};
871879

880+
_onStackFrameClick = (stackIndex: IndexIntoStackTable) => {
881+
const { thread, selectedTab, updateBottomBoxContentsAndMaybeOpen } =
882+
this.props;
883+
const bottomBoxInfo = getBottomBoxInfoForStackFrame(stackIndex, thread);
884+
updateBottomBoxContentsAndMaybeOpen(selectedTab, bottomBoxInfo);
885+
};
886+
887+
isMarkerVisible = (markerIndex: MarkerIndex): boolean => {
888+
const { markerTimingAndBuckets } = this.props;
889+
// Check if the marker appears in the visible marker timing data
890+
for (const markerTiming of markerTimingAndBuckets) {
891+
if (typeof markerTiming === 'string') {
892+
continue;
893+
}
894+
if (markerTiming.index.includes(markerIndex)) {
895+
return true;
896+
}
897+
}
898+
return false;
899+
};
900+
872901
getHoveredMarkerInfo = (markerIndex: MarkerIndex): React.ReactNode => {
873902
if (!this.props.shouldDisplayTooltips() || markerIndex === null) {
874903
return null;
875904
}
876905

906+
// Check if the marker is visible (not filtered out)
907+
if (!this.isMarkerVisible(markerIndex)) {
908+
return null;
909+
}
910+
877911
const marker = this.props.getMarker(markerIndex);
878912
return (
879913
<TooltipMarker
880914
markerIndex={markerIndex}
881915
marker={marker}
882916
threadsKey={this.props.threadsKey}
883917
restrictHeightWidth={true}
918+
onStackFrameClick={this._onStackFrameClick}
884919
/>
885920
);
886921
};

src/components/marker-chart/index.tsx

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,16 @@ import {
1717
getMarkerSchemaByName,
1818
} from 'firefox-profiler/selectors/profile';
1919
import { selectedThreadSelectors } from 'firefox-profiler/selectors/per-thread';
20-
import { getSelectedThreadsKey } from 'firefox-profiler/selectors/url-state';
20+
import {
21+
getSelectedThreadsKey,
22+
getSelectedTab,
23+
} from 'firefox-profiler/selectors/url-state';
2124
import {
2225
updatePreviewSelection,
2326
changeRightClickedMarker,
2427
changeMouseTimePosition,
2528
changeSelectedMarker,
29+
updateBottomBoxContentsAndMaybeOpen,
2630
} from 'firefox-profiler/actions/profile-view';
2731
import { ContextMenuTrigger } from 'firefox-profiler/components/shared/ContextMenuTrigger';
2832

@@ -35,7 +39,9 @@ import type {
3539
StartEndRange,
3640
PreviewSelection,
3741
ThreadsKey,
42+
Thread,
3843
} from 'firefox-profiler/types';
44+
import type { TabSlug } from 'firefox-profiler/app-logic/tabs-handling';
3945

4046
import type { ConnectedProps } from 'firefox-profiler/utils/connect';
4147

@@ -48,6 +54,7 @@ type DispatchProps = {
4854
readonly changeRightClickedMarker: typeof changeRightClickedMarker;
4955
readonly changeMouseTimePosition: typeof changeMouseTimePosition;
5056
readonly changeSelectedMarker: typeof changeSelectedMarker;
57+
readonly updateBottomBoxContentsAndMaybeOpen: typeof updateBottomBoxContentsAndMaybeOpen;
5158
};
5259

5360
type StateProps = {
@@ -62,6 +69,8 @@ type StateProps = {
6269
readonly previewSelection: PreviewSelection | null;
6370
readonly rightClickedMarkerIndex: MarkerIndex | null;
6471
readonly selectedMarkerIndex: MarkerIndex | null;
72+
readonly thread: Thread;
73+
readonly selectedTab: TabSlug;
6574
};
6675

6776
type Props = ConnectedProps<{}, StateProps, DispatchProps>;
@@ -171,6 +180,10 @@ class MarkerChartImpl extends React.PureComponent<Props> {
171180
selectedMarkerIndex,
172181
rightClickedMarkerIndex,
173182
shouldDisplayTooltips: this._shouldDisplayTooltips,
183+
thread: this.props.thread,
184+
updateBottomBoxContentsAndMaybeOpen:
185+
this.props.updateBottomBoxContentsAndMaybeOpen,
186+
selectedTab: this.props.selectedTab,
174187
}}
175188
/>
176189
</ContextMenuTrigger>
@@ -206,13 +219,16 @@ export const MarkerChart = explicitConnect<{}, StateProps, DispatchProps>({
206219
selectedThreadSelectors.getRightClickedMarkerIndex(state),
207220
selectedMarkerIndex:
208221
selectedThreadSelectors.getSelectedMarkerIndex(state),
222+
thread: selectedThreadSelectors.getThread(state),
223+
selectedTab: getSelectedTab(state),
209224
};
210225
},
211226
mapDispatchToProps: {
212227
updatePreviewSelection,
213228
changeMouseTimePosition,
214229
changeRightClickedMarker,
215230
changeSelectedMarker,
231+
updateBottomBoxContentsAndMaybeOpen,
216232
},
217233
component: MarkerChartImpl,
218234
});

src/components/shared/Backtrace.css

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,13 @@
3030
.backtraceStackFrame_isFrameLabel {
3131
color: rgb(0 0 0 / 0.6);
3232
}
33+
34+
.backtraceStackFrame_link {
35+
display: block;
36+
color: inherit;
37+
text-decoration: none;
38+
}
39+
40+
.backtraceStackFrame_link:hover {
41+
background-color: rgb(0 0 0 / 0.05);
42+
}

0 commit comments

Comments
 (0)