Skip to content

Commit 56b5a71

Browse files
Connor ClarkDevtools-frontend LUCI CQ
authored andcommitted
[RPP] Begin using ParsedTraceFile as definitive way to pass around trace
Trace.TraceModel.Model and TraceLoader contained internal objects that wrapped a trace, it's parsed handler data, insights, and metadata. But this wasn't available to outside callers. Turns out, we want to pass around all of these things together pretty often, so let's start doing that. This CL should have no functional changes, and is a first pass at refactoring this trace data into a simpler interface. Bug: 358583420 Change-Id: I115577695b8a58f36ee3c2957d0b518ceea82192 Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/6931525 Reviewed-by: Paul Irish <[email protected]> Auto-Submit: Connor Clark <[email protected]> Commit-Queue: Connor Clark <[email protected]>
1 parent 606988b commit 56b5a71

File tree

12 files changed

+111
-126
lines changed

12 files changed

+111
-126
lines changed

front_end/models/ai_assistance/agents/PerformanceAgent.test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,8 @@ const FAKE_INP_MODEL = {
269269
const FAKE_PARSED_TRACE = {
270270
Meta: {traceBounds: {min: 0, max: 10}, mainFrameURL: 'https://www.example.com'},
271271
} as unknown as Trace.Handlers.Types.ParsedTrace;
272-
const FAKE_INSIGHTS = new Map() as unknown as Trace.Insights.Types.TraceInsightSets;
272+
const FAKE_INSIGHTS = new Map([['', {model: {LCPBreakdown: FAKE_LCP_MODEL, INPBreakdown: FAKE_INP_MODEL}}]]) as
273+
unknown as Trace.Insights.Types.TraceInsightSets;
273274
const FAKE_METADATA = {} as unknown as Trace.Types.File.MetaData;
274275

275276
function createAgentForInsightConversation(opts: {aidaClient?: Host.AidaClient.AidaClient} = {}) {

front_end/models/trace/ModelImpl.ts

Lines changed: 36 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -100,14 +100,8 @@ export class Model extends EventTarget {
100100

101101
this.#processor.addEventListener(TraceParseProgressEvent.eventName, onTraceUpdate);
102102

103-
// Create a parsed trace file. It will be populated with data from the processor.
104-
const file: ParsedTraceFile = {
105-
traceEvents,
106-
metadata,
107-
parsedTrace: null,
108-
traceInsights: null,
109-
syntheticEventsManager: Helpers.SyntheticEvents.SyntheticEventsManager.createAndActivate(traceEvents),
110-
};
103+
// TODO(cjamcl): this.#processor.parse needs this to work. So it should either take it as input, or create it itself.
104+
const syntheticEventsManager = Helpers.SyntheticEvents.SyntheticEventsManager.createAndActivate(traceEvents);
111105

112106
try {
113107
// Wait for all outstanding promises before finishing the async execution,
@@ -119,7 +113,11 @@ export class Model extends EventTarget {
119113
resolveSourceMap: config?.resolveSourceMap,
120114
};
121115
await this.#processor.parse(traceEvents, parseConfig);
122-
this.#storeParsedFileData(file, this.#processor.parsedTrace, this.#processor.insights);
116+
if (!this.#processor.parsedTrace) {
117+
throw new Error('processor did not parse trace');
118+
}
119+
const file = this.#storeAndCreateParsedTraceFile(
120+
syntheticEventsManager, traceEvents, metadata, this.#processor.parsedTrace, this.#processor.insights);
123121
// We only push the file onto this.#traces here once we know it's valid
124122
// and there's been no errors in the parsing.
125123
this.#traces.push(file);
@@ -133,39 +131,51 @@ export class Model extends EventTarget {
133131
}
134132
}
135133

136-
#storeParsedFileData(
137-
file: ParsedTraceFile, data: Handlers.Types.ParsedTrace|null,
138-
insights: Insights.Types.TraceInsightSets|null): void {
139-
file.parsedTrace = data;
140-
file.traceInsights = insights;
134+
#storeAndCreateParsedTraceFile(
135+
syntheticEventsManager: Helpers.SyntheticEvents.SyntheticEventsManager,
136+
traceEvents: readonly Types.Events.Event[], metadata: Types.File.MetaData, data: Handlers.Types.ParsedTrace,
137+
traceInsights: Insights.Types.TraceInsightSets|null): ParsedTraceFile {
141138
this.#lastRecordingIndex++;
142139
let recordingName = `Trace ${this.#lastRecordingIndex}`;
143-
let origin: string|null = null;
144-
if (file.parsedTrace) {
145-
origin = Helpers.Trace.extractOriginFromTrace(file.parsedTrace.Meta.mainFrameURL);
146-
if (origin) {
147-
const nextSequenceForDomain = Platform.MapUtilities.getWithDefault(this.#nextNumberByDomain, origin, () => 1);
148-
recordingName = `${origin} (${nextSequenceForDomain})`;
149-
this.#nextNumberByDomain.set(origin, nextSequenceForDomain + 1);
150-
}
140+
const origin = Helpers.Trace.extractOriginFromTrace(data.Meta.mainFrameURL);
141+
if (origin) {
142+
const nextSequenceForDomain = Platform.MapUtilities.getWithDefault(this.#nextNumberByDomain, origin, () => 1);
143+
recordingName = `${origin} (${nextSequenceForDomain})`;
144+
this.#nextNumberByDomain.set(origin, nextSequenceForDomain + 1);
151145
}
152146
this.#recordingsAvailable.push(recordingName);
147+
148+
return {
149+
traceEvents,
150+
metadata,
151+
parsedTrace: data,
152+
insights: traceInsights,
153+
syntheticEventsManager,
154+
};
153155
}
154156

155157
lastTraceIndex(): number {
156158
return this.size() - 1;
157159
}
158160

161+
findIndexForParsedTrace(parsedTrace: Handlers.Types.ParsedTrace): number {
162+
return this.#traces.findIndex(file => file.parsedTrace === parsedTrace);
163+
}
164+
159165
/**
160166
* Returns the parsed trace data indexed by the order in which it was stored.
161167
* If no index is given, the last stored parsed data is returned.
162168
*/
169+
parsedTraceFile(index: number = this.#traces.length - 1): ParsedTraceFile|null {
170+
return this.#traces.at(index) ?? null;
171+
}
172+
163173
parsedTrace(index: number = this.#traces.length - 1): Handlers.Types.ParsedTrace|null {
164174
return this.#traces.at(index)?.parsedTrace ?? null;
165175
}
166176

167177
traceInsights(index: number = this.#traces.length - 1): Insights.Types.TraceInsightSets|null {
168-
return this.#traces.at(index)?.traceInsights ?? null;
178+
return this.#traces.at(index)?.insights ?? null;
169179
}
170180

171181
metadata(index: number = this.#traces.length - 1): Types.File.MetaData|null {
@@ -211,8 +221,9 @@ export class Model extends EventTarget {
211221
* essentially the TraceFile plus whatever the model has parsed from it.
212222
*/
213223
export type ParsedTraceFile = Types.File.TraceFile&{
214-
parsedTrace: Handlers.Types.ParsedTrace | null,
215-
traceInsights: Insights.Types.TraceInsightSets | null,
224+
parsedTrace: Handlers.Types.ParsedTrace,
225+
/** Is null for CPU profiles. */
226+
insights: Insights.Types.TraceInsightSets | null,
216227
syntheticEventsManager: Helpers.SyntheticEvents.SyntheticEventsManager,
217228
};
218229

front_end/models/trace/handlers/Threads.test.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,13 @@ describeWithEnvironment('Handler Threads helper', function() {
4444
Trace.Types.Events.ThreadID(1),
4545
);
4646

47-
const {parsedTrace} = await TraceLoader.executeTraceEngineOnFileContents(contents);
47+
const {parsedTraceFile} = await TraceLoader.executeTraceEngineOnFileContents(contents);
4848

4949
// Check that we did indeed parse this properly as a CPU Profile.
50-
assert.strictEqual(parsedTrace.Renderer.processes.size, 0);
51-
assert.strictEqual(parsedTrace.Samples.profilesInProcess.size, 1);
50+
assert.strictEqual(parsedTraceFile.parsedTrace.Renderer.processes.size, 0);
51+
assert.strictEqual(parsedTraceFile.parsedTrace.Samples.profilesInProcess.size, 1);
5252

53-
const threads = Trace.Handlers.Threads.threadsInTrace(parsedTrace);
53+
const threads = Trace.Handlers.Threads.threadsInTrace(parsedTraceFile.parsedTrace);
5454
assert.lengthOf(threads, 1);
5555

5656
assert.strictEqual(threads.at(0)?.type, Trace.Handlers.Threads.ThreadType.CPU_PROFILE);

front_end/models/trace/helpers/SyntheticEvents.test.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,11 @@ describe('SyntheticEvents', function() {
4141
async function() {
4242
const contents = await TraceLoader.fixtureContents(this, 'web-dev.json.gz');
4343
const rawEvents = 'traceEvents' in contents ? contents.traceEvents as Trace.Types.Events.Event[] : contents;
44-
const {parsedTrace} = await TraceLoader.executeTraceEngineOnFileContents(rawEvents);
44+
const {parsedTraceFile} = await TraceLoader.executeTraceEngineOnFileContents(rawEvents);
4545
const allSyntheticEvents = [
46-
...parsedTrace.Animations.animations,
47-
...parsedTrace.NetworkRequests.byTime,
48-
...parsedTrace.Screenshots.legacySyntheticScreenshots ?? [],
46+
...parsedTraceFile.parsedTrace.Animations.animations,
47+
...parsedTraceFile.parsedTrace.NetworkRequests.byTime,
48+
...parsedTraceFile.parsedTrace.Screenshots.legacySyntheticScreenshots ?? [],
4949
];
5050
const syntheticEventsManager = Trace.Helpers.SyntheticEvents.SyntheticEventsManager.getActiveManager();
5151
for (const syntheticEvent of allSyntheticEvents) {

front_end/models/trace/insights/DuplicatedJavaScript.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,8 +114,8 @@ describeWithEnvironment('DuplicatedJavaScript', function() {
114114
}
115115
}
116116

117-
const parsedTraceData = await TraceLoader.executeTraceEngineOnFileContents(fileContents);
118-
const {parsedTrace: data, insights} = parsedTraceData;
117+
const {parsedTraceFile} = await TraceLoader.executeTraceEngineOnFileContents(fileContents);
118+
const {parsedTrace: data, insights} = parsedTraceFile;
119119
if (!insights) {
120120
throw new Error('invalid insights');
121121
}

front_end/panels/timeline/ModificationsManager.ts

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -75,25 +75,19 @@ export class ModificationsManager extends EventTarget {
7575
activeManager = modificationsManagerByTraceIndex[traceIndex];
7676
ModificationsManager.activeManager()?.applyModificationsIfPresent();
7777
}
78-
const parsedTrace = traceModel.parsedTrace(traceIndex);
79-
if (!parsedTrace) {
78+
79+
const parsedTraceFile = traceModel.parsedTraceFile(traceIndex);
80+
if (!parsedTraceFile) {
8081
throw new Error('ModificationsManager was initialized without a corresponding trace data');
8182
}
83+
84+
const {parsedTrace, metadata, traceEvents, syntheticEventsManager} = parsedTraceFile;
8285
const traceBounds = parsedTrace.Meta.traceBounds;
83-
const traceEvents = traceModel.rawTraceEvents(traceIndex);
84-
if (!traceEvents) {
85-
throw new Error('ModificationsManager was initialized without a corresponding raw trace events array');
86-
}
87-
const syntheticEventsManager = traceModel.syntheticTraceEventsManager(traceIndex);
88-
if (!syntheticEventsManager) {
89-
throw new Error('ModificationsManager was initialized without a corresponding SyntheticEventsManager');
90-
}
91-
const metadata = traceModel.metadata(traceIndex);
9286
const newModificationsManager = new ModificationsManager({
9387
parsedTrace,
9488
traceBounds,
9589
rawTraceEvents: traceEvents,
96-
modifications: metadata?.modifications,
90+
modifications: metadata.modifications,
9791
syntheticEvents: syntheticEventsManager.getSyntheticTraces(),
9892
});
9993
modificationsManagerByTraceIndex[traceIndex] = newModificationsManager;

front_end/panels/timeline/TimelinePanel.ts

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1377,12 +1377,9 @@ export class TimelinePanel extends Common.ObjectWrapper.eventMixin<EventTypes, t
13771377
if (this.#viewMode.mode !== 'VIEWING_TRACE') {
13781378
return;
13791379
}
1380-
const trace = this.#traceEngineModel.parsedTrace(this.#viewMode.traceIndex);
1381-
if (!trace) {
1382-
return;
1383-
}
1384-
let traceEvents = this.#traceEngineModel.rawTraceEvents(this.#viewMode.traceIndex);
1385-
if (!traceEvents) {
1380+
1381+
const parsedTraceFile = this.#traceEngineModel.parsedTraceFile(this.#viewMode.traceIndex);
1382+
if (!parsedTraceFile) {
13861383
return;
13871384
}
13881385

@@ -1394,9 +1391,7 @@ export class TimelinePanel extends Common.ObjectWrapper.eventMixin<EventTypes, t
13941391
scriptByIdMap.set(`${mapScript.isolate}.${mapScript.scriptId}`, mapScript);
13951392
}
13961393

1397-
const metadata = this.#traceEngineModel.metadata(this.#viewMode.traceIndex) ?? {};
1398-
1399-
traceEvents = traceEvents.map(event => {
1394+
const traceEvents = parsedTraceFile.traceEvents.map(event => {
14001395
if (Trace.Types.Events.isAnyScriptCatchupEvent(event) && event.name !== 'StubScriptCatchup') {
14011396
const mappedScript = scriptByIdMap.get(`${event.args.data.isolate}.${event.args.data.scriptId}`);
14021397

@@ -1422,6 +1417,7 @@ export class TimelinePanel extends Common.ObjectWrapper.eventMixin<EventTypes, t
14221417
return event;
14231418
});
14241419

1420+
const metadata = parsedTraceFile.metadata;
14251421
metadata.modifications = config.addModifications ? ModificationsManager.activeManager()?.toJSON() : undefined;
14261422

14271423
// NOTE: we used to export the track configuration changes into the trace

front_end/panels/timeline/components/Utils.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,8 @@ describeWithEnvironment('Utils', () => {
7171

7272
before(async function() {
7373
const events = await TraceLoader.fixtureContents(this, 'load-simple.json.gz');
74-
const {parsedTrace} = await TraceLoader.executeTraceEngineOnFileContents(events);
75-
req = parsedTrace.NetworkRequests.byId.get('2648544.35');
74+
const {parsedTraceFile} = await TraceLoader.executeTraceEngineOnFileContents(events);
75+
req = parsedTraceFile.parsedTrace.NetworkRequests.byId.get('2648544.35');
7676
});
7777

7878
function tweakRequest(

front_end/panels/timeline/utils/AICallTree.test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,8 @@ describeWithEnvironment('AICallTree', () => {
6060
rawEvents,
6161
Trace.Types.Events.ThreadID(1),
6262
);
63-
const {parsedTrace} = await TraceLoader.executeTraceEngineOnFileContents(events);
63+
const {parsedTraceFile} = await TraceLoader.executeTraceEngineOnFileContents(events);
64+
const parsedTrace = parsedTraceFile.parsedTrace;
6465
// Find a random function call in the trace.
6566
const funcCall = parsedTrace.Samples.entryToNode.keys().find(event => {
6667
return Trace.Types.Events.isProfileCall(event) && event.callFrame.functionName === 'callAndPauseOnStart';

front_end/testing/TraceHelpers.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ export interface RenderFlameChartOptions {
3636
* name so that the TraceLoader can take care of loading and caching the
3737
* trace.
3838
*/
39-
traceFile: string|Trace.Handlers.Types.ParsedTrace;
39+
fileNameOrParsedTrace: string|Trace.Handlers.Types.ParsedTrace;
4040
/**
4141
* Filter the tracks that will be rendered by their name. The name here is
4242
* the user visible name that is drawn onto the flame chart.
@@ -90,10 +90,10 @@ export async function renderFlameChartIntoDOM(context: Mocha.Context|null, optio
9090

9191
let parsedTrace: Trace.Handlers.Types.ParsedTrace|null = null;
9292

93-
if (typeof options.traceFile === 'string') {
94-
parsedTrace = (await TraceLoader.traceEngine(context, options.traceFile)).parsedTrace;
93+
if (typeof options.fileNameOrParsedTrace === 'string') {
94+
parsedTrace = (await TraceLoader.traceEngine(context, options.fileNameOrParsedTrace)).parsedTrace;
9595
} else {
96-
parsedTrace = options.traceFile;
96+
parsedTrace = options.fileNameOrParsedTrace;
9797
}
9898

9999
if (options.preloadScreenshots) {

0 commit comments

Comments
 (0)