Skip to content

Commit 01c93cc

Browse files
committed
Merge two stacks.
2 parents 2d6f6d0 + b451387 commit 01c93cc

68 files changed

Lines changed: 8027 additions & 1296 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

locales/en-US/app.ftl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -900,6 +900,7 @@ StackSettings--panel-search =
900900
## Tab Bar for the bottom half of the analysis UI.
901901

902902
TabBar--calltree-tab = Call Tree
903+
TabBar--function-list-tab = Function List
903904
TabBar--flame-graph-tab = Flame Graph
904905
TabBar--stack-chart-tab = Stack Chart
905906
TabBar--marker-chart-tab = Marker Chart

res/css/style.css

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,8 @@ body {
5757
flex-shrink: 1;
5858
}
5959

60-
.treeAndSidebarWrapper {
60+
.treeAndSidebarWrapper,
61+
.functionTableAndSidebarWrapper {
6162
display: flex;
6263
flex: 1;
6364
flex-flow: column nowrap;

src/actions/profile-view.ts

Lines changed: 283 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import {
3434
getHiddenLocalTracks,
3535
getInvertCallstack,
3636
getHash,
37+
getUrlState,
3738
} from 'firefox-profiler/selectors/url-state';
3839
import {
3940
assertExhaustiveCheck,
@@ -74,14 +75,17 @@ import type {
7475
TableViewOptions,
7576
SelectionContext,
7677
BottomBoxInfo,
78+
IndexIntoFuncTable,
7779
} from 'firefox-profiler/types';
7880
import {
7981
funcHasDirectRecursiveCall,
8082
funcHasRecursiveCall,
8183
} from '../profile-logic/transforms';
8284
import { changeStoredProfileNameInDb } from 'firefox-profiler/app-logic/uploaded-profiles-db';
85+
import { replaceHistoryWithUrlState } from 'firefox-profiler/app-logic/url-handling';
8386
import type { TabSlug } from '../app-logic/tabs-handling';
8487
import type { CallNodeInfo } from '../profile-logic/call-node-info';
88+
import type { SingleColumnSortState } from '../components/shared/TreeView';
8589
import { intersectSets } from 'firefox-profiler/utils/set';
8690

8791
/**
@@ -120,7 +124,7 @@ export function changeSelectedCallNode(
120124
const isInverted = getInvertCallstack(getState());
121125
dispatch({
122126
type: 'CHANGE_SELECTED_CALL_NODE',
123-
isInverted,
127+
area: isInverted ? 'INVERTED_TREE' : 'NON_INVERTED_TREE',
124128
selectedCallNodePath,
125129
optionalExpandedToCallNodePath,
126130
threadsKey,
@@ -129,6 +133,62 @@ export function changeSelectedCallNode(
129133
};
130134
}
131135

136+
export function changeLowerWingSelectedCallNode(
137+
threadsKey: ThreadsKey,
138+
selectedCallNodePath: CallNodePath,
139+
context: SelectionContext = { source: 'auto' }
140+
): Action {
141+
return {
142+
type: 'CHANGE_SELECTED_CALL_NODE',
143+
area: 'LOWER_WING',
144+
selectedCallNodePath,
145+
optionalExpandedToCallNodePath: [],
146+
threadsKey,
147+
context,
148+
};
149+
}
150+
151+
export function changeUpperWingSelectedCallNode(
152+
threadsKey: ThreadsKey,
153+
selectedCallNodePath: CallNodePath,
154+
context: SelectionContext = { source: 'auto' }
155+
): Action {
156+
return {
157+
type: 'CHANGE_SELECTED_CALL_NODE',
158+
area: 'UPPER_WING',
159+
selectedCallNodePath,
160+
optionalExpandedToCallNodePath: [],
161+
threadsKey,
162+
context,
163+
};
164+
}
165+
166+
/**
167+
* Select a function for a given thread in the function list.
168+
*
169+
* Replaces the current history entry rather than pushing a new one, so that
170+
* holding e.g. the down arrow key in the function list doesn't get rate-limited
171+
* by the browser and doesn't flood the back/forward history.
172+
*/
173+
export function changeSelectedFunctionIndex(
174+
threadsKey: ThreadsKey,
175+
selectedFunctionIndex: IndexIntoFuncTable | null,
176+
context: SelectionContext = { source: 'auto' }
177+
): ThunkAction<void> {
178+
return (dispatch, getState) => {
179+
dispatch({
180+
type: 'CHANGE_SELECTED_FUNCTION',
181+
selectedFunctionIndex,
182+
threadsKey,
183+
context,
184+
});
185+
// Update window.history synchronously instead of waiting for the
186+
// UrlManager's componentDidUpdate, which is deferred by React's render
187+
// scheduling and would otherwise pushState a new entry.
188+
replaceHistoryWithUrlState(getUrlState(getState()));
189+
};
190+
}
191+
132192
/**
133193
* This action is used when the user right clicks on a call node (in panels such
134194
* as the call tree, the flame chart, or the stack chart). It's especially used
@@ -137,10 +197,49 @@ export function changeSelectedCallNode(
137197
export function changeRightClickedCallNode(
138198
threadsKey: ThreadsKey,
139199
callNodePath: CallNodePath | null
200+
): ThunkAction<void> {
201+
return (dispatch, getState) => {
202+
const isInverted = getInvertCallstack(getState());
203+
dispatch({
204+
type: 'CHANGE_RIGHT_CLICKED_CALL_NODE',
205+
threadsKey,
206+
area: isInverted ? 'INVERTED_TREE' : 'NON_INVERTED_TREE',
207+
callNodePath,
208+
});
209+
};
210+
}
211+
212+
export function changeRightClickedFunctionIndex(
213+
threadsKey: ThreadsKey,
214+
functionIndex: IndexIntoFuncTable | null
215+
): Action {
216+
return {
217+
type: 'CHANGE_RIGHT_CLICKED_FUNCTION',
218+
threadsKey,
219+
functionIndex,
220+
};
221+
}
222+
223+
export function changeLowerWingRightClickedCallNode(
224+
threadsKey: ThreadsKey,
225+
callNodePath: CallNodePath | null
140226
): Action {
141227
return {
142228
type: 'CHANGE_RIGHT_CLICKED_CALL_NODE',
143229
threadsKey,
230+
area: 'LOWER_WING',
231+
callNodePath,
232+
};
233+
}
234+
235+
export function changeUpperWingRightClickedCallNode(
236+
threadsKey: ThreadsKey,
237+
callNodePath: CallNodePath | null
238+
) {
239+
return {
240+
type: 'CHANGE_RIGHT_CLICKED_CALL_NODE',
241+
threadsKey,
242+
area: 'UPPER_WING',
144243
callNodePath,
145244
};
146245
}
@@ -207,6 +306,40 @@ export function selectSelfCallNode(
207306
};
208307
}
209308

309+
/**
310+
* Like selectSelfCallNode, but selects the function of the self call node
311+
* instead. Used when the function list tab is active.
312+
*/
313+
export function selectSelfFunction(
314+
threadsKey: ThreadsKey,
315+
sampleIndex: IndexIntoSamplesTable | null
316+
): ThunkAction<void> {
317+
return (dispatch, getState) => {
318+
if (sampleIndex === null || sampleIndex < 0) {
319+
dispatch(changeSelectedFunctionIndex(threadsKey, null));
320+
return;
321+
}
322+
const threadSelectors = getThreadSelectorsFromThreadsKey(threadsKey);
323+
const sampleCallNodes =
324+
threadSelectors.getSampleIndexToNonInvertedCallNodeIndexForFilteredThread(
325+
getState()
326+
);
327+
if (sampleIndex >= sampleCallNodes.length) {
328+
dispatch(changeSelectedFunctionIndex(threadsKey, null));
329+
return;
330+
}
331+
const nonInvertedSelfCallNode = sampleCallNodes[sampleIndex];
332+
if (nonInvertedSelfCallNode === null) {
333+
dispatch(changeSelectedFunctionIndex(threadsKey, null));
334+
return;
335+
}
336+
const callNodeInfo = threadSelectors.getCallNodeInfo(getState());
337+
const funcIndex =
338+
callNodeInfo.getCallNodeTable().func[nonInvertedSelfCallNode];
339+
dispatch(changeSelectedFunctionIndex(threadsKey, funcIndex));
340+
};
341+
}
342+
210343
/**
211344
* This selects a set of thread from thread indexes.
212345
* Please use it in tests only.
@@ -1545,12 +1678,37 @@ export function changeExpandedCallNodes(
15451678
const isInverted = getInvertCallstack(getState());
15461679
dispatch({
15471680
type: 'CHANGE_EXPANDED_CALL_NODES',
1548-
isInverted,
1681+
area: isInverted ? 'INVERTED_TREE' : 'NON_INVERTED_TREE',
15491682
threadsKey,
15501683
expandedCallNodePaths,
15511684
});
15521685
};
15531686
}
1687+
1688+
export function changeLowerWingExpandedCallNodes(
1689+
threadsKey: ThreadsKey,
1690+
expandedCallNodePaths: Array<CallNodePath>
1691+
): Action {
1692+
return {
1693+
type: 'CHANGE_EXPANDED_CALL_NODES',
1694+
area: 'LOWER_WING',
1695+
threadsKey,
1696+
expandedCallNodePaths,
1697+
};
1698+
}
1699+
1700+
export function changeUpperWingExpandedCallNodes(
1701+
threadsKey: ThreadsKey,
1702+
expandedCallNodePaths: Array<CallNodePath>
1703+
): Action {
1704+
return {
1705+
type: 'CHANGE_EXPANDED_CALL_NODES',
1706+
area: 'UPPER_WING',
1707+
threadsKey,
1708+
expandedCallNodePaths,
1709+
};
1710+
}
1711+
15541712
export function changeSelectedMarker(
15551713
threadsKey: ThreadsKey,
15561714
selectedMarker: MarkerIndex | null,
@@ -1615,6 +1773,31 @@ export function changeMarkersSearchString(searchString: string): Action {
16151773
};
16161774
}
16171775

1776+
export function changeMarkerTableSort(sort: SingleColumnSortState[]): Action {
1777+
return {
1778+
type: 'CHANGE_MARKER_TABLE_SORT',
1779+
sort,
1780+
};
1781+
}
1782+
1783+
export function changeFunctionListSort(sort: SingleColumnSortState[]): Action {
1784+
return {
1785+
type: 'CHANGE_FUNCTION_LIST_SORT',
1786+
sort,
1787+
};
1788+
}
1789+
1790+
export function changeFunctionListSectionOpen(
1791+
section: 'descendants' | 'ancestors' | 'self',
1792+
isOpen: boolean
1793+
): Action {
1794+
return {
1795+
type: 'CHANGE_FUNCTION_LIST_SECTION_OPEN',
1796+
section,
1797+
isOpen,
1798+
};
1799+
}
1800+
16181801
export function changeNetworkSearchString(searchString: string): Action {
16191802
return {
16201803
type: 'CHANGE_NETWORK_SEARCH_STRING',
@@ -2015,6 +2198,7 @@ export function toggleBottomBoxFullscreen(): ThunkAction<void> {
20152198
export function handleCallNodeTransformShortcut(
20162199
event: React.KeyboardEvent<HTMLElement>,
20172200
threadsKey: ThreadsKey,
2201+
callNodeInfo: CallNodeInfo,
20182202
callNodeIndex: IndexIntoCallNodeTable
20192203
): ThunkAction<void> {
20202204
return (dispatch, getState) => {
@@ -2023,7 +2207,6 @@ export function handleCallNodeTransformShortcut(
20232207
}
20242208
const threadSelectors = getThreadSelectorsFromThreadsKey(threadsKey);
20252209
const unfilteredThread = threadSelectors.getThread(getState());
2026-
const callNodeInfo = threadSelectors.getCallNodeInfo(getState());
20272210
const implementation = getImplementationFilter(getState());
20282211
const inverted = getInvertCallstack(getState());
20292212
const callNodePath = callNodeInfo.getCallNodePathFromIndex(callNodeIndex);
@@ -2133,3 +2316,100 @@ export function handleCallNodeTransformShortcut(
21332316
}
21342317
};
21352318
}
2319+
2320+
export function handleFunctionTransformShortcut(
2321+
event: React.KeyboardEvent<HTMLElement>,
2322+
threadsKey: ThreadsKey,
2323+
funcIndex: IndexIntoFuncTable
2324+
): ThunkAction<void> {
2325+
return (dispatch, getState) => {
2326+
if (event.metaKey || event.ctrlKey || event.altKey) {
2327+
return;
2328+
}
2329+
const threadSelectors = getThreadSelectorsFromThreadsKey(threadsKey);
2330+
const callNodeInfo = threadSelectors.getCallNodeInfo(getState());
2331+
const implementation = getImplementationFilter(getState());
2332+
const callNodeTable = callNodeInfo.getCallNodeTable();
2333+
const unfilteredThread = threadSelectors.getThread(getState());
2334+
2335+
switch (event.key) {
2336+
case 'f':
2337+
dispatch(
2338+
addTransformToStack(threadsKey, {
2339+
type: 'focus-function',
2340+
funcIndex,
2341+
})
2342+
);
2343+
break;
2344+
case 'S':
2345+
dispatch(
2346+
addTransformToStack(threadsKey, {
2347+
type: 'focus-self',
2348+
funcIndex,
2349+
implementation,
2350+
})
2351+
);
2352+
break;
2353+
case 'm':
2354+
dispatch(
2355+
addTransformToStack(threadsKey, {
2356+
type: 'merge-function',
2357+
funcIndex,
2358+
})
2359+
);
2360+
break;
2361+
case 'd':
2362+
dispatch(
2363+
addTransformToStack(threadsKey, {
2364+
type: 'drop-function',
2365+
funcIndex,
2366+
})
2367+
);
2368+
break;
2369+
case 'C': {
2370+
const resourceIndex = unfilteredThread.funcTable.resource[funcIndex];
2371+
dispatch(
2372+
addCollapseResourceTransformToStack(
2373+
threadsKey,
2374+
resourceIndex,
2375+
implementation
2376+
)
2377+
);
2378+
break;
2379+
}
2380+
case 'r': {
2381+
if (funcHasRecursiveCall(callNodeTable, funcIndex)) {
2382+
dispatch(
2383+
addTransformToStack(threadsKey, {
2384+
type: 'collapse-recursion',
2385+
funcIndex,
2386+
})
2387+
);
2388+
}
2389+
break;
2390+
}
2391+
case 'R': {
2392+
if (funcHasDirectRecursiveCall(callNodeTable, funcIndex)) {
2393+
dispatch(
2394+
addTransformToStack(threadsKey, {
2395+
type: 'collapse-direct-recursion',
2396+
funcIndex,
2397+
implementation,
2398+
})
2399+
);
2400+
}
2401+
break;
2402+
}
2403+
case 'c':
2404+
dispatch(
2405+
addTransformToStack(threadsKey, {
2406+
type: 'collapse-function-subtree',
2407+
funcIndex,
2408+
})
2409+
);
2410+
break;
2411+
default:
2412+
// This did not match a function transform.
2413+
}
2414+
};
2415+
}

0 commit comments

Comments
 (0)