Skip to content
Draft
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
14 changes: 14 additions & 0 deletions locales/en-US/app.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,15 @@ CallNodeContextMenu--transform-focus-category = Focus on category <strong>{ $cat
.title =
Focusing on the nodes that belong to the same category as the selected node,
thereby merging all nodes that belong to another category.

# This is used as the context menu item to apply the "Drop category" transform.
# Variables:
# $categoryName (String) - Name of the category to drop.
CallNodeContextMenu--transform-drop-category = Drop samples with category <strong>{ $categoryName }</strong>
.title =
Dropping samples with a category removes all samples that have a frame
belonging to that category.

CallNodeContextMenu--transform-collapse-function-subtree = Collapse function
.title =
Collapsing a function will remove everything it called, and assign
Expand Down Expand Up @@ -1180,6 +1189,11 @@ TransformNavigator--focus-self = Focus Self: { $item }
# $item (String) - Name of the category that transform applied to.
TransformNavigator--focus-category = Focus category: { $item }

# "Drop samples with category" transform.
# Variables:
# $item (String) - Name of the category that transform applied to.
TransformNavigator--drop-category = Drop samples with category: { $item }

# "Merge call node" transform.
# See: https://profiler.firefox.com/docs/#/./guide-filtering-call-trees?id=merge
# Variables:
Expand Down
8 changes: 8 additions & 0 deletions src/actions/profile-view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2103,6 +2103,14 @@ export function handleCallNodeTransformShortcut(
})
);
break;
case 'G':
dispatch(
addTransformToStack(threadsKey, {
type: 'drop-category',
category,
})
);
break;
default:
// This did not match a call tree transform.
}
Expand Down
6 changes: 5 additions & 1 deletion src/app-logic/url-handling.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ import { StringTable } from 'firefox-profiler/utils/string-table';
import type { ProfileUpgradeInfo } from 'firefox-profiler/profile-logic/processed-profile-versioning';
import type { ProfileAndProfileUpgradeInfo } from 'firefox-profiler/actions/receive-profile';

export const CURRENT_URL_VERSION = 15;
export const CURRENT_URL_VERSION = 16;

/**
* This static piece of state might look like an anti-pattern, but it's a relatively
Expand Down Expand Up @@ -1351,6 +1351,10 @@ const _upgraders: {
.map(mapIndexesInTransform)
.join('~');
},
[16]: (_) => {
// Added the 'drop-category' transform (short key 'dg'). No existing URLs
// need rewriting; this is a new transform with no prior encoding to migrate.
},
};

for (let destVersion = 1; destVersion <= CURRENT_URL_VERSION; destVersion++) {
Expand Down
23 changes: 23 additions & 0 deletions src/components/shared/CallNodeContextMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,13 @@ class CallNodeContextMenuImpl extends React.PureComponent<Props> {
});
break;
}
case 'drop-category': {
addTransformToStack(threadsKey, {
type: 'drop-category',
category,
});
break;
}
case 'filter-samples':
throw new Error(
"Filter samples transform can't be applied from the call node context menu."
Expand Down Expand Up @@ -714,6 +721,22 @@ class CallNodeContextMenuImpl extends React.PureComponent<Props> {
})
: null}

{hasCategory
? this.renderTransformMenuItem({
l10nId: 'CallNodeContextMenu--transform-drop-category',
l10nProps: {
vars: { categoryName },
elems: { strong: <strong /> },
},
shortcut: 'G',
icon: 'Drop',
onClick: this._handleClick,
transform: 'drop-category',
title: '',
content: 'Drop samples with this category',
})
: null}

{this.renderTransformMenuItem({
l10nId: 'CallNodeContextMenu--transform-collapse-function-subtree',
shortcut: 'c',
Expand Down
70 changes: 69 additions & 1 deletion src/profile-logic/transforms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ const TRANSFORM_OBJ: { [key in TransformType]: true } = {
'collapse-recursion': true,
'collapse-function-subtree': true,
'focus-category': true,
'drop-category': true,
'filter-samples': true,
};
export const ALL_TRANSFORM_TYPES: TransformType[] = Object.keys(
Expand All @@ -100,6 +101,9 @@ ALL_TRANSFORM_TYPES.forEach((transform: TransformType) => {
case 'focus-category':
shortKey = 'fg';
break;
case 'drop-category':
shortKey = 'dg';
break;
case 'merge-call-node':
shortKey = 'mcn';
break;
Expand Down Expand Up @@ -277,6 +281,19 @@ export function parseTransforms(transformString: string): TransformStack {
}
break;
}
case 'drop-category': {
// e.g. "dg-3"
const [, categoryRaw] = tuple;
const category = parseInt(categoryRaw, 10);
// Validate that the category makes sense.
if (!isNaN(category) && category >= 0) {
transforms.push({
type: 'drop-category',
category,
});
}
break;
}
case 'focus-subtree':
case 'merge-call-node': {
// e.g. "f-js-xFFpUMl-i" or "f-cpp-0KV4KV5KV61KV7KV8K"
Expand Down Expand Up @@ -378,6 +395,7 @@ export function stringifyTransforms(transformStack: TransformStack): string {
case 'focus-function':
return `${shortKey}-${transform.funcIndex}`;
case 'focus-category':
case 'drop-category':
return `${shortKey}-${transform.category}`;
case 'collapse-resource':
return `${shortKey}-${transform.implementation}-${transform.resourceIndex}-${transform.collapsedFuncIndex}`;
Expand Down Expand Up @@ -449,6 +467,16 @@ export function getTransformLabelL10nIds(
};
}

if (transform.type === 'drop-category') {
if (categories === undefined) {
throw new Error('Expected categories to be defined.');
}
return {
l10nId: 'TransformNavigator--drop-category',
item: categories[transform.category].name,
};
}

if (transform.type === 'filter-samples') {
switch (transform.filterType) {
case 'marker-search':
Expand Down Expand Up @@ -551,6 +579,12 @@ export function applyTransformToCallNodePath(
callNodePath,
callNodeInfo
);
case 'drop-category':
return _dropCategoryInCallNodePath(
transform.category,
callNodePath,
callNodeInfo
);
case 'merge-call-node':
return _mergeNodeInCallNodePath(transform.callNodePath, callNodePath);
case 'merge-function':
Expand Down Expand Up @@ -672,6 +706,23 @@ function _removeOtherCategoryFunctionsInNodePathWithFunction(
return newCallNodePath;
}

// If the leaf node of the call node path belongs to the dropped category,
// return an empty path — the whole sample is gone.
function _dropCategoryInCallNodePath(
category: IndexIntoCategoryList,
callNodePath: CallNodePath,
callNodeInfo: CallNodeInfo
): CallNodePath {
const leafCallNodeIndex = callNodeInfo.getCallNodeIndexFromPath(callNodePath);
if (
leafCallNodeIndex !== null &&
callNodeInfo.categoryForNode(leafCallNodeIndex) === category
) {
return [];
}
return callNodePath;
}

function _collapseResourceInCallNodePath(
resourceIndex: IndexIntoResourceTable,
collapsedFuncIndex: IndexIntoFuncTable,
Expand Down Expand Up @@ -1456,6 +1507,20 @@ export function focusCategory(thread: Thread, category: IndexIntoCategoryList) {
});
}

/**
* Drop any samples whose leaf stack frame belongs to the given category.
*/
export function dropCategory(thread: Thread, category: IndexIntoCategoryList) {
return timeCode('dropCategory', () => {
const { stackTable } = thread;

return updateThreadStacks(thread, stackTable, (stack) =>
// Drop any sample whose leaf frame belongs to the given category.
stack !== null && stackTable.category[stack] === category ? null : stack
);
});
}

/**
* When restoring function in a CallNodePath there can be multiple correct CallNodePaths
* that could be restored. The best approach would probably be to restore to the
Expand Down Expand Up @@ -1834,6 +1899,8 @@ export function applyTransform(
return focusSelf(thread, transform.funcIndex, transform.implementation);
case 'focus-category':
return focusCategory(thread, transform.category);
case 'drop-category':
return dropCategory(thread, transform.category);
case 'collapse-resource':
return collapseResource(
thread,
Expand Down Expand Up @@ -2059,7 +2126,8 @@ export function translateTransform(
funcIndex: newFuncIndex,
};
}
case 'focus-category': {
case 'focus-category':
case 'drop-category': {
// We don't sanitize-away categories, so this transform doesn't need to
// be translated.
return transform;
Expand Down
5 changes: 3 additions & 2 deletions src/test/components/CallNodeContextMenu.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,8 @@ describe('calltree/CallNodeContextMenu', function () {
{ matcher: /Merge node only/, type: 'merge-call-node' },
{ matcher: /Focus on subtree only/, type: 'focus-subtree' },
{ matcher: /Focus on function/, type: 'focus-function' },
{ matcher: /Other/, type: 'focus-category' },
{ matcher: /Focus on category/, type: 'focus-category' },
{ matcher: /Drop samples with category/, type: 'drop-category' },
{ matcher: /Collapse function/, type: 'collapse-function-subtree' },
{ matcher: /XUL/, type: 'collapse-resource' },
{
Expand All @@ -136,7 +137,7 @@ describe('calltree/CallNodeContextMenu', function () {
matcher: /Collapse direct recursion/,
type: 'collapse-direct-recursion',
},
{ matcher: /Drop samples/, type: 'drop-function' },
{ matcher: /Drop samples with this function/, type: 'drop-function' },
];

fixtures.forEach(({ matcher, type }) => {
Expand Down
9 changes: 9 additions & 0 deletions src/test/components/TransformShortcuts.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,15 @@ function testTransformKeyboardShortcuts(setup: () => TestSetup) {
});
});

it('handles drop-category', () => {
const { pressKey, getTransform, expectedCategory } = setup();
pressKey({ key: 'G' });
expect(getTransform()).toEqual({
type: 'drop-category',
category: expectedCategory,
});
});

it('handles merge call node', () => {
const { pressKey, getTransform, expectedCallNodePath } = setup();
pressKey({ key: 'M' });
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,30 @@ exports[`calltree/CallNodeContextMenu basic rendering renders a full context men
g
</kbd>
</div>
<div
aria-disabled="false"
class="react-contextmenu-item"
role="menuitem"
tabindex="-1"
>
<span
class="react-contextmenu-icon callNodeContextMenuIconDrop"
/>
<div
class="react-contextmenu-item-content"
title="Dropping samples with a category removes all samples that have a frame belonging to that category."
>
Drop samples with category
<strong>
⁨Other⁩
</strong>
</div>
<kbd
class="callNodeContextMenuShortcut"
>
G
</kbd>
</div>
<div
aria-disabled="false"
class="react-contextmenu-item"
Expand Down
Loading
Loading