Skip to content

Commit 33c9f6a

Browse files
authored
Construct new stack tables with the correct column lengths. (#5906)
Fixes #5905.
1 parent 9cb77f1 commit 33c9f6a

File tree

3 files changed

+115
-42
lines changed

3 files changed

+115
-42
lines changed

src/profile-logic/data-structures.ts

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import type {
1313
RawSamplesTable,
1414
FrameTable,
1515
RawStackTable,
16-
StackTable,
1716
FuncTable,
1817
RawMarkerTable,
1918
JsAllocationsTable,
@@ -33,20 +32,6 @@ import type {
3332
* This module collects all of the creation of new empty profile data structures.
3433
*/
3534

36-
export function getEmptyStackTable(): StackTable {
37-
return {
38-
// Important!
39-
// If modifying this structure, please update all callers of this function to ensure
40-
// that they are pushing on correctly to the data structure. These pushes may not
41-
// be caught by the type system.
42-
frame: [],
43-
prefix: [],
44-
category: new Uint8Array(),
45-
subcategory: new Uint8Array(),
46-
length: 0,
47-
};
48-
}
49-
5035
export function getEmptySamplesTable(): RawSamplesTable {
5136
return {
5237
// Important!

src/profile-logic/profile-data.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2714,6 +2714,34 @@ export function createThreadFromDerivedTables(
27142714
return thread;
27152715
}
27162716

2717+
/**
2718+
* Throws if the column lengths of a StackTable don't match stackTable.length.
2719+
* Call this after constructing a new StackTable to catch bugs early.
2720+
*/
2721+
export function validateStackTableShape(stackTable: StackTable): void {
2722+
const { length, frame, prefix, category, subcategory } = stackTable;
2723+
if (frame.length !== length) {
2724+
throw new Error(
2725+
`StackTable frame column length ${frame.length} does not match stackTable.length ${length}`
2726+
);
2727+
}
2728+
if (prefix.length !== length) {
2729+
throw new Error(
2730+
`StackTable prefix column length ${prefix.length} does not match stackTable.length ${length}`
2731+
);
2732+
}
2733+
if (category.length !== length) {
2734+
throw new Error(
2735+
`StackTable category column length ${category.length} does not match stackTable.length ${length}`
2736+
);
2737+
}
2738+
if (subcategory.length !== length) {
2739+
throw new Error(
2740+
`StackTable subcategory column length ${subcategory.length} does not match stackTable.length ${length}`
2741+
);
2742+
}
2743+
}
2744+
27172745
/**
27182746
* Sometimes we want to update the stacks for a thread, for instance while searching
27192747
* for a text string, or doing a call tree transformation. This function abstracts
@@ -2738,6 +2766,7 @@ export function updateThreadStacksByGeneratingNewStackColumns(
27382766
markerData: Array<MarkerPayload | null>
27392767
) => Array<MarkerPayload | null>
27402768
): Thread {
2769+
validateStackTableShape(newStackTable);
27412770
const { jsAllocations, nativeAllocations, samples, markers } = thread;
27422771

27432772
const newSamples = {

src/profile-logic/transforms.ts

Lines changed: 86 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ import {
2020
getSearchFilteredMarkerIndexes,
2121
stringsToMarkerRegExps,
2222
} from '../profile-logic/marker-data';
23-
import { getEmptyStackTable } from './data-structures';
2423
import { getFunctionName } from './function-info';
2524
import { splitSearchString } from '../utils/string';
2625

@@ -766,7 +765,11 @@ export function mergeCallNode(
766765
// A root stack's prefix will be null. Maintain that relationship from old to new
767766
// stacks by mapping from null to null.
768767
oldStackToNewStack.set(null, null);
769-
const newStackTable = getEmptyStackTable();
768+
const newFrameCol = [];
769+
const newPrefixCol = [];
770+
const newCategoryCol = [];
771+
const newSubcategoryCol = [];
772+
let newLength = 0;
770773
// Provide two arrays to efficiently cache values for the algorithm. This probably
771774
// could be refactored to use only one array here.
772775
const stackDepths = [];
@@ -822,17 +825,28 @@ export function mergeCallNode(
822825
newStackPrefix === undefined ? null : newStackPrefix
823826
);
824827
} else {
825-
const newStackIndex = newStackTable.length++;
828+
const newStackIndex = newLength++;
826829
const newStackPrefix = oldStackToNewStack.get(prefix);
827-
newStackTable.prefix[newStackIndex] =
830+
newPrefixCol[newStackIndex] =
828831
newStackPrefix === undefined ? null : newStackPrefix;
829-
newStackTable.frame[newStackIndex] = frameIndex;
830-
newStackTable.category[newStackIndex] = category;
831-
newStackTable.subcategory[newStackIndex] = subcategory;
832+
newFrameCol[newStackIndex] = frameIndex;
833+
newCategoryCol[newStackIndex] = category;
834+
newSubcategoryCol[newStackIndex] = subcategory;
832835
oldStackToNewStack.set(stackIndex, newStackIndex);
833836
}
834837
}
835838

839+
const newStackTable = {
840+
frame: newFrameCol,
841+
prefix: newPrefixCol,
842+
category: new Uint8Array(newCategoryCol),
843+
subcategory:
844+
stackTable.subcategory instanceof Uint8Array
845+
? new Uint8Array(newSubcategoryCol)
846+
: new Uint16Array(newSubcategoryCol),
847+
length: newLength,
848+
};
849+
836850
return updateThreadStacks(
837851
thread,
838852
newStackTable,
@@ -1247,7 +1261,11 @@ export function focusSubtree(
12471261
// A root stack's prefix will be null. Maintain that relationship from old to new
12481262
// stacks by mapping from null to null.
12491263
oldStackToNewStack.set(null, null);
1250-
const newStackTable = getEmptyStackTable();
1264+
const newFrameCol = [];
1265+
const newPrefixCol = [];
1266+
const newCategoryCol = [];
1267+
const newSubcategoryCol = [];
1268+
let newLength = 0;
12511269
for (let stackIndex = 0; stackIndex < stackTable.length; stackIndex++) {
12521270
const prefix = stackTable.prefix[stackIndex];
12531271
const prefixMatchesUpTo = prefix !== null ? stackMatches[prefix] : 0;
@@ -1267,18 +1285,29 @@ export function focusSubtree(
12671285
}
12681286
}
12691287
if (stackMatchesUpTo === prefixDepth) {
1270-
const newStackIndex = newStackTable.length++;
1288+
const newStackIndex = newLength++;
12711289
const newStackPrefix = oldStackToNewStack.get(prefix);
1272-
newStackTable.prefix[newStackIndex] = newStackPrefix ?? null;
1273-
newStackTable.frame[newStackIndex] = frame;
1274-
newStackTable.category[newStackIndex] = category;
1275-
newStackTable.subcategory[newStackIndex] = subcategory;
1290+
newPrefixCol[newStackIndex] = newStackPrefix ?? null;
1291+
newFrameCol[newStackIndex] = frame;
1292+
newCategoryCol[newStackIndex] = category;
1293+
newSubcategoryCol[newStackIndex] = subcategory;
12761294
oldStackToNewStack.set(stackIndex, newStackIndex);
12771295
}
12781296
}
12791297
stackMatches[stackIndex] = stackMatchesUpTo;
12801298
}
12811299

1300+
const newStackTable = {
1301+
frame: newFrameCol,
1302+
prefix: newPrefixCol,
1303+
category: new Uint8Array(newCategoryCol),
1304+
subcategory:
1305+
stackTable.subcategory instanceof Uint8Array
1306+
? new Uint8Array(newSubcategoryCol)
1307+
: new Uint16Array(newSubcategoryCol),
1308+
length: newLength,
1309+
};
1310+
12821311
return updateThreadStacks(thread, newStackTable, (oldStack) => {
12831312
if (oldStack === null || stackMatches[oldStack] !== prefixDepth) {
12841313
return null;
@@ -1359,7 +1388,12 @@ export function focusFunction(
13591388
// Typed arrays are initialized to zero, which we interpret as null.
13601389
const oldStackToNewStackPlusOne = new Uint32Array(stackTable.length);
13611390

1362-
const newStackTable = getEmptyStackTable();
1391+
const newFrameCol = [];
1392+
const newPrefixCol = [];
1393+
const newCategoryCol = [];
1394+
const newSubcategoryCol = [];
1395+
let newLength = 0;
1396+
13631397
for (let stackIndex = 0; stackIndex < stackTable.length; stackIndex++) {
13641398
const prefix = stackTable.prefix[stackIndex];
13651399
const frameIndex = stackTable.frame[stackIndex];
@@ -1369,16 +1403,26 @@ export function focusFunction(
13691403
prefix === null ? 0 : oldStackToNewStackPlusOne[prefix];
13701404
const newPrefix = newPrefixPlusOne === 0 ? null : newPrefixPlusOne - 1;
13711405
if (newPrefix !== null || funcIndex === funcIndexToFocus) {
1372-
const newStackIndex = newStackTable.length++;
1373-
newStackTable.prefix[newStackIndex] = newPrefix;
1374-
newStackTable.frame[newStackIndex] = frameIndex;
1375-
newStackTable.category[newStackIndex] = stackTable.category[stackIndex];
1376-
newStackTable.subcategory[newStackIndex] =
1377-
stackTable.subcategory[stackIndex];
1406+
const newStackIndex = newLength++;
1407+
newPrefixCol[newStackIndex] = newPrefix;
1408+
newFrameCol[newStackIndex] = frameIndex;
1409+
newCategoryCol[newStackIndex] = stackTable.category[stackIndex];
1410+
newSubcategoryCol[newStackIndex] = stackTable.subcategory[stackIndex];
13781411
oldStackToNewStackPlusOne[stackIndex] = newStackIndex + 1;
13791412
}
13801413
}
13811414

1415+
const newStackTable = {
1416+
frame: newFrameCol,
1417+
prefix: newPrefixCol,
1418+
category: new Uint8Array(newCategoryCol),
1419+
subcategory:
1420+
stackTable.subcategory instanceof Uint8Array
1421+
? new Uint8Array(newSubcategoryCol)
1422+
: new Uint16Array(newSubcategoryCol),
1423+
length: newLength,
1424+
};
1425+
13821426
return updateThreadStacks(thread, newStackTable, (oldStack) => {
13831427
if (oldStack === null) {
13841428
return null;
@@ -1445,7 +1489,11 @@ export function focusCategory(thread: Thread, category: IndexIntoCategoryList) {
14451489
> = new Map();
14461490
oldStackToNewStack.set(null, null);
14471491

1448-
const newStackTable = getEmptyStackTable();
1492+
const newFrameCol = [];
1493+
const newPrefixCol = [];
1494+
const newCategoryCol = [];
1495+
const newSubcategoryCol = [];
1496+
let newLength = 0;
14491497

14501498
// fill the new stack table with the kept frames
14511499
for (let stackIndex = 0; stackIndex < stackTable.length; stackIndex++) {
@@ -1460,14 +1508,25 @@ export function focusCategory(thread: Thread, category: IndexIntoCategoryList) {
14601508
continue;
14611509
}
14621510

1463-
const newStackIndex = newStackTable.length++;
1464-
newStackTable.prefix[newStackIndex] = newPrefix;
1465-
newStackTable.frame[newStackIndex] = stackTable.frame[stackIndex];
1466-
newStackTable.category[newStackIndex] = stackTable.category[stackIndex];
1467-
newStackTable.subcategory[newStackIndex] =
1468-
stackTable.subcategory[stackIndex];
1511+
const newStackIndex = newLength++;
1512+
newPrefixCol[newStackIndex] = newPrefix;
1513+
newFrameCol[newStackIndex] = stackTable.frame[stackIndex];
1514+
newCategoryCol[newStackIndex] = stackTable.category[stackIndex];
1515+
newSubcategoryCol[newStackIndex] = stackTable.subcategory[stackIndex];
14691516
oldStackToNewStack.set(stackIndex, newStackIndex);
14701517
}
1518+
1519+
const newStackTable = {
1520+
frame: newFrameCol,
1521+
prefix: newPrefixCol,
1522+
category: new Uint8Array(newCategoryCol),
1523+
subcategory:
1524+
stackTable.subcategory instanceof Uint8Array
1525+
? new Uint8Array(newSubcategoryCol)
1526+
: new Uint16Array(newSubcategoryCol),
1527+
length: newLength,
1528+
};
1529+
14711530
const updated = updateThreadStacks(
14721531
thread,
14731532
newStackTable,

0 commit comments

Comments
 (0)