diff --git a/docs-developer/CHANGELOG-formats.md b/docs-developer/CHANGELOG-formats.md index dcbcb8f484..0ce87b2770 100644 --- a/docs-developer/CHANGELOG-formats.md +++ b/docs-developer/CHANGELOG-formats.md @@ -6,6 +6,19 @@ Note that this is not an exhaustive list. Processed profile format upgraders can ## Processed profile format +### Version 72 + +The columns of the native symbol table (`profile.shared.nativeSymbols`) can now optionally be stored as typed arrays, for profiles loaded from [JsonSlabs](https://github.com/mstange/json-slabs/) files (.jslb, .jslb.gz). Regular JS / JSON arrays are still accepted. + +The "function size not known" sentinel value has changed from `null` to `-1` in both representations (JSON and JSLB). + +The column types are as follows: + +- `libIndex` (`Int32Array`) +- `address` (`Uint32Array`) +- `name` (`Int32Array`) +- `functionSize` (`Int32Array`) + ### Version 71 The frame table (`profile.shared.frameTable`) representation changed in such a way that all its columns can now be typed arrays when using [JsonSlabs](https://github.com/mstange/json-slabs/) profiles. diff --git a/src/app-logic/constants.ts b/src/app-logic/constants.ts index e0f67bd2ff..034f51bdc5 100644 --- a/src/app-logic/constants.ts +++ b/src/app-logic/constants.ts @@ -12,7 +12,7 @@ export const GECKO_PROFILE_VERSION = 36; // The current version of the "processed" profile format. // Please don't forget to update the processed profile format changelog in // `docs-developer/CHANGELOG-formats.md`. -export const PROCESSED_PROFILE_VERSION = 71; +export const PROCESSED_PROFILE_VERSION = 72; // The following are the margin sizes for the left and right of the timeline. Independent // components need to share these values. diff --git a/src/profile-logic/data-structures.ts b/src/profile-logic/data-structures.ts index 5a97b81b9a..257db159ea 100644 --- a/src/profile-logic/data-structures.ts +++ b/src/profile-logic/data-structures.ts @@ -6,7 +6,11 @@ import { GECKO_PROFILE_VERSION, PROCESSED_PROFILE_VERSION, } from '../app-logic/constants'; -import { toUint8OrUint16Array, valuesFitInUint8 } from '../utils/typed-arrays'; +import { + toFloat64ArraySetNullToZero, + toUint8OrUint16Array, + valuesFitInUint8, +} from '../utils/typed-arrays'; import type { RawProfileSharedData, @@ -20,7 +24,7 @@ import type { FuncTable, RawMarkerTable, ResourceTable, - NativeSymbolTable, + RawNativeSymbolTable, Profile, ExtensionTable, CategoryList, @@ -30,13 +34,13 @@ import type { SourceLocationTable, IndexIntoFrameTable, IndexIntoFuncTable, + IndexIntoLibs, IndexIntoStackTable, IndexIntoStringTable, IndexIntoCategoryList, IndexIntoSubcategoryListForCategory, IndexIntoNativeSymbolTable, IndexIntoSourceLocationTable, - IndexIntoLibs, InnerWindowID, Address, Bytes, @@ -208,6 +212,22 @@ export function finishRawSamplesTableBuilder( }; } +export function getRawMarkerTableBuilder(): RawMarkerTableBuilder { + return { + // Important! + // If modifying this structure, please update all callers of this function to ensure + // that they are pushing on correctly to the data structure. These pushes may not + // be caught by the type system. + data: [], + name: [], + startTime: [], + endTime: [], + phase: [], + category: [], + length: 0, + }; +} + export function getRawMarkerTableBuilderFromExisting( markerTable: RawMarkerTable ): RawMarkerTableBuilder { @@ -230,6 +250,19 @@ export function getRawMarkerTableBuilderFromExisting( return builder; } +export function finishRawMarkerTableBuilder( + builder: RawMarkerTableBuilder +): RawMarkerTable { + return { + ...builder, + // The nulls in these columns become zeros. This is fine: whether a marker's + // start / end time is meaningful is determined by its phase, and the times + // which are not used are allowed to be arbitrary values. + startTime: toFloat64ArraySetNullToZero(builder.startTime), + endTime: toFloat64ArraySetNullToZero(builder.endTime), + }; +} + export function getRawStackTableBuilderWithExistingContents( existing: RawStackTable ): RawStackTableBuilder { @@ -408,61 +441,66 @@ export function shallowCloneSourceLocationTable( }; } -export function shallowCloneNativeSymbolTable( - nativeSymbols: NativeSymbolTable -): NativeSymbolTable { +export type RawNativeSymbolTableBuilder = { + libIndex: IndexIntoLibs[]; + address: Address[]; + name: IndexIntoStringTable[]; + functionSize: Array; + length: number; +}; + +export function getRawNativeSymbolTableBuilder(): RawNativeSymbolTableBuilder { return { // Important! // If modifying this structure, please update all callers of this function to ensure // that they are pushing on correctly to the data structure. These pushes may not // be caught by the type system. - libIndex: nativeSymbols.libIndex.slice(), - address: nativeSymbols.address.slice(), - name: nativeSymbols.name.slice(), - functionSize: nativeSymbols.functionSize.slice(), - length: nativeSymbols.length, + libIndex: [], + address: [], + name: [], + functionSize: [], + length: 0, }; } -export function getEmptyResourceTable(): ResourceTable { +export function getRawNativeSymbolTableBuilderWithExistingContents( + nativeSymbols: RawNativeSymbolTable +): RawNativeSymbolTableBuilder { return { // Important! // If modifying this structure, please update all callers of this function to ensure // that they are pushing on correctly to the data structure. These pushes may not // be caught by the type system. - name: [], - host: [], - type: [], - length: 0, + libIndex: Array.from(nativeSymbols.libIndex), + address: Array.from(nativeSymbols.address), + name: Array.from(nativeSymbols.name), + functionSize: Array.from(nativeSymbols.functionSize), + length: nativeSymbols.length, }; } -export function getEmptyNativeSymbolTable(): NativeSymbolTable { +export function finishRawNativeSymbolTableBuilder( + builder: RawNativeSymbolTableBuilder +): RawNativeSymbolTable { return { - // Important! - // If modifying this structure, please update all callers of this function to ensure - // that they are pushing on correctly to the data structure. These pushes may not - // be caught by the type system. - libIndex: [], - address: [], - name: [], - functionSize: [], - length: 0, + libIndex: new Int32Array(builder.libIndex), + // Uint32Array, like frameTable.address, so that the two can be compared. + address: new Uint32Array(builder.address), + name: new Int32Array(builder.name), + functionSize: new Int32Array(builder.functionSize), + length: builder.length, }; } -export function getEmptyRawMarkerTable(): RawMarkerTableBuilder { - // Important! - // If modifying this structure, please update all callers of this function to ensure - // that they are pushing on correctly to the data structure. These pushes may not - // be caught by the type system. +export function getEmptyResourceTable(): ResourceTable { return { - data: [], + // Important! + // If modifying this structure, please update all callers of this function to ensure + // that they are pushing on correctly to the data structure. These pushes may not + // be caught by the type system. name: [], - startTime: [], - endTime: [], - phase: [], - category: [], + host: [], + type: [], length: 0, }; } @@ -625,7 +663,7 @@ export function getEmptyThread(overrides?: Partial): RawThread { samples: finishRawSamplesTableBuilder( getRawSamplesTableBuilderWithEventDelay() ), - markers: getEmptyRawMarkerTable(), + markers: finishRawMarkerTableBuilder(getRawMarkerTableBuilder()), }; return { @@ -640,7 +678,9 @@ export function getEmptySharedData(): RawProfileSharedData { frameTable: finishRawFrameTableBuilder(getRawFrameTableBuilder()), funcTable: getEmptyFuncTable(), resourceTable: getEmptyResourceTable(), - nativeSymbols: getEmptyNativeSymbolTable(), + nativeSymbols: finishRawNativeSymbolTableBuilder( + getRawNativeSymbolTableBuilder() + ), sources: getEmptySourceTable(), stringArray: [], sourceLocationTable: getEmptySourceLocationTable(), diff --git a/src/profile-logic/global-data-collector.ts b/src/profile-logic/global-data-collector.ts index 01ae67b233..894347eec7 100644 --- a/src/profile-logic/global-data-collector.ts +++ b/src/profile-logic/global-data-collector.ts @@ -5,10 +5,11 @@ import { StringTable } from '../utils/string-table'; import { finishRawFrameTableBuilder, + finishRawNativeSymbolTableBuilder, finishRawStackTableBuilder, getRawFrameTableBuilder, + getRawNativeSymbolTableBuilder, getEmptyFuncTable, - getEmptyNativeSymbolTable, getEmptyResourceTable, getEmptySourceTable, getEmptySourceLocationTable, @@ -25,7 +26,6 @@ import type { SourceTable, FuncTable, ResourceTable, - NativeSymbolTable, IndexIntoResourceTable, IndexIntoFuncTable, ExtensionTable, @@ -36,6 +36,7 @@ import type { import { ResourceType } from 'firefox-profiler/types'; import type { RawFrameTableBuilder, + RawNativeSymbolTableBuilder, RawStackTableBuilder, } from './data-structures'; @@ -57,7 +58,8 @@ export class GlobalDataCollector { _stackTableBuilder: RawStackTableBuilder = getRawStackTableBuilder(); _funcTable: FuncTable = getEmptyFuncTable(); _resourceTable: ResourceTable = getEmptyResourceTable(); - _nativeSymbols: NativeSymbolTable = getEmptyNativeSymbolTable(); + _nativeSymbols: RawNativeSymbolTableBuilder = + getRawNativeSymbolTableBuilder(); _funcKeyToFuncIndex: Map = new Map(); _nativeSymbolKeyToNativeSymbolIndex: Map = new Map(); @@ -273,7 +275,7 @@ export class GlobalDataCollector { this._nativeSymbols.libIndex[nativeSymbolIndex] = libIndex; this._nativeSymbols.address[nativeSymbolIndex] = address; this._nativeSymbols.name[nativeSymbolIndex] = name; - this._nativeSymbols.functionSize[nativeSymbolIndex] = functionSize; + this._nativeSymbols.functionSize[nativeSymbolIndex] = functionSize ?? -1; this._nativeSymbolKeyToNativeSymbolIndex.set(key, nativeSymbolIndex); } return nativeSymbolIndex; @@ -299,7 +301,7 @@ export class GlobalDataCollector { frameTable: finishRawFrameTableBuilder(this._frameTable), funcTable: this._funcTable, resourceTable: this._resourceTable, - nativeSymbols: this._nativeSymbols, + nativeSymbols: finishRawNativeSymbolTableBuilder(this._nativeSymbols), stringArray: this._stringArray, sources: this._sources, sourceLocationTable: getEmptySourceLocationTable(), diff --git a/src/profile-logic/import/chrome.ts b/src/profile-logic/import/chrome.ts index 06b723b68b..de4a402928 100644 --- a/src/profile-logic/import/chrome.ts +++ b/src/profile-logic/import/chrome.ts @@ -12,6 +12,7 @@ import { FrameFlag } from 'firefox-profiler/types'; import { finishRawSamplesTableBuilder, + finishRawMarkerTableBuilder, getEmptyProfile, getEmptyThread, getRawSamplesTableBuilderWithEventDelay, @@ -431,7 +432,6 @@ function getThreadInfo( nodeIdToStackId.set(undefined, null); const markers = getRawMarkerTableBuilderFromExisting(thread.markers); - thread.markers = markers; const threadInfo: ThreadInfo = { thread, @@ -851,6 +851,7 @@ async function processTracingEvents( for (const [thread, threadInfo] of threadInfoByThread) { thread.samples = finishRawSamplesTableBuilder(threadInfo.samples); + thread.markers = finishRawMarkerTableBuilder(threadInfo.markers); } return profile; diff --git a/src/profile-logic/import/simpleperf.ts b/src/profile-logic/import/simpleperf.ts index c780515b71..b9d1a56b2c 100644 --- a/src/profile-logic/import/simpleperf.ts +++ b/src/profile-logic/import/simpleperf.ts @@ -32,8 +32,10 @@ import { type RawStackTableBuilder, getRawSamplesTableBuilder, type RawSamplesTableBuilder, - getEmptyRawMarkerTable, - getEmptyNativeSymbolTable, + getRawMarkerTableBuilder, + finishRawMarkerTableBuilder, + getRawNativeSymbolTableBuilder, + finishRawNativeSymbolTableBuilder, getEmptySourceTable, getEmptySourceLocationTable, } from 'firefox-profiler/profile-logic/data-structures'; @@ -239,7 +241,9 @@ class FirefoxSharedData { frameTable: this.frameTable.toJson(), funcTable: this.funcTable.toJson(), resourceTable: this.resourceTable.toJson(), - nativeSymbols: getEmptyNativeSymbolTable(), + nativeSymbols: finishRawNativeSymbolTableBuilder( + getRawNativeSymbolTableBuilder() + ), sources: getEmptySourceTable(), stringArray: this.stringArray, sourceLocationTable: getEmptySourceLocationTable(), @@ -292,7 +296,7 @@ class FirefoxThread { pid: this.pid.toString(), tid: this.tid, samples: finishRawSamplesTableBuilder(this.sampleTable), - markers: getEmptyRawMarkerTable(), + markers: finishRawMarkerTableBuilder(getRawMarkerTableBuilder()), }; } diff --git a/src/profile-logic/js-tracer.ts b/src/profile-logic/js-tracer.ts index a3f2d20382..b7f8aac500 100644 --- a/src/profile-logic/js-tracer.ts +++ b/src/profile-logic/js-tracer.ts @@ -3,7 +3,8 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ import { getRawSamplesTableBuilderWithEventDelay, - getEmptyRawMarkerTable, + getRawMarkerTableBuilder, + finishRawMarkerTableBuilder, finishRawFrameTableBuilder, finishRawSamplesTableBuilder, finishRawStackTableBuilder, @@ -511,7 +512,7 @@ export function convertJsTracerToThreadWithoutSamples( weight: [], weightType: 'tracing-ms', }; - const markers = getEmptyRawMarkerTable(); + const markers = getRawMarkerTableBuilder(); const thread: RawThread = { ...fromThread, @@ -646,6 +647,7 @@ export function convertJsTracerToThreadWithoutSamples( shared.stackTable = finishRawStackTableBuilder(stackTable); shared.frameTable = finishRawFrameTableBuilder(frameTable); thread.samples = finishRawSamplesTableBuilder(samples); + thread.markers = finishRawMarkerTableBuilder(markers); return { thread, stackMap }; } diff --git a/src/profile-logic/marker-data.ts b/src/profile-logic/marker-data.ts index 5c196abd14..2e3d3b518b 100644 --- a/src/profile-logic/marker-data.ts +++ b/src/profile-logic/marker-data.ts @@ -3,7 +3,8 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ import { getDefaultCategories, - getEmptyRawMarkerTable, + getRawMarkerTableBuilder, + finishRawMarkerTableBuilder, type RawMarkerTableBuilder, } from './data-structures'; import { getFriendlyThreadName, getTimeRangeForThread } from './profile-data'; @@ -1115,7 +1116,7 @@ export function filterRawMarkerTableToRange( rangeStart: number, rangeEnd: number ): RawMarkerTable { - const newMarkerTable = getEmptyRawMarkerTable(); + const newMarkerTable = getRawMarkerTableBuilder(); if (markerTable.threadId) { newMarkerTable.threadId = []; } @@ -1140,7 +1141,7 @@ export function filterRawMarkerTableToRange( newMarkerTable.length++; } - return newMarkerTable; + return finishRawMarkerTableBuilder(newMarkerTable); } /** @@ -1192,7 +1193,7 @@ export function filterRawMarkerTableToRangeWithMarkersToDelete( rawMarkerTable: RawMarkerTableBuilder; oldMarkerIndexToNew: Map; } { - const newMarkerTable = getEmptyRawMarkerTable(); + const newMarkerTable = getRawMarkerTableBuilder(); const newThreadId: (Tid | null)[] = []; if (oldMarkerTable.threadId) { newMarkerTable.threadId = newThreadId; diff --git a/src/profile-logic/merge-compare.ts b/src/profile-logic/merge-compare.ts index e09b38b507..3304cfcf13 100644 --- a/src/profile-logic/merge-compare.ts +++ b/src/profile-logic/merge-compare.ts @@ -10,16 +10,18 @@ import { adjustMarkerTimestamps } from './process-profile'; import { getEmptyProfile, getEmptyResourceTable, - getEmptyNativeSymbolTable, + getRawNativeSymbolTableBuilder, + finishRawNativeSymbolTableBuilder, finishRawFrameTableBuilder, finishRawSamplesTableBuilder, getRawFrameTableBuilder, getEmptyFuncTable, getRawStackTableBuilder, finishRawStackTableBuilder, - getEmptyRawMarkerTable, + getRawMarkerTableBuilder, getRawSamplesTableBuilderWithEventDelay, getRawMarkerTableBuilderFromExisting, + finishRawMarkerTableBuilder, getEmptySourceTable, type RawMarkerTableBuilder, } from './data-structures'; @@ -56,7 +58,7 @@ import type { FuncTable, RawFrameTable, Lib, - NativeSymbolTable, + RawNativeSymbolTable, ResourceTable, RawSamplesTable, RawStackTable, @@ -882,13 +884,13 @@ function mergeNativeSymbolTables( translationMapsForStrings: TranslationMapForStrings[], translationMapsForLibs: TranslationMapForLibs[] ): { - nativeSymbols: NativeSymbolTable; + nativeSymbols: RawNativeSymbolTable; translationMaps: TranslationMapForNativeSymbols[]; } { const mapOfInsertedNativeSymbols: Map = new Map(); const translationMaps: TranslationMapForNativeSymbols[] = []; - const newNativeSymbols = getEmptyNativeSymbolTable(); + const newNativeSymbols = getRawNativeSymbolTableBuilder(); profiles.forEach((profile, profileIndex) => { const oldLibToNewLibPlusOne = translationMapsForLibs[profileIndex]; @@ -934,7 +936,10 @@ function mergeNativeSymbolTables( translationMaps.push(oldNativeSymbolToNewNativeSymbolPlusOne); }); - return { nativeSymbols: newNativeSymbols, translationMaps }; + return { + nativeSymbols: finishRawNativeSymbolTableBuilder(newNativeSymbols), + translationMaps, + }; } /** @@ -1269,7 +1274,7 @@ function getComparisonThread( tid: 'Diff between 1 and 2', isMainThread: true, samples: newSamples, - markers: getEmptyRawMarkerTable(), + markers: finishRawMarkerTableBuilder(getRawMarkerTableBuilder()), }; return mergedThread; @@ -1440,7 +1445,7 @@ function combineSamplesForMerging(threads: RawThread[]): RawSamplesTable { function mergeMarkers(threads: RawThread[]): RawMarkerTable { const newThreadId: Array = []; const newMarkerTable: RawMarkerTableBuilder = { - ...getEmptyRawMarkerTable(), + ...getRawMarkerTableBuilder(), threadId: newThreadId, }; @@ -1461,7 +1466,7 @@ function mergeMarkers(threads: RawThread[]): RawMarkerTable { } }); - return newMarkerTable; + return finishRawMarkerTableBuilder(newMarkerTable); } /** @@ -1510,7 +1515,7 @@ function getThreadMarkersAndScreenshotMarkers( } } - return targetMarkerTable; + return finishRawMarkerTableBuilder(targetMarkerTable); } /** diff --git a/src/profile-logic/process-profile.ts b/src/profile-logic/process-profile.ts index 8be48440cb..644e728a24 100644 --- a/src/profile-logic/process-profile.ts +++ b/src/profile-logic/process-profile.ts @@ -15,8 +15,9 @@ import { finishRawBalancedNativeAllocationsTableBuilder, finishRawJsAllocationsTableBuilder, finishRawUnbalancedNativeAllocationsTableBuilder, + finishRawMarkerTableBuilder, getEmptyExtensions, - getEmptyRawMarkerTable, + getRawMarkerTableBuilder, getEmptyRawJsAllocationsTable, getEmptyRawUnbalancedNativeAllocationsTable, getRawMarkerTableBuilderFromExisting, @@ -705,7 +706,7 @@ function _processMarkers( jsAllocations: RawJsAllocationsTable | null; nativeAllocations: RawNativeAllocationsTable | null; } { - const markers = getEmptyRawMarkerTable(); + const markers = getRawMarkerTableBuilder(); const jsAllocations = getEmptyRawJsAllocationsTable(); const inProgressNativeAllocations = getEmptyRawUnbalancedNativeAllocationsTable(); @@ -829,7 +830,7 @@ function _processMarkers( } return { - markers: markers, + markers: finishRawMarkerTableBuilder(markers), jsAllocations: jsAllocations.length === 0 ? null @@ -2153,7 +2154,7 @@ function convertSharedTablesEligibleColumns( shared: RawProfileSharedData, categories: CategoryList | undefined ): RawProfileSharedData { - const { stackTable, frameTable } = shared; + const { stackTable, frameTable, nativeSymbols } = shared; return { ...shared, stackTable: { @@ -2178,6 +2179,13 @@ function convertSharedTablesEligibleColumns( column: toInt32Array(frameTable.column), originalLocation: toInt32Array(frameTable.originalLocation), }, + nativeSymbols: { + libIndex: toInt32Array(nativeSymbols.libIndex), + address: toUint32Array(nativeSymbols.address), + name: toInt32Array(nativeSymbols.name), + functionSize: toInt32Array(nativeSymbols.functionSize), + length: nativeSymbols.length, + }, }; } @@ -2547,11 +2555,9 @@ export function processVisualMetrics( const mainThreadMarkers = getRawMarkerTableBuilderFromExisting( mainThread.markers ); - mainThread.markers = mainThreadMarkers; const tabThreadMarkers = getRawMarkerTableBuilderFromExisting( tabThread.markers ); - tabThread.markers = tabThreadMarkers; function maybeAddMetricMarker( markers: RawMarkerTableBuilder, @@ -2587,12 +2593,12 @@ export function processVisualMetrics( if (stringTable.hasString('Navigation::Start')) { const navigationStartStrIdx = stringTable.indexForString('Navigation::Start'); - const navigationStartMarkerIdx = tabThread.markers.name.findIndex( + const navigationStartMarkerIdx = tabThreadMarkers.name.findIndex( (m) => m === navigationStartStrIdx ); if (navigationStartMarkerIdx !== -1) { navigationStartTime = - tabThread.markers.startTime[navigationStartMarkerIdx]; + tabThreadMarkers.startTime[navigationStartMarkerIdx]; } } @@ -2664,6 +2670,9 @@ export function processVisualMetrics( ); } } + + mainThread.markers = finishRawMarkerTableBuilder(mainThreadMarkers); + tabThread.markers = finishRawMarkerTableBuilder(tabThreadMarkers); } /** diff --git a/src/profile-logic/processed-profile-versioning.ts b/src/profile-logic/processed-profile-versioning.ts index c009bd052d..e3aad643c6 100644 --- a/src/profile-logic/processed-profile-versioning.ts +++ b/src/profile-logic/processed-profile-versioning.ts @@ -3433,6 +3433,33 @@ const _upgraders: { frameTable.address = new Uint32Array(frameTable.address); } }, + [72]: (profile: any) => { + // The columns of the native symbol table (`profile.shared.nativeSymbols`) + // can now optionally be stored as typed arrays: + // - `libIndex` (`Int32Array`) + // - `address` (`Int32Array`) + // - `name` (`Int32Array`) + // - `functionSize` (`Int32Array`). + // + // Regular JS / JSON arrays are still accepted. + // + // For `functionSize`, the sentinel for "size unknown" is now `-1`. + // + // This upgrader also removes any `null` values in the `address` column; + // the `address` column never allowed null according to the type, but the + // upgrader for version 36 inserted null in some cases, so we fix it here. + const { nativeSymbols } = profile.shared; + for (let i = 0; i < nativeSymbols.length; i++) { + // null -> -1 for functionSize: + if (nativeSymbols.functionSize[i] === null) { + nativeSymbols.functionSize[i] = -1; + } + // null -> 0 for address (null was never valid but the 36 upgrader used it) + if (nativeSymbols.address[i] === null) { + nativeSymbols.address[i] = 0; + } + } + }, // If you add a new upgrader here, please document the change in // `docs-developer/CHANGELOG-formats.md`. }; diff --git a/src/profile-logic/profile-compacting.ts b/src/profile-logic/profile-compacting.ts index fd9423b491..d232638fd1 100644 --- a/src/profile-logic/profile-compacting.ts +++ b/src/profile-logic/profile-compacting.ts @@ -16,7 +16,7 @@ import type { RawFrameTable, FuncTable, ResourceTable, - NativeSymbolTable, + RawNativeSymbolTable, Lib, SourceTable, SourceLocationTable, @@ -251,11 +251,11 @@ export function computeCompactedProfile( host: ColDesc.indexRefOrNull(tcs.stringArray), type: ColDesc.noRef(), }; - const nativeSymbolsDesc: TableDescription = { - libIndex: ColDesc.indexRef(tcs.libs), - address: ColDesc.noRef(), - name: ColDesc.indexRef(tcs.stringArray), - functionSize: ColDesc.noRef(), + const nativeSymbolsDesc: TableDescription = { + libIndex: ColDesc.indexRefInt32(tcs.libs), + address: ColDesc.noRefTyped(Uint32Array), + name: ColDesc.indexRefInt32(tcs.stringArray), + functionSize: ColDesc.noRefTyped(Int32Array), }; const sourcesDesc: TableDescription = { id: ColDesc.noRef(), diff --git a/src/profile-logic/profile-data.ts b/src/profile-logic/profile-data.ts index 3d20ba8478..e6be14f14a 100644 --- a/src/profile-logic/profile-data.ts +++ b/src/profile-logic/profile-data.ts @@ -59,6 +59,7 @@ import type { FrameTable, FuncTable, NativeSymbolTable, + RawNativeSymbolTable, ResourceTable, CategoryList, IndexIntoCategoryList, @@ -4616,20 +4617,22 @@ export function getNativeSymbolInfo( frameTable: FrameTable, stringTable: StringTable ): NativeSymbolInfo { - const functionSizeOrNull = nativeSymbols.functionSize[nativeSymbol]; - const functionSize = - functionSizeOrNull ?? - calculateFunctionSizeLowerBound( - frameTable, - nativeSymbols.address[nativeSymbol], - nativeSymbol - ); + // `-1` is the sentinel for "size unknown" in the derived table. + const rawFunctionSize = nativeSymbols.functionSize[nativeSymbol]; + const functionSizeIsKnown = rawFunctionSize !== -1; + const functionSize = functionSizeIsKnown + ? rawFunctionSize + : calculateFunctionSizeLowerBound( + frameTable, + nativeSymbols.address[nativeSymbol], + nativeSymbol + ); return { libIndex: nativeSymbols.libIndex[nativeSymbol], address: nativeSymbols.address[nativeSymbol], name: stringTable.getString(nativeSymbols.name[nativeSymbol]), functionSize, - functionSizeIsKnown: functionSizeOrNull !== null, + functionSizeIsKnown, }; } @@ -4797,6 +4800,18 @@ export function computeFrameTableFromRawFrameTable( }; } +export function computeNativeSymbolTableFromRawNativeSymbolTable( + raw: RawNativeSymbolTable +): NativeSymbolTable { + return { + libIndex: toInt32Array(raw.libIndex), + address: toUint32Array(raw.address), + name: toInt32Array(raw.name), + functionSize: toInt32Array(raw.functionSize), + length: raw.length, + }; +} + export function computeStackTableFromRawStackTable( rawStackTable: RawStackTable, frameTable: FrameTable, diff --git a/src/profile-logic/sanitize.ts b/src/profile-logic/sanitize.ts index b296b647b5..7d2263070c 100644 --- a/src/profile-logic/sanitize.ts +++ b/src/profile-logic/sanitize.ts @@ -5,6 +5,7 @@ import { getEmptyExtensions, getRawMarkerTableBuilderFromExisting, + finishRawMarkerTableBuilder, shallowCloneFuncTable, } from './data-structures'; import { computeCompactedProfile } from './profile-compacting'; @@ -693,7 +694,7 @@ function sanitizeThreadPII( } // Remove the old markerTable and replace it with the new updated one. - newThread.markers = markerTable; + newThread.markers = finishRawMarkerTableBuilder(markerTable); // Have we removed everything from this thread? if (isThreadNonEmpty(newThread) || !isThreadNonEmpty(thread)) { diff --git a/src/profile-logic/symbolication.ts b/src/profile-logic/symbolication.ts index d2a4ed2e69..76a7dd71a1 100644 --- a/src/profile-logic/symbolication.ts +++ b/src/profile-logic/symbolication.ts @@ -6,10 +6,14 @@ import { finishRawFrameTableBuilder, finishRawStackTableBuilder, shallowCloneFuncTable, - shallowCloneNativeSymbolTable, + finishRawNativeSymbolTableBuilder, + getRawNativeSymbolTableBuilderWithExistingContents, getRawFrameTableBuilderWithExistingContents, } from './data-structures'; -import type { RawFrameTableBuilder } from './data-structures'; +import type { + RawFrameTableBuilder, + RawNativeSymbolTableBuilder, +} from './data-structures'; import { SymbolsNotFoundError } from './errors'; import type { @@ -18,7 +22,6 @@ import type { RawThread, RawStackTable, FuncTable, - NativeSymbolTable, SourceTable, IndexIntoFuncTable, IndexIntoFrameTable, @@ -241,7 +244,7 @@ export type FuncToFuncsMap = Map; type SymbolicationTables = { frameTable: RawFrameTableBuilder; funcTable: FuncTable; - nativeSymbols: NativeSymbolTable; + nativeSymbols: RawNativeSymbolTableBuilder; sources: SourceTable; // Maps a filename string index to the index of the native (id === null) // source entry for that filename, so that we don't have to scan the sources @@ -544,7 +547,9 @@ export function applySymbolicationSteps( oldShared.frameTable ); const funcTable = shallowCloneFuncTable(oldShared.funcTable); - const nativeSymbols = shallowCloneNativeSymbolTable(oldShared.nativeSymbols); + const nativeSymbols = getRawNativeSymbolTableBuilderWithExistingContents( + oldShared.nativeSymbols + ); const { sources, stringArray } = oldShared; const stringTable = StringTable.withBackingArray(stringArray); const sourceIndexForNativeFilename = new Map< @@ -579,7 +584,7 @@ export function applySymbolicationSteps( ...oldShared, frameTable: finishRawFrameTableBuilder(frameTable), funcTable, - nativeSymbols, + nativeSymbols: finishRawNativeSymbolTableBuilder(nativeSymbols), }; const newStackInfo = _computeStackTableWithAddedExpansionStacks( @@ -795,8 +800,7 @@ function _partiallyApplySymbolicationStep( // Update the symbol properties. nativeSymbols.address[symbolIndex] = symbolAddress; nativeSymbols.name[symbolIndex] = symbolStringIndex; - nativeSymbols.functionSize[symbolIndex] = - addressResult.functionSize ?? null; + nativeSymbols.functionSize[symbolIndex] = addressResult.functionSize ?? -1; } // Now we have a canonical symbol for every symbolAddress. diff --git a/src/selectors/per-thread/thread.tsx b/src/selectors/per-thread/thread.tsx index f83961bbbe..4b9cf5026f 100644 --- a/src/selectors/per-thread/thread.tsx +++ b/src/selectors/per-thread/thread.tsx @@ -201,8 +201,7 @@ export function getBasicThreadSelectorsPerThread( ProfileSelectors.getStackTable, ProfileSelectors.getFrameTable, ProfileSelectors.getFunctionsReservedFuncTable, - (state: State) => - ProfileSelectors.getRawProfileSharedData(state).nativeSymbols, + ProfileSelectors.getNativeSymbolTable, (state: State) => ProfileSelectors.getRawProfileSharedData(state).resourceTable, ProfileSelectors.getStringTable, diff --git a/src/selectors/profile.ts b/src/selectors/profile.ts index 8c65d4f1a0..2d03725270 100644 --- a/src/selectors/profile.ts +++ b/src/selectors/profile.ts @@ -21,6 +21,7 @@ import { computeTabToThreadIndexesMap, computeStackTableFromRawStackTable, computeFrameTableFromRawFrameTable, + computeNativeSymbolTableFromRawNativeSymbolTable, reserveFunctionsForCollapsedResources, computeSamplesTableFromRawSamplesTable, } from '../profile-logic/profile-data'; @@ -43,6 +44,7 @@ import type { RawProfileSharedData, StackTable, FrameTable, + NativeSymbolTable, CategoryList, IndexIntoCategoryList, RawThread, @@ -285,6 +287,11 @@ export const getFrameTable: Selector = createSelector( computeFrameTableFromRawFrameTable ); +export const getNativeSymbolTable: Selector = createSelector( + (state: State) => getRawProfileSharedData(state).nativeSymbols, + computeNativeSymbolTableFromRawNativeSymbolTable +); + export const getStackTable: Selector = createSelector( (state: State) => getRawProfileSharedData(state).stackTable, getFrameTable, diff --git a/src/test/components/TrackCustomMarker.test.tsx b/src/test/components/TrackCustomMarker.test.tsx index 65185e65f9..28b505cdf2 100644 --- a/src/test/components/TrackCustomMarker.test.tsx +++ b/src/test/components/TrackCustomMarker.test.tsx @@ -27,7 +27,10 @@ import { getMouseEvent, } from '../fixtures/utils'; import { getProfileFromTextSamples } from '../fixtures/profiles/processed-profile'; -import { getRawMarkerTableBuilderFromExisting } from '../../profile-logic/data-structures'; +import { + getRawMarkerTableBuilderFromExisting, + finishRawMarkerTableBuilder, +} from '../../profile-logic/data-structures'; import { autoMockElementSize, setMockedElementSize, @@ -81,7 +84,6 @@ function setup( ], }); const markers = getRawMarkerTableBuilderFromExisting(thread.markers); - thread.markers = markers; const addMarker = (startTime: number, first: number, second: number) => { // @ts-expect-error - Invalid payload by our type system markers.data.push({ type: 'Marker', first: first, second: second }); @@ -96,6 +98,7 @@ function setup( values.forEach((value, index) => { addMarker(index, value, value * 2); }); + thread.markers = finishRawMarkerTableBuilder(markers); const store = storeWithProfile(profile); const { getState, dispatch } = store; const flushRafCalls = mockRaf(); diff --git a/src/test/fixtures/profiles/processed-profile.ts b/src/test/fixtures/profiles/processed-profile.ts index 3b5dc077db..ffdca15c3a 100644 --- a/src/test/fixtures/profiles/processed-profile.ts +++ b/src/test/fixtures/profiles/processed-profile.ts @@ -16,6 +16,7 @@ import { finishRawUnbalancedNativeAllocationsTableBuilder, getRawSamplesTableBuilderFromExisting, getRawMarkerTableBuilderFromExisting, + finishRawMarkerTableBuilder, finishRawStackTableBuilder, getRawFrameTableBuilderWithExistingContents, getRawSamplesTableBuilderWithEventDelay, @@ -130,7 +131,6 @@ export function addRawMarkersToThread( ) { const stringTable = StringTable.withBackingArray(shared.stringArray); const markersTable = getRawMarkerTableBuilderFromExisting(thread.markers); - thread.markers = markersTable; for (const { name, startTime, endTime, phase, category, data } of markers) { markersTable.name.push( @@ -143,6 +143,8 @@ export function addRawMarkersToThread( markersTable.category.push(category || 0); markersTable.length++; } + + thread.markers = finishRawMarkerTableBuilder(markersTable); } // This function is called with test-defined payloads. For convenience, we allow @@ -184,7 +186,6 @@ export function addMarkersToThreadWithCorrespondingSamples( ) { const stringTable = StringTable.withBackingArray(shared.stringArray); const markersTable = getRawMarkerTableBuilderFromExisting(thread.markers); - thread.markers = markersTable; const allTimes = new Set(); markers.forEach((tuple) => { @@ -268,6 +269,7 @@ export function addMarkersToThreadWithCorrespondingSamples( } } thread.samples = finishRawSamplesTableBuilder(samples); + thread.markers = finishRawMarkerTableBuilder(markersTable); } export function getThreadWithMarkers( diff --git a/src/test/fixtures/utils.ts b/src/test/fixtures/utils.ts index 468363a02a..c955a2d233 100644 --- a/src/test/fixtures/utils.ts +++ b/src/test/fixtures/utils.ts @@ -19,6 +19,7 @@ import { createThreadFromDerivedTables, computeStackTableFromRawStackTable, computeFrameTableFromRawFrameTable, + computeNativeSymbolTableFromRawNativeSymbolTable, computeSamplesTableFromRawSamplesTable, computeJsAllocationsTableFromRawJsAllocationsTable, computeNativeAllocationsTableFromRawNativeAllocationsTable, @@ -153,6 +154,9 @@ export function computeThreadFromRawThread( shared.frameTable, categories ); + const nativeSymbols = computeNativeSymbolTableFromRawNativeSymbolTable( + shared.nativeSymbols + ); const stackTable = computeStackTableFromRawStackTable( shared.stackTable, frameTable, @@ -185,7 +189,7 @@ export function computeThreadFromRawThread( stackTable, frameTable, shared.funcTable, - shared.nativeSymbols, + nativeSymbols, shared.resourceTable, stringTable, shared.sources, diff --git a/src/test/integration/profiler-edit/__snapshots__/profiler-edit.test.ts.snap b/src/test/integration/profiler-edit/__snapshots__/profiler-edit.test.ts.snap index 27af272e13..cfec2157bc 100644 --- a/src/test/integration/profiler-edit/__snapshots__/profiler-edit.test.ts.snap +++ b/src/test/integration/profiler-edit/__snapshots__/profiler-edit.test.ts.snap @@ -87,7 +87,7 @@ Object { "markerSchema": Array [], "oscpu": "macOS 14.6.1", "pausedRanges": Array [], - "preprocessedProfileVersion": 71, + "preprocessedProfileVersion": 72, "processType": 0, "product": "a.out", "sampleUnits": Object { @@ -1488,7 +1488,7 @@ Object { "markerSchema": Array [], "oscpu": "macOS 14.6.1", "pausedRanges": Array [], - "preprocessedProfileVersion": 71, + "preprocessedProfileVersion": 72, "processType": 0, "product": "a.out", "sampleUnits": Object { @@ -2889,7 +2889,7 @@ Object { "markerSchema": Array [], "oscpu": "macOS 14.6.1", "pausedRanges": Array [], - "preprocessedProfileVersion": 71, + "preprocessedProfileVersion": 72, "processType": 0, "product": "a.out", "sampleUnits": Object { @@ -4290,7 +4290,7 @@ Object { "markerSchema": Array [], "oscpu": "macOS 14.6.1", "pausedRanges": Array [], - "preprocessedProfileVersion": 71, + "preprocessedProfileVersion": 72, "processType": 0, "product": "a.out", "sampleUnits": Object { diff --git a/src/test/store/__snapshots__/profile-view.test.ts.snap b/src/test/store/__snapshots__/profile-view.test.ts.snap index 92fd2e39d8..574980ada9 100644 --- a/src/test/store/__snapshots__/profile-view.test.ts.snap +++ b/src/test/store/__snapshots__/profile-view.test.ts.snap @@ -428,7 +428,7 @@ Object { "oscpu": "", "physicalCPUs": 0, "platform": "", - "preprocessedProfileVersion": 71, + "preprocessedProfileVersion": 72, "processType": 0, "product": "Firefox", "sourceURL": "", @@ -663,11 +663,11 @@ Object { ], }, "nativeSymbols": Object { - "address": Array [], - "functionSize": Array [], + "address": Uint32Array [], + "functionSize": Int32Array [], "length": 0, - "libIndex": Array [], - "name": Array [], + "libIndex": Int32Array [], + "name": Int32Array [], }, "resourceTable": Object { "host": Array [], @@ -735,11 +735,11 @@ Object { "markers": Object { "category": Array [], "data": Array [], - "endTime": Array [], + "endTime": Float64Array [], "length": 0, "name": Array [], "phase": Array [], - "startTime": Array [], + "startTime": Float64Array [], }, "name": "Thread with samples", "pausedRanges": Array [], @@ -1010,13 +1010,13 @@ Object { "type": "Network", }, ], - "endTime": Array [ - null, - null, - null, - null, - null, - null, + "endTime": Float64Array [ + 0, + 0, + 0, + 0, + 0, + 0, 6.5, 7, 7.5, @@ -1047,7 +1047,7 @@ Object { 1, 1, ], - "startTime": Array [ + "startTime": Float64Array [ 0, 1, 2, @@ -1090,11 +1090,11 @@ Array [ "markers": Object { "category": Array [], "data": Array [], - "endTime": Array [], + "endTime": Float64Array [], "length": 0, "name": Array [], "phase": Array [], - "startTime": Array [], + "startTime": Float64Array [], }, "name": "Thread with samples", "pausedRanges": Array [], @@ -1365,13 +1365,13 @@ Array [ "type": "Network", }, ], - "endTime": Array [ - null, - null, - null, - null, - null, - null, + "endTime": Float64Array [ + 0, + 0, + 0, + 0, + 0, + 0, 6.5, 7, 7.5, @@ -1402,7 +1402,7 @@ Array [ 1, 1, ], - "startTime": Array [ + "startTime": Float64Array [ 0, 1, 2, @@ -2651,20 +2651,20 @@ CallTree { "markers": Object { "category": Array [], "data": Array [], - "endTime": Array [], + "endTime": Float64Array [], "length": 0, "name": Array [], "phase": Array [], - "startTime": Array [], + "startTime": Float64Array [], }, "name": "Thread with samples", "nativeAllocations": undefined, "nativeSymbols": Object { - "address": Array [], - "functionSize": Array [], + "address": Uint32Array [], + "functionSize": Int32Array [], "length": 0, - "libIndex": Array [], - "name": Array [], + "libIndex": Int32Array [], + "name": Int32Array [], }, "pausedRanges": Array [], "pid": "0", @@ -3049,20 +3049,20 @@ Object { "markers": Object { "category": Array [], "data": Array [], - "endTime": Array [], + "endTime": Float64Array [], "length": 0, "name": Array [], "phase": Array [], - "startTime": Array [], + "startTime": Float64Array [], }, "name": "Thread with samples", "nativeAllocations": undefined, "nativeSymbols": Object { - "address": Array [], - "functionSize": Array [], + "address": Uint32Array [], + "functionSize": Int32Array [], "length": 0, - "libIndex": Array [], - "name": Array [], + "libIndex": Int32Array [], + "name": Int32Array [], }, "pausedRanges": Array [], "pid": "0", @@ -3527,20 +3527,20 @@ Object { "markers": Object { "category": Array [], "data": Array [], - "endTime": Array [], + "endTime": Float64Array [], "length": 0, "name": Array [], "phase": Array [], - "startTime": Array [], + "startTime": Float64Array [], }, "name": "Thread with samples", "nativeAllocations": undefined, "nativeSymbols": Object { - "address": Array [], - "functionSize": Array [], + "address": Uint32Array [], + "functionSize": Int32Array [], "length": 0, - "libIndex": Array [], - "name": Array [], + "libIndex": Int32Array [], + "name": Int32Array [], }, "pausedRanges": Array [], "pid": "0", @@ -3911,20 +3911,20 @@ Object { "markers": Object { "category": Array [], "data": Array [], - "endTime": Array [], + "endTime": Float64Array [], "length": 0, "name": Array [], "phase": Array [], - "startTime": Array [], + "startTime": Float64Array [], }, "name": "Thread with samples", "nativeAllocations": undefined, "nativeSymbols": Object { - "address": Array [], - "functionSize": Array [], + "address": Uint32Array [], + "functionSize": Int32Array [], "length": 0, - "libIndex": Array [], - "name": Array [], + "libIndex": Int32Array [], + "name": Int32Array [], }, "pausedRanges": Array [], "pid": "0", @@ -4307,20 +4307,20 @@ Object { "markers": Object { "category": Array [], "data": Array [], - "endTime": Array [], + "endTime": Float64Array [], "length": 0, "name": Array [], "phase": Array [], - "startTime": Array [], + "startTime": Float64Array [], }, "name": "Thread with samples", "nativeAllocations": undefined, "nativeSymbols": Object { - "address": Array [], - "functionSize": Array [], + "address": Uint32Array [], + "functionSize": Int32Array [], "length": 0, - "libIndex": Array [], - "name": Array [], + "libIndex": Int32Array [], + "name": Int32Array [], }, "pausedRanges": Array [], "pid": "0", diff --git a/src/test/store/symbolication.test.ts b/src/test/store/symbolication.test.ts index 6013d9fd5f..d48ebfb130 100644 --- a/src/test/store/symbolication.test.ts +++ b/src/test/store/symbolication.test.ts @@ -25,7 +25,10 @@ import { import * as ProfileViewSelectors from '../../selectors/profile'; import { selectedThreadSelectors } from '../../selectors/per-thread'; import { INTERVAL } from 'firefox-profiler/app-logic/constants'; -import { getEmptyRawMarkerTable } from '../../profile-logic/data-structures'; +import { + getRawMarkerTableBuilder, + finishRawMarkerTableBuilder, +} from '../../profile-logic/data-structures'; import { doSymbolicateProfile } from '../../actions/receive-profile'; import { changeSelectedCallNode, @@ -650,7 +653,7 @@ function _createUnsymbolicatedProfile() { }, }; - const markers = getEmptyRawMarkerTable(); + const markers = getRawMarkerTableBuilder(); const markerIndex = markers.length++; markers.data[markerIndex] = markerData; markers.name[markerIndex] = stringTable.indexForString('MarkerWithStack'); @@ -659,7 +662,7 @@ function _createUnsymbolicatedProfile() { markers.phase[markerIndex] = INTERVAL; markers.category[markerIndex] = 0; - thread.markers = markers; + thread.markers = finishRawMarkerTableBuilder(markers); return profile; } diff --git a/src/test/unit/__snapshots__/profile-conversion.test.ts.snap b/src/test/unit/__snapshots__/profile-conversion.test.ts.snap index 46f5bf9b8e..3bffbb2edf 100644 --- a/src/test/unit/__snapshots__/profile-conversion.test.ts.snap +++ b/src/test/unit/__snapshots__/profile-conversion.test.ts.snap @@ -42,7 +42,7 @@ Object { "RefreshDriverTick", "Network", ], - "preprocessedProfileVersion": 71, + "preprocessedProfileVersion": 72, "product": "ART Trace (Android)", "symbolicated": true, "version": 36, @@ -1022,7 +1022,7 @@ Object { "RefreshDriverTick", "Network", ], - "preprocessedProfileVersion": 71, + "preprocessedProfileVersion": 72, "product": "ART Trace (Android)", "symbolicated": true, "version": 36, @@ -2305,7 +2305,7 @@ Object { "markerSchemaNames": Array [ "EventDispatch", ], - "preprocessedProfileVersion": 71, + "preprocessedProfileVersion": 72, "product": "Chrome Trace", "symbolicated": true, "version": 36, @@ -2697,7 +2697,7 @@ Object { "markerSchemaNames": Array [ "EventDispatch", ], - "preprocessedProfileVersion": 71, + "preprocessedProfileVersion": 72, "product": "Chrome Trace", "symbolicated": true, "version": 36, @@ -3086,7 +3086,7 @@ Object { "markerSchemaNames": Array [ "EventDispatch", ], - "preprocessedProfileVersion": 71, + "preprocessedProfileVersion": 72, "product": "Chrome Trace", "symbolicated": true, "version": 36, @@ -3187,7 +3187,7 @@ Object { "markerSchemaNames": Array [ "EventDispatch", ], - "preprocessedProfileVersion": 71, + "preprocessedProfileVersion": 72, "product": "Chrome Trace", "symbolicated": true, "version": 36, @@ -3540,7 +3540,7 @@ Object { "markerSchemaNames": Array [ "EventDispatch", ], - "preprocessedProfileVersion": 71, + "preprocessedProfileVersion": 72, "product": "Chrome Trace", "symbolicated": true, "version": 36, @@ -3605,7 +3605,7 @@ Object { "markerSchemaNames": Array [ "EventDispatch", ], - "preprocessedProfileVersion": 71, + "preprocessedProfileVersion": 72, "product": "Chrome Trace", "symbolicated": true, "version": 36, @@ -3759,7 +3759,7 @@ Object { "markerSchemaNames": Array [ "EventDispatch", ], - "preprocessedProfileVersion": 71, + "preprocessedProfileVersion": 72, "product": "Chrome Trace", "symbolicated": true, "version": 36, @@ -3817,7 +3817,7 @@ Object { "importedFrom": undefined, "interval": 1, "markerSchemaNames": Array [], - "preprocessedProfileVersion": 71, + "preprocessedProfileVersion": 72, "product": "Firefox", "symbolicated": true, "version": 36, @@ -4207,7 +4207,7 @@ Object { "importedFrom": undefined, "interval": 1, "markerSchemaNames": Array [], - "preprocessedProfileVersion": 71, + "preprocessedProfileVersion": 72, "product": "Firefox", "symbolicated": true, "version": 36, @@ -4265,7 +4265,7 @@ Object { "importedFrom": undefined, "interval": 1, "markerSchemaNames": Array [], - "preprocessedProfileVersion": 71, + "preprocessedProfileVersion": 72, "product": "Firefox", "symbolicated": true, "version": 36, @@ -4323,7 +4323,7 @@ Object { "importedFrom": undefined, "interval": 1, "markerSchemaNames": Array [], - "preprocessedProfileVersion": 71, + "preprocessedProfileVersion": 72, "product": "Firefox", "symbolicated": true, "version": 36, @@ -4643,7 +4643,7 @@ Object { "importedFrom": "Simpleperf", "interval": 0, "markerSchemaNames": Array [], - "preprocessedProfileVersion": 71, + "preprocessedProfileVersion": 72, "product": "com.example.sampleapplication", "symbolicated": undefined, "version": 30, @@ -5019,7 +5019,7 @@ Object { "importedFrom": "Simpleperf", "interval": 0, "markerSchemaNames": Array [], - "preprocessedProfileVersion": 71, + "preprocessedProfileVersion": 72, "product": "com.example.sampleapplication", "symbolicated": undefined, "version": 30, @@ -5319,7 +5319,7 @@ Object { "importedFrom": "dhat", "interval": 1, "markerSchemaNames": Array [], - "preprocessedProfileVersion": 71, + "preprocessedProfileVersion": 72, "product": "target/debug/examples/work_log (dhat)", "symbolicated": true, "version": 36, @@ -5452,7 +5452,7 @@ Object { "importedFrom": undefined, "interval": 1, "markerSchemaNames": Array [], - "preprocessedProfileVersion": 71, + "preprocessedProfileVersion": 72, "product": "Flamegraph", "symbolicated": true, "version": 36, @@ -5510,7 +5510,7 @@ Object { "importedFrom": undefined, "interval": 1, "markerSchemaNames": Array [], - "preprocessedProfileVersion": 71, + "preprocessedProfileVersion": 72, "product": "Flamegraph", "symbolicated": true, "version": 36, diff --git a/src/test/unit/__snapshots__/profile-upgrading.test.ts.snap b/src/test/unit/__snapshots__/profile-upgrading.test.ts.snap index 4d039c9117..4c9e1b577e 100644 --- a/src/test/unit/__snapshots__/profile-upgrading.test.ts.snap +++ b/src/test/unit/__snapshots__/profile-upgrading.test.ts.snap @@ -40,7 +40,7 @@ Object { "oscpu": undefined, "physicalCPUs": undefined, "platform": undefined, - "preprocessedProfileVersion": 71, + "preprocessedProfileVersion": 72, "processType": 0, "product": "Firefox", "sampleUnits": undefined, @@ -2904,11 +2904,11 @@ Object { ], }, "nativeSymbols": Object { - "address": Array [], - "functionSize": Array [], + "address": Uint32Array [], + "functionSize": Int32Array [], "length": 0, - "libIndex": Array [], - "name": Array [], + "libIndex": Int32Array [], + "name": Int32Array [], }, "resourceTable": Object { "host": Array [ @@ -3589,11 +3589,11 @@ Object { "markers": Object { "category": Array [], "data": Array [], - "endTime": Array [], + "endTime": Float64Array [], "length": 0, "name": Array [], "phase": Array [], - "startTime": Array [], + "startTime": Float64Array [], }, "name": "firefox", "pausedRanges": Array [], @@ -3653,11 +3653,11 @@ Object { "markers": Object { "category": Array [], "data": Array [], - "endTime": Array [], + "endTime": Float64Array [], "length": 0, "name": Array [], "phase": Array [], - "startTime": Array [], + "startTime": Float64Array [], }, "name": "firefox", "pausedRanges": Array [], @@ -3699,11 +3699,11 @@ Object { "markers": Object { "category": Array [], "data": Array [], - "endTime": Array [], + "endTime": Float64Array [], "length": 0, "name": Array [], "phase": Array [], - "startTime": Array [], + "startTime": Float64Array [], }, "name": "firefox", "pausedRanges": Array [], @@ -3745,11 +3745,11 @@ Object { "markers": Object { "category": Array [], "data": Array [], - "endTime": Array [], + "endTime": Float64Array [], "length": 0, "name": Array [], "phase": Array [], - "startTime": Array [], + "startTime": Float64Array [], }, "name": "firefox", "pausedRanges": Array [], @@ -3794,11 +3794,11 @@ Object { "markers": Object { "category": Array [], "data": Array [], - "endTime": Array [], + "endTime": Float64Array [], "length": 0, "name": Array [], "phase": Array [], - "startTime": Array [], + "startTime": Float64Array [], }, "name": "firefox", "pausedRanges": Array [], @@ -3840,11 +3840,11 @@ Object { "markers": Object { "category": Array [], "data": Array [], - "endTime": Array [], + "endTime": Float64Array [], "length": 0, "name": Array [], "phase": Array [], - "startTime": Array [], + "startTime": Float64Array [], }, "name": "gdbus", "pausedRanges": Array [], @@ -3901,11 +3901,11 @@ Object { "markers": Object { "category": Array [], "data": Array [], - "endTime": Array [], + "endTime": Float64Array [], "length": 0, "name": Array [], "phase": Array [], - "startTime": Array [], + "startTime": Float64Array [], }, "name": "firefox", "pausedRanges": Array [], @@ -3947,11 +3947,11 @@ Object { "markers": Object { "category": Array [], "data": Array [], - "endTime": Array [], + "endTime": Float64Array [], "length": 0, "name": Array [], "phase": Array [], - "startTime": Array [], + "startTime": Float64Array [], }, "name": "firefox", "pausedRanges": Array [], @@ -3993,11 +3993,11 @@ Object { "markers": Object { "category": Array [], "data": Array [], - "endTime": Array [], + "endTime": Float64Array [], "length": 0, "name": Array [], "phase": Array [], - "startTime": Array [], + "startTime": Float64Array [], }, "name": "firefox", "pausedRanges": Array [], @@ -4039,11 +4039,11 @@ Object { "markers": Object { "category": Array [], "data": Array [], - "endTime": Array [], + "endTime": Float64Array [], "length": 0, "name": Array [], "phase": Array [], - "startTime": Array [], + "startTime": Float64Array [], }, "name": "firefox", "pausedRanges": Array [], @@ -4085,11 +4085,11 @@ Object { "markers": Object { "category": Array [], "data": Array [], - "endTime": Array [], + "endTime": Float64Array [], "length": 0, "name": Array [], "phase": Array [], - "startTime": Array [], + "startTime": Float64Array [], }, "name": "firefox", "pausedRanges": Array [], @@ -4131,11 +4131,11 @@ Object { "markers": Object { "category": Array [], "data": Array [], - "endTime": Array [], + "endTime": Float64Array [], "length": 0, "name": Array [], "phase": Array [], - "startTime": Array [], + "startTime": Float64Array [], }, "name": "firefox", "pausedRanges": Array [], @@ -4177,11 +4177,11 @@ Object { "markers": Object { "category": Array [], "data": Array [], - "endTime": Array [], + "endTime": Float64Array [], "length": 0, "name": Array [], "phase": Array [], - "startTime": Array [], + "startTime": Float64Array [], }, "name": "firefox", "pausedRanges": Array [], @@ -4223,11 +4223,11 @@ Object { "markers": Object { "category": Array [], "data": Array [], - "endTime": Array [], + "endTime": Float64Array [], "length": 0, "name": Array [], "phase": Array [], - "startTime": Array [], + "startTime": Float64Array [], }, "name": "firefox", "pausedRanges": Array [], @@ -4269,11 +4269,11 @@ Object { "markers": Object { "category": Array [], "data": Array [], - "endTime": Array [], + "endTime": Float64Array [], "length": 0, "name": Array [], "phase": Array [], - "startTime": Array [], + "startTime": Float64Array [], }, "name": "firefox", "pausedRanges": Array [], @@ -4315,11 +4315,11 @@ Object { "markers": Object { "category": Array [], "data": Array [], - "endTime": Array [], + "endTime": Float64Array [], "length": 0, "name": Array [], "phase": Array [], - "startTime": Array [], + "startTime": Float64Array [], }, "name": "FS Broker 5906", "pausedRanges": Array [], @@ -7834,7 +7834,7 @@ Object { "misc": "rv:48.0", "oscpu": "Intel Mac OS X 10.11", "platform": "Macintosh", - "preprocessedProfileVersion": 71, + "preprocessedProfileVersion": 72, "processType": 0, "product": "Firefox", "stackwalk": 1, @@ -8182,10 +8182,10 @@ Object { 6725, ], "functionSize": Array [ - null, - null, - null, - null, + -1, + -1, + -1, + -1, ], "length": 4, "libIndex": Array [ @@ -9229,7 +9229,7 @@ Object { "misc": "rv:48.0", "oscpu": "Intel Mac OS X 10.11", "platform": "Macintosh", - "preprocessedProfileVersion": 71, + "preprocessedProfileVersion": 72, "processType": 0, "product": "Firefox", "stackwalk": 1, @@ -9615,10 +9615,10 @@ Object { 6725, ], "functionSize": Array [ - null, - null, - null, - null, + -1, + -1, + -1, + -1, ], "length": 4, "libIndex": Array [ @@ -10794,7 +10794,7 @@ Object { "misc": "rv:48.0", "oscpu": "Intel Mac OS X 10.11", "platform": "Macintosh", - "preprocessedProfileVersion": 71, + "preprocessedProfileVersion": 72, "processType": 0, "product": "Firefox", "stackwalk": 1, @@ -11206,10 +11206,10 @@ Object { 6725, ], "functionSize": Array [ - null, - null, - null, - null, + -1, + -1, + -1, + -1, ], "length": 4, "libIndex": Array [ diff --git a/src/test/unit/marker-data.test.ts b/src/test/unit/marker-data.test.ts index 9a5d06beff..e8b42906f1 100644 --- a/src/test/unit/marker-data.test.ts +++ b/src/test/unit/marker-data.test.ts @@ -960,7 +960,7 @@ describe('filterRawMarkerTableToRange', () => { ], }); - expect(rawMarkerTable.startTime).toEqual([0, 3]); + expect(Array.from(rawMarkerTable.startTime)).toEqual([0, 3]); }); it('keeps a screenshot markers happening before the range if there is no other marker', () => { diff --git a/src/test/unit/merge-compare.test.ts b/src/test/unit/merge-compare.test.ts index f565e08bc2..6b4fc0a3c4 100644 --- a/src/test/unit/merge-compare.test.ts +++ b/src/test/unit/merge-compare.test.ts @@ -257,7 +257,7 @@ describe('mergeProfilesForDiffing function', function () { ], address: [0x20, 0x50], libIndex: [0, 0], - functionSize: [null, null], + functionSize: [-1, -1], }; sampleProfileB.profile.shared.nativeSymbols = { @@ -268,7 +268,7 @@ describe('mergeProfilesForDiffing function', function () { ], address: [0x25, 0x45], libIndex: [0, 0], - functionSize: [null, null], + functionSize: [-1, -1], }; const profileState = stateFromLocation({ @@ -283,7 +283,9 @@ describe('mergeProfilesForDiffing function', function () { // The merged profile has a single merged libs list, so the native symbols // should now be merged with updated libIndexes. - expect(mergedProfile.shared.nativeSymbols.libIndex).toEqual([0, 0, 1, 1]); + expect([...mergedProfile.shared.nativeSymbols.libIndex]).toEqual([ + 0, 0, 1, 1, + ]); }); it('should use marker timing if there are no samples', () => { @@ -636,7 +638,9 @@ describe('mergeThreads function', function () { // New marker table doesn't have to be sorted. Because we sort it while we // are getting it from selector anyway. expect(markerStartTimes).toEqual([2, 3, 6, 1, 3, 8]); - expect(markerEndTimes).toEqual([null, 5, 7, null, 4, 9]); + // The end times of instant markers are meaningless; they're stored as zero + // in the Float64Array end time column. + expect(markerEndTimes).toEqual([0, 5, 7, 0, 4, 9]); expect(markerThreadIds).toEqual([0, 0, 0, 1, 1, 1]); }); diff --git a/src/test/unit/profile-data.test.ts b/src/test/unit/profile-data.test.ts index 9574fa17f6..a3423b1a21 100644 --- a/src/test/unit/profile-data.test.ts +++ b/src/test/unit/profile-data.test.ts @@ -23,6 +23,7 @@ import { findAddressProofForFile, calculateFunctionSizeLowerBound, computeFrameTableFromRawFrameTable, + computeNativeSymbolTableFromRawNativeSymbolTable, getNativeSymbolsForCallNode, getNativeSymbolInfo, computeTimeColumnForRawSamplesTable, @@ -1785,14 +1786,12 @@ describe('getNativeSymbolInfo', function () { shared.frameTable, profile.meta.categories ); + const nativeSymbols = computeNativeSymbolTableFromRawNativeSymbolTable( + shared.nativeSymbols + ); expect( - getNativeSymbolInfo( - symSomeFunc, - shared.nativeSymbols, - frameTable, - stringTable - ) + getNativeSymbolInfo(symSomeFunc, nativeSymbols, frameTable, stringTable) ).toEqual({ name: 'symSomeFunc', address: 0x1000, @@ -1801,12 +1800,7 @@ describe('getNativeSymbolInfo', function () { libIndex: profile.libs.findIndex((l) => l.name === 'XUL'), }); expect( - getNativeSymbolInfo( - symOtherFunc, - shared.nativeSymbols, - frameTable, - stringTable - ) + getNativeSymbolInfo(symOtherFunc, nativeSymbols, frameTable, stringTable) ).toEqual({ name: 'symOtherFunc', address: 0x2000, diff --git a/src/test/unit/profile-query/marker-utils.test.ts b/src/test/unit/profile-query/marker-utils.test.ts index 8c911cb674..95f12c019d 100644 --- a/src/test/unit/profile-query/marker-utils.test.ts +++ b/src/test/unit/profile-query/marker-utils.test.ts @@ -25,7 +25,10 @@ import type { } from '../../fixtures/profiles/processed-profile'; import { storeWithProfile } from '../../fixtures/stores'; import { StringTable } from 'firefox-profiler/utils/string-table'; -import { getRawMarkerTableBuilderFromExisting } from 'firefox-profiler/profile-logic/data-structures'; +import { + getRawMarkerTableBuilderFromExisting, + finishRawMarkerTableBuilder, +} from 'firefox-profiler/profile-logic/data-structures'; import { INTERVAL } from 'firefox-profiler/app-logic/constants'; import type { Marker } from 'firefox-profiler/types'; @@ -760,7 +763,6 @@ describe('collectMarkerStack', function () { ); const markerNameIdx = stringTable.indexForString('TestMarker'); const markers = getRawMarkerTableBuilderFromExisting(thread.markers); - thread.markers = markers; markers.name.push(markerNameIdx); markers.startTime.push(1); markers.endTime.push(5); @@ -772,6 +774,7 @@ describe('collectMarkerStack', function () { cause: { stack: stackIndex }, }); markers.length++; + thread.markers = finishRawMarkerTableBuilder(markers); const store = storeWithProfile(profile); const threadMap = new ThreadMap(); @@ -903,7 +906,6 @@ describe('marker time base', function () { ); const markerNameIdx = stringTable.indexForString('TestMarker'); const markers = getRawMarkerTableBuilderFromExisting(thread.markers); - thread.markers = markers; markers.name.push(markerNameIdx); markers.startTime.push(ZERO_AT + 30); markers.endTime.push(ZERO_AT + 50); @@ -915,6 +917,7 @@ describe('marker time base', function () { cause: { stack: stackIndex, time: ZERO_AT + 30 }, }); markers.length++; + thread.markers = finishRawMarkerTableBuilder(markers); const store = storeWithProfile(profile); const threadMap = new ThreadMap(); diff --git a/src/test/url-handling.test.ts b/src/test/url-handling.test.ts index c3f5919768..044983ca6d 100644 --- a/src/test/url-handling.test.ts +++ b/src/test/url-handling.test.ts @@ -61,7 +61,10 @@ import { encodeUintSetForUrlComponent, } from '../utils/uintarray-encoding'; import { getProfile } from '../selectors/profile'; -import { getRawMarkerTableBuilderFromExisting } from '../profile-logic/data-structures'; +import { + getRawMarkerTableBuilderFromExisting, + finishRawMarkerTableBuilder, +} from '../profile-logic/data-structures'; import { SYMBOL_SERVER_URL } from '../app-logic/constants'; import { getThreadsKey } from '../profile-logic/profile-data'; import { StringTable } from 'firefox-profiler/utils/string-table'; @@ -1360,7 +1363,6 @@ describe('url upgrading', function () { const mainThreadMarkers = getRawMarkerTableBuilderFromExisting( mainThread.markers ); - mainThread.markers = mainThreadMarkers; mainThreadMarkers.name.push(stringTable.indexForString('IPC')); mainThreadMarkers.phase.push(0); mainThreadMarkers.startTime.push(0); @@ -1379,6 +1381,7 @@ describe('url upgrading', function () { sync: false, } as any); mainThreadMarkers.length++; + mainThread.markers = finishRawMarkerTableBuilder(mainThreadMarkers); const memoryCounter = getCounterForThread(mainThread, 0); memoryCounter.category = 'Memory'; diff --git a/src/types/profile-derived.ts b/src/types/profile-derived.ts index fb1ffd032a..fb74792a12 100644 --- a/src/types/profile-derived.ts +++ b/src/types/profile-derived.ts @@ -22,7 +22,6 @@ import type { RawMarkerTable, FuncTable, ResourceTable, - NativeSymbolTable, JsTracerTable, IndexIntoStackTable, WeightType, @@ -306,6 +305,22 @@ export type FrameTable = { length: number; }; +/** + * The `NativeSymbolTable` type of the derived thread. + * + * Differs from `RawNativeSymbolTable` in that all columns are always stored as + * typed arrays, and `functionSize` uses `-1` as the sentinel for "size unknown" + * (rather than `null`). + */ +export type NativeSymbolTable = { + libIndex: Int32Array; + address: Uint32Array; + name: Int32Array; + // `-1` means "size unknown". + functionSize: Int32Array; + length: number; +}; + /** * Similar to the StackTable, but based on functions rather than on frames. * diff --git a/src/types/profile.ts b/src/types/profile.ts index 7703a31e44..6414dc43f0 100644 --- a/src/types/profile.ts +++ b/src/types/profile.ts @@ -441,16 +441,19 @@ export type FuncTable = { * considered a "symbol table" - normally, a "symbol table" is something that * contains *all* symbols of a given library. But this table only contains a * subset of those symbols, and mixes symbols from multiple libraries. + * + * Each column may be stored as a regular array or as a typed array. */ -export type NativeSymbolTable = { +export type RawNativeSymbolTable = { // The library that this native symbol is in. - libIndex: Array; + libIndex: Array | Int32Array; // The library-relative offset of this symbol. - address: Array
; + address: Array
| Uint32Array; // The symbol name, demangled. - name: Array; - // The size of the function's machine code (if known), in bytes. - functionSize: Array; + name: Array | Int32Array; + // The size of the function's machine code, in bytes. + // The sentinel `-1` means "size unknown". + functionSize: Array | Int32Array; length: number; }; @@ -1152,7 +1155,7 @@ export type RawProfileSharedData = { frameTable: RawFrameTable; funcTable: FuncTable; resourceTable: ResourceTable; - nativeSymbols: NativeSymbolTable; + nativeSymbols: RawNativeSymbolTable; // Strings for profiles are collected into a single table, and are referred to by // their index by other tables. stringArray: string[];