Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion locales/en-US/app.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -227,10 +227,13 @@ CallTree--inlining-badge = (inlined)
.title = Calls to { $calledFunction } were inlined into { $outerFunction } by the compiler.

## CallTreeSidebar
## This is the sidebar component that is used in Call Tree and Flame Graph panels.
## This is the sidebar component that is used in Call Tree, Function List and
## Flame Graph panels.

CallTreeSidebar--select-a-node = Select a node to display information about it.
CallTreeSidebar--call-node-details = Call node details
CallTreeSidebar--select-a-function = Select a function to display information about it.
CallTreeSidebar--function-details = Function details

## CallTreeSidebar timing information
##
Expand Down Expand Up @@ -940,6 +943,7 @@ StackSettings--panel-search =
## Tab Bar for the bottom half of the analysis UI.

TabBar--calltree-tab = Call Tree
TabBar--function-list-tab = Function List
TabBar--flame-graph-tab = Flame Graph
TabBar--stack-chart-tab = Stack Chart
TabBar--marker-chart-tab = Marker Chart
Expand Down
3 changes: 2 additions & 1 deletion res/css/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ body {
flex-shrink: 1;
}

.treeAndSidebarWrapper {
.treeAndSidebarWrapper,
.functionTableAndSidebarWrapper {
display: flex;
flex: 1;
flex-flow: column nowrap;
Expand Down
172 changes: 142 additions & 30 deletions src/actions/profile-view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import {
getHiddenLocalTracks,
getInvertCallstack,
getHash,
getUrlState,
} from 'firefox-profiler/selectors/url-state';
import {
assertExhaustiveCheck,
Expand Down Expand Up @@ -75,12 +76,14 @@ import type {
TableViewOptions,
SelectionContext,
BottomBoxInfo,
IndexIntoFuncTable,
} from 'firefox-profiler/types';
import {
funcHasDirectRecursiveCall,
funcHasRecursiveCall,
} from '../profile-logic/transforms';
import { changeStoredProfileNameInDb } from 'firefox-profiler/app-logic/uploaded-profiles-db';
import { replaceHistoryWithUrlState } from 'firefox-profiler/app-logic/url-handling';
import type { TabSlug } from '../app-logic/tabs-handling';
import type { CallNodeInfo } from '../profile-logic/call-node-info';
import type { SingleColumnSortState } from '../components/shared/TreeView';
Expand Down Expand Up @@ -131,6 +134,32 @@ export function changeSelectedCallNode(
};
}

/**
* Select a function for a given thread in the function list.
*
* Replaces the current history entry rather than pushing a new one, so that
* holding e.g. the down arrow key in the function list doesn't get rate-limited
* by the browser and doesn't flood the back/forward history.
*/
export function changeSelectedFunctionIndex(
threadsKey: ThreadsKey,
selectedFunctionIndex: IndexIntoFuncTable | null,
context: SelectionContext = { source: 'auto' }
): ThunkAction<void> {
return (dispatch, getState) => {
dispatch({
type: 'CHANGE_SELECTED_FUNCTION',
selectedFunctionIndex,
threadsKey,
context,
});
// Update window.history synchronously instead of waiting for the
// UrlManager's componentDidUpdate, which is deferred by React's render
// scheduling and would otherwise pushState a new entry.
replaceHistoryWithUrlState(getUrlState(getState()));
};
}

/**
* This action is used when the user right clicks on a call node (in panels such
* as the call tree, the flame chart, or the stack chart). It's especially used
Expand All @@ -147,6 +176,17 @@ export function changeRightClickedCallNode(
};
}

export function changeRightClickedFunctionIndex(
threadsKey: ThreadsKey,
functionIndex: IndexIntoFuncTable | null
): Action {
return {
type: 'CHANGE_RIGHT_CLICKED_FUNCTION',
threadsKey,
functionIndex,
};
}

/**
* Given a threadIndex and a sampleIndex, select the call node which carries the
* sample's self time. In the inverted tree, this will be a root node.
Expand Down Expand Up @@ -207,6 +247,38 @@ export function selectSelfCallNode(
};
}

/**
* Like selectSelfCallNode, but selects the function of the self call node
* instead. Used when the function list tab is active.
*/
export function selectSelfFunction(
threadsKey: ThreadsKey,
sampleIndex: IndexIntoSamplesTable | null
): ThunkAction<void> {
return (dispatch, getState) => {
if (sampleIndex === null || sampleIndex < 0) {
dispatch(changeSelectedFunctionIndex(threadsKey, null));
return;
}
const threadSelectors = getThreadSelectorsFromThreadsKey(threadsKey);
const sampleCallNodes =
threadSelectors.getSampleCallNodesForFilteredThread(getState());
if (sampleIndex >= sampleCallNodes.length) {
dispatch(changeSelectedFunctionIndex(threadsKey, null));
return;
}
const nonInvertedSelfCallNode = sampleCallNodes[sampleIndex];
if (nonInvertedSelfCallNode === null) {
dispatch(changeSelectedFunctionIndex(threadsKey, null));
return;
}
const callNodeInfo = threadSelectors.getCallNodeInfo(getState());
const funcIndex =
callNodeInfo.getCallNodeTable().func[nonInvertedSelfCallNode];
dispatch(changeSelectedFunctionIndex(threadsKey, funcIndex));
};
}

/**
* This selects a set of thread from thread indexes.
* Please use it in tests only.
Expand Down Expand Up @@ -1624,6 +1696,15 @@ export function changeMarkerTableSort(
};
}

export function changeFunctionListSort(
sort: SingleColumnSortState[] | null
): Action {
return {
type: 'CHANGE_FUNCTION_LIST_SORT',
sort,
};
}

export function changeNetworkSearchString(searchString: string): Action {
return {
type: 'CHANGE_NETWORK_SEARCH_STRING',
Expand Down Expand Up @@ -2032,49 +2113,89 @@ export function handleCallNodeTransformShortcut(
if (event.metaKey || event.ctrlKey || event.altKey) {
return;
}
const threadSelectors = getThreadSelectorsFromThreadsKey(threadsKey);
const unfilteredThread = threadSelectors.getThread(getState());
const implementation = getImplementationFilter(getState());
const inverted = getInvertCallstack(getState());
const callNodePath = callNodeInfo.getCallNodePathFromIndex(callNodeIndex);
const funcIndex = callNodeInfo.funcForNode(callNodeIndex);
const category = callNodeInfo.categoryForNode(callNodeIndex);

const callNodeTable = callNodeInfo.getCallNodeTable();

switch (event.key) {
case 'F':
case 'F': {
const callNodePath =
callNodeInfo.getCallNodePathFromIndex(callNodeIndex);
const implementation = getImplementationFilter(getState());
const inverted = getInvertCallstack(getState());
dispatch(
addTransformToStack(threadsKey, {
type: 'focus-subtree',
callNodePath: callNodePath,
callNodePath,
implementation,
inverted,
})
);
break;
case 'f':
}
case 'M': {
const callNodePath =
callNodeInfo.getCallNodePathFromIndex(callNodeIndex);
const implementation = getImplementationFilter(getState());
dispatch(
addTransformToStack(threadsKey, {
type: 'focus-function',
funcIndex,
type: 'merge-call-node',
callNodePath,
implementation,
})
);
break;
case 'S':
}
case 'g': {
const category = callNodeInfo.categoryForNode(callNodeIndex);
dispatch(
addTransformToStack(threadsKey, {
type: 'focus-self',
type: 'focus-category',
category,
})
);
break;
}
default:
dispatch(
handleFunctionTransformShortcut(
event,
threadsKey,
callNodeInfo,
funcIndex
)
);
}
};
}

export function handleFunctionTransformShortcut(
event: React.KeyboardEvent<HTMLElement>,
threadsKey: ThreadsKey,
callNodeInfo: CallNodeInfo,
funcIndex: IndexIntoFuncTable
): ThunkAction<void> {
return (dispatch, getState) => {
if (event.metaKey || event.ctrlKey || event.altKey) {
return;
}
const threadSelectors = getThreadSelectorsFromThreadsKey(threadsKey);
const implementation = getImplementationFilter(getState());
const callNodeTable = callNodeInfo.getCallNodeTable();
const unfilteredThread = threadSelectors.getThread(getState());

switch (event.key) {
case 'f':
dispatch(
addTransformToStack(threadsKey, {
type: 'focus-function',
funcIndex,
implementation,
})
);
break;
case 'M':
case 'S':
dispatch(
addTransformToStack(threadsKey, {
type: 'merge-call-node',
callNodePath: callNodePath,
type: 'focus-self',
funcIndex,
implementation,
})
);
Expand Down Expand Up @@ -2133,25 +2254,16 @@ export function handleCallNodeTransformShortcut(
}
break;
}
case 'c': {
case 'c':
dispatch(
addTransformToStack(threadsKey, {
type: 'collapse-function-subtree',
funcIndex,
})
);
break;
}
case 'g':
dispatch(
addTransformToStack(threadsKey, {
type: 'focus-category',
category,
})
);
break;
default:
// This did not match a call tree transform.
// This did not match a function transform.
}
};
}
2 changes: 2 additions & 0 deletions src/app-logic/tabs-handling.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
*/
export const tabsWithTitleL10nId = {
calltree: 'TabBar--calltree-tab',
'function-list': 'TabBar--function-list-tab',
'flame-graph': 'TabBar--flame-graph-tab',
'stack-chart': 'TabBar--stack-chart-tab',
'marker-chart': 'TabBar--marker-chart-tab',
Expand Down Expand Up @@ -41,6 +42,7 @@ export const tabsWithTitleL10nIdArray: readonly TabsWithTitleL10nId[] =

export const tabsShowingSampleData: readonly TabSlug[] = [
'calltree',
'function-list',
'flame-graph',
'stack-chart',
];
Loading
Loading