Skip to content

Commit 5aadcf5

Browse files
and-oliDevtools-frontend LUCI CQ
authored andcommitted
[RPP] Parse trace event for consoleTask.run
Recently, an event was added as an entrypoint of the task being run with the console.createTask / run API [1]. This allows us to simplify our parsing of async js tasks, because now all of these have a single trace event entry point for the scheduled task. Before, for consoleTask.run we had to assign all the top "run" profile calls as the scheduled task, because of limitations with sampling it's possible that multiple profile calls are made from a single call to the API. With tracing we don't have this limitation, yielding a more simple code. [1] https://chromium-review.googlesource.com/c/v8/v8/+/6054136 Bug: 381392952 Change-Id: I18178c17657dc0e6c2733729f4692f214f5654c5 Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/6084415 Reviewed-by: Jack Franklin <[email protected]> Commit-Queue: Andres Olivares <[email protected]>
1 parent 4a23e06 commit 5aadcf5

File tree

7 files changed

+41
-73
lines changed

7 files changed

+41
-73
lines changed

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

Lines changed: 6 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,8 @@ describe('AsyncJSCallsHandler', function() {
4040
const allEvents = [...rendererEvents, ...flowEvents];
4141

4242
const asyncCallStacksData = await buildAsyncJSCallsHandlerData(allEvents);
43-
const testRunEntryPoints = asyncCallStacksData.schedulerToRunEntryPoints.get(jsTaskScheduler);
44-
assert.strictEqual(testRunEntryPoints?.length, 1);
45-
assert.strictEqual(testRunEntryPoints?.[0], jsTaskRunEntryPoint);
43+
const testRunEntryPoint = asyncCallStacksData.schedulerToRunEntryPoints.get(jsTaskScheduler);
44+
assert.strictEqual(testRunEntryPoint, jsTaskRunEntryPoint);
4645
});
4746

4847
it('uses the nearest profile call ancestor of a debuggerTaskScheduled as JS task scheduler', async function() {
@@ -63,9 +62,8 @@ describe('AsyncJSCallsHandler', function() {
6362
const allEvents = [...rendererEvents, ...flowEvents];
6463

6564
const asyncCallStacksData = await buildAsyncJSCallsHandlerData(allEvents);
66-
const testRunEntryPoints = asyncCallStacksData.schedulerToRunEntryPoints.get(jsTaskScheduler);
67-
assert.strictEqual(testRunEntryPoints?.length, 1);
68-
assert.strictEqual(testRunEntryPoints?.[0], jsTaskRunEntryPoint);
65+
const testRunEntryPoint = asyncCallStacksData.schedulerToRunEntryPoints.get(jsTaskScheduler);
66+
assert.strictEqual(testRunEntryPoint, jsTaskRunEntryPoint);
6967
});
7068

7169
it('uses the nearest JS entry point descendant of a debuggerTaskRun as async task run', async function() {
@@ -87,38 +85,9 @@ describe('AsyncJSCallsHandler', function() {
8785
const allEvents = [...rendererEvents, ...flowEvents];
8886

8987
const asyncCallStacksData = await buildAsyncJSCallsHandlerData(allEvents);
90-
const testRunEntryPoints = asyncCallStacksData.schedulerToRunEntryPoints.get(jsTaskScheduler);
91-
assert.strictEqual(testRunEntryPoints?.length, 1);
92-
assert.strictEqual(testRunEntryPoints?.[0], jsTaskRunEntryPoint);
88+
const testRunEntryPoint = asyncCallStacksData.schedulerToRunEntryPoints.get(jsTaskScheduler);
89+
assert.strictEqual(testRunEntryPoint, jsTaskRunEntryPoint);
9390
});
94-
it('returns multiple JS entry point descendants of a debuggerTaskRun when they are not in the same subtree',
95-
async function() {
96-
const jsTaskScheduler = makeProfileCall('setTimeout', 0, 30, pid, tid);
97-
const asyncTaskScheduled =
98-
makeCompleteEvent(Trace.Types.Events.Name.DEBUGGER_ASYNC_TASK_SCHEDULED, 0, 0, cat, pid, tid);
99-
100-
const asyncTaskRun =
101-
makeCompleteEvent(Trace.Types.Events.Name.DEBUGGER_ASYNC_TASK_RUN, 60, 100, cat, tid, pid);
102-
103-
// Two JS entry points belonging to different subtrees are
104-
// descendants to the debuggerTaskRun event. Test both are
105-
// used.
106-
const firstJSTaskRunEntryPoint =
107-
makeCompleteEvent(Trace.Types.Events.Name.FUNCTION_CALL, 70, 20, cat, tid, pid);
108-
const secondJSTaskRunEntryPoint =
109-
makeCompleteEvent(Trace.Types.Events.Name.FUNCTION_CALL, 90, 10, cat, tid, pid);
110-
111-
const flowEvents = makeFlowEvents([asyncTaskScheduled, asyncTaskRun]);
112-
const rendererEvents =
113-
[jsTaskScheduler, asyncTaskScheduled, asyncTaskRun, firstJSTaskRunEntryPoint, secondJSTaskRunEntryPoint];
114-
const allEvents = [...rendererEvents, ...flowEvents];
115-
116-
const asyncCallStacksData = await buildAsyncJSCallsHandlerData(allEvents);
117-
const testRunEntryPoints = asyncCallStacksData.schedulerToRunEntryPoints.get(jsTaskScheduler);
118-
assert.strictEqual(testRunEntryPoints?.length, 2);
119-
assert.strictEqual(testRunEntryPoints?.[0], firstJSTaskRunEntryPoint);
120-
assert.strictEqual(testRunEntryPoints?.[1], secondJSTaskRunEntryPoint);
121-
});
12291
});
12392
describe('Resolving async JS tasks to schedulers', function() {
12493
it('associates an async JS task to its scheduler', async function() {

front_end/models/trace/handlers/AsyncJSCallsHandler.ts

Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import * as Types from '../types/types.js';
77
import {data as flowsHandlerData} from './FlowsHandler.js';
88
import {data as rendererHandlerData} from './RendererHandler.js';
99

10-
const schedulerToRunEntryPoints: Map<Types.Events.SyntheticProfileCall, Types.Events.Event[]> = new Map();
10+
const schedulerToRunEntryPoints: Map<Types.Events.SyntheticProfileCall, Types.Events.Event> = new Map();
1111
const asyncCallToScheduler:
1212
Map<Types.Events.SyntheticProfileCall, {taskName: string, scheduler: Types.Events.SyntheticProfileCall}> =
1313
new Map();
@@ -40,14 +40,14 @@ export async function finalize(): Promise<void> {
4040
// Unexpected async call trace data shape, ignore.
4141
continue;
4242
}
43-
const asyncEntryPoints = findFirstJsInvocationsForAsyncTaskRun(asyncTaskRun, entryToNode);
44-
if (!asyncEntryPoints) {
43+
const asyncEntryPoint = findFirstJsInvocationForAsyncTaskRun(asyncTaskRun, entryToNode);
44+
if (!asyncEntryPoint) {
4545
// Unexpected async call trace data shape, ignore.
4646
continue;
4747
}
4848
// Set scheduler -> schedulee mapping.
4949
// The schedulee being the JS entrypoint
50-
schedulerToRunEntryPoints.set(asyncCaller, asyncEntryPoints);
50+
schedulerToRunEntryPoints.set(asyncCaller, asyncEntryPoint);
5151

5252
// Set schedulee -> scheduler mapping.
5353
// The schedulees being the JS calls (instead of the entrypoints as
@@ -82,26 +82,23 @@ function findNearestProfileCallAncestor(
8282
* returns events that end up in the flame chart.
8383
*/
8484
function acceptJSInvocationsPredicate(event: Types.Events.Event): event is Types.Events.Event {
85-
return Types.Events.isJSInvocationEvent(event) && !event.name.startsWith('v8') && !event.name.startsWith('V8');
85+
const eventIsConsoleRunTask = event.name === Types.Events.Name.V8_CONSOLE_RUN_TASK;
86+
const eventIsV8EntryPoint = event.name.startsWith('v8') || event.name.startsWith('V8');
87+
return Types.Events.isJSInvocationEvent(event) && (eventIsConsoleRunTask || !eventIsV8EntryPoint);
8688
}
8789

8890
/**
8991
* Given a DebuggerAsyncTaskRun event, returns its closest JS entry
90-
* point descendants, which represent the task being scheduled.
91-
*
92-
* We return multiple entry points beacuse some of these are built
93-
* from samples (like `consoleTask.run()` ). Because of limitations with
94-
* sampling, multiple entry points can mistakenly be made from a single
95-
* entry point, so we return all of them to ensure the async stack is
96-
* in every event that applies.
92+
* point descendant, which contains the task being scheduled.
9793
*/
98-
function findFirstJsInvocationsForAsyncTaskRun(
94+
function findFirstJsInvocationForAsyncTaskRun(
9995
asyncTaskRun: Types.Events.DebuggerAsyncTaskRun,
100-
entryToNode: Map<Types.Events.Event, Helpers.TreeHelpers.TraceEntryNode>): Types.Events.Event[] {
96+
entryToNode: Map<Types.Events.Event, Helpers.TreeHelpers.TraceEntryNode>): Types.Events.Event|undefined {
10197
// Ignore descendants of other DebuggerAsyncTaskRuns since they
10298
// are part of another async task and have to be handled separately
10399
return findFirstDescendantsOfType(
104-
asyncTaskRun, entryToNode, acceptJSInvocationsPredicate, Types.Events.isDebuggerAsyncTaskRun);
100+
asyncTaskRun, entryToNode, acceptJSInvocationsPredicate, Types.Events.isDebuggerAsyncTaskRun)
101+
.at(0);
105102
}
106103

107104
/**
@@ -137,7 +134,7 @@ function findFirstJSCallsForAsyncTaskRun(
137134
}
138135

139136
/**
140-
* Given a root event returns all the top level descendants that meet a
137+
* Given a root event returns all the first descendants that meet a
141138
* predicate condition (predicateAccept) while ignoring subtrees whose
142139
* top event meets an ignore condition (predicateIgnore).
143140
*/
@@ -166,7 +163,7 @@ function findFirstDescendantsOfType<T extends Types.Events.Event>(
166163
}
167164

168165
export function data(): {
169-
// Given a profile call, returns the JS entrypoints it scheduled (if any).
166+
// Given a profile call, returns the JS entrypoint it scheduled (if any).
170167
// For example, given a setTimeout call, returns the JS entry point
171168
// trace event for the timeout callback run event (usually a
172169
// FunctionCall event).

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

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -139,16 +139,11 @@ describeWithEnvironment('InitiatorsHandler', () => {
139139
if (!schedulerFuntion) {
140140
throw new Error('Could not find scheduler function call');
141141
}
142-
const taskRunCalls = parsedTrace.Renderer.allTraceEntries.filter(
143-
e => Trace.Types.Events.isConsoleTaskRun(e) && e.ts > schedulerFuntion.ts);
144-
if (!taskRunCalls!.length) {
145-
throw new Error('Could not find task.run call');
146-
}
147-
148-
assert.strictEqual(parsedTrace.Initiators.eventToInitiator.get(taskRunCalls[0]), schedulerFuntion);
149-
assert.strictEqual(parsedTrace.Initiators.eventToInitiator.get(taskRunCalls[1]), schedulerFuntion);
150-
assert.deepEqual(
151-
parsedTrace.Initiators.initiatorToEvents.get(schedulerFuntion), [taskRunCalls[1], taskRunCalls[0]]);
142+
const consoleRunTask = parsedTrace.Renderer.allTraceEntries.find(
143+
e => Trace.Types.Events.isConsoleRunTask(e) && e.ts > schedulerFuntion.ts);
144+
assert.exists(consoleRunTask);
145+
assert.strictEqual(parsedTrace.Initiators.eventToInitiator.get(consoleRunTask), schedulerFuntion);
146+
assert.deepEqual(parsedTrace.Initiators.initiatorToEvents.get(schedulerFuntion), [consoleRunTask]);
152147
});
153148

154149
it('for a WebSocketSendHandshakeRequest the initiator is the WebSocketCreate event', async function() {

front_end/models/trace/handlers/InitiatorsHandler.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -149,10 +149,8 @@ function createRelationshipsFromFlows(): void {
149149

150150
function createRelationshipsFromAsyncJSCalls(): void {
151151
const asyncCallPairs = AsyncJSCallsHandlerData().schedulerToRunEntryPoints.entries();
152-
for (const [asyncCaller, asyncCallees] of asyncCallPairs) {
153-
for (const asyncCallee of asyncCallees) {
154-
storeInitiator({event: asyncCallee, initiator: asyncCaller});
155-
}
152+
for (const [asyncCaller, asyncCallee] of asyncCallPairs) {
153+
storeInitiator({event: asyncCallee, initiator: asyncCaller});
156154
}
157155
}
158156

front_end/models/trace/types/TraceEvents.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2705,22 +2705,24 @@ export function isJSInvocationEvent(event: Event): boolean {
27052705
case Name.EVALUATE_MODULE:
27062706
case Name.EVENT_DISPATCH:
27072707
case Name.V8_EXECUTE:
2708+
case Name.V8_CONSOLE_RUN_TASK:
27082709
return true;
27092710
}
27102711
// Also consider any new v8 trace events. (eg 'V8.RunMicrotasks' and 'v8.run')
27112712
if (event.name.startsWith('v8') || event.name.startsWith('V8')) {
27122713
return true;
27132714
}
2714-
2715-
if (isConsoleTaskRun(event)) {
2715+
if (isConsoleRunTask(event)) {
27162716
return true;
27172717
}
27182718
return false;
27192719
}
2720+
export interface ConsoleRunTask extends Event {
2721+
name: Name.V8_CONSOLE_RUN_TASK;
2722+
}
27202723

2721-
export function isConsoleTaskRun(event: Event): boolean {
2722-
return isProfileCall(event) && event.callFrame.functionName === 'run' && event.callFrame.columnNumber === -1 &&
2723-
event.callFrame.lineNumber === -1;
2724+
export function isConsoleRunTask(event: Event): event is ConsoleRunTask {
2725+
return event.name === Name.V8_CONSOLE_RUN_TASK;
27242726
}
27252727

27262728
export interface FlowEvent extends Event {
@@ -2805,6 +2807,7 @@ export const enum Name {
28052807
CRYPTO_DO_VERIFY = 'DoVerify',
28062808
CRYPTO_DO_VERIFY_REPLY = 'DoVerifyReply',
28072809
V8_EXECUTE = 'V8.Execute',
2810+
V8_CONSOLE_RUN_TASK = 'V8Console::runTask',
28082811
SCHEDULE_POST_TASK_CALLBACK = 'SchedulePostTaskCallback',
28092812
RUN_POST_TASK_CALLBACK = 'RunPostTaskCallback',
28102813
ABORT_POST_TASK_CALLBACK = 'AbortPostTaskCallback',
18 Bytes
Binary file not shown.

front_end/panels/timeline/utils/EntryStyles.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@ const UIStrings = {
4747
*@description Text in Timeline UIUtils of the Performance panel
4848
*/
4949
task: 'Task',
50+
/**
51+
*@description Text in Timeline UIUtils of the Performance panel
52+
*/
53+
consoleTaskRun: 'Run console task',
5054
/**
5155
*@description Text for other types of items
5256
*/
@@ -1060,6 +1064,8 @@ export function maybeInitSylesMap(): EventStylesMap {
10601064

10611065
[Trace.Types.Events.Name.ABORT_POST_TASK_CALLBACK]:
10621066
new TimelineRecordStyle(i18nString(UIStrings.abortPostTaskCallback), defaultCategoryStyles.scripting),
1067+
[Trace.Types.Events.Name.V8_CONSOLE_RUN_TASK]:
1068+
new TimelineRecordStyle(i18nString(UIStrings.consoleTaskRun), defaultCategoryStyles.scripting),
10631069
};
10641070
return eventStylesMap;
10651071
}

0 commit comments

Comments
 (0)