Skip to content
26 changes: 17 additions & 9 deletions src/actions/profile-view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1918,8 +1918,19 @@ export function changeTableViewOptions(

export function updateBottomBoxContentsAndMaybeOpen(
currentTab: TabSlug,
{ libIndex, sourceIndex, nativeSymbols, lineNumber }: BottomBoxInfo
bottomBoxInfo: BottomBoxInfo
): Action {
const {
libIndex,
sourceIndex,
nativeSymbols,
initialNativeSymbol,
scrollToLineNumber,
scrollToInstructionAddress,
highlightedLineNumber,
highlightedInstructionAddress,
} = bottomBoxInfo;

const haveSource = sourceIndex !== null;
const haveAssembly = nativeSymbols.length !== 0;

Expand All @@ -1929,22 +1940,19 @@ export function updateBottomBoxContentsAndMaybeOpen(
// view closed - unless the only thing we have is assembly.
const shouldOpenAssemblyView = !haveSource && haveAssembly;

// If we have at least one native symbol to show assembly for, pick
// the first one arbitrarily.
// TODO: If we have more than one native symbol, pick the one
// with the highest total sample count.
const currentNativeSymbol = nativeSymbols.length !== 0 ? 0 : null;

return {
type: 'UPDATE_BOTTOM_BOX',
libIndex,
sourceIndex,
nativeSymbols,
currentNativeSymbol,
currentNativeSymbol: initialNativeSymbol,
currentTab,
shouldOpenBottomBox,
shouldOpenAssemblyView,
lineNumber,
scrollToLineNumber,
scrollToInstructionAddress,
highlightedLineNumber,
highlightedInstructionAddress,
};
}

Expand Down
2 changes: 2 additions & 0 deletions src/app-logic/url-handling.ts
Original file line number Diff line number Diff line change
Expand Up @@ -511,12 +511,14 @@ export function stateFromLocation(
scrollGeneration: 0,
libIndex: null,
sourceIndex: null,
highlightedLine: null,
};
const assemblyView: AssemblyViewState = {
isOpen: false,
scrollGeneration: 0,
nativeSymbols: [],
currentNativeSymbol: null,
highlightedInstruction: null,
};
const isBottomBoxOpenPerPanel: any = {};
tabSlugs.forEach((tabSlug) => (isBottomBoxOpenPerPanel[tabSlug] = false));
Expand Down
60 changes: 28 additions & 32 deletions src/components/app/BottomBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,25 +14,22 @@ import { CodeLoadingOverlay } from './CodeLoadingOverlay';
import { CodeErrorOverlay } from './CodeErrorOverlay';
import {
getSourceViewScrollGeneration,
getSourceViewLineNumber,
getSourceViewScrollToLineNumber,
getSourceViewHighlightedLine,
getAssemblyViewIsOpen,
getAssemblyViewNativeSymbol,
getAssemblyViewScrollGeneration,
getAssemblyViewScrollToInstructionAddress,
getAssemblyViewHighlightedInstruction,
} from 'firefox-profiler/selectors/url-state';
import {
selectedThreadSelectors,
selectedNodeSelectors,
} from 'firefox-profiler/selectors/per-thread';
import { selectedThreadSelectors } from 'firefox-profiler/selectors/per-thread';
import { closeBottomBox } from 'firefox-profiler/actions/profile-view';
import { parseFileNameFromSymbolication } from 'firefox-profiler/utils/special-paths';
import {
getSourceViewCode,
getAssemblyViewCode,
} from 'firefox-profiler/selectors/code';
import {
getPreviewSelectionIsBeingModified,
getSourceViewFile,
} from 'firefox-profiler/selectors/profile';
import { getSourceViewFile } from 'firefox-profiler/selectors/profile';
import explicitConnect from 'firefox-profiler/utils/connect';

import type { ConnectedProps } from 'firefox-profiler/utils/connect';
Expand All @@ -55,16 +52,16 @@ type StateProps = {
readonly sourceViewFile: string | null;
readonly sourceViewCode: SourceCodeStatus | void;
readonly sourceViewScrollGeneration: number;
readonly sourceViewLineNumber?: number;
readonly sourceViewScrollToLineNumber?: number;
readonly sourceViewHighlightedLine: number | null;
readonly globalLineTimings: LineTimings;
readonly selectedCallNodeLineTimings: LineTimings;
readonly assemblyViewIsOpen: boolean;
readonly assemblyViewNativeSymbol: NativeSymbolInfo | null;
readonly assemblyViewCode: AssemblyCodeStatus | void;
readonly assemblyViewScrollGeneration: number;
readonly assemblyViewScrollToInstructionAddress?: number;
readonly assemblyViewHighlightedInstruction: number | null;
readonly globalAddressTimings: AddressTimings;
readonly selectedCallNodeAddressTimings: AddressTimings;
readonly disableOverscan: boolean;
};

type DispatchProps = {
Expand Down Expand Up @@ -160,16 +157,16 @@ class BottomBoxImpl extends React.PureComponent<Props> {
sourceViewFile,
sourceViewCode,
globalLineTimings,
disableOverscan,
sourceViewScrollGeneration,
sourceViewLineNumber,
selectedCallNodeLineTimings,
sourceViewScrollToLineNumber,
sourceViewHighlightedLine,
assemblyViewIsOpen,
assemblyViewScrollGeneration,
assemblyViewScrollToInstructionAddress,
assemblyViewHighlightedInstruction,
assemblyViewNativeSymbol,
assemblyViewCode,
globalAddressTimings,
selectedCallNodeAddressTimings,
} = this.props;
const sourceCode =
sourceViewCode && sourceViewCode.type === 'AVAILABLE'
Expand Down Expand Up @@ -223,20 +220,17 @@ class BottomBoxImpl extends React.PureComponent<Props> {
{displayIonGraph ? (
<IonGraphView
timings={globalLineTimings}
hotSpotTimings={selectedCallNodeLineTimings}
sourceCode={sourceCode}
/>
) : null}
{displaySourceView ? (
<SourceView
disableOverscan={disableOverscan}
timings={globalLineTimings}
sourceCode={sourceCode}
filePath={path}
scrollToHotSpotGeneration={sourceViewScrollGeneration}
scrollToLineNumber={sourceViewLineNumber}
hotSpotTimings={selectedCallNodeLineTimings}
highlightedLine={sourceViewLineNumber}
scrollGeneration={sourceViewScrollGeneration}
scrollToLineNumber={sourceViewScrollToLineNumber}
highlightedLine={sourceViewHighlightedLine}
ref={this._sourceView}
/>
) : null}
Expand Down Expand Up @@ -264,12 +258,14 @@ class BottomBoxImpl extends React.PureComponent<Props> {
<div className="bottom-assemblyview-wrapper">
{assemblyViewNativeSymbol !== null ? (
<AssemblyView
disableOverscan={disableOverscan}
timings={globalAddressTimings}
assemblyCode={assemblyCode}
nativeSymbol={assemblyViewNativeSymbol}
scrollToHotSpotGeneration={assemblyViewScrollGeneration}
hotSpotTimings={selectedCallNodeAddressTimings}
scrollGeneration={assemblyViewScrollGeneration}
scrollToInstructionAddress={
assemblyViewScrollToInstructionAddress
}
highlightedInstruction={assemblyViewHighlightedInstruction}
ref={this._assemblyView}
/>
) : null}
Expand Down Expand Up @@ -302,19 +298,19 @@ export const BottomBox = explicitConnect<{}, StateProps, DispatchProps>({
sourceViewFile: getSourceViewFile(state),
sourceViewCode: getSourceViewCode(state),
globalLineTimings: selectedThreadSelectors.getSourceViewLineTimings(state),
selectedCallNodeLineTimings:
selectedNodeSelectors.getSourceViewLineTimings(state),
sourceViewScrollGeneration: getSourceViewScrollGeneration(state),
sourceViewLineNumber: getSourceViewLineNumber(state),
sourceViewScrollToLineNumber: getSourceViewScrollToLineNumber(state),
sourceViewHighlightedLine: getSourceViewHighlightedLine(state),
assemblyViewNativeSymbol: getAssemblyViewNativeSymbol(state),
assemblyViewCode: getAssemblyViewCode(state),
globalAddressTimings:
selectedThreadSelectors.getAssemblyViewAddressTimings(state),
selectedCallNodeAddressTimings:
selectedNodeSelectors.getAssemblyViewAddressTimings(state),
assemblyViewScrollGeneration: getAssemblyViewScrollGeneration(state),
assemblyViewScrollToInstructionAddress:
getAssemblyViewScrollToInstructionAddress(state),
assemblyViewHighlightedInstruction:
getAssemblyViewHighlightedInstruction(state),
assemblyViewIsOpen: getAssemblyViewIsOpen(state),
disableOverscan: getPreviewSelectionIsBeingModified(state),
}),
mapDispatchToProps: {
closeBottomBox,
Expand Down
2 changes: 1 addition & 1 deletion src/components/marker-chart/Canvas.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ import {
isValidGraphColor,
} from 'firefox-profiler/profile-logic/graph-color';
import { getSchemaFromMarker } from 'firefox-profiler/profile-logic/marker-schema';
import { getBottomBoxInfoForStackFrame } from 'firefox-profiler/profile-logic/profile-data';
import { getBottomBoxInfoForStackFrame } from 'firefox-profiler/profile-logic/bottom-box';

import type {
ChartCanvasScale,
Expand Down
41 changes: 40 additions & 1 deletion src/components/shared/AssemblyView-codemirror.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,12 @@
* width of the editor, it covers both the gutter and the main area.
*/
import { EditorView, gutter } from '@codemirror/view';
import { EditorState, StateField, StateEffect } from '@codemirror/state';
import {
EditorState,
StateField,
StateEffect,
Compartment,
} from '@codemirror/state';
import { syntaxHighlighting } from '@codemirror/language';
import { classHighlighter } from '@lezer/highlight';
import clamp from 'clamp';
Expand All @@ -37,12 +42,16 @@ import {
timingsExtension,
updateTimingsEffect,
StringMarker,
createHighlightedLineExtension,
} from 'firefox-profiler/utils/codemirror-shared';

// An "effect" is like a redux action. This effect is used to replace the value
// of the state field addressToLineMapField.
const updateAddressToLineMapEffect = StateEffect.define<AddressToLineMap>();

// This "compartment" allows us to swap the highlighted line when it changes.
const highlightedLineConf = new Compartment();

// This "state field" stores the current AddressToLineMap. This field allows the
// instructionAddressGutter to map line numbers to addresses.
const addressToLineMapField = StateField.define<AddressToLineMap>({
Expand Down Expand Up @@ -174,23 +183,31 @@ export class AssemblyViewEditor {
_view: EditorView;
_addressToLineMap: AddressToLineMap;
_addressTimings: AddressTimings;
_highlightedAddress: Address | null;

// Create a CodeMirror editor and add it as a child element of domParent.
constructor(
initialAssemblyCode: DecodedInstruction[],
addressTimings: AddressTimings,
highlightedAddress: Address | null,
domParent: Element
) {
this._addressToLineMap = new AddressToLineMap(
getInstructionAddresses(initialAssemblyCode)
);
this._addressTimings = addressTimings;
this._highlightedAddress = highlightedAddress;
const highlightedLine =
highlightedAddress !== null
? this._addressToLineMap.addressToLine(highlightedAddress)
: null;
let state = EditorState.create({
doc: instructionsToText(initialAssemblyCode),
extensions: [
timingsExtension,
addressToLineMapField,
instructionAddressGutter,
highlightedLineConf.of(createHighlightedLineExtension(highlightedLine)),
syntaxHighlighting(classHighlighter),
EditorState.readOnly.of(true),
EditorView.editable.of(false),
Expand Down Expand Up @@ -220,6 +237,11 @@ export class AssemblyViewEditor {
this._addressTimings,
this._addressToLineMap
);
// Recalculate the highlighted line based on the new address-to-line mapping.
const highlightedLine =
this._highlightedAddress !== null
? this._addressToLineMap.addressToLine(this._highlightedAddress)
: null;
// The CodeMirror way of replacing the entire contents is to insert new text
// and overwrite the full range of existing text.
const text = instructionsToText(assemblyCode);
Expand All @@ -236,6 +258,9 @@ export class AssemblyViewEditor {
effects: [
updateAddressToLineMapEffect.of(this._addressToLineMap),
updateTimingsEffect.of(lineTimings),
highlightedLineConf.reconfigure(
createHighlightedLineExtension(highlightedLine)
),
],
});
}
Expand Down Expand Up @@ -280,4 +305,18 @@ export class AssemblyViewEditor {
this.scrollToLine(lineNumber - topSpaceLines);
}
}

setHighlightedInstruction(address: Address | null) {
// Store the highlighted address so we can recalculate the line number
// when the address-to-line mapping changes.
this._highlightedAddress = address;
// Convert the address to a line number and update the highlighted line.
const lineNumber =
address !== null ? this._addressToLineMap.addressToLine(address) : null;
this._view.dispatch({
effects: highlightedLineConf.reconfigure(
createHighlightedLineExtension(lineNumber)
),
});
}
}
Loading