Skip to content

Commit 87841ba

Browse files
committed
Introduce RawStackTableBuilder for stack table construction.
`RawStackTable` is being prepared to allow `frame` to be an `Int32Array` in a later commit. `Int32Array` is fixed-size and doesn't support `push`, so the existing "push to .frame and bump .length" pattern needs a builder that uses a plain `number[]` during construction and converts to the final representation via `finishRawStackTableBuilder` at the end. Switch all stack table construction sites to use the builder. The builder still produces a plain `number[]` for `frame` in this commit; the type change happens in a follow-up.
1 parent 1c83cf2 commit 87841ba

13 files changed

Lines changed: 98 additions & 34 deletions

src/profile-logic/data-structures.ts

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ import type {
2727
CallNodeTable,
2828
SourceTable,
2929
SourceLocationTable,
30+
IndexIntoFrameTable,
31+
IndexIntoStackTable,
3032
} from 'firefox-profiler/types';
3133

3234
/**
@@ -47,7 +49,13 @@ export function getEmptySamplesTable(): RawSamplesTable {
4749
};
4850
}
4951

50-
export function getEmptyRawStackTable(): RawStackTable {
52+
export type RawStackTableBuilder = {
53+
frame: IndexIntoFrameTable[];
54+
prefix: Array<IndexIntoStackTable | null>;
55+
length: number;
56+
};
57+
58+
export function getRawStackTableBuilder(): RawStackTableBuilder {
5159
return {
5260
// Important!
5361
// If modifying this structure, please update all callers of this function to ensure
@@ -59,6 +67,27 @@ export function getEmptyRawStackTable(): RawStackTable {
5967
};
6068
}
6169

70+
export function getRawStackTableBuilderWithExistingContents(
71+
existing: RawStackTable
72+
): RawStackTableBuilder {
73+
return {
74+
frame: [...existing.frame],
75+
prefix: [...existing.prefix],
76+
length: existing.length,
77+
};
78+
}
79+
80+
export function finishRawStackTableBuilder(
81+
builder: RawStackTableBuilder
82+
): RawStackTable {
83+
const { frame, prefix, length } = builder;
84+
return {
85+
frame,
86+
prefix,
87+
length,
88+
};
89+
}
90+
6291
/**
6392
* Returns an empty samples table with eventDelay field instead of responsiveness.
6493
* eventDelay is a new field and it replaced responsiveness. We should still
@@ -393,7 +422,7 @@ export function getEmptyThread(overrides?: Partial<RawThread>): RawThread {
393422

394423
export function getEmptySharedData(): RawProfileSharedData {
395424
return {
396-
stackTable: getEmptyRawStackTable(),
425+
stackTable: finishRawStackTableBuilder(getRawStackTableBuilder()),
397426
frameTable: getEmptyFrameTable(),
398427
funcTable: getEmptyFuncTable(),
399428
resourceTable: getEmptyResourceTable(),

src/profile-logic/global-data-collector.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,14 @@
44

55
import { StringTable } from '../utils/string-table';
66
import {
7+
finishRawStackTableBuilder,
78
getEmptyFrameTable,
89
getEmptyFuncTable,
910
getEmptyNativeSymbolTable,
10-
getEmptyRawStackTable,
1111
getEmptyResourceTable,
1212
getEmptySourceTable,
1313
getEmptySourceLocationTable,
14+
getRawStackTableBuilder,
1415
} from './data-structures';
1516

1617
import type {
@@ -22,7 +23,6 @@ import type {
2223
RawProfileSharedData,
2324
SourceTable,
2425
FrameTable,
25-
RawStackTable,
2626
FuncTable,
2727
ResourceTable,
2828
NativeSymbolTable,
@@ -34,6 +34,7 @@ import type {
3434
Bytes,
3535
} from 'firefox-profiler/types';
3636
import { ResourceType } from 'firefox-profiler/types';
37+
import type { RawStackTableBuilder } from './data-structures';
3738

3839
/**
3940
* GlobalDataCollector collects data which is global in the processed profile
@@ -50,7 +51,7 @@ export class GlobalDataCollector {
5051
_stringTable: StringTable = StringTable.withBackingArray(this._stringArray);
5152
_sources: SourceTable = getEmptySourceTable();
5253
_frameTable: FrameTable = getEmptyFrameTable();
53-
_stackTable: RawStackTable = getEmptyRawStackTable();
54+
_stackTableBuilder: RawStackTableBuilder = getRawStackTableBuilder();
5455
_funcTable: FuncTable = getEmptyFuncTable();
5556
_resourceTable: ResourceTable = getEmptyResourceTable();
5657
_nativeSymbols: NativeSymbolTable = getEmptyNativeSymbolTable();
@@ -302,15 +303,15 @@ export class GlobalDataCollector {
302303
return this._frameTable;
303304
}
304305

305-
getStackTable(): RawStackTable {
306-
return this._stackTable;
306+
getStackTableBuilder(): RawStackTableBuilder {
307+
return this._stackTableBuilder;
307308
}
308309

309310
// Package up all de-duplicated global tables so that they can be embedded in
310311
// the profile.
311312
finish(): { libs: Lib[]; shared: RawProfileSharedData } {
312313
const shared: RawProfileSharedData = {
313-
stackTable: this._stackTable,
314+
stackTable: finishRawStackTableBuilder(this._stackTableBuilder),
314315
frameTable: this._frameTable,
315316
funcTable: this._funcTable,
316317
resourceTable: this._resourceTable,

src/profile-logic/import/chrome.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -501,7 +501,7 @@ async function processTracingEvents(
501501
const stringTable = globalDataCollector.getStringTable();
502502

503503
const frameTable = globalDataCollector.getFrameTable();
504-
const stackTable = globalDataCollector.getStackTable();
504+
const stackTable = globalDataCollector.getStackTableBuilder();
505505

506506
let profileEvents: (ProfileEvent | CpuProfileEvent)[] = (eventsByName.get(
507507
'Profile'

src/profile-logic/import/dhat.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ export function attemptToConvertDhat(json: unknown): Profile | null {
183183
profile.meta.product = dhat.cmd + ' (dhat)';
184184
profile.meta.importedFrom = `dhat`;
185185
const globalDataCollector = new GlobalDataCollector();
186-
const stackTable = globalDataCollector.getStackTable();
186+
const stackTable = globalDataCollector.getStackTableBuilder();
187187
const frameTable = globalDataCollector.getFrameTable();
188188
const stringTable = globalDataCollector.getStringTable();
189189

src/profile-logic/import/flame-graph.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ export function convertFlameGraphProfile(profileText: string): Profile {
6060
});
6161

6262
const frameTable = globalDataCollector.getFrameTable();
63-
const stackTable = globalDataCollector.getStackTable();
63+
const stackTable = globalDataCollector.getStackTableBuilder();
6464
const { samples } = thread;
6565

6666
// Maps to deduplicate stacks, frames, and functions.

src/profile-logic/import/simpleperf.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,9 @@ import {
2424
getEmptyFuncTable,
2525
getEmptyResourceTable,
2626
getEmptyFrameTable,
27-
getEmptyRawStackTable,
27+
getRawStackTableBuilder,
28+
finishRawStackTableBuilder,
29+
type RawStackTableBuilder,
2830
getEmptySamplesTable,
2931
getEmptyRawMarkerTable,
3032
getEmptyNativeSymbolTable,
@@ -189,15 +191,15 @@ class FirefoxFrameTable {
189191
class FirefoxSampleTable {
190192
strings: StringTable;
191193

192-
stackTable: RawStackTable = getEmptyRawStackTable();
194+
stackTable: RawStackTableBuilder = getRawStackTableBuilder();
193195
stackMap: Map<string, IndexIntoStackTable> = new Map();
194196

195197
constructor(strings: StringTable) {
196198
this.strings = strings;
197199
}
198200

199201
toJson(): RawStackTable {
200-
return this.stackTable;
202+
return finishRawStackTableBuilder(this.stackTable);
201203
}
202204

203205
findOrAddStack(

src/profile-logic/js-tracer.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
import {
55
getEmptySamplesTableWithEventDelay,
66
getEmptyRawMarkerTable,
7+
finishRawStackTableBuilder,
8+
getRawStackTableBuilderWithExistingContents,
79
} from './data-structures';
810
import { StringTable } from '../utils/string-table';
911
import { ensureExists } from '../utils/types';
@@ -512,7 +514,10 @@ export function convertJsTracerToThreadWithoutSamples(
512514
samples,
513515
};
514516

515-
const { funcTable, frameTable, stackTable } = shared;
517+
const { funcTable, frameTable } = shared;
518+
const stackTable = getRawStackTableBuilderWithExistingContents(
519+
shared.stackTable
520+
);
516521

517522
// Keep a stack of js tracer events, and end timings, that will be used to find
518523
// the stack prefixes. Once a JS tracer event starts past another event end, the
@@ -620,6 +625,9 @@ export function convertJsTracerToThreadWithoutSamples(
620625
unmatchedEventEnds[unmatchedIndex] = end;
621626
}
622627

628+
// Write the augmented stackTable back to the shared data.
629+
shared.stackTable = finishRawStackTableBuilder(stackTable);
630+
623631
return { thread, stackMap };
624632
}
625633

src/profile-logic/merge-compare.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ import {
1313
getEmptyNativeSymbolTable,
1414
getEmptyFrameTable,
1515
getEmptyFuncTable,
16-
getEmptyRawStackTable,
16+
getRawStackTableBuilder,
17+
finishRawStackTableBuilder,
1718
getEmptyRawMarkerTable,
1819
getEmptySamplesTableWithEventDelay,
1920
shallowCloneRawMarkerTable,
@@ -1117,7 +1118,7 @@ function mergeStackTables(
11171118
translationMapsForFrames: TranslationMapForFrames[]
11181119
): { stackTable: RawStackTable; translationMaps: TranslationMapForStacks[] } {
11191120
const translationMaps: TranslationMapForStacks[] = [];
1120-
const newStackTable = getEmptyRawStackTable();
1121+
const newStackTable = getRawStackTableBuilder();
11211122

11221123
profiles.forEach((profile, profileIndex) => {
11231124
const { stackTable } = profile.shared;
@@ -1143,7 +1144,10 @@ function mergeStackTables(
11431144
translationMaps.push(oldStackToNewStackPlusOne);
11441145
});
11451146

1146-
return { stackTable: newStackTable, translationMaps };
1147+
return {
1148+
stackTable: finishRawStackTableBuilder(newStackTable),
1149+
translationMaps,
1150+
};
11471151
}
11481152

11491153
/**

src/profile-logic/process-profile.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
getEmptyRawMarkerTable,
1717
getEmptyJsAllocationsTable,
1818
getEmptyUnbalancedNativeAllocationsTable,
19+
type RawStackTableBuilder,
1920
} from './data-structures';
2021
import { immutableUpdate, ensureExists } from '../utils/types';
2122
import { verifyMagic, SIMPLEPERF as SIMPLEPERF_MAGIC } from '../utils/magic';
@@ -53,7 +54,6 @@ import type {
5354
FrameTable,
5455
RawCounterSamplesTable,
5556
RawSamplesTable,
56-
RawStackTable,
5757
RawMarkerTable,
5858
LibMapping,
5959
IndexIntoStackTable,
@@ -549,7 +549,7 @@ function _processFrameTable(
549549
*/
550550
function _processStackTable(
551551
geckoStackTable: GeckoStackStruct,
552-
sharedStackTable: RawStackTable,
552+
sharedStackTable: RawStackTableBuilder,
553553
frameIndexOffset: IndexIntoFrameTable
554554
): IndexIntoStackTable {
555555
const stackIndexOffset = sharedStackTable.length;
@@ -1337,7 +1337,7 @@ function _processThread(
13371337
);
13381338
const stackIndexOffset = _processStackTable(
13391339
geckoStackTable,
1340-
globalDataCollector.getStackTable(),
1340+
globalDataCollector.getStackTableBuilder(),
13411341
frameIndexOffset
13421342
);
13431343

src/profile-logic/profile-data.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ import memoize from 'memoize-immutable';
66
import MixedTupleMap from 'mixedtuplemap';
77
import { oneLine } from 'common-tags';
88
import {
9-
getEmptyRawStackTable,
9+
getRawStackTableBuilder,
10+
finishRawStackTableBuilder,
1011
getEmptyCallNodeTable,
1112
shallowCloneFrameTable,
1213
shallowCloneFuncTable,
@@ -4343,7 +4344,7 @@ export function nudgeReturnAddresses(profile: Profile): Profile {
43434344
// Now the frame table contains adjusted / "nudged" addresses.
43444345

43454346
// Make a new stack table which refers to the adjusted frames.
4346-
const newStackTable = getEmptyRawStackTable();
4347+
const newStackTable = getRawStackTableBuilder();
43474348
const mapForSamplingSelfStacks = new Map<
43484349
null | IndexIntoStackTable,
43494350
null | IndexIntoStackTable
@@ -4385,7 +4386,7 @@ export function nudgeReturnAddresses(profile: Profile): Profile {
43854386
const newShared: RawProfileSharedData = {
43864387
...profile.shared,
43874388
frameTable: newFrameTable,
4388-
stackTable: newStackTable,
4389+
stackTable: finishRawStackTableBuilder(newStackTable),
43894390
};
43904391

43914392
const newThreads = updateRawThreadStacksSeparate(

0 commit comments

Comments
 (0)