Skip to content

Commit

Permalink
Split out part of CallTree into CallTreeInternal.
Browse files Browse the repository at this point in the history
This will let us add a second implementation of CallTreeInternal for the
inverted tree. What's still in CallTree will be used for both the inverted
and the non-inverted tree.
  • Loading branch information
mstange committed Jan 22, 2024
1 parent cd5e534 commit f764cbc
Show file tree
Hide file tree
Showing 2 changed files with 370 additions and 117 deletions.
174 changes: 110 additions & 64 deletions src/profile-logic/call-tree.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import type {
ExtraBadgeInfo,
BottomBoxInfo,
CallNodeLeafAndSummary,
SelfAndTotal,
} from 'firefox-profiler/types';

import ExtensionIcon from '../../res/img/svg/extension.svg';
Expand Down Expand Up @@ -63,89 +64,151 @@ function extractFaviconFromLibname(libname: string): string | null {
}
}

export class CallTree {
_categories: CategoryList;
interface CallTreeInternal {
hasChildren(callNodeIndex: IndexIntoCallNodeTable): boolean;
createChildren(nodeIndex: IndexIntoCallNodeTable): CallNodeChildren;
createRoots(): CallNodeChildren;
getSelfAndTotal(nodeIndex: IndexIntoCallNodeTable): SelfAndTotal;
findHeaviestPathInSubtree(
callNodeIndex: IndexIntoCallNodeTable
): CallNodePath;
}

export class CallTreeInternalImpl implements CallTreeInternal {
_callNodeInfo: CallNodeInfo;
_callNodeTable: CallNodeTable;
_callTreeTimings: CallTreeTimings;
_callNodeHasChildren: Uint8Array; // A table column matching the callNodeTable

constructor(callNodeInfo: CallNodeInfo, callTreeTimings: CallTreeTimings) {
this._callNodeInfo = callNodeInfo;
this._callNodeTable = callNodeInfo.getCallNodeTable();
this._callTreeTimings = callTreeTimings;
this._callNodeHasChildren = callTreeTimings.callNodeHasChildren;
}

_getFirstChildIndex(
callNodeIndex: IndexIntoCallNodeTable | -1
): IndexIntoCallNodeTable | -1 {
if (callNodeIndex === -1) {
return this._callNodeTable.length !== 0 ? 0 : -1;
}
const subtreeRangeEnd = this._callNodeTable.subtreeRangeEnd[callNodeIndex];
if (subtreeRangeEnd !== callNodeIndex + 1) {
return callNodeIndex + 1;
}
return -1;
}

createRoots() {
return this.createChildren(-1);
}

createChildren(callNodeIndex: IndexIntoCallNodeTable): CallNodeChildren {
const firstChild = this._getFirstChildIndex(callNodeIndex);
const children = [];
for (
let childCallNodeIndex = firstChild;
childCallNodeIndex !== -1;
childCallNodeIndex = this._callNodeTable.nextSibling[childCallNodeIndex]
) {
const childTotalSummary = this._callTreeTimings.total[childCallNodeIndex];
const childHasChildren = this._callNodeHasChildren[childCallNodeIndex];

if (childTotalSummary !== 0 || childHasChildren !== 0) {
children.push(childCallNodeIndex);
}
}
children.sort(
(a, b) =>
Math.abs(this._callTreeTimings.total[b]) -
Math.abs(this._callTreeTimings.total[a])
);
return children;
}

hasChildren(callNodeIndex: IndexIntoCallNodeTable): boolean {
return this._callNodeHasChildren[callNodeIndex] !== 0;
}

getSelfAndTotal(callNodeIndex: IndexIntoCallNodeTable): SelfAndTotal {
const self = this._callTreeTimings.self[callNodeIndex];
const total = this._callTreeTimings.total[callNodeIndex];
return { self, total };
}

findHeaviestPathInSubtree(
callNodeIndex: IndexIntoCallNodeTable
): CallNodePath {
const rangeEnd = this._callNodeTable.subtreeRangeEnd[callNodeIndex];

// Find the call node with the highest leaf time.
let maxNode = -1;
let maxAbs = 0;
for (let nodeIndex = callNodeIndex; nodeIndex < rangeEnd; nodeIndex++) {
const nodeLeaf = Math.abs(this._callTreeTimings.leaf[nodeIndex]);
if (maxNode === -1 || nodeLeaf > maxAbs) {
maxNode = nodeIndex;
maxAbs = nodeLeaf;
}
}

return this._callNodeInfo.getCallNodePathFromIndex(maxNode);
}
}

export class CallTree {
_categories: CategoryList;
_internal: CallTreeInternal;
_callNodeInfo: CallNodeInfo;
_callNodeTable: CallNodeTable;
_thread: Thread;
_rootTotalSummary: number;
_displayDataByIndex: Map<IndexIntoCallNodeTable, CallNodeDisplayData>;
// _children is indexed by IndexIntoCallNodeTable. Since they are
// integers, using an array directly is faster than going through a Map.
_children: Array<CallNodeChildren>;
_roots: IndexIntoCallNodeTable[];
_isHighPrecision: boolean;
_weightType: WeightType;

constructor(
thread: Thread,
categories: CategoryList,
callNodeInfo: CallNodeInfo,
callTreeTimings: CallTreeTimings,
internal: CallTreeInternal,
rootTotalSummary: number,
isHighPrecision: boolean,
weightType: WeightType
) {
this._categories = categories;
this._internal = internal;
this._callNodeInfo = callNodeInfo;
this._callNodeTable = callNodeInfo.getCallNodeTable();
this._callTreeTimings = callTreeTimings;
this._callNodeHasChildren = callTreeTimings.callNodeHasChildren;
this._thread = thread;
this._rootTotalSummary = callTreeTimings.rootTotalSummary;
this._rootTotalSummary = rootTotalSummary;
this._displayDataByIndex = new Map();
this._children = [];
this._roots = internal.createRoots();
this._isHighPrecision = isHighPrecision;
this._weightType = weightType;
}

_getFirstChildIndex(
callNodeIndex: IndexIntoCallNodeTable | -1
): IndexIntoCallNodeTable | -1 {
if (callNodeIndex === -1) {
return this._callNodeTable.length !== 0 ? 0 : -1;
}
const subtreeRangeEnd = this._callNodeTable.subtreeRangeEnd[callNodeIndex];
if (subtreeRangeEnd !== callNodeIndex + 1) {
return callNodeIndex + 1;
}
return -1;
}

getRoots() {
return this.getChildren(-1);
return this._roots;
}

getChildren(callNodeIndex: IndexIntoCallNodeTable): CallNodeChildren {
let children = this._children[callNodeIndex];
if (children === undefined) {
children = [];
const firstChild = this._getFirstChildIndex(callNodeIndex);
for (
let childCallNodeIndex = firstChild;
childCallNodeIndex !== -1;
childCallNodeIndex = this._callNodeTable.nextSibling[childCallNodeIndex]
) {
const childTotalSummary =
this._callTreeTimings.total[childCallNodeIndex];
const childHasChildren = this._callNodeHasChildren[childCallNodeIndex];

if (childTotalSummary !== 0 || childHasChildren !== 0) {
children.push(childCallNodeIndex);
}
}
children.sort(
(a, b) =>
Math.abs(this._callTreeTimings.total[b]) -
Math.abs(this._callTreeTimings.total[a])
);
children = this._internal.createChildren(callNodeIndex);
this._children[callNodeIndex] = children;
}
return children;
}

hasChildren(callNodeIndex: IndexIntoCallNodeTable): boolean {
return this._callNodeHasChildren[callNodeIndex] !== 0;
return this._internal.hasChildren(callNodeIndex);
}

_addDescendantsToSet(
Expand Down Expand Up @@ -181,9 +244,9 @@ export class CallTree {
const funcName = this._thread.stringTable.getString(
this._thread.funcTable.name[funcIndex]
);
const total = this._callTreeTimings.total[callNodeIndex];

const { self, total } = this._internal.getSelfAndTotal(callNodeIndex);
const totalRelative = total / this._rootTotalSummary;
const self = this._callTreeTimings.self[callNodeIndex];
const selfRelative = self / this._rootTotalSummary;

return {
Expand Down Expand Up @@ -382,30 +445,12 @@ export class CallTree {
if (callNodeIndex === null) {
return [];
}
const heaviestPath = this.findHeaviestPathInSubtree(callNodeIndex);
const heaviestPath =
this._internal.findHeaviestPathInSubtree(callNodeIndex);
const startingDepth = this._callNodeTable.depth[callNodeIndex];
const partialPath = heaviestPath.slice(startingDepth);
return partialPath.reverse();
}

findHeaviestPathInSubtree(
callNodeIndex: IndexIntoCallNodeTable
): CallNodePath {
const rangeEnd = this._callNodeTable.subtreeRangeEnd[callNodeIndex];

// Find the call node with the highest leaf time.
let maxNode = -1;
let maxAbs = 0;
for (let nodeIndex = callNodeIndex; nodeIndex < rangeEnd; nodeIndex++) {
const nodeLeaf = Math.abs(this._callTreeTimings.leaf[nodeIndex]);
if (maxNode === -1 || nodeLeaf > maxAbs) {
maxNode = nodeIndex;
maxAbs = nodeLeaf;
}
}

return this._callNodeInfo.getCallNodePathFromIndex(maxNode);
}
}

// In an inverted profile, all the amount of self unit (time, bytes, count, etc.) is
Expand Down Expand Up @@ -547,7 +592,8 @@ export function getCallTree(
thread,
categories,
callNodeInfo,
callTreeTimings,
new CallTreeInternalImpl(callNodeInfo, callTreeTimings),
callTreeTimings.rootTotalSummary,
Boolean(thread.isJsTracer),
weightType
);
Expand Down
Loading

0 comments on commit f764cbc

Please sign in to comment.