diff --git a/jest.config.js b/jest.config.js index 54682dfe31..5eea22e8ff 100644 --- a/jest.config.js +++ b/jest.config.js @@ -15,10 +15,14 @@ module.exports = { '!src/types/libdef/**', ], + transform: { + '\\.([jt]sx?|mjs)$': 'babel-jest', + }, + // Transform ESM modules to CommonJS for Jest // These packages ship as pure ESM and need to be transformed by Babel transformIgnorePatterns: [ - '/node_modules/(?!(query-string|decode-uri-component|iongraph-web|split-on-first|filter-obj|fetch-mock)/)', + '/node_modules/(?!(query-string|decode-uri-component|iongraph-web|split-on-first|filter-obj|fetch-mock|devtools-reps)/)', ], // Mock static assets (images, CSS, etc.) diff --git a/package.json b/package.json index 51f2cd6c56..35326962f3 100644 --- a/package.json +++ b/package.json @@ -78,6 +78,7 @@ "common-tags": "^1.8.2", "copy-to-clipboard": "^3.3.3", "core-js": "^3.47.0", + "devtools-reps": "^0.27.3", "escape-string-regexp": "^4.0.0", "gecko-profiler-demangle": "^0.4.0", "idb": "^8.0.3", diff --git a/res/css/global.css b/res/css/global.css index 72c99c9064..264f985ace 100644 --- a/res/css/global.css +++ b/res/css/global.css @@ -29,3 +29,17 @@ .colored-border.ellipsis { opacity: 0; } + +/* Colors for DevTools Reps */ +:root { + --number-color: #058b00; + --string-color: #dd00a9; + --null-color: #5c5c5f; + --object-color: #0074e8; + --caption-color: #0074e8; + --location-color: #5c5c5f; + --source-link-color: #0060df; + --node-color: #003eaa; + --reference-color: #0074e8; + --comment-node-color: #5c5c5f; +} diff --git a/src/components/stack-chart/Canvas.tsx b/src/components/stack-chart/Canvas.tsx index 0c113a3872..a31a81a8f6 100644 --- a/src/components/stack-chart/Canvas.tsx +++ b/src/components/stack-chart/Canvas.tsx @@ -17,6 +17,7 @@ import type { type ChangeMouseTimePosition = typeof changeMouseTimePosition; import { mapCategoryColorNameToStackChartStyles } from '../../utils/colors'; +import { ValueSummaryReader } from 'devtools-reps'; import { TooltipCallNode } from '../tooltip/CallNode'; import { TooltipMarker } from '../tooltip/Marker'; @@ -624,6 +625,17 @@ class StackChartCanvasImpl extends React.PureComponent { timelineUnit === 'bytes' ? formatBytes(duration) : formatMilliseconds(duration); + let argumentSummaries = undefined; + if (timing.argumentValues) { + const argumentValues = timing.argumentValues[stackTimingIndex]; + if (argumentValues !== -1) { + argumentSummaries = ValueSummaryReader.getArgumentSummaries( + thread.tracedValuesBuffer as ArrayBuffer, + thread.tracedObjectShapes as Array, + argumentValues + ); + } + } return ( { callTreeSummaryStrategy="timing" durationText={durationText} displayStackType={displayStackType} + argumentValues={argumentSummaries} /> ); }; diff --git a/src/components/tooltip/CallNode.tsx b/src/components/tooltip/CallNode.tsx index e77f765143..fec0bb2a3c 100644 --- a/src/components/tooltip/CallNode.tsx +++ b/src/components/tooltip/CallNode.tsx @@ -31,6 +31,8 @@ import type { OneCategoryBreakdown, } from 'firefox-profiler/profile-logic/profile-data'; import type { CallNodeInfo } from 'firefox-profiler/profile-logic/call-node-info'; +import { REPS, MODE } from 'devtools-reps'; +const { Rep } = REPS; import './CallNode.css'; import classNames from 'classnames'; @@ -129,6 +131,7 @@ type Props = { readonly timings?: TimingsForPath; readonly callTreeSummaryStrategy: CallTreeSummaryStrategy; readonly displayStackType: boolean; + readonly argumentValues?: Array; }; /** @@ -358,6 +361,7 @@ export class TooltipCallNode extends React.PureComponent { thread, durationText, categories, + argumentValues, displayData, timings, callTreeSummaryStrategy, @@ -426,6 +430,29 @@ export class TooltipCallNode extends React.PureComponent { ]; } + let argumentsElement = null; + if (argumentValues) { + if (argumentValues.length === 0) { + argumentsElement =
No arguments.
; + } else { + const argumentValuesEl = []; + for (const previewObject of argumentValues) { + argumentValuesEl.push( + Rep({ + object: previewObject, + mode: MODE.LONG, + }) + ); + } + argumentsElement = ( +
+
Arguments
+ {argumentValuesEl} +
+ ); + } + } + // Finding current frame and parent frame URL(if there is). let pageAndParentPageURL; if (innerWindowIDToPageMap) { @@ -542,6 +569,7 @@ export class TooltipCallNode extends React.PureComponent { {resource} {this._renderCategoryTimings(timings)} + {argumentsElement} ); diff --git a/src/components/tooltip/Tooltip.css b/src/components/tooltip/Tooltip.css index bd119676ca..8ce9c68e22 100644 --- a/src/components/tooltip/Tooltip.css +++ b/src/components/tooltip/Tooltip.css @@ -156,3 +156,17 @@ .sidebar .tooltipDetailSeparator { display: none; } + +.argumentsLabel { + /* match tooltips label, without being aligned to the right */ + color: var(--grey-50); +} + +.arguments > span { + display: block; + padding: 0.25em; +} + +.arguments > span:nth-child(odd) { + background-color: rgb(0 0 0 / 0.05); +} diff --git a/src/index.tsx b/src/index.tsx index 20a3d4a286..19898c3a20 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -13,6 +13,7 @@ import '../res/css/global.css'; import '../res/css/categories.css'; import '../res/css/network.css'; import 'react-splitter-layout/lib/index.css'; +import 'devtools-reps/reps.css'; // React imported for JSX in Root component import { createRoot } from 'react-dom/client'; diff --git a/src/profile-logic/process-profile.ts b/src/profile-logic/process-profile.ts index b74f5f0aec..458df8607c 100644 --- a/src/profile-logic/process-profile.ts +++ b/src/profile-logic/process-profile.ts @@ -1011,6 +1011,10 @@ function _processSamples(geckoSamples: GeckoSampleStruct): RawSamplesTable { } } + if ('argumentValues' in geckoSamples) { + samples.argumentValues = geckoSamples.argumentValues; + } + if ('eventDelay' in geckoSamples) { samples.eventDelay = geckoSamples.eventDelay; } else if ('responsiveness' in geckoSamples) { @@ -1251,6 +1255,14 @@ function _processThread( newThread.nativeAllocations = nativeAllocations; } + if (thread.tracedValues) { + newThread.tracedValuesBuffer = thread.tracedValues; + } + + if (thread.tracedObjectShapes) { + newThread.tracedObjectShapes = thread.tracedObjectShapes; + } + function processJsTracer() { // Optionally extract the JS Tracer information, if they exist. const { jsTracerEvents } = thread; diff --git a/src/profile-logic/profile-data.ts b/src/profile-logic/profile-data.ts index 21af66fcd8..f1b8d75d6d 100644 --- a/src/profile-logic/profile-data.ts +++ b/src/profile-logic/profile-data.ts @@ -1732,6 +1732,13 @@ export function filterThreadSamplesToRange( ); } + if (samples.argumentValues) { + newSamples.argumentValues = samples.argumentValues.slice( + beginSampleIndex, + endSampleIndex + ); + } + if (samples.threadId) { newSamples.threadId = samples.threadId.slice( beginSampleIndex, @@ -1858,6 +1865,13 @@ export function filterRawThreadSamplesToRange( ); } + if (samples.argumentValues) { + newSamples.argumentValues = samples.argumentValues.slice( + beginSampleIndex, + endSampleIndex + ); + } + if (samples.threadId) { newSamples.threadId = samples.threadId.slice( beginSampleIndex, @@ -1964,6 +1978,9 @@ export function filterCounterSamplesToRange( number: samples.number ? samples.number.slice(beginSampleIndex, endSampleIndex) : undefined, + argumentValues: samples.argumentValues + ? samples.argumentValues.slice(beginSampleIndex, endSampleIndex) + : undefined, }; return newCounter; @@ -2288,6 +2305,7 @@ export function computeSamplesTableFromRawSamplesTable( const { responsiveness, eventDelay, + argumentValues, stack, weight, weightType, @@ -2309,6 +2327,7 @@ export function computeSamplesTableFromRawSamplesTable( // These fields are copied from the raw samples table: responsiveness, eventDelay, + argumentValues, stack, weight, weightType, @@ -2329,7 +2348,8 @@ export function createThreadFromDerivedTables( samples: SamplesTable, stackTable: StackTable, stringTable: StringTable, - sources: SourceTable + sources: SourceTable, + tracedValuesBuffer: ArrayBuffer | undefined ): Thread { const { processType, @@ -2356,6 +2376,7 @@ export function createThreadFromDerivedTables( jsTracer, isPrivateBrowsing, userContextId, + tracedObjectShapes, } = rawThread; const thread: Thread = { @@ -2384,12 +2405,14 @@ export function createThreadFromDerivedTables( jsTracer, isPrivateBrowsing, userContextId, + tracedObjectShapes, // These fields are derived: samples, stackTable, stringTable, sources, + tracedValuesBuffer, }; return thread; } diff --git a/src/profile-logic/stack-timing.ts b/src/profile-logic/stack-timing.ts index 7ea7f20991..e7de80e372 100644 --- a/src/profile-logic/stack-timing.ts +++ b/src/profile-logic/stack-timing.ts @@ -87,6 +87,7 @@ export type StackTiming = { sameWidthsStart: number[]; sameWidthsEnd: number[]; callNode: IndexIntoCallNodeTable[]; + argumentValues?: number[]; length: number; }; @@ -115,14 +116,20 @@ export function getStackTimingByDepth( } = callNodeTable; const stackTimingByDepth: StackTimingByDepth = Array.from( { length: maxDepthPlusOne }, - (): StackTiming => ({ - start: [], - end: [], - sameWidthsStart: [], - sameWidthsEnd: [], - callNode: [], - length: 0, - }) + (): StackTiming => { + const shape: StackTiming = { + start: [], + end: [], + sameWidthsStart: [], + sameWidthsEnd: [], + callNode: [], + length: 0, + }; + if ('argumentValues' in samples) { + shape.argumentValues = []; + } + return shape; + } ); const sameWidthsIndexToTimestampMap: SameWidthsIndexToTimestampMap = []; @@ -154,6 +161,7 @@ export function getStackTimingByDepth( let deepestOpenBoxDepth = -1; const openBoxStartTimeByDepth = new Float64Array(maxDepthPlusOne); const openBoxStartTickByDepth = new Float64Array(maxDepthPlusOne); + const openBoxArgsByDepth = new Int32Array(maxDepthPlusOne); let currentStackTick = 0; for (let sampleIndex = 0; sampleIndex < samples.length; sampleIndex++) { @@ -162,6 +170,14 @@ export function getStackTimingByDepth( continue; } + let sampleArgs: number = -1; + if ('argumentValues' in samples && samples.argumentValues !== undefined) { + const val = samples.argumentValues[sampleIndex]; + if (val !== null) { + sampleArgs = val; + } + } + const sampleTime = samples.time[sampleIndex]; // Phase 1: Commit open boxes which are not shared by the current call node, @@ -193,6 +209,10 @@ export function getStackTimingByDepth( stackTimingForThisDepth.sameWidthsStart[index] = startStackTick; stackTimingForThisDepth.sameWidthsEnd[index] = currentStackTick; stackTimingForThisDepth.callNode[index] = deepestOpenBoxCallNodeIndex; + if (stackTimingForThisDepth.argumentValues) { + stackTimingForThisDepth.argumentValues[index] = + openBoxArgsByDepth[deepestOpenBoxDepth]; + } deepestOpenBoxCallNodeIndex = callNodeTablePrefixColumn[deepestOpenBoxCallNodeIndex]; deepestOpenBoxDepth--; @@ -208,6 +228,12 @@ export function getStackTimingByDepth( deepestOpenBoxDepth++; openBoxStartTimeByDepth[deepestOpenBoxDepth] = sampleTime; openBoxStartTickByDepth[deepestOpenBoxDepth] = currentStackTick; + if ( + 'argumentValues' in samples && + samples.argumentValues !== undefined + ) { + openBoxArgsByDepth[deepestOpenBoxDepth] = sampleArgs; + } } } @@ -229,6 +255,10 @@ export function getStackTimingByDepth( stackTimingForThisDepth.sameWidthsStart[index] = startStackTick; stackTimingForThisDepth.sameWidthsEnd[index] = currentStackTick; stackTimingForThisDepth.callNode[index] = deepestOpenBoxCallNodeIndex; + if (stackTimingForThisDepth.argumentValues) { + stackTimingForThisDepth.argumentValues[index] = + openBoxArgsByDepth[deepestOpenBoxDepth]; + } deepestOpenBoxCallNodeIndex = callNodeTablePrefixColumn[deepestOpenBoxCallNodeIndex]; deepestOpenBoxDepth--; diff --git a/src/selectors/per-thread/thread.tsx b/src/selectors/per-thread/thread.tsx index fc8b619b89..e6fec6c67d 100644 --- a/src/selectors/per-thread/thread.tsx +++ b/src/selectors/per-thread/thread.tsx @@ -18,6 +18,7 @@ import { ensureExists, getFirstItemFromSet, } from '../../utils/types'; +import { base64StringToBytes } from '../../utils/base64'; import type { Thread, @@ -92,6 +93,12 @@ export function getBasicThreadSelectorsPerThread( ? ProfileSelectors.getProfile(state).threads[singleThreadIndex] : getMergedRawThread(state); + const getTracedValuesBuffer: Selector = + createSelector( + (state: State) => getRawThread(state).tracedValuesBuffer, + (tracedValuesBuffer: string | undefined) => + tracedValuesBuffer ? base64StringToBytes(tracedValuesBuffer) : undefined + ); const getRawSamplesTable: Selector = (state) => getRawThread(state).samples; const getSamplesTable: Selector = createSelector( @@ -147,6 +154,7 @@ export function getBasicThreadSelectorsPerThread( getStackTable, ProfileSelectors.getStringTable, ProfileSelectors.getSourceTable, + getTracedValuesBuffer, ProfileData.createThreadFromDerivedTables ); @@ -386,6 +394,7 @@ export function getBasicThreadSelectorsPerThread( getRawThread, getThread, getSamplesTable, + getTracedValuesBuffer, getSamplesWeightType, getNativeAllocations, getJsAllocations, diff --git a/src/test/components/StackChart.test.tsx b/src/test/components/StackChart.test.tsx index 19b0a69a56..b4968c2458 100644 --- a/src/test/components/StackChart.test.tsx +++ b/src/test/components/StackChart.test.tsx @@ -314,6 +314,19 @@ describe('StackChart', function () { }); }); +describe('ArgumentValues', () => { + it('shows argument values when a profile has them', () => { + const profile = require('../fixtures/upgrades/argument-values.json'); + const store = storeWithProfile(profile); + const { getTooltip, moveMouse, findFillTextPosition } = setup(store); + + moveMouse(findFillTextPosition('bar')); + const tooltip = getTooltip(); + expect(within(ensureExists(tooltip)).getByText('bar')).toBeInTheDocument(); + expect(tooltip).toMatchSnapshot(); + }); +}); + describe('MarkerChart', function () { it('can turn on the show user timings', () => { const { getByLabelText, getState } = setupUserTimings({ diff --git a/src/test/components/__snapshots__/StackChart.test.tsx.snap b/src/test/components/__snapshots__/StackChart.test.tsx.snap index b0c3908117..ba36ba1779 100644 --- a/src/test/components/__snapshots__/StackChart.test.tsx.snap +++ b/src/test/components/__snapshots__/StackChart.test.tsx.snap @@ -1,5 +1,102 @@ // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing +exports[`ArgumentValues shows argument values when a profile has them 1`] = ` +
+
+
+
+ 36ms +
+
+ bar +
+
+
+
+
+
+ Stack Type: +
+
+ JavaScript +
+
+ Category: +
+
+ + JavaScript: JIT (baseline) +
+
+ Page URL: +
+
+ file://<Page #15> +
+
+ File: +
+
+ file://<URL>:11:13 +
+
+ Resource: +
+ file://<URL> +
+
+
+ Arguments +
+ + 0 + +
+
+
+
+`; + exports[`CombinedChart renders combined stack chart 1`] = `
", + "embedderInnerWindowID": 0, + "isPrivateBrowsing": false, + "favicon": null + }, + { + "tabID": 6, + "innerWindowID": 10737418242, + "url": "moz-extension://", + "embedderInnerWindowID": 0, + "isPrivateBrowsing": false, + "favicon": null + }, + { + "tabID": 7, + "innerWindowID": 10737418243, + "url": "moz-extension://", + "embedderInnerWindowID": 0, + "isPrivateBrowsing": false, + "favicon": null + }, + { + "tabID": 8, + "innerWindowID": 10737418244, + "url": "moz-extension://", + "embedderInnerWindowID": 0, + "isPrivateBrowsing": false, + "favicon": null + }, + { + "tabID": 9, + "innerWindowID": 10737418245, + "url": "moz-extension://", + "embedderInnerWindowID": 0, + "isPrivateBrowsing": false, + "favicon": null + }, + { + "tabID": 10, + "innerWindowID": 10737418246, + "url": "moz-extension://", + "embedderInnerWindowID": 0, + "isPrivateBrowsing": false, + "favicon": null + }, + { + "tabID": 14, + "innerWindowID": 17179869185, + "url": "http://", + "embedderInnerWindowID": 0, + "isPrivateBrowsing": false, + "favicon": null + }, + { + "tabID": 12, + "innerWindowID": 15032385537, + "url": "http://", + "embedderInnerWindowID": 0, + "isPrivateBrowsing": false, + "favicon": null + }, + { + "tabID": 11, + "innerWindowID": 6442450946, + "url": "about:newtab", + "embedderInnerWindowID": 0, + "isPrivateBrowsing": false, + "favicon": null + }, + { + "tabID": 13, + "innerWindowID": 21474836482, + "url": "file://", + "embedderInnerWindowID": 0, + "isPrivateBrowsing": false, + "favicon": null + }, + { + "tabID": 15, + "innerWindowID": 21474836484, + "url": "file://", + "embedderInnerWindowID": 0, + "isPrivateBrowsing": false, + "favicon": null + }, + { + "tabID": 3, + "innerWindowID": 21474836485, + "url": "file://", + "embedderInnerWindowID": 0, + "isPrivateBrowsing": false, + "favicon": null + } + ], + "counters": [], + "profilerOverhead": [], + "shared": { + "stringArray": [ + "default-theme@mozilla.org", + "Extension \"System theme — auto\" (ID: default-theme@mozilla.org)", + "data-leak-blocker@mozilla.com", + "Extension \"Data Leak Blocker\" (ID: data-leak-blocker@mozilla.com)", + "webcompat@mozilla.org", + "Extension \"Web Compatibility Interventions\" (ID: webcompat@mozilla.org)", + "pictureinpicture@mozilla.org", + "Extension \"Picture-In-Picture\" (ID: pictureinpicture@mozilla.org)", + "newtab@mozilla.org", + "Extension \"New Tab\" (ID: newtab@mozilla.org)", + "addons-search-detection@mozilla.com", + "Extension \"Add-ons Search Detection\" (ID: addons-search-detection@mozilla.com)", + "formautofill@mozilla.org", + "Extension \"Form Autofill\" (ID: formautofill@mozilla.org)", + "ipp-activator@mozilla.com", + "Extension \"IPP Activator\" (ID: ipp-activator@mozilla.com)", + "(root)", + "(DOM) Event.type", + "(DOM) MouseEvent.screenX", + "(DOM) MouseEvent.screenY", + "mousedown", + "(DOM) MouseEvent.button", + "(DOM) MouseEvent.altKey", + "(DOM) MouseEvent.ctrlKey", + "(DOM) MouseEvent.metaKey", + "(DOM) MouseEvent.shiftKey", + "EventStateManager::PostHandleEvent", + "js::RunScript", + "XUL", + "Task RefreshDriver::EnsureTimerStarted::catch-up", + "RefreshDriver tick", + "Update the rendering Layout, content-visibility and resize observers", + "EventStateManager::PreHandleEvent", + "ElementStateChanged", + "EventDispatcher::Dispatch", + "RefreshObserver", + "TaskController::AddTask", + "Runnable", + "DOMEvent", + "Awake", + "XRE_InitChildProcess", + "Task NotifyObservers", + "Task PContent::Msg_AddPermission", + "PContent::Msg_AddPermission", + "IPC Accumulator", + "setTimeout", + "(DOM) Document.activeElement", + "file://", + "foo", + "bar", + "(DOM) JSWindowActorChild.constructor", + "(DOM) JSWindowActorChild.document", + "(DOM) JSWindowActorChild.contentWindow", + "(DOM) Window.getSelection", + "(DOM) Selection.__stringifier", + "(DOM) Event.timeStamp", + "(DOM) MouseEvent.isTrusted", + "(DOM) Event.defaultPrevented", + "(DOM) Window.setTimeout", + "(DOM) nsIAsyncShutdownBlocker.name", + "Task PBrowser::Msg_RealMouseEnterExitWidgetEvent", + "PBrowser::Msg_RealMouseEnterExitWidgetEvent", + "Task PBrowser::Msg_RealMouseMoveEvent", + "PBrowser::Msg_RealMouseMoveEvent", + "Task PBrowser::Msg_RealMouseButtonEvent", + "PBrowser::Msg_RealMouseButtonEvent", + "EventDispatcher::Dispatch mousedown", + "PContent::Msg_InsertNewFocusActionId", + "PWindowGlobal::Msg_UpdateDocumentHasUserInteracted", + "EventDispatcher::Dispatch mouseup", + "JSWindowActorProtocol::HandleEvent", + "resource:///actors/GenAIChild.sys.mjs", + "handleEvent", + "Window.setTimeout", + "Task PVsync::Msg_Notify", + "PVsync::Msg_Notify", + "f67ae7353bf8e5000", + "f67ae7353bf8e9600", + "ModuleEvaluation", + "ChromeUtils.importESModule", + "Preference Read", + "mozilla::base_profiler_markers_detail::AddMarkerToBuffer >(mozilla::ProfileChunkedBuffer&, mozilla::ProfilerStringView const&, mozilla::MarkerCategory const&, mozilla::MarkerOptions&&, bool (*)(mozilla::ProfileChunkedBuffer&, mozilla::StackCaptureOptions), nsTString const&)", + "profiler_capture_backtrace()", + "mozilla::base_profiler_markers_detail::AddMarkerToBuffer(mozilla::ProfileChunkedBuffer&, mozilla::ProfilerStringView const&, mozilla::MarkerCategory const&, mozilla::MarkerOptions&&, bool (*)(mozilla::ProfileChunkedBuffer&, mozilla::StackCaptureOptions), Flow const&)::{lambda(mozilla::ProfileChunkedBuffer&)#1}::operator()(mozilla::ProfileChunkedBuffer&) const", + "/Users/alexical/mozilla-unified/obj-opt/dist/include/mozilla/BaseProfilerMarkersDetail.h", + "mozilla::base_profiler_markers_detail::AddMarkerToBuffer >(mozilla::ProfileChunkedBuffer&, mozilla::ProfilerStringView const&, mozilla::MarkerCategory const&, mozilla::MarkerOptions&&, bool (*)(mozilla::ProfileChunkedBuffer&, mozilla::StackCaptureOptions), nsTString const&)::{lambda(mozilla::ProfileChunkedBuffer&)#1}::operator()(mozilla::ProfileChunkedBuffer&) const", + "/Users/alexical/mozilla-unified/tools/profiler/core/platform.cpp" + ], + "sources": { + "length": 4, + "uuid": [ + null, + null, + null, + null + ], + "filename": [ + 47, + 71, + 84, + 86 + ] + } + }, + "threads": [ + { + "name": "GeckoMain", + "isMainThread": true, + "processType": "tab", + "processName": "file:// Content", + "processStartupTime": 5584.521484375, + "processShutdownTime": null, + "registerTime": 5605.748109375, + "unregisterTime": null, + "tid": 2503164, + "pid": "39156", + "pausedRanges": [ + { + "startTime": 319713.765958, + "endTime": null, + "reason": "profiler-paused" + } + ], + "frameTable": { + "address": [ + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + 4256519, + -1, + -1, + -1, + -1, + 75486643, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + 4264179, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + 4256519 + ], + "inlineDepth": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1 + ], + "category": [ + null, + 8, + 4, + 4, + 8, + 8, + 8, + 8, + 8, + 8, + 8, + 8, + 8, + 8, + 8, + 8, + 8, + 8, + 8, + 8, + 8, + 8, + 8, + 8, + 1, + 1, + null, + 1, + 1, + 8, + 3, + null, + 1, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + null, + 8, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 8, + 1, + 1, + null + ], + "subcategory": [ + null, + 0, + 0, + 6, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + null, + 0, + 0, + 0, + 5, + null, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + null, + 0, + 0, + 0, + 0, + 0, + 0, + 4, + 4, + 0, + 0, + 0, + null + ], + "func": [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 42, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39, + 40, + 41, + 54, + 43, + 44, + 45, + 46, + 47, + 48, + 49, + 50, + 51, + 52, + 53, + 31 + ], + "nativeSymbol": [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + 0, + null, + null, + null, + null, + 1, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + 2, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + 0 + ], + "innerWindowID": [ + 0, + 0, + 21474836485, + 21474836485, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "line": [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + 317, + null, + null, + null, + null, + 7903, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + 303, + null, + null, + null, + null, + null, + null, + null, + 81, + null, + null, + null, + 303 + ], + "column": [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + 50, + null, + null, + null, + null + ], + "length": 55 + }, + "funcTable": { + "isJS": [ + false, + false, + true, + true, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + true, + false, + false, + false, + false + ], + "relevantForJS": [ + false, + true, + false, + false, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + false, + false, + false, + false, + false, + false, + true, + false, + false, + true, + true, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + true, + false, + false, + true, + false, + false, + false + ], + "name": [ + 16, + 20, + 48, + 49, + 50, + 17, + 51, + 46, + 52, + 53, + 54, + 55, + 56, + 57, + 21, + 22, + 23, + 24, + 25, + 18, + 19, + 58, + 59, + 45, + 40, + 41, + 81, + 60, + 61, + 26, + 33, + 85, + 29, + 30, + 31, + 62, + 63, + 64, + 65, + 34, + 66, + 67, + 82, + 32, + 68, + 42, + 43, + 69, + 70, + 27, + 72, + 73, + 74, + 75, + 83 + ], + "resource": [ + -1, + -1, + 8, + 8, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + 9, + -1, + -1, + -1, + -1, + 9, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + 9, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + 10, + -1, + -1, + -1, + 9 + ], + "source": [ + null, + null, + 0, + 0, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + 2, + null, + null, + null, + null, + 2, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + 3, + null, + null, + null, + null, + null, + null, + null, + 1, + null, + null, + null, + 2 + ], + "lineNumber": [ + null, + null, + 19, + 11, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + 47, + null, + null, + null, + null + ], + "columnNumber": [ + null, + null, + 13, + 13, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + 14, + null, + null, + null, + null + ], + "length": 55 + }, + "nativeSymbols": { + "libIndex": [ + 0, + 0, + 0 + ], + "address": [ + 4256352, + 75486340, + 4264108 + ], + "name": [ + 81, + 82, + 83 + ], + "functionSize": [ + 840, + 540, + 432 + ], + "length": 3 + }, + "resourceTable": { + "lib": [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + 0, + null + ], + "name": [ + 1, + 3, + 5, + 7, + 9, + 11, + 13, + 15, + 47, + 28, + 71 + ], + "host": [ + 0, + 2, + 4, + 6, + 8, + 10, + 12, + 14, + null, + null, + null + ], + "type": [ + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 1, + 5 + ], + "length": 11 + }, + "stackTable": { + "frame": [ + 0, + 0, + 1, + 1, + 2, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 23, + 6, + 7, + 8, + 9, + 10, + 11, + 24, + 25, + 26, + 54, + 27, + 28, + 29, + 30, + 31, + 31, + 32, + 33, + 34, + 26, + 54, + 35, + 36, + 31, + 37, + 38, + 39, + 40, + 26, + 54, + 29, + 41, + 42, + 30, + 31, + 31, + 43, + 26, + 54, + 31, + 43, + 44, + 42, + 45, + 46, + 26, + 54, + 47, + 48, + 49, + 50, + 51, + 31, + 43, + 30, + 31, + 52, + 53, + 33, + 34, + 26, + 54 + ], + "prefix": [ + null, + null, + 0, + 0, + 2, + 2, + 4, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 26, + 26, + 26, + 26, + 26, + 26, + 0, + 34, + 35, + 36, + 34, + 38, + 39, + 40, + 39, + 41, + 34, + 44, + 45, + 46, + 47, + 34, + 49, + 50, + 34, + 52, + 53, + 54, + 55, + 56, + 53, + 58, + 59, + 58, + 53, + 61, + 45, + 64, + 65, + 55, + 53, + 68, + 69, + 34, + 71, + 72, + 73, + 54, + 75, + 76, + 77, + 78, + 79, + 39, + 81, + 82, + 34, + 84, + 85, + 86, + 87, + 88 + ], + "length": 90 + }, + "markers": { + "data": [ + { + "type": "Awake", + "QoS": "User Interactive" + }, + { + "innerWindowID": 21474836485, + "type": "DOMEvent", + "eventType": "mousedown", + "target": "html@10bc0c080", + "latency": 2.965583333333333 + }, + { + "type": "FlowMarker", + "flow": 77 + }, + { + "type": "TextStack", + "name": "" + }, + { + "type": "TextStack", + "name": "resource:///actors/GenAIChild.sys.mjs", + "cause": { + "tid": 2503164, + "time": 324383.877234375, + "stack": 57 + } + }, + { + "type": "Preference", + "prefName": "browser.drag_out_of_frame_style", + "prefKind": "User", + "prefType": "Int", + "prefValue": "1" + }, + { + "type": "TextStack", + "name": "" + }, + { + "type": "TextStack", + "name": "resource://gre/actors/AutoScrollChild.sys.mjs", + "cause": { + "tid": 2503164, + "time": 324384.231984375, + "stack": 57 + } + }, + { + "innerWindowID": 21474836485, + "type": "DOMEvent", + "eventType": "mousedown", + "target": "html@10bc0c080", + "latency": 2.965583333333333 + }, + { + "innerWindowID": 21474836485, + "type": "Text", + "name": "Coalesced input move flusher [Event]", + "cause": { + "tid": 2503164, + "time": 324347.262526375, + "stack": 62 + } + }, + { + "type": "Task", + "name": "PBrowser::Msg_RealMouseButtonEvent", + "priority": 6, + "priorityName": "InputHigh", + "task": 76 + }, + { + "type": "Awake", + "CPU Time": 37.453 + }, + null + ], + "name": [ + 39, + 38, + 36, + 78, + 79, + 80, + 78, + 79, + 38, + 35, + 37, + 39, + 44 + ], + "startTime": [ + 324347.219609375, + 324347.499109375, + 324383.871026375, + 324383.866109375, + 324383.823942375, + 324384.170276375, + 324384.230526375, + 324384.208942375, + 5584.521484375, + 324347.251067375, + 324347.233234375, + 5584.521484375, + 5584.521484375 + ], + "endTime": [ + 5584.521484375, + 5584.521484375, + 5584.521484375, + 324383.873359375, + 324383.874359375, + 5584.521484375, + 324384.231109375, + 324384.231776375, + 324384.271151375, + 324384.347234375, + 324384.353442375, + 324384.692192375, + 324576.394067375 + ], + "phase": [ + 2, + 2, + 0, + 1, + 1, + 0, + 1, + 1, + 3, + 1, + 1, + 3, + 3 + ], + "category": [ + 1, + 8, + 1, + 4, + 4, + 1, + 4, + 4, + 8, + 7, + 1, + 1, + 23 + ], + "length": 13 + }, + "samples": { + "length": 32, + "time": [ + 324347.532151375, + 324347.697817375, + 324347.735734375, + 324383.79677637503, + 324383.806276375, + 324383.811067375, + 324383.89902637503, + 324383.90765137505, + 324383.9553173751, + 324383.95594237506, + 324383.95960937504, + 324383.96073437505, + 324383.97785937507, + 324384.01777637505, + 324384.01919237507, + 324384.0209843751, + 324384.0264843751, + 324384.06756737514, + 324384.0863173751, + 324384.1059013751, + 324384.1337343751, + 324384.1485263751, + 324384.23594237515, + 324384.23819237517, + 324384.25665137515, + 324384.25690137513, + 324384.26069237513, + 324384.26073437516, + 324384.2628173752, + 324384.2628593752, + 324384.2653593752, + 324384.2655263752 + ], + "weight": null, + "weightType": "samples", + "stack": [ + 3, + 5, + 6, + 5, + 3, + 1, + 7, + 1, + 8, + 1, + 9, + 1, + 10, + 1, + 11, + 1, + 12, + 1, + 13, + 1, + 14, + 1, + 7, + 1, + 8, + 1, + 15, + 1, + 16, + 1, + 17, + 1 + ], + "eventDelay": [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null + ], + "argumentValues": [ + null, + 6, + 24, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null + ] + }, + "tracedValuesBuffer": "AgAAABIAAQAAAAwHAAAAAAYAAAABAAcAAQAAABE=", + "tracedObjectShapes": [ + [ + "MouseEvent" + ] + ] + } + ], + "profilingLog": { + "39133": { + "bufferGlobalController": { + "controllerCreationTime_TSms": 323947.267208, + "logBegin_TSms": 323947.267042, + "updates": [], + "updatesSchema": "0: pid, 1: chunkRelease_TSms, 3: chunkDiff" + }, + "profilingLogBegin_TSms": 323946.88225, + "profilingLogEnd_TSms": 325307.459125 + }, + "39136": { + "profilingLogBegin_TSms": 323451.186208, + "profilingLogEnd_TSms": 324804.74575 + }, + "39137": { + "profilingLogBegin_TSms": 323414.368792, + "profilingLogEnd_TSms": 324769.997375 + }, + "39138": { + "profilingLogBegin_TSms": 323030.653583, + "profilingLogEnd_TSms": 324388.046958 + }, + "39139": { + "profilingLogBegin_TSms": 323014.021875, + "profilingLogEnd_TSms": 324366.797167 + }, + "39143": { + "profilingLogBegin_TSms": 322645.606, + "profilingLogEnd_TSms": 323999.851042 + }, + "39144": { + "profilingLogBegin_TSms": 322335.497542, + "profilingLogEnd_TSms": 323689.253375 + }, + "39151": { + "profilingLogBegin_TSms": 321587.79575, + "profilingLogEnd_TSms": 322941.864333 + }, + "39152": { + "profilingLogBegin_TSms": 321587.792458, + "profilingLogEnd_TSms": 322941.85975 + }, + "39153": { + "profilingLogBegin_TSms": 321565.734083, + "profilingLogEnd_TSms": 322918.633625 + }, + "39156": { + "profilingLogBegin_TSms": 318363.045375, + "profilingLogEnd_TSms": 319719.776417 + }, + "39162": { + "profilingLogBegin_TSms": 314471.372125, + "profilingLogEnd_TSms": 315826.738375 + }, + "39378": { + "profilingLogBegin_TSms": 142455.614167, + "profilingLogEnd_TSms": 143810.263 + } + }, + "profileGatheringLog": { + "39133": { + "events": [ + [ + 325307.830042, + "Generated parent process profile, size:", + 804072 + ], + [ + 325307.831708, + "No exit profiles." + ], + [ + 325307.837125, + "Waiting for pending profile, pid:", + 39136 + ], + [ + 325307.837667, + "Waiting for pending profile, pid:", + 39137 + ], + [ + 325307.838083, + "Waiting for pending profile, pid:", + 39138 + ], + [ + 325307.838458, + "Waiting for pending profile, pid:", + 39139 + ], + [ + 325307.838833, + "Waiting for pending profile, pid:", + 39143 + ], + [ + 325307.839167, + "Waiting for pending profile, pid:", + 39144 + ], + [ + 325307.8395, + "Waiting for pending profile, pid:", + 39151 + ], + [ + 325307.839792, + "Waiting for pending profile, pid:", + 39152 + ], + [ + 325307.840125, + "Waiting for pending profile, pid:", + 39153 + ], + [ + 325307.840417, + "Waiting for pending profile, pid:", + 39156 + ], + [ + 325307.84075, + "Waiting for pending profile, pid:", + 39162 + ], + [ + 325307.841042, + "Waiting for pending profile, pid:", + 39378 + ], + [ + 325312.892, + "Got profile from pid, with size:", + 39136, + 370202 + ], + [ + 325313.827083, + "Got profile from pid, with size:", + 39144, + 369231 + ], + [ + 325314.569458, + "Got profile from pid, with size:", + 39139, + 370802 + ], + [ + 325315.407292, + "Got profile from pid, with size:", + 39153, + 374670 + ], + [ + 325316.251625, + "Got profile from pid, with size:", + 39143, + 374172 + ], + [ + 325317.081667, + "Got profile from pid, with size:", + 39152, + 376043 + ], + [ + 325317.775167, + "Got profile from pid, with size:", + 39151, + 376231 + ], + [ + 325318.563583, + "Got profile from pid, with size:", + 39378, + 371592 + ], + [ + 325319.515042, + "Got profile from pid, with size:", + 39137, + 481709 + ], + [ + 325320.320875, + "Got profile from pid, with size:", + 39138, + 761951 + ], + [ + 325321.083458, + "Got profile from pid, with size:", + 39162, + 371752 + ], + [ + 325321.857458, + "Got profile from pid, with size:", + 39156, + 541826 + ], + [ + 325322.156667, + "Finished gathering, total size:", + 5944267 + ] + ], + "profileGatheringLogBegin_TSms": 325298.33725, + "profileGatheringLogEnd_TSms": 325322.157125 + } + } +} \ No newline at end of file diff --git a/src/test/fixtures/utils.ts b/src/test/fixtures/utils.ts index 475cf7a3c4..f011fa628f 100644 --- a/src/test/fixtures/utils.ts +++ b/src/test/fixtures/utils.ts @@ -22,6 +22,7 @@ import { } from 'firefox-profiler/profile-logic/profile-data'; import { getProfileWithDicts } from './profiles/processed-profile'; import { StringTable } from '../../utils/string-table'; +import { base64StringToBytes } from '../../utils/base64'; import type { IndexIntoCallNodeTable, @@ -153,12 +154,16 @@ export function computeThreadFromRawThread( sampleUnits, referenceCPUDeltaPerMs ); + const tracedValuesBuffer = rawThread.tracedValuesBuffer + ? base64StringToBytes(rawThread.tracedValuesBuffer) + : undefined; return createThreadFromDerivedTables( rawThread, samples, stackTable, stringTable, - shared.sources + shared.sources, + tracedValuesBuffer ); } diff --git a/src/test/store/__snapshots__/profile-view.test.ts.snap b/src/test/store/__snapshots__/profile-view.test.ts.snap index a0d8a23ea5..e8dcc09287 100644 --- a/src/test/store/__snapshots__/profile-view.test.ts.snap +++ b/src/test/store/__snapshots__/profile-view.test.ts.snap @@ -2962,6 +2962,8 @@ CallTree { }, }, "tid": 0, + "tracedObjectShapes": undefined, + "tracedValuesBuffer": undefined, "unregisterTime": null, "userContextId": undefined, }, @@ -3296,6 +3298,8 @@ Object { }, }, "tid": 0, + "tracedObjectShapes": undefined, + "tracedValuesBuffer": undefined, "unregisterTime": null, "userContextId": undefined, } @@ -3702,6 +3706,8 @@ Object { }, }, "tid": 0, + "tracedObjectShapes": undefined, + "tracedValuesBuffer": undefined, "unregisterTime": null, "userContextId": undefined, } @@ -4034,6 +4040,8 @@ Object { }, }, "tid": 0, + "tracedObjectShapes": undefined, + "tracedValuesBuffer": undefined, "unregisterTime": null, "userContextId": undefined, } @@ -4366,6 +4374,8 @@ Object { }, }, "tid": 0, + "tracedObjectShapes": undefined, + "tracedValuesBuffer": undefined, "unregisterTime": null, "userContextId": undefined, } diff --git a/src/types/@types/devtools-reps/index.d.ts b/src/types/@types/devtools-reps/index.d.ts new file mode 100644 index 0000000000..11bbce5292 --- /dev/null +++ b/src/types/@types/devtools-reps/index.d.ts @@ -0,0 +1,28 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +declare module 'devtools-reps' { + export const MODE: { + readonly TINY: unique symbol; + readonly SHORT: unique symbol; + readonly LONG: unique symbol; + readonly HEADER: unique symbol; + }; + + export const REPS: any; + export function getRep(object: any, defaultRep?: any): any; + + export function parseURLEncodedText(text: string): any; + export function parseURLParams(url: string): any; + export function maybeEscapePropertyName(name: string): string; + export function getGripPreviewItems(grip: any): any[]; + + export const ValueSummaryReader: { + getArgumentSummaries: ( + valuesBuffer: ArrayBuffer, + shapes: Array, + valuesBufferIndex: number + ) => any; + }; +} diff --git a/src/types/gecko-profile.ts b/src/types/gecko-profile.ts index 7d1be14d87..8f3f9678f0 100644 --- a/src/types/gecko-profile.ts +++ b/src/types/gecko-profile.ts @@ -124,6 +124,7 @@ export type GeckoSamples = { stack: 0; time: 1; eventDelay: 2; + argumentValues?: 3; threadCPUDelta?: 3; }; data: Array< @@ -140,8 +141,14 @@ export type GeckoSamples = { // milliseconds since the last event was processed in this // thread's event loop at the time that the sample was taken Milliseconds, + // Index into the values buffer containing a binary representation of the argumentValues + // It's present only when the JS Execution Tracing feature is enabled in Firefox + // OR // CPU usage value of the current thread. // It's present only when the CPU Utilization feature is enabled in Firefox. + // + // NOTE: these two options are mutually exclusive since CPU Utilization is + // mutually exclusive with JS Execution Tracing number | null, ] >; @@ -158,6 +165,7 @@ export type GeckoSampleStructWithResponsiveness = { // versions may not have it or that feature could be disabled. No upgrader was // written for this change because it's a completely new data source. threadCPUDelta?: Array; + argumentValues?: Array; length: number; }; @@ -172,6 +180,7 @@ export type GeckoSampleStructWithEventDelay = { // versions may not have it or that feature could be disabled. No upgrader was // written for this change because it's a completely new data source. threadCPUDelta?: Array; + argumentValues?: Array; length: number; }; @@ -282,6 +291,8 @@ export type GeckoThread = { stackTable: GeckoStackTable; stringTable: string[]; jsTracerEvents?: JsTracerTable; + tracedValues?: string; + tracedObjectShapes?: Array>; }; export type GeckoExtensionMeta = { diff --git a/src/types/profile-derived.ts b/src/types/profile-derived.ts index 3ee00c355b..281ea8fe1a 100644 --- a/src/types/profile-derived.ts +++ b/src/types/profile-derived.ts @@ -95,6 +95,7 @@ export type Thread = { // It's absent in Firefox 97 and before, or in Firefox 98+ when this thread // had no extra attribute at all. userContextId?: number; + tracedObjectShapes?: Array; // The fields below this comment are derived data, and not present on the RawThread // in the same form. @@ -108,6 +109,7 @@ export type Thread = { // The stack samples collected for this thread. This field is different from // RawThread in that the `time` column is always present. samples: SamplesTable; + tracedValuesBuffer?: ArrayBuffer; }; /** @@ -134,6 +136,7 @@ export type SamplesTable = { // This property isn't present in normal threads. However it's present for // merged threads, so that we know the origin thread for these samples. threadId?: Tid[]; + argumentValues?: Array; length: number; }; @@ -144,6 +147,7 @@ type SamplesLikeTableShape = { // See the WeightType type for more information. weight: null | number[]; weightType: WeightType; + argumentValues?: Array; length: number; }; @@ -160,6 +164,7 @@ export type CounterSamplesTable = { number?: number[]; // The count of the data, for instance for memory this would be bytes. count: number[]; + argumentValues?: Array; length: number; }; diff --git a/src/types/profile.ts b/src/types/profile.ts index 3e5e02466e..d463ca724d 100644 --- a/src/types/profile.ts +++ b/src/types/profile.ts @@ -117,6 +117,7 @@ export type RawSamplesTable = { time?: Milliseconds[]; // If the `time` column is not present, then the `timeDeltas` column must be present. timeDeltas?: Milliseconds[]; + argumentValues?: Array; // An optional weight array. If not present, then the weight is assumed to be 1. // See the WeightType type for more information. weight: null | number[]; @@ -164,6 +165,7 @@ export type UnbalancedNativeAllocationsTable = { weight: Bytes[]; weightType: 'bytes'; stack: Array; + argumentValues?: Array; length: number; }; @@ -503,6 +505,7 @@ export type RawCounterSamplesTable = { number?: number[]; // The count of the data, for instance for memory this would be bytes. count: number[]; + argumentValues?: Array; length: number; }; @@ -670,6 +673,8 @@ export type RawThread = { // It's absent in Firefox 97 and before, or in Firefox 98+ when this thread // had no extra attribute at all. userContextId?: number; + tracedValuesBuffer?: string; + tracedObjectShapes?: Array; }; export type ExtensionTable = { diff --git a/src/utils/base64.ts b/src/utils/base64.ts index 31571311ec..4dbff80a3a 100644 --- a/src/utils/base64.ts +++ b/src/utils/base64.ts @@ -25,3 +25,21 @@ export async function dataUrlToBytes(dataUrl: string): Promise { const res = await fetch(dataUrl); return res.arrayBuffer(); } + +function base64StringToBytesFallback(base64: string): ArrayBuffer { + const binaryString = atob(base64); + const bytes = new Uint8Array(binaryString.length); + for (let i = 0; i < binaryString.length; i++) { + bytes[i] = binaryString.charCodeAt(i); + } + return bytes.buffer; +} + +export function base64StringToBytes(base64: string): ArrayBuffer { + if ('fromBase64' in Uint8Array) { + // @ts-expect-error Uint8Array.fromBase64 is a relatively new API + return Uint8Array.fromBase64(base64).buffer; + } + + return base64StringToBytesFallback(base64); +} diff --git a/webpack.config.js b/webpack.config.js index 274766717a..d42c0cceab 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -58,6 +58,7 @@ const config = { path.join(__dirname, 'node_modules', 'photon-colors'), path.join(__dirname, 'node_modules', 'react-splitter-layout'), path.join(__dirname, 'node_modules', 'iongraph-web'), + path.join(__dirname, 'node_modules', 'devtools-reps'), ], }, { diff --git a/yarn.lock b/yarn.lock index bbb1da7a0a..1fd696443a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4600,6 +4600,16 @@ devtools-license-check@^0.9.0: dependencies: license-checker "^9.0.3" +devtools-reps@^0.27.3: + version "0.27.3" + resolved "https://registry.yarnpkg.com/devtools-reps/-/devtools-reps-0.27.3.tgz#a41cb6c1c5b4aed5d0bdcac7505b0e3e6ad41526" + integrity sha512-bG3kr0jOvYqx0xAyTvkq/w5cKU6aJm3J53gKetomgFZYrsZiW7Z7U2APHD9uylj2EZ5OrJZaovHkxDRqHqb01g== + dependencies: + prop-types "^15.7.2" + react "^16.8.6" + react-dom "^16.8.6" + react-dom-factories "^1.0.2" + dezalgo@^1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/dezalgo/-/dezalgo-1.0.3.tgz#7f742de066fc748bc8db820569dddce49bf0d456" @@ -10248,7 +10258,7 @@ process-nextick-args@~2.0.0: resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== -prop-types@^15.6.2, prop-types@^15.8.1: +prop-types@^15.6.2, prop-types@^15.7.2, prop-types@^15.8.1: version "15.8.1" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== @@ -10417,6 +10427,21 @@ rc@1.2.8: minimist "^1.2.0" strip-json-comments "~2.0.1" +react-dom-factories@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/react-dom-factories/-/react-dom-factories-1.0.2.tgz#eb7705c4db36fb501b3aa38ff759616aa0ff96e0" + integrity sha512-Bmic2N3oKji7vw9qjDr2dmwHvOATbFSnKy7EH0uT/qjvzIUsiXp6Yquk72LJ3WfMtRnq3ujXMMo7GsJeLPfFWw== + +react-dom@^16.8.6: + version "16.14.0" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.14.0.tgz#7ad838ec29a777fb3c75c3a190f661cf92ab8b89" + integrity sha512-1gCeQXDLoIqMgqD3IO2Ah9bnf0w9kzhwN5q4FGnHZ67hBm9yePzB5JJAIQCc8x3pFnNlwFq4RidZggNAAkzWWw== + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + prop-types "^15.6.2" + scheduler "^0.19.1" + react-dom@^18.3.1: version "18.3.1" resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.3.1.tgz#c2265d79511b57d479b3dd3fdfa51536494c5cb4" @@ -10468,6 +10493,15 @@ react-transition-group@^4.4.5: loose-envify "^1.4.0" prop-types "^15.6.2" +react@^16.8.6: + version "16.14.0" + resolved "https://registry.yarnpkg.com/react/-/react-16.14.0.tgz#94d776ddd0aaa37da3eda8fc5b6b18a4c9a3114d" + integrity sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g== + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + prop-types "^15.6.2" + react@^18.3.1: version "18.3.1" resolved "https://registry.yarnpkg.com/react/-/react-18.3.1.tgz#49ab892009c53933625bd16b2533fc754cab2891" @@ -11085,6 +11119,14 @@ saxes@^6.0.0: dependencies: xmlchars "^2.2.0" +scheduler@^0.19.1: + version "0.19.1" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.19.1.tgz#4f3e2ed2c1a7d65681f4c854fa8c5a1ccb40f196" + integrity sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA== + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + scheduler@^0.23.2: version "0.23.2" resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.2.tgz#414ba64a3b282892e944cf2108ecc078d115cdc3" @@ -11583,7 +11625,16 @@ string-length@^4.0.2: char-regex "^1.0.2" strip-ansi "^6.0.0" -"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0": + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -11705,7 +11756,7 @@ stringify-object@^3.3.0: is-obj "^1.0.1" is-regexp "^1.0.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -11719,6 +11770,13 @@ strip-ansi@^0.3.0: dependencies: ansi-regex "^0.2.1" +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + strip-ansi@^7.0.1, strip-ansi@^7.1.0: version "7.1.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45"