chrome-devtools-frontend 1.0.1153166 → 1.0.1155899
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/config/gni/devtools_grd_files.gni +3 -2
- package/front_end/core/common/Revealer.ts +1 -1
- package/front_end/core/host/InspectorFrontendHostAPI.ts +2 -0
- package/front_end/core/host/UserMetrics.ts +32 -12
- package/front_end/core/platform/array-utilities.ts +25 -9
- package/front_end/core/sdk/ChildTargetManager.ts +2 -1
- package/front_end/core/sdk/FilmStripModel.ts +35 -25
- package/front_end/devtools_compatibility.js +2 -0
- package/front_end/entrypoints/lighthouse_worker/LighthouseWorkerService.ts +8 -2
- package/front_end/entrypoints/worker_app/worker_app.ts +0 -1
- package/front_end/generated/InspectorBackendCommands.js +2 -1
- package/front_end/generated/protocol.ts +36 -0
- package/front_end/models/bindings/PresentationConsoleMessageHelper.ts +90 -73
- package/front_end/models/issues_manager/IssuesManager.ts +5 -0
- package/front_end/models/issues_manager/SourceFrameIssuesManager.ts +31 -61
- package/front_end/models/issues_manager/StylesheetLoadingIssue.ts +69 -0
- package/front_end/models/issues_manager/descriptions/stylesheetLateImport.md +4 -0
- package/front_end/models/issues_manager/descriptions/stylesheetRequestFailed.md +3 -0
- package/front_end/models/issues_manager/issues_manager.ts +2 -0
- package/front_end/models/timeline_model/TimelineModel.ts +4 -0
- package/front_end/models/trace/ModelImpl.ts +1 -0
- package/front_end/models/trace/README.md +73 -17
- package/front_end/models/trace/handlers/NetworkRequestsHandler.ts +1 -1
- package/front_end/models/trace/handlers/RendererHandler.ts +33 -143
- package/front_end/models/trace/handlers/UserTimings.md +1 -1
- package/front_end/models/trace/types/TraceEvents.ts +3 -2
- package/front_end/panels/application/ApplicationPanelSidebar.ts +9 -6
- package/front_end/panels/application/PreloadingTreeElement.ts +25 -7
- package/front_end/panels/application/preloading/PreloadingView.ts +64 -31
- package/front_end/panels/application/preloading/components/UsedPreloadingView.ts +19 -9
- package/front_end/panels/console/ConsoleViewMessage.ts +14 -2
- package/front_end/panels/elements/ElementsPanel.ts +2 -3
- package/front_end/panels/elements/components/LayoutPane.ts +256 -60
- package/front_end/panels/elements/elements-legacy.ts +0 -3
- package/front_end/panels/elements/elements-meta.ts +10 -2
- package/front_end/panels/elements/elements.ts +0 -2
- package/front_end/panels/network/NetworkDataGridNode.ts +8 -0
- package/front_end/panels/network/NetworkLogView.ts +2 -1
- package/front_end/panels/network/NetworkPanel.ts +12 -1
- package/front_end/panels/recorder/components/ExtensionView.ts +1 -1
- package/front_end/panels/sources/DebuggerPlugin.ts +7 -4
- package/front_end/panels/sources/SourcesPanel.ts +1 -1
- package/front_end/panels/sources/components/BreakpointsView.ts +406 -89
- package/front_end/panels/sources/sources-meta.ts +13 -4
- package/front_end/panels/sources/sources.ts +0 -2
- package/front_end/panels/timeline/TimelineFlameChartDataProvider.ts +1 -1
- package/front_end/panels/timeline/TimelineFlameChartNetworkDataProvider.ts +106 -95
- package/front_end/panels/timeline/TimelineFlameChartView.ts +1 -1
- package/front_end/panels/timeline/TimelinePaintProfilerView.ts +5 -0
- package/front_end/panels/timeline/TimelinePanel.ts +8 -8
- package/front_end/ui/legacy/UIUtils.ts +1 -1
- package/front_end/ui/legacy/components/perf_ui/.eslintrc.js +18 -0
- package/front_end/ui/legacy/components/perf_ui/FlameChart.ts +5 -1
- package/front_end/ui/legacy/components/perf_ui/TimelineOverviewPane.ts +5 -2
- package/front_end/ui/legacy/components/utils/Linkifier.ts +10 -0
- package/package.json +1 -1
- package/scripts/build/generate_deprecations.py +3 -0
- package/front_end/panels/elements/LayoutSidebarPane.ts +0 -249
- package/front_end/panels/sources/BreakpointsSidebarPane.ts +0 -480
- package/front_end/ui/components/docs/layout_pane/basic.html +0 -25
- package/front_end/ui/components/docs/layout_pane/basic.ts +0 -78
@@ -2,29 +2,89 @@
|
|
2
2
|
|
3
3
|
This folder contains the new trace engine that was first implemented for the Performance Insights panel and is now being repurposed as the primary trace engine that we use within DevTools.
|
4
4
|
|
5
|
+
## High level architecture
|
6
|
+
|
7
|
+
```
|
8
|
+
┌──────────────┐
|
9
|
+
│ Model#parse ├───┐
|
10
|
+
└──────────────┘ │
|
11
|
+
│
|
12
|
+
┌──────────▼──────────┐
|
13
|
+
│async processor#parse│
|
14
|
+
└──────────┬──────────┘
|
15
|
+
│
|
16
|
+
┌──────────▼────────────┐
|
17
|
+
│for handler of handlers│
|
18
|
+
└───┬────────────────┬──┘
|
19
|
+
│ │
|
20
|
+
┌────────────────▼────┐ ┌─────▼────────────────┐
|
21
|
+
│NetworkRequestHandler│ │...many more handlers │
|
22
|
+
│ │ │ │
|
23
|
+
│ reset() │ │ │
|
24
|
+
│ initialize() │ │ │
|
25
|
+
│ │ │ │
|
26
|
+
│ handleEvent() │ │ │
|
27
|
+
│ │ │ │
|
28
|
+
│ finalize() │ │ │
|
29
|
+
│ │ │ │
|
30
|
+
│ data() │ │ │ │
|
31
|
+
└─────────────────────┘ │ └──────────────────────┘
|
32
|
+
│
|
33
|
+
│
|
34
|
+
┌──────────────────▼─────────────────┐
|
35
|
+
│const data = model.traceParsedData()│
|
36
|
+
└────────────────────────────────────┘
|
37
|
+
```
|
38
|
+
|
39
|
+
`Model#parse` is the entrypoint into the engine and is the public interface that consumers use to initiate tracing and to fetch data back.
|
40
|
+
|
41
|
+
All the processing is done by the `Processor`. The processor contains a series of *Handlers*, each of which is responsible for parsing events of a particular category.
|
42
|
+
|
43
|
+
The trace processor loops over every event in the trace and calls each handler in turn (done this way so we only loop over the trace file once, rather than doing it once-per-handler). A Handler is a file that exposes a set of methods, most importantly `handleEvent()` and `data()`. The `handleEvent` function will be called for each event in the trace, and it is up to an individual handler to do something with that event if it needs to. The `data` method should return the final data that has been parsed and generated by the handler.
|
44
|
+
|
45
|
+
Once processing is done (read on for more details on how to track this), you can use the `traceParsedData()` method to fetch the result of parsing a given trace.
|
46
|
+
|
47
|
+
## Enabled handlers and creating a model
|
48
|
+
|
49
|
+
At the time of writing (06 June 2023) we are midway through a migration of the Performanc Panel's trace engine from the SDK.TracingModel to the new TraceEngine. Consequently, not all of the TraceEngine's handlers are currently active, because we don't want to run expensive handlers whose data we do not yet use in the UI.
|
50
|
+
|
51
|
+
Therefore, to create a model instance, we use `Model.createWithRequiredHandlersForMigration()`, which initializes a model configured correctly with the right handlers.
|
52
|
+
|
53
|
+
Once the migration is done, we can swap to `Model.createWithAllHandlers()` and remove the code that enables a subset of the handlers to run.
|
54
|
+
|
55
|
+
If you want to strictly control the set of handlers that are run (for example, if you only want to run one particular handler), you can initialize the model yourself and pass in the set of handlers:
|
56
|
+
|
57
|
+
```ts
|
58
|
+
const model = new Model({
|
59
|
+
NetworkRequestHandler: Handlers.ModelHandlers.NetworkRequestHandler,
|
60
|
+
})
|
61
|
+
```
|
62
|
+
|
5
63
|
## Parsing a trace and getting data back
|
6
64
|
|
7
65
|
Once you have an instance of the model, you can call the `parse` method to take a set of raw events and parse them. Once parsed, you then have to call the `traceParsedData` method, providing an index of the trace you want to have the data for. This is because any model can store a number of traces. Each trace is given an index, which starts at 0 and increments by one as a new trace is parsed.
|
8
66
|
|
9
67
|
If you are managing multiple traces, you should store them in some form of indexed data structure so you can easily know which index to use to fetch any data from the model. You may delete a trace with `deleteTraceByIndex`, which will then update the indexes of all other traces too.
|
10
68
|
|
11
|
-
|
69
|
+
If you need to check how many traces you have, you can call `model.size()`. The latest trace's index is therefore always `model.size() - 1`.
|
70
|
+
|
71
|
+
## Waiting for updates from the model
|
12
72
|
|
13
73
|
When you call `parse` you have two options. You can `await` it, which will wait until the trace is fully parsed:
|
14
74
|
|
15
|
-
```
|
75
|
+
```ts
|
16
76
|
await this.model.parse();
|
17
77
|
```
|
18
78
|
|
19
79
|
But it's likely preferable to instead use events, to avoid blocking the UI whilst parsing is in progress. You can listen to the `ModelUpdateEvent` for updates:
|
20
80
|
|
21
|
-
```
|
81
|
+
```ts
|
22
82
|
this.model.addEventListener(Model.ModelUpdateEvent.eventName, event => {
|
23
83
|
const {data} = event as Model.ModelUpdateEvent;
|
24
84
|
|
25
85
|
if (data.data === 'done') {
|
26
86
|
// trace is complete
|
27
|
-
const newestData = this.model.traceParsedData(this.model.
|
87
|
+
const newestData = this.model.traceParsedData(this.model.size() - 1);
|
28
88
|
} else {
|
29
89
|
// data.data will be an object: { index: X, total: Y}, which represents how many events (X) have been processed out of a total (Y).
|
30
90
|
// This can be used to show a progress bar, for example.
|
@@ -32,18 +92,14 @@ this.model.addEventListener(Model.ModelUpdateEvent.eventName, event => {
|
|
32
92
|
})
|
33
93
|
```
|
34
94
|
|
35
|
-
##
|
95
|
+
## The structure of the final data object
|
36
96
|
|
37
|
-
|
97
|
+
The object returned from `traceParsedData()` is an object of key-value pairs where each key is the name of a handler, and the value is the data that was parsed and returned from that handler.
|
38
98
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
When you call `parse()`, the model will post a message to the worker. This starts processing, and sends messages back. `models/trace/worker/Types.ts` contains the messages that can be sent to and from the worker.
|
48
|
-
|
49
|
-
The model listens to these messages and will deal with them. If you want to listen out for updates from the model, you can listen to the `ModelUpdateEvent`, which will be emitted periodically with progress stats, and finally once a trace is complete.
|
99
|
+
```ts
|
100
|
+
{
|
101
|
+
NetworkRequestHandler: ReturnType<typeof NetworkRequestHandler['data']>,
|
102
|
+
LayoutShiftHandler: ReturnType<typeof LayoutShiftHandler['data']>,
|
103
|
+
// and so on for each enabled Handler
|
104
|
+
}
|
105
|
+
```
|
@@ -196,7 +196,7 @@ export async function finalize(): Promise<void> {
|
|
196
196
|
Types.Timing.MicroSeconds(request.willSendRequests[request.willSendRequests.length - 1].ts) :
|
197
197
|
Types.Timing.MicroSeconds(finalSendRequest.ts);
|
198
198
|
|
199
|
-
//
|
199
|
+
// Finish time and end time
|
200
200
|
// =======================
|
201
201
|
// The finish time and the end time are subtly different.
|
202
202
|
// - Finish time: records the point at which the network stack stopped receiving the data
|
@@ -6,18 +6,15 @@ import * as Platform from '../../../core/platform/platform.js';
|
|
6
6
|
import * as Helpers from '../helpers/helpers.js';
|
7
7
|
|
8
8
|
import {data as metaHandlerData} from './MetaHandler.js';
|
9
|
-
import {
|
10
|
-
buildStackTraceAsCallFramesFromId,
|
11
|
-
data as samplesData,
|
12
|
-
getAllHotFunctionsBetweenTimestamps,
|
13
|
-
} from './SamplesHandler.js';
|
14
9
|
|
15
10
|
import {KnownEventName, KNOWN_EVENTS, type TraceEventHandlerName, HandlerState} from './types.js';
|
16
11
|
import * as Types from '../types/types.js';
|
17
12
|
|
18
13
|
const processes = new Map<Types.TraceEvents.ProcessID, RendererProcess>();
|
19
|
-
const traceEventToNode = new Map<
|
20
|
-
const allRendererEvents:
|
14
|
+
const traceEventToNode = new Map<RendererTraceEvent, RendererEventNode>();
|
15
|
+
const allRendererEvents: RendererTraceEvent[] = [];
|
16
|
+
let nodeIdCount = 0;
|
17
|
+
const makeRendererEventNodeId = (): RendererEventNodeId => (++nodeIdCount) as RendererEventNodeId;
|
21
18
|
|
22
19
|
let handlerState = HandlerState.UNINITIALIZED;
|
23
20
|
|
@@ -38,18 +35,14 @@ const makeEmptyRendererEventTree = (): RendererEventTree => ({
|
|
38
35
|
maxDepth: 0,
|
39
36
|
});
|
40
37
|
|
41
|
-
const makeEmptyRendererEventNode = (
|
42
|
-
|
38
|
+
const makeEmptyRendererEventNode = (event: RendererTraceEvent, id: RendererEventNodeId): RendererEventNode => ({
|
39
|
+
event,
|
40
|
+
id,
|
43
41
|
parentId: null,
|
44
42
|
childrenIds: new Set(),
|
45
43
|
depth: 0,
|
46
44
|
});
|
47
45
|
|
48
|
-
const makeRendererEventNodeIdGenerator = (): () => RendererEventNodeId => {
|
49
|
-
let i = 0;
|
50
|
-
return (): RendererEventNodeId => i++ as RendererEventNodeId;
|
51
|
-
};
|
52
|
-
|
53
46
|
const getOrCreateRendererProcess =
|
54
47
|
(processes: Map<Types.TraceEvents.ProcessID, RendererProcess>, pid: Types.TraceEvents.ProcessID):
|
55
48
|
RendererProcess => {
|
@@ -64,6 +57,7 @@ export function reset(): void {
|
|
64
57
|
processes.clear();
|
65
58
|
traceEventToNode.clear();
|
66
59
|
allRendererEvents.length = 0;
|
60
|
+
nodeIdCount = -1;
|
67
61
|
handlerState = HandlerState.UNINITIALIZED;
|
68
62
|
}
|
69
63
|
|
@@ -292,69 +286,46 @@ export function buildHierarchy(
|
|
292
286
|
* Complexity: O(n), where n = number of events
|
293
287
|
*/
|
294
288
|
export function treify(
|
295
|
-
events:
|
289
|
+
events: RendererTraceEvent[], options: {filter: {has: (name: KnownEventName) => boolean}}): RendererEventTree {
|
296
290
|
const stack = [];
|
291
|
+
// Reset the node id counter for every new renderer.
|
292
|
+
nodeIdCount = -1;
|
297
293
|
const tree = makeEmptyRendererEventTree();
|
298
|
-
const makeRendererEventNodeId = makeRendererEventNodeIdGenerator();
|
299
|
-
let lastScheduleStyleRecalcEvent: RendererEvent|null = null;
|
300
|
-
let lastInvalidateLayout: RendererEvent|null = null;
|
301
|
-
let lastForcedStyleRecalc: RendererEvent|null = null;
|
302
|
-
|
303
294
|
for (let i = 0; i < events.length; i++) {
|
304
295
|
const event = events[i];
|
305
296
|
|
306
|
-
buildHotFunctionsStackTracesForTask(event);
|
307
|
-
|
308
|
-
if (event.name === KnownEventName.ScheduleStyleRecalculation) {
|
309
|
-
lastScheduleStyleRecalcEvent = event;
|
310
|
-
}
|
311
|
-
if (event.name === KnownEventName.InvalidateLayout) {
|
312
|
-
lastInvalidateLayout = event;
|
313
|
-
}
|
314
|
-
if (event.name === KnownEventName.RecalculateStyles) {
|
315
|
-
lastForcedStyleRecalc = event;
|
316
|
-
}
|
317
297
|
// If the current event should not be part of the tree, then simply proceed
|
318
298
|
// with the next event.
|
319
299
|
if (!options.filter.has(event.name as KnownEventName)) {
|
320
300
|
continue;
|
321
301
|
}
|
322
302
|
|
323
|
-
const duration =
|
303
|
+
const duration = event.dur || 0;
|
304
|
+
const nodeId = makeRendererEventNodeId();
|
305
|
+
const node = makeEmptyRendererEventNode(event, nodeId);
|
324
306
|
|
325
307
|
// If the parent stack is empty, then the current event is a root. Create a
|
326
308
|
// node for it, mark it as a root, then proceed with the next event.
|
327
309
|
if (stack.length === 0) {
|
328
|
-
const node = makeEmptyRendererEventNode(i);
|
329
|
-
const nodeId = makeRendererEventNodeId();
|
330
310
|
tree.nodes.set(nodeId, node);
|
331
311
|
tree.roots.add(nodeId);
|
332
|
-
event.totalTime = Types.Timing.MicroSeconds(duration);
|
333
312
|
event.selfTime = Types.Timing.MicroSeconds(duration);
|
334
|
-
stack.push(
|
313
|
+
stack.push(node);
|
335
314
|
tree.maxDepth = Math.max(tree.maxDepth, stack.length);
|
336
315
|
traceEventToNode.set(event, node);
|
337
316
|
continue;
|
338
317
|
}
|
339
318
|
|
340
|
-
const
|
341
|
-
if (
|
342
|
-
throw new Error('Impossible: no parent node
|
319
|
+
const parentNode = stack.at(-1);
|
320
|
+
if (parentNode === undefined) {
|
321
|
+
throw new Error('Impossible: no parent node found in the stack');
|
343
322
|
}
|
344
323
|
|
345
|
-
const
|
346
|
-
if (!parentNode) {
|
347
|
-
throw new Error('Impossible: no parent node exists for the given id');
|
348
|
-
}
|
349
|
-
|
350
|
-
const parentEvent = events[parentNode.eventIndex];
|
351
|
-
if (!parentEvent) {
|
352
|
-
throw new Error('Impossible: no parent event exists for the given node');
|
353
|
-
}
|
324
|
+
const parentEvent = parentNode.event;
|
354
325
|
|
355
326
|
const begin = event.ts;
|
356
327
|
const parentBegin = parentEvent.ts;
|
357
|
-
const parentDuration =
|
328
|
+
const parentDuration = parentEvent.dur || 0;
|
358
329
|
const end = begin + duration;
|
359
330
|
const parentEnd = parentBegin + parentDuration;
|
360
331
|
// Check the relationship between the parent event at the top of the stack,
|
@@ -378,6 +349,8 @@ export function treify(
|
|
378
349
|
if (startsAfterParent) {
|
379
350
|
stack.pop();
|
380
351
|
i--;
|
352
|
+
// The last created node has been discarded, so discard this id.
|
353
|
+
nodeIdCount--;
|
381
354
|
continue;
|
382
355
|
}
|
383
356
|
|
@@ -392,105 +365,21 @@ export function treify(
|
|
392
365
|
// contained within the parent event. Create a node for the current
|
393
366
|
// event, establish the parent/child relationship, then proceed with the
|
394
367
|
// next event.
|
395
|
-
const node = makeEmptyRendererEventNode(i);
|
396
|
-
const nodeId = makeRendererEventNodeId();
|
397
368
|
tree.nodes.set(nodeId, node);
|
398
369
|
node.depth = stack.length;
|
399
|
-
node.parentId =
|
370
|
+
node.parentId = parentNode.id;
|
400
371
|
parentNode.childrenIds.add(nodeId);
|
401
372
|
event.selfTime = Types.Timing.MicroSeconds(duration);
|
402
|
-
event.totalTime = Types.Timing.MicroSeconds(duration);
|
403
373
|
if (parentEvent.selfTime !== undefined) {
|
404
|
-
parentEvent.selfTime = Types.Timing.MicroSeconds(parentEvent.selfTime - event.
|
374
|
+
parentEvent.selfTime = Types.Timing.MicroSeconds(parentEvent.selfTime - (event.dur || 0));
|
405
375
|
}
|
406
|
-
stack.push(
|
376
|
+
stack.push(node);
|
407
377
|
tree.maxDepth = Math.max(tree.maxDepth, stack.length);
|
408
378
|
traceEventToNode.set(event, node);
|
409
|
-
// For checking if an event is a forced layout, this point in the process
|
410
|
-
// is the perfect spot.
|
411
|
-
// An event is a forced layout if it is in a subset of all events (see
|
412
|
-
// EVENTS_THAT_MAY_BE_FORCED_LAYOUTS constant) AND if its parent event is
|
413
|
-
// not a RunTask. Rather than navigate the tree and perform this check
|
414
|
-
// later on, we can do this at this point, where we already have done the
|
415
|
-
// work to calculate the event and its parent event.
|
416
|
-
checkIfEventIsForcedLayoutAndStore(
|
417
|
-
event, lastScheduleStyleRecalcEvent, lastInvalidateLayout, lastForcedStyleRecalc);
|
418
379
|
}
|
419
380
|
return tree;
|
420
381
|
}
|
421
382
|
|
422
|
-
function checkIfEventIsForcedLayoutAndStore(
|
423
|
-
event: RendererEvent, lastScheduleStyleRecalcEvent: RendererEvent|null, lastInvalidateLayout: RendererEvent|null,
|
424
|
-
lastForcedStyleRecalc: RendererEvent|null): void {
|
425
|
-
// Forced re relayout
|
426
|
-
if (FORCED_LAYOUT_EVENT_NAMES.has(event.name as KnownEventName)) {
|
427
|
-
if (lastInvalidateLayout) {
|
428
|
-
// By default, the initiator of a forced re layout is the last InvalidateLayout event.
|
429
|
-
event.initiator = lastInvalidateLayout;
|
430
|
-
}
|
431
|
-
|
432
|
-
// However if the last InvalidateLayout ended before the last forced styles recalc,
|
433
|
-
// set the initiator to be the last ScheduleStylesRecalc.
|
434
|
-
const lastForcedStyleEndTime = lastForcedStyleRecalc && lastForcedStyleRecalc.ts + (lastForcedStyleRecalc.dur || 0);
|
435
|
-
const hasInitiator = lastScheduleStyleRecalcEvent && lastInvalidateLayout && lastForcedStyleEndTime;
|
436
|
-
if (hasInitiator && lastForcedStyleEndTime > lastInvalidateLayout.ts) {
|
437
|
-
event.initiator = lastScheduleStyleRecalcEvent;
|
438
|
-
}
|
439
|
-
}
|
440
|
-
|
441
|
-
// Forced styles recalc
|
442
|
-
if (FORCED_RECALC_STYLE_EVENTS.has(event.name as KnownEventName)) {
|
443
|
-
lastForcedStyleRecalc = event;
|
444
|
-
if (lastScheduleStyleRecalcEvent) {
|
445
|
-
// By default, the initiator of a forced styles recalc is the last ScheduleStylesRecalc event.
|
446
|
-
event.initiator = lastScheduleStyleRecalcEvent;
|
447
|
-
}
|
448
|
-
}
|
449
|
-
}
|
450
|
-
|
451
|
-
function eventIsLongTask(event: RendererEvent, mustBeOnMainFrame: boolean): boolean {
|
452
|
-
if (event.name !== KnownEventName.RunTask) {
|
453
|
-
return false;
|
454
|
-
}
|
455
|
-
const eventProcess = processes.get(event.pid);
|
456
|
-
const isOnMainFrame = Boolean(eventProcess && eventProcess.isOnMainFrame);
|
457
|
-
if (!isOnMainFrame && mustBeOnMainFrame) {
|
458
|
-
return false;
|
459
|
-
}
|
460
|
-
|
461
|
-
const errorTimeThreshold = Helpers.Timing.millisecondsToMicroseconds(Types.Timing.MilliSeconds(50));
|
462
|
-
const eventDuration = Types.TraceEvents.isTraceEventInstant(event) ? 0 : event.dur;
|
463
|
-
return eventDuration > errorTimeThreshold;
|
464
|
-
}
|
465
|
-
|
466
|
-
export function buildHotFunctionsStackTracesForTask(task: RendererEvent): void {
|
467
|
-
if (!eventIsLongTask(task, true)) {
|
468
|
-
// Don't spend time computing this data for tasks that aren't long.
|
469
|
-
return;
|
470
|
-
}
|
471
|
-
const {processes} = samplesData();
|
472
|
-
const thread = processes.get(task.pid)?.threads.get(task.tid);
|
473
|
-
// This threshold is temporarily set to 0 until so that at the moment
|
474
|
-
// we want to always show what functions were executed in a long task.
|
475
|
-
// In the future however, as we provide more sophisticated details
|
476
|
-
// for long tasks, we should set the threshold to a more valuable value.
|
477
|
-
const HOT_FUNCTION_MIN_SELF_PERCENTAGE = 0;
|
478
|
-
const calls = thread?.calls;
|
479
|
-
const taskStart = task.ts;
|
480
|
-
const taskEnd = Types.Timing.MicroSeconds(task.ts + (task.dur || 0));
|
481
|
-
const hotFunctions =
|
482
|
-
calls && getAllHotFunctionsBetweenTimestamps(calls, taskStart, taskEnd, HOT_FUNCTION_MIN_SELF_PERCENTAGE);
|
483
|
-
const tree = thread?.tree;
|
484
|
-
if (!hotFunctions || !tree) {
|
485
|
-
return;
|
486
|
-
}
|
487
|
-
// Store the top 10 hot functions.
|
488
|
-
const MAX_HOT_FUNCTION_COUNT = 10;
|
489
|
-
task.hotFunctionsStackTraces =
|
490
|
-
hotFunctions.slice(0, MAX_HOT_FUNCTION_COUNT)
|
491
|
-
.map(hotFunction => buildStackTraceAsCallFramesFromId(tree, hotFunction.stackFrame.nodeId).reverse());
|
492
|
-
}
|
493
|
-
|
494
383
|
export const FORCED_LAYOUT_EVENT_NAMES = new Set([
|
495
384
|
KnownEventName.Layout,
|
496
385
|
]);
|
@@ -506,8 +395,8 @@ export function deps(): TraceEventHandlerName[] {
|
|
506
395
|
|
507
396
|
export interface RendererHandlerData {
|
508
397
|
processes: Map<Types.TraceEvents.ProcessID, RendererProcess>;
|
509
|
-
traceEventToNode: Map<
|
510
|
-
allRendererEvents:
|
398
|
+
traceEventToNode: Map<RendererTraceEvent, RendererEventNode>;
|
399
|
+
allRendererEvents: RendererTraceEvent[];
|
511
400
|
}
|
512
401
|
|
513
402
|
export interface RendererProcess {
|
@@ -520,18 +409,18 @@ export interface RendererProcess {
|
|
520
409
|
|
521
410
|
export interface RendererThread {
|
522
411
|
name: string|null;
|
523
|
-
events:
|
412
|
+
events: RendererTraceEvent[];
|
524
413
|
tree?: RendererEventTree;
|
525
414
|
}
|
526
415
|
|
527
416
|
interface RendererEventData {
|
528
417
|
selfTime: Types.Timing.MicroSeconds;
|
529
|
-
|
530
|
-
|
418
|
+
initiator: RendererTraceEvent;
|
419
|
+
parent?: RendererTraceEvent;
|
531
420
|
hotFunctionsStackTraces: Types.TraceEvents.TraceEventCallFrame[][];
|
532
421
|
}
|
533
422
|
|
534
|
-
export type
|
423
|
+
export type RendererTraceEvent = Types.TraceEvents.TraceEventRendererData&Partial<RendererEventData>;
|
535
424
|
|
536
425
|
export interface RendererEventTree {
|
537
426
|
nodes: Map<RendererEventNodeId, RendererEventNode>;
|
@@ -540,8 +429,9 @@ export interface RendererEventTree {
|
|
540
429
|
}
|
541
430
|
|
542
431
|
export interface RendererEventNode {
|
543
|
-
|
432
|
+
event: RendererTraceEvent;
|
544
433
|
depth: number;
|
434
|
+
id: RendererEventNodeId;
|
545
435
|
parentId?: RendererEventNodeId|null;
|
546
436
|
childrenIds: Set<RendererEventNodeId>;
|
547
437
|
}
|
@@ -20,7 +20,7 @@ Events that represent a `performance.measure` call will come in pairs: a begin e
|
|
20
20
|
|
21
21
|
### Parsing out user timings
|
22
22
|
|
23
|
-
For `performance.mark` calls, the handler looks for events with the user timing category and the `Mark` trace event phase (
|
23
|
+
For `performance.mark` calls, the handler looks for events with the user timing category and the `Mark` trace event phase (`I`). However, pre-June 2023, these are emitted with phase `R`. These events are then filtered to exclude timings appendend by Chrome (1).
|
24
24
|
|
25
25
|
For `performance.measure` calls, the handler looks for begin or end events in the trace. Once we have these, we then pair them up, and create a list of synthetic user timing events. These are events that do not actually exist in the trace, but we create because we need one event to map to each block. The event is made up of information from the begin and/or end event and includes most crucially:
|
26
26
|
|
@@ -738,7 +738,7 @@ export interface TraceEventTimeStamp extends TraceEventData {
|
|
738
738
|
|
739
739
|
export interface TraceEventPerformanceMark extends TraceEventData {
|
740
740
|
cat: 'blink.user_timing';
|
741
|
-
ph: Phase.MARK;
|
741
|
+
ph: Phase.INSTANT|Phase.MARK;
|
742
742
|
id: string;
|
743
743
|
}
|
744
744
|
|
@@ -1057,7 +1057,8 @@ export function isTraceEventPerformanceMeasure(traceEventData: TraceEventData):
|
|
1057
1057
|
|
1058
1058
|
export function isTraceEventPerformanceMark(traceEventData: TraceEventData):
|
1059
1059
|
traceEventData is TraceEventPerformanceMark {
|
1060
|
-
return traceEventData.ph === Phase.MARK
|
1060
|
+
return (traceEventData.ph === Phase.MARK || traceEventData.ph === Phase.INSTANT) &&
|
1061
|
+
traceEventData.cat === 'blink.user_timing';
|
1061
1062
|
}
|
1062
1063
|
|
1063
1064
|
export function isTraceEventConsoleTime(traceEventData: TraceEventData): traceEventData is TraceEventConsoleTimeBegin|
|
@@ -50,6 +50,7 @@ import {BackgroundServiceModel} from './BackgroundServiceModel.js';
|
|
50
50
|
import {BackgroundServiceView} from './BackgroundServiceView.js';
|
51
51
|
import {BounceTrackingMitigationsTreeElement} from './BounceTrackingMitigationsTreeElement.js';
|
52
52
|
import * as ApplicationComponents from './components/components.js';
|
53
|
+
import {type PreloadingResultView, type PreloadingView} from './preloading/PreloadingView.js';
|
53
54
|
import {PreloadingTreeElement} from './PreloadingTreeElement.js';
|
54
55
|
import resourcesSidebarStyles from './resourcesSidebar.css.js';
|
55
56
|
import {ServiceWorkerCacheTreeElement} from './ServiceWorkerCacheTreeElement.js';
|
@@ -264,7 +265,8 @@ export class ApplicationPanelSidebar extends UI.Widget.VBox implements SDK.Targe
|
|
264
265
|
periodicBackgroundSyncTreeElement: BackgroundServiceTreeElement;
|
265
266
|
pushMessagingTreeElement: BackgroundServiceTreeElement;
|
266
267
|
reportingApiTreeElement: ReportingApiTreeElement;
|
267
|
-
preloadingTreeElement: PreloadingTreeElement
|
268
|
+
preloadingTreeElement: PreloadingTreeElement<PreloadingView>|undefined;
|
269
|
+
preloadingResultTreeElement: PreloadingTreeElement<PreloadingResultView>|undefined;
|
268
270
|
private readonly resourcesSection: ResourcesSection;
|
269
271
|
private readonly databaseTableViews: Map<DatabaseModelDatabase, {
|
270
272
|
[x: string]: DatabaseTableView,
|
@@ -400,8 +402,10 @@ export class ApplicationPanelSidebar extends UI.Widget.VBox implements SDK.Targe
|
|
400
402
|
const preloadingSectionTitle = i18nString(UIStrings.preloading);
|
401
403
|
const preloadingSectionTreeElement = this.addSidebarSection(preloadingSectionTitle);
|
402
404
|
|
403
|
-
this.preloadingTreeElement =
|
405
|
+
this.preloadingTreeElement = PreloadingTreeElement.newForPreloadingView(panel);
|
406
|
+
this.preloadingResultTreeElement = PreloadingTreeElement.newForPreloadingResultView(panel);
|
404
407
|
preloadingSectionTreeElement.appendChild(this.preloadingTreeElement);
|
408
|
+
preloadingSectionTreeElement.appendChild(this.preloadingResultTreeElement);
|
405
409
|
}
|
406
410
|
|
407
411
|
const resourcesSectionTitle = i18nString(UIStrings.frames);
|
@@ -561,12 +565,11 @@ export class ApplicationPanelSidebar extends UI.Widget.VBox implements SDK.Targe
|
|
561
565
|
this.periodicBackgroundSyncTreeElement.initialize(backgroundServiceModel);
|
562
566
|
this.pushMessagingTreeElement.initialize(backgroundServiceModel);
|
563
567
|
|
564
|
-
|
565
|
-
// `Root.Runtime.experiments.isEnabled(Root.Runtime.ExperimentName.PRELOADING_STATUS_PANEL)`.
|
566
|
-
if (this.preloadingTreeElement) {
|
568
|
+
if (Root.Runtime.experiments.isEnabled(Root.Runtime.ExperimentName.PRELOADING_STATUS_PANEL)) {
|
567
569
|
const preloadingModel = this.target?.model(SDK.PreloadingModel.PreloadingModel);
|
568
570
|
if (preloadingModel) {
|
569
|
-
this.preloadingTreeElement
|
571
|
+
this.preloadingTreeElement?.initialize(preloadingModel);
|
572
|
+
this.preloadingResultTreeElement?.initialize(preloadingModel);
|
570
573
|
}
|
571
574
|
}
|
572
575
|
}
|
@@ -10,24 +10,42 @@ import type * as Platform from '../../core/platform/platform.js';
|
|
10
10
|
import {ApplicationPanelTreeElement} from './ApplicationPanelTreeElement.js';
|
11
11
|
import {type ResourcesPanel} from './ResourcesPanel.js';
|
12
12
|
|
13
|
-
import {PreloadingView} from './preloading/PreloadingView.js';
|
13
|
+
import {PreloadingView, PreloadingResultView} from './preloading/PreloadingView.js';
|
14
14
|
|
15
15
|
const UIStrings = {
|
16
16
|
/**
|
17
17
|
*@description Text in Application Panel Sidebar of the Application panel
|
18
18
|
*/
|
19
19
|
prefetchingAndPrerendering: 'Prefetching & Prerendering',
|
20
|
+
/**
|
21
|
+
*@description Text in Application Panel Sidebar of the Application panel
|
22
|
+
*/
|
23
|
+
thisPage: 'This Page',
|
20
24
|
};
|
21
25
|
const str_ = i18n.i18n.registerUIStrings('panels/application/PreloadingTreeElement.ts', UIStrings);
|
22
26
|
const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
|
23
27
|
|
24
|
-
|
25
|
-
|
26
|
-
|
28
|
+
type M = SDK.PreloadingModel.PreloadingModel;
|
29
|
+
|
30
|
+
export class PreloadingTreeElement<V extends PreloadingView|PreloadingResultView> extends ApplicationPanelTreeElement {
|
31
|
+
private model?: M;
|
32
|
+
private ctorV: {new(model: M): V};
|
33
|
+
private view?: V;
|
27
34
|
#selectedInternal: boolean;
|
28
35
|
|
29
|
-
|
30
|
-
|
36
|
+
// TODO(https://crbug.com/1410709): Split this view into "SpeculationRules" and "Preload".
|
37
|
+
static newForPreloadingView(resourcesPanel: ResourcesPanel): PreloadingTreeElement<PreloadingView> {
|
38
|
+
return new PreloadingTreeElement(resourcesPanel, PreloadingView, i18nString(UIStrings.prefetchingAndPrerendering));
|
39
|
+
}
|
40
|
+
|
41
|
+
static newForPreloadingResultView(resourcesPanel: ResourcesPanel): PreloadingTreeElement<PreloadingResultView> {
|
42
|
+
return new PreloadingTreeElement(resourcesPanel, PreloadingResultView, i18nString(UIStrings.thisPage));
|
43
|
+
}
|
44
|
+
|
45
|
+
constructor(resourcesPanel: ResourcesPanel, ctorV: {new(model: M): V}, title: string) {
|
46
|
+
super(resourcesPanel, title, false);
|
47
|
+
|
48
|
+
this.ctorV = ctorV;
|
31
49
|
|
32
50
|
const icon = UI.Icon.Icon.create('arrow-up-down', 'resource-tree-item');
|
33
51
|
this.setLeadingIcons([icon]);
|
@@ -58,7 +76,7 @@ export class PreloadingTreeElement extends ApplicationPanelTreeElement {
|
|
58
76
|
}
|
59
77
|
|
60
78
|
if (!this.view) {
|
61
|
-
this.view = new
|
79
|
+
this.view = new this.ctorV(this.model);
|
62
80
|
}
|
63
81
|
|
64
82
|
this.showView(this.view);
|