Skip to content

Commit f764cbc

Browse files
committed
Split out part of CallTree into CallTreeInternal.
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.
1 parent cd5e534 commit f764cbc

File tree

2 files changed

+370
-117
lines changed

2 files changed

+370
-117
lines changed

src/profile-logic/call-tree.js

Lines changed: 110 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import type {
2828
ExtraBadgeInfo,
2929
BottomBoxInfo,
3030
CallNodeLeafAndSummary,
31+
SelfAndTotal,
3132
} from 'firefox-profiler/types';
3233

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

66-
export class CallTree {
67-
_categories: CategoryList;
67+
interface CallTreeInternal {
68+
hasChildren(callNodeIndex: IndexIntoCallNodeTable): boolean;
69+
createChildren(nodeIndex: IndexIntoCallNodeTable): CallNodeChildren;
70+
createRoots(): CallNodeChildren;
71+
getSelfAndTotal(nodeIndex: IndexIntoCallNodeTable): SelfAndTotal;
72+
findHeaviestPathInSubtree(
73+
callNodeIndex: IndexIntoCallNodeTable
74+
): CallNodePath;
75+
}
76+
77+
export class CallTreeInternalImpl implements CallTreeInternal {
6878
_callNodeInfo: CallNodeInfo;
6979
_callNodeTable: CallNodeTable;
7080
_callTreeTimings: CallTreeTimings;
7181
_callNodeHasChildren: Uint8Array; // A table column matching the callNodeTable
82+
83+
constructor(callNodeInfo: CallNodeInfo, callTreeTimings: CallTreeTimings) {
84+
this._callNodeInfo = callNodeInfo;
85+
this._callNodeTable = callNodeInfo.getCallNodeTable();
86+
this._callTreeTimings = callTreeTimings;
87+
this._callNodeHasChildren = callTreeTimings.callNodeHasChildren;
88+
}
89+
90+
_getFirstChildIndex(
91+
callNodeIndex: IndexIntoCallNodeTable | -1
92+
): IndexIntoCallNodeTable | -1 {
93+
if (callNodeIndex === -1) {
94+
return this._callNodeTable.length !== 0 ? 0 : -1;
95+
}
96+
const subtreeRangeEnd = this._callNodeTable.subtreeRangeEnd[callNodeIndex];
97+
if (subtreeRangeEnd !== callNodeIndex + 1) {
98+
return callNodeIndex + 1;
99+
}
100+
return -1;
101+
}
102+
103+
createRoots() {
104+
return this.createChildren(-1);
105+
}
106+
107+
createChildren(callNodeIndex: IndexIntoCallNodeTable): CallNodeChildren {
108+
const firstChild = this._getFirstChildIndex(callNodeIndex);
109+
const children = [];
110+
for (
111+
let childCallNodeIndex = firstChild;
112+
childCallNodeIndex !== -1;
113+
childCallNodeIndex = this._callNodeTable.nextSibling[childCallNodeIndex]
114+
) {
115+
const childTotalSummary = this._callTreeTimings.total[childCallNodeIndex];
116+
const childHasChildren = this._callNodeHasChildren[childCallNodeIndex];
117+
118+
if (childTotalSummary !== 0 || childHasChildren !== 0) {
119+
children.push(childCallNodeIndex);
120+
}
121+
}
122+
children.sort(
123+
(a, b) =>
124+
Math.abs(this._callTreeTimings.total[b]) -
125+
Math.abs(this._callTreeTimings.total[a])
126+
);
127+
return children;
128+
}
129+
130+
hasChildren(callNodeIndex: IndexIntoCallNodeTable): boolean {
131+
return this._callNodeHasChildren[callNodeIndex] !== 0;
132+
}
133+
134+
getSelfAndTotal(callNodeIndex: IndexIntoCallNodeTable): SelfAndTotal {
135+
const self = this._callTreeTimings.self[callNodeIndex];
136+
const total = this._callTreeTimings.total[callNodeIndex];
137+
return { self, total };
138+
}
139+
140+
findHeaviestPathInSubtree(
141+
callNodeIndex: IndexIntoCallNodeTable
142+
): CallNodePath {
143+
const rangeEnd = this._callNodeTable.subtreeRangeEnd[callNodeIndex];
144+
145+
// Find the call node with the highest leaf time.
146+
let maxNode = -1;
147+
let maxAbs = 0;
148+
for (let nodeIndex = callNodeIndex; nodeIndex < rangeEnd; nodeIndex++) {
149+
const nodeLeaf = Math.abs(this._callTreeTimings.leaf[nodeIndex]);
150+
if (maxNode === -1 || nodeLeaf > maxAbs) {
151+
maxNode = nodeIndex;
152+
maxAbs = nodeLeaf;
153+
}
154+
}
155+
156+
return this._callNodeInfo.getCallNodePathFromIndex(maxNode);
157+
}
158+
}
159+
160+
export class CallTree {
161+
_categories: CategoryList;
162+
_internal: CallTreeInternal;
163+
_callNodeInfo: CallNodeInfo;
164+
_callNodeTable: CallNodeTable;
72165
_thread: Thread;
73166
_rootTotalSummary: number;
74167
_displayDataByIndex: Map<IndexIntoCallNodeTable, CallNodeDisplayData>;
75168
// _children is indexed by IndexIntoCallNodeTable. Since they are
76169
// integers, using an array directly is faster than going through a Map.
77170
_children: Array<CallNodeChildren>;
171+
_roots: IndexIntoCallNodeTable[];
78172
_isHighPrecision: boolean;
79173
_weightType: WeightType;
80174

81175
constructor(
82176
thread: Thread,
83177
categories: CategoryList,
84178
callNodeInfo: CallNodeInfo,
85-
callTreeTimings: CallTreeTimings,
179+
internal: CallTreeInternal,
180+
rootTotalSummary: number,
86181
isHighPrecision: boolean,
87182
weightType: WeightType
88183
) {
89184
this._categories = categories;
185+
this._internal = internal;
90186
this._callNodeInfo = callNodeInfo;
91187
this._callNodeTable = callNodeInfo.getCallNodeTable();
92-
this._callTreeTimings = callTreeTimings;
93-
this._callNodeHasChildren = callTreeTimings.callNodeHasChildren;
94188
this._thread = thread;
95-
this._rootTotalSummary = callTreeTimings.rootTotalSummary;
189+
this._rootTotalSummary = rootTotalSummary;
96190
this._displayDataByIndex = new Map();
97191
this._children = [];
192+
this._roots = internal.createRoots();
98193
this._isHighPrecision = isHighPrecision;
99194
this._weightType = weightType;
100195
}
101196

102-
_getFirstChildIndex(
103-
callNodeIndex: IndexIntoCallNodeTable | -1
104-
): IndexIntoCallNodeTable | -1 {
105-
if (callNodeIndex === -1) {
106-
return this._callNodeTable.length !== 0 ? 0 : -1;
107-
}
108-
const subtreeRangeEnd = this._callNodeTable.subtreeRangeEnd[callNodeIndex];
109-
if (subtreeRangeEnd !== callNodeIndex + 1) {
110-
return callNodeIndex + 1;
111-
}
112-
return -1;
113-
}
114-
115197
getRoots() {
116-
return this.getChildren(-1);
198+
return this._roots;
117199
}
118200

119201
getChildren(callNodeIndex: IndexIntoCallNodeTable): CallNodeChildren {
120202
let children = this._children[callNodeIndex];
121203
if (children === undefined) {
122-
children = [];
123-
const firstChild = this._getFirstChildIndex(callNodeIndex);
124-
for (
125-
let childCallNodeIndex = firstChild;
126-
childCallNodeIndex !== -1;
127-
childCallNodeIndex = this._callNodeTable.nextSibling[childCallNodeIndex]
128-
) {
129-
const childTotalSummary =
130-
this._callTreeTimings.total[childCallNodeIndex];
131-
const childHasChildren = this._callNodeHasChildren[childCallNodeIndex];
132-
133-
if (childTotalSummary !== 0 || childHasChildren !== 0) {
134-
children.push(childCallNodeIndex);
135-
}
136-
}
137-
children.sort(
138-
(a, b) =>
139-
Math.abs(this._callTreeTimings.total[b]) -
140-
Math.abs(this._callTreeTimings.total[a])
141-
);
204+
children = this._internal.createChildren(callNodeIndex);
142205
this._children[callNodeIndex] = children;
143206
}
144207
return children;
145208
}
146209

147210
hasChildren(callNodeIndex: IndexIntoCallNodeTable): boolean {
148-
return this._callNodeHasChildren[callNodeIndex] !== 0;
211+
return this._internal.hasChildren(callNodeIndex);
149212
}
150213

151214
_addDescendantsToSet(
@@ -181,9 +244,9 @@ export class CallTree {
181244
const funcName = this._thread.stringTable.getString(
182245
this._thread.funcTable.name[funcIndex]
183246
);
184-
const total = this._callTreeTimings.total[callNodeIndex];
247+
248+
const { self, total } = this._internal.getSelfAndTotal(callNodeIndex);
185249
const totalRelative = total / this._rootTotalSummary;
186-
const self = this._callTreeTimings.self[callNodeIndex];
187250
const selfRelative = self / this._rootTotalSummary;
188251

189252
return {
@@ -382,30 +445,12 @@ export class CallTree {
382445
if (callNodeIndex === null) {
383446
return [];
384447
}
385-
const heaviestPath = this.findHeaviestPathInSubtree(callNodeIndex);
448+
const heaviestPath =
449+
this._internal.findHeaviestPathInSubtree(callNodeIndex);
386450
const startingDepth = this._callNodeTable.depth[callNodeIndex];
387451
const partialPath = heaviestPath.slice(startingDepth);
388452
return partialPath.reverse();
389453
}
390-
391-
findHeaviestPathInSubtree(
392-
callNodeIndex: IndexIntoCallNodeTable
393-
): CallNodePath {
394-
const rangeEnd = this._callNodeTable.subtreeRangeEnd[callNodeIndex];
395-
396-
// Find the call node with the highest leaf time.
397-
let maxNode = -1;
398-
let maxAbs = 0;
399-
for (let nodeIndex = callNodeIndex; nodeIndex < rangeEnd; nodeIndex++) {
400-
const nodeLeaf = Math.abs(this._callTreeTimings.leaf[nodeIndex]);
401-
if (maxNode === -1 || nodeLeaf > maxAbs) {
402-
maxNode = nodeIndex;
403-
maxAbs = nodeLeaf;
404-
}
405-
}
406-
407-
return this._callNodeInfo.getCallNodePathFromIndex(maxNode);
408-
}
409454
}
410455

411456
// In an inverted profile, all the amount of self unit (time, bytes, count, etc.) is
@@ -547,7 +592,8 @@ export function getCallTree(
547592
thread,
548593
categories,
549594
callNodeInfo,
550-
callTreeTimings,
595+
new CallTreeInternalImpl(callNodeInfo, callTreeTimings),
596+
callTreeTimings.rootTotalSummary,
551597
Boolean(thread.isJsTracer),
552598
weightType
553599
);

0 commit comments

Comments
 (0)