@paulirish/trace_engine 0.0.33 → 0.0.34
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/.tmp/tsbuildinfo/analyze-trace.d.mts +18 -0
- package/.tmp/tsbuildinfo/analyze-trace.d.mts.map +1 -0
- package/.tmp/tsbuildinfo/models/trace/LanternComputationData.d.ts +46 -0
- package/.tmp/tsbuildinfo/models/trace/LanternComputationData.d.ts.map +1 -0
- package/.tmp/tsbuildinfo/models/trace/LegacyTracingModel.d.ts +2 -0
- package/.tmp/tsbuildinfo/models/trace/LegacyTracingModel.d.ts.map +1 -0
- package/.tmp/tsbuildinfo/models/trace/ModelImpl.d.ts +72 -0
- package/.tmp/tsbuildinfo/models/trace/ModelImpl.d.ts.map +1 -0
- package/.tmp/tsbuildinfo/models/trace/Processor.d.ts +25 -0
- package/.tmp/tsbuildinfo/models/trace/Processor.d.ts.map +1 -0
- package/.tmp/tsbuildinfo/models/trace/TracingManager.d.ts +2 -0
- package/.tmp/tsbuildinfo/models/trace/TracingManager.d.ts.map +1 -0
- package/.tmp/tsbuildinfo/models/trace/trace.d.ts +13 -0
- package/.tmp/tsbuildinfo/models/trace/trace.d.ts.map +1 -0
- package/.tmp/tsbuildinfo/test/test-trace-engine.d.mts +2 -0
- package/.tmp/tsbuildinfo/test/test-trace-engine.d.mts.map +1 -0
- package/.tmp/tsbuildinfo/third_party/third-party-web/third-party-web.d.ts +3 -0
- package/.tmp/tsbuildinfo/third_party/third-party-web/third-party-web.d.ts.map +1 -0
- package/.tmp/tsbuildinfo/tsconfig.tsbuildinfo +1 -0
- package/analyze-trace.mjs +47 -11
- package/generated/protocol.d.ts +31 -2
- package/models/trace/Processor.js +4 -1
- package/models/trace/Processor.js.map +1 -1
- package/models/trace/extras/ThirdParties.d.ts +23 -0
- package/models/trace/extras/ThirdParties.js +152 -0
- package/models/trace/extras/ThirdParties.js.map +1 -0
- package/models/trace/extras/extras-tsconfig.json +1 -0
- package/models/trace/extras/extras.d.ts +4 -1
- package/models/trace/extras/extras.js +4 -1
- package/models/trace/extras/extras.js.map +1 -1
- package/models/trace/handlers/FramesHandler.d.ts +11 -4
- package/models/trace/handlers/FramesHandler.js +8 -1
- package/models/trace/handlers/FramesHandler.js.map +1 -1
- package/models/trace/handlers/InitiatorsHandler.js +11 -0
- package/models/trace/handlers/InitiatorsHandler.js.map +1 -1
- package/models/trace/handlers/LayoutShiftsHandler.d.ts +2 -0
- package/models/trace/handlers/LayoutShiftsHandler.js +14 -0
- package/models/trace/handlers/LayoutShiftsHandler.js.map +1 -1
- package/models/trace/helpers/Timing.d.ts +2 -0
- package/models/trace/helpers/Timing.js +10 -0
- package/models/trace/helpers/Timing.js.map +1 -1
- package/models/trace/helpers/TreeHelpers.d.ts +26 -15
- package/models/trace/helpers/TreeHelpers.js +113 -53
- package/models/trace/helpers/TreeHelpers.js.map +1 -1
- package/models/trace/helpers/helpers-tsconfig.json +3 -0
- package/models/trace/insights/CumulativeLayoutShift.d.ts +2 -0
- package/models/trace/insights/CumulativeLayoutShift.js +52 -18
- package/models/trace/insights/CumulativeLayoutShift.js.map +1 -1
- package/models/trace/insights/ThirdPartyWeb.d.ts +7 -12
- package/models/trace/insights/ThirdPartyWeb.js +3 -135
- package/models/trace/insights/ThirdPartyWeb.js.map +1 -1
- package/models/trace/lantern/core/NetworkAnalyzer.d.ts +3 -3
- package/models/trace/lantern/core/NetworkAnalyzer.js +6 -3
- package/models/trace/lantern/core/NetworkAnalyzer.js.map +1 -1
- package/models/trace/lantern/simulation/Simulator.js +1 -1
- package/models/trace/lantern/simulation/Simulator.js.map +1 -1
- package/models/trace/types/TraceEvents.d.ts +50 -0
- package/models/trace/types/TraceEvents.js +12 -0
- package/models/trace/types/TraceEvents.js.map +1 -1
- package/package.json +7 -3
- package/test/test-trace-engine.mjs +14 -6
- package/tsconfig.json +29 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Timing.js","sourceRoot":"","sources":["../../../../../../../front_end/models/trace/helpers/Timing.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2022 The Chromium Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\nimport * as Types from '../types/types.js';\n\nimport {getNavigationForTraceEvent} from './Trace.js';\n\nexport const millisecondsToMicroseconds = (value: Types.Timing.MilliSeconds): Types.Timing.MicroSeconds =>\n Types.Timing.MicroSeconds(value * 1000);\n\nexport const secondsToMilliseconds = (value: Types.Timing.Seconds): Types.Timing.MilliSeconds =>\n Types.Timing.MilliSeconds(value * 1000);\n\nexport const secondsToMicroseconds = (value: Types.Timing.Seconds): Types.Timing.MicroSeconds =>\n millisecondsToMicroseconds(secondsToMilliseconds(value));\n\nexport const microSecondsToMilliseconds = (value: Types.Timing.MicroSeconds): Types.Timing.MilliSeconds =>\n Types.Timing.MilliSeconds(value / 1000);\n\nexport const microSecondsToSeconds = (value: Types.Timing.MicroSeconds): Types.Timing.Seconds =>\n Types.Timing.Seconds(value / 1000 / 1000);\n\nexport function timeStampForEventAdjustedByClosestNavigation(\n event: Types.Events.Event,\n traceBounds: Types.Timing.TraceWindowMicroSeconds,\n navigationsByNavigationId: Map<string, Types.Events.NavigationStart>,\n navigationsByFrameId: Map<string, Types.Events.NavigationStart[]>,\n ): Types.Timing.MicroSeconds {\n let eventTimeStamp = event.ts - traceBounds.min;\n if (event.args?.data?.navigationId) {\n const navigationForEvent = navigationsByNavigationId.get(event.args.data.navigationId);\n if (navigationForEvent) {\n eventTimeStamp = event.ts - navigationForEvent.ts;\n }\n } else if (event.args?.data?.frame) {\n const navigationForEvent = getNavigationForTraceEvent(event, event.args.data.frame, navigationsByFrameId);\n if (navigationForEvent) {\n eventTimeStamp = event.ts - navigationForEvent.ts;\n }\n }\n return Types.Timing.MicroSeconds(eventTimeStamp);\n}\n\n// Expands the trace window by a provided percentage or, if it the expanded window is smaller than 1 millisecond, expands it to 1 millisecond.\n// If the expanded window is outside of the max trace window, cut the overflowing bound to the max trace window bound.\nexport function expandWindowByPercentOrToOneMillisecond(\n annotationWindow: Types.Timing.TraceWindowMicroSeconds, maxTraceWindow: Types.Timing.TraceWindowMicroSeconds,\n percentage: number): Types.Timing.TraceWindowMicroSeconds {\n // Expand min and max of the window by half of the provided percentage. That way, in total, the window will be expanded by the provided percentage.\n let newMin = annotationWindow.min - annotationWindow.range * (percentage / 100) / 2;\n let newMax = annotationWindow.max + annotationWindow.range * (percentage / 100) / 2;\n\n if (newMax - newMin < 1_000) {\n const rangeMiddle = (annotationWindow.min + annotationWindow.max) / 2;\n newMin = rangeMiddle - 500;\n newMax = rangeMiddle + 500;\n }\n\n newMin = Math.max(newMin, maxTraceWindow.min);\n newMax = Math.min(newMax, maxTraceWindow.max);\n\n const expandedWindow: Types.Timing.TraceWindowMicroSeconds = {\n min: Types.Timing.MicroSeconds(newMin),\n max: Types.Timing.MicroSeconds(newMax),\n range: Types.Timing.MicroSeconds(newMax - newMin),\n };\n\n return expandedWindow;\n}\n\nexport interface EventTimingsData<\n ValueType extends Types.Timing.MicroSeconds|Types.Timing.MilliSeconds|Types.Timing.Seconds,\n> {\n startTime: ValueType;\n endTime: ValueType;\n duration: ValueType;\n}\n\nexport function eventTimingsMicroSeconds(event: Types.Events.Event): EventTimingsData<Types.Timing.MicroSeconds> {\n return {\n startTime: event.ts,\n endTime: Types.Timing.MicroSeconds(event.ts + (event.dur ?? Types.Timing.MicroSeconds(0))),\n duration: Types.Timing.MicroSeconds(event.dur || 0),\n };\n}\nexport function eventTimingsMilliSeconds(event: Types.Events.Event): EventTimingsData<Types.Timing.MilliSeconds> {\n const microTimes = eventTimingsMicroSeconds(event);\n return {\n startTime: microSecondsToMilliseconds(microTimes.startTime),\n endTime: microSecondsToMilliseconds(microTimes.endTime),\n duration: microSecondsToMilliseconds(microTimes.duration),\n };\n}\nexport function eventTimingsSeconds(event: Types.Events.Event): EventTimingsData<Types.Timing.Seconds> {\n const microTimes = eventTimingsMicroSeconds(event);\n return {\n startTime: microSecondsToSeconds(microTimes.startTime),\n endTime: microSecondsToSeconds(microTimes.endTime),\n duration: microSecondsToSeconds(microTimes.duration),\n };\n}\n\nexport function traceWindowMilliSeconds(bounds: Types.Timing.TraceWindowMicroSeconds):\n Types.Timing.TraceWindowMilliSeconds {\n return {\n min: microSecondsToMilliseconds(bounds.min),\n max: microSecondsToMilliseconds(bounds.max),\n range: microSecondsToMilliseconds(bounds.range),\n };\n}\n\nexport function traceWindowMillisecondsToMicroSeconds(bounds: Types.Timing.TraceWindowMilliSeconds):\n Types.Timing.TraceWindowMicroSeconds {\n return {\n min: millisecondsToMicroseconds(bounds.min),\n max: millisecondsToMicroseconds(bounds.max),\n range: millisecondsToMicroseconds(bounds.range),\n };\n}\n\nexport function traceWindowFromMilliSeconds(\n min: Types.Timing.MilliSeconds, max: Types.Timing.MilliSeconds): Types.Timing.TraceWindowMicroSeconds {\n const traceWindow: Types.Timing.TraceWindowMicroSeconds = {\n min: millisecondsToMicroseconds(min),\n max: millisecondsToMicroseconds(max),\n range: Types.Timing.MicroSeconds(millisecondsToMicroseconds(max) - millisecondsToMicroseconds(min)),\n };\n return traceWindow;\n}\n\nexport function traceWindowFromMicroSeconds(\n min: Types.Timing.MicroSeconds, max: Types.Timing.MicroSeconds): Types.Timing.TraceWindowMicroSeconds {\n const traceWindow: Types.Timing.TraceWindowMicroSeconds = {\n min,\n max,\n range: Types.Timing.MicroSeconds(max - min),\n };\n return traceWindow;\n}\n\nexport interface BoundsIncludeTimeRange {\n timeRange: Types.Timing.TraceWindowMicroSeconds;\n bounds: Types.Timing.TraceWindowMicroSeconds;\n}\n\n/**\n * Checks to see if the timeRange is within the bounds. By \"within\" we mean\n * \"has any overlap\":\n * |------------------------|\n * == no overlap (entirely before)\n * ========= overlap\n * ========= overlap\n * ========= overlap\n * ==== no overlap (entirely after)\n * ============================== overlap (time range is larger than bounds)\n * |------------------------|\n */\nexport function boundsIncludeTimeRange(data: BoundsIncludeTimeRange): boolean {\n const {min: visibleMin, max: visibleMax} = data.bounds;\n const {min: rangeMin, max: rangeMax} = data.timeRange;\n\n return visibleMin <= rangeMax && visibleMax >= rangeMin;\n}\n\n/** Checks to see if the event is within or overlaps the bounds */\nexport function eventIsInBounds(event: Types.Events.Event, bounds: Types.Timing.TraceWindowMicroSeconds): boolean {\n const startTime = event.ts;\n return startTime <= bounds.max && bounds.min <= (startTime + (event.dur ?? 0));\n}\n\nexport function timestampIsInBounds(\n bounds: Types.Timing.TraceWindowMicroSeconds, timestamp: Types.Timing.MicroSeconds): boolean {\n return timestamp >= bounds.min && timestamp <= bounds.max;\n}\n\nexport interface WindowFitsInsideBounds {\n window: Types.Timing.TraceWindowMicroSeconds;\n bounds: Types.Timing.TraceWindowMicroSeconds;\n}\n\n/**\n * Returns true if the window fits entirely within the bounds.\n * Note that if the window is equivalent to the bounds, that is considered to fit\n */\nexport function windowFitsInsideBounds(data: WindowFitsInsideBounds): boolean {\n return data.window.min >= data.bounds.min && data.window.max <= data.bounds.max;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"Timing.js","sourceRoot":"","sources":["../../../../../../../front_end/models/trace/helpers/Timing.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2022 The Chromium Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\nimport * as Types from '../types/types.js';\n\nimport {getNavigationForTraceEvent} from './Trace.js';\n\nexport const millisecondsToMicroseconds = (value: Types.Timing.MilliSeconds): Types.Timing.MicroSeconds =>\n Types.Timing.MicroSeconds(value * 1000);\n\nexport const secondsToMilliseconds = (value: Types.Timing.Seconds): Types.Timing.MilliSeconds =>\n Types.Timing.MilliSeconds(value * 1000);\n\nexport const secondsToMicroseconds = (value: Types.Timing.Seconds): Types.Timing.MicroSeconds =>\n millisecondsToMicroseconds(secondsToMilliseconds(value));\n\nexport const microSecondsToMilliseconds = (value: Types.Timing.MicroSeconds): Types.Timing.MilliSeconds =>\n Types.Timing.MilliSeconds(value / 1000);\n\nexport const microSecondsToSeconds = (value: Types.Timing.MicroSeconds): Types.Timing.Seconds =>\n Types.Timing.Seconds(value / 1000 / 1000);\n\nexport function timeStampForEventAdjustedByClosestNavigation(\n event: Types.Events.Event,\n traceBounds: Types.Timing.TraceWindowMicroSeconds,\n navigationsByNavigationId: Map<string, Types.Events.NavigationStart>,\n navigationsByFrameId: Map<string, Types.Events.NavigationStart[]>,\n ): Types.Timing.MicroSeconds {\n let eventTimeStamp = event.ts - traceBounds.min;\n if (event.args?.data?.navigationId) {\n const navigationForEvent = navigationsByNavigationId.get(event.args.data.navigationId);\n if (navigationForEvent) {\n eventTimeStamp = event.ts - navigationForEvent.ts;\n }\n } else if (event.args?.data?.frame) {\n const navigationForEvent = getNavigationForTraceEvent(event, event.args.data.frame, navigationsByFrameId);\n if (navigationForEvent) {\n eventTimeStamp = event.ts - navigationForEvent.ts;\n }\n }\n return Types.Timing.MicroSeconds(eventTimeStamp);\n}\n\n// Expands the trace window by a provided percentage or, if it the expanded window is smaller than 1 millisecond, expands it to 1 millisecond.\n// If the expanded window is outside of the max trace window, cut the overflowing bound to the max trace window bound.\nexport function expandWindowByPercentOrToOneMillisecond(\n annotationWindow: Types.Timing.TraceWindowMicroSeconds, maxTraceWindow: Types.Timing.TraceWindowMicroSeconds,\n percentage: number): Types.Timing.TraceWindowMicroSeconds {\n // Expand min and max of the window by half of the provided percentage. That way, in total, the window will be expanded by the provided percentage.\n let newMin = annotationWindow.min - annotationWindow.range * (percentage / 100) / 2;\n let newMax = annotationWindow.max + annotationWindow.range * (percentage / 100) / 2;\n\n if (newMax - newMin < 1_000) {\n const rangeMiddle = (annotationWindow.min + annotationWindow.max) / 2;\n newMin = rangeMiddle - 500;\n newMax = rangeMiddle + 500;\n }\n\n newMin = Math.max(newMin, maxTraceWindow.min);\n newMax = Math.min(newMax, maxTraceWindow.max);\n\n const expandedWindow: Types.Timing.TraceWindowMicroSeconds = {\n min: Types.Timing.MicroSeconds(newMin),\n max: Types.Timing.MicroSeconds(newMax),\n range: Types.Timing.MicroSeconds(newMax - newMin),\n };\n\n return expandedWindow;\n}\n\nexport interface EventTimingsData<\n ValueType extends Types.Timing.MicroSeconds|Types.Timing.MilliSeconds|Types.Timing.Seconds,\n> {\n startTime: ValueType;\n endTime: ValueType;\n duration: ValueType;\n}\n\nexport function eventTimingsMicroSeconds(event: Types.Events.Event): EventTimingsData<Types.Timing.MicroSeconds> {\n return {\n startTime: event.ts,\n endTime: Types.Timing.MicroSeconds(event.ts + (event.dur ?? Types.Timing.MicroSeconds(0))),\n duration: Types.Timing.MicroSeconds(event.dur || 0),\n };\n}\nexport function eventTimingsMilliSeconds(event: Types.Events.Event): EventTimingsData<Types.Timing.MilliSeconds> {\n const microTimes = eventTimingsMicroSeconds(event);\n return {\n startTime: microSecondsToMilliseconds(microTimes.startTime),\n endTime: microSecondsToMilliseconds(microTimes.endTime),\n duration: microSecondsToMilliseconds(microTimes.duration),\n };\n}\nexport function eventTimingsSeconds(event: Types.Events.Event): EventTimingsData<Types.Timing.Seconds> {\n const microTimes = eventTimingsMicroSeconds(event);\n return {\n startTime: microSecondsToSeconds(microTimes.startTime),\n endTime: microSecondsToSeconds(microTimes.endTime),\n duration: microSecondsToSeconds(microTimes.duration),\n };\n}\n\nexport function traceWindowMilliSeconds(bounds: Types.Timing.TraceWindowMicroSeconds):\n Types.Timing.TraceWindowMilliSeconds {\n return {\n min: microSecondsToMilliseconds(bounds.min),\n max: microSecondsToMilliseconds(bounds.max),\n range: microSecondsToMilliseconds(bounds.range),\n };\n}\n\nexport function traceWindowMillisecondsToMicroSeconds(bounds: Types.Timing.TraceWindowMilliSeconds):\n Types.Timing.TraceWindowMicroSeconds {\n return {\n min: millisecondsToMicroseconds(bounds.min),\n max: millisecondsToMicroseconds(bounds.max),\n range: millisecondsToMicroseconds(bounds.range),\n };\n}\nexport function traceWindowMicroSecondsToMilliSeconds(bounds: Types.Timing.TraceWindowMicroSeconds):\n Types.Timing.TraceWindowMilliSeconds {\n return {\n min: microSecondsToMilliseconds(bounds.min),\n max: microSecondsToMilliseconds(bounds.max),\n range: microSecondsToMilliseconds(bounds.range),\n };\n}\n\nexport function traceWindowFromMilliSeconds(\n min: Types.Timing.MilliSeconds, max: Types.Timing.MilliSeconds): Types.Timing.TraceWindowMicroSeconds {\n const traceWindow: Types.Timing.TraceWindowMicroSeconds = {\n min: millisecondsToMicroseconds(min),\n max: millisecondsToMicroseconds(max),\n range: Types.Timing.MicroSeconds(millisecondsToMicroseconds(max) - millisecondsToMicroseconds(min)),\n };\n return traceWindow;\n}\n\nexport function traceWindowFromMicroSeconds(\n min: Types.Timing.MicroSeconds, max: Types.Timing.MicroSeconds): Types.Timing.TraceWindowMicroSeconds {\n const traceWindow: Types.Timing.TraceWindowMicroSeconds = {\n min,\n max,\n range: Types.Timing.MicroSeconds(max - min),\n };\n return traceWindow;\n}\n\nexport interface BoundsIncludeTimeRange {\n timeRange: Types.Timing.TraceWindowMicroSeconds;\n bounds: Types.Timing.TraceWindowMicroSeconds;\n}\n\n/**\n * Checks to see if the timeRange is within the bounds. By \"within\" we mean\n * \"has any overlap\":\n * |------------------------|\n * == no overlap (entirely before)\n * ========= overlap\n * ========= overlap\n * ========= overlap\n * ==== no overlap (entirely after)\n * ============================== overlap (time range is larger than bounds)\n * |------------------------|\n */\nexport function boundsIncludeTimeRange(data: BoundsIncludeTimeRange): boolean {\n const {min: visibleMin, max: visibleMax} = data.bounds;\n const {min: rangeMin, max: rangeMax} = data.timeRange;\n\n return visibleMin <= rangeMax && visibleMax >= rangeMin;\n}\n\n/** Checks to see if the event is within or overlaps the bounds */\nexport function eventIsInBounds(event: Types.Events.Event, bounds: Types.Timing.TraceWindowMicroSeconds): boolean {\n const startTime = event.ts;\n return startTime <= bounds.max && bounds.min <= (startTime + (event.dur ?? 0));\n}\n\nexport function timestampIsInBounds(\n bounds: Types.Timing.TraceWindowMicroSeconds, timestamp: Types.Timing.MicroSeconds): boolean {\n return timestamp >= bounds.min && timestamp <= bounds.max;\n}\n\nexport interface WindowFitsInsideBounds {\n window: Types.Timing.TraceWindowMicroSeconds;\n bounds: Types.Timing.TraceWindowMicroSeconds;\n}\n\n/**\n * Returns true if the window fits entirely within the bounds.\n * Note that if the window is equivalent to the bounds, that is considered to fit\n */\nexport function windowFitsInsideBounds(data: WindowFitsInsideBounds): boolean {\n return data.window.min >= data.bounds.min && data.window.max <= data.bounds.max;\n}\n\nexport function windowsEqual(\n w1: Types.Timing.TraceWindowMicroSeconds, w2: Types.Timing.TraceWindowMicroSeconds): boolean {\n return w1.min === w2.min && w1.max === w2.max;\n}\n"]}
|
|
@@ -6,6 +6,7 @@ export interface TraceEntryTree {
|
|
|
6
6
|
roots: Set<TraceEntryNode>;
|
|
7
7
|
maxDepth: number;
|
|
8
8
|
}
|
|
9
|
+
/** Node in the graph that defines all parent/child relationships. */
|
|
9
10
|
export interface TraceEntryNode {
|
|
10
11
|
entry: Types.Events.Event;
|
|
11
12
|
depth: number;
|
|
@@ -14,26 +15,36 @@ export interface TraceEntryNode {
|
|
|
14
15
|
parent: TraceEntryNode | null;
|
|
15
16
|
children: TraceEntryNode[];
|
|
16
17
|
}
|
|
18
|
+
export interface AINodeSerialized {
|
|
19
|
+
name: string;
|
|
20
|
+
dur?: number;
|
|
21
|
+
self?: number;
|
|
22
|
+
children?: AINodeSerialized[];
|
|
23
|
+
url?: string;
|
|
24
|
+
selected?: boolean;
|
|
25
|
+
}
|
|
17
26
|
/**
|
|
18
|
-
*
|
|
27
|
+
* Node in a graph simplified for AI Assistance processing. The graph mirrors the TraceEntryNode one.
|
|
28
|
+
* Huge tip of the hat to Victor Porof for prototyping this with some great work: https://crrev.com/c/5711249
|
|
19
29
|
*/
|
|
20
|
-
export declare class
|
|
30
|
+
export declare class AINode {
|
|
21
31
|
#private;
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
selfTime?: Types.Timing.MilliSeconds | undefined;
|
|
32
|
+
event: Types.Events.Event;
|
|
33
|
+
name: string;
|
|
34
|
+
duration?: Types.Timing.MilliSeconds;
|
|
35
|
+
selfDuration?: Types.Timing.MilliSeconds;
|
|
27
36
|
id?: TraceEntryNodeId;
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
column?: number;
|
|
31
|
-
function?: string;
|
|
32
|
-
children?: TraceEntryNodeForAI[];
|
|
37
|
+
children?: AINode[];
|
|
38
|
+
url?: string;
|
|
33
39
|
selected?: boolean;
|
|
34
|
-
constructor(
|
|
35
|
-
|
|
36
|
-
|
|
40
|
+
constructor(event: Types.Events.Event);
|
|
41
|
+
toJSON(): AINodeSerialized;
|
|
42
|
+
/**
|
|
43
|
+
* Builds a TraceEntryNodeForAI tree from a node and marks the selected node. Primary entrypoint from EntriesFilter
|
|
44
|
+
*/
|
|
45
|
+
static fromEntryNode(selectedNode: TraceEntryNode, entryIsVisibleInTimeline: (event: Types.Events.Event) => boolean): AINode;
|
|
46
|
+
static getSelectedNodeWithinTree(node: AINode): AINode | null;
|
|
47
|
+
sanitize(): void;
|
|
37
48
|
}
|
|
38
49
|
declare class TraceEntryNodeIdTag {
|
|
39
50
|
#private;
|
|
@@ -17,71 +17,87 @@ export const makeEmptyTraceEntryNode = (entry, id) => ({
|
|
|
17
17
|
depth: 0,
|
|
18
18
|
});
|
|
19
19
|
/**
|
|
20
|
-
*
|
|
20
|
+
* Node in a graph simplified for AI Assistance processing. The graph mirrors the TraceEntryNode one.
|
|
21
|
+
* Huge tip of the hat to Victor Porof for prototyping this with some great work: https://crrev.com/c/5711249
|
|
21
22
|
*/
|
|
22
|
-
export class
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
23
|
+
export class AINode {
|
|
24
|
+
event;
|
|
25
|
+
// event: Types.Events.Event; // Set in the constructor.
|
|
26
|
+
name;
|
|
27
|
+
duration;
|
|
28
|
+
selfDuration;
|
|
28
29
|
id;
|
|
29
|
-
domain;
|
|
30
|
-
line;
|
|
31
|
-
column;
|
|
32
|
-
function;
|
|
33
30
|
children;
|
|
31
|
+
url;
|
|
34
32
|
selected;
|
|
35
|
-
constructor(
|
|
36
|
-
this.
|
|
37
|
-
this.
|
|
38
|
-
this.
|
|
39
|
-
this.totalTime = totalTime;
|
|
40
|
-
this.selfTime = selfTime;
|
|
41
|
-
}
|
|
42
|
-
static #fromTraceEvent(event) {
|
|
43
|
-
const start = microSecondsToMilliseconds(event.ts);
|
|
44
|
-
const duration = event.dur === undefined ? undefined : microSecondsToMilliseconds(event.dur);
|
|
45
|
-
const nodeForAI = new TraceEntryNodeForAI(event.name, start, duration);
|
|
33
|
+
constructor(event) {
|
|
34
|
+
this.event = event;
|
|
35
|
+
this.name = event.name;
|
|
36
|
+
this.duration = event.dur === undefined ? undefined : microSecondsToMilliseconds(event.dur);
|
|
46
37
|
if (Types.Events.isProfileCall(event)) {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
const url = new URL(event.callFrame.url);
|
|
50
|
-
nodeForAI.domain = url.origin;
|
|
51
|
-
nodeForAI.line = event.callFrame.lineNumber;
|
|
52
|
-
nodeForAI.column = event.callFrame.columnNumber;
|
|
53
|
-
}
|
|
54
|
-
catch (e) {
|
|
55
|
-
}
|
|
38
|
+
this.name = event.callFrame.functionName || '(anonymous)';
|
|
39
|
+
this.url = event.callFrame.url;
|
|
56
40
|
}
|
|
57
|
-
|
|
41
|
+
}
|
|
42
|
+
// Manually handle how nodes in this tree are serialized. We'll drop serveral properties that we don't need in the JSON string.
|
|
43
|
+
// FYI: toJSON() is invoked implicitly via JSON.stringify()
|
|
44
|
+
toJSON() {
|
|
45
|
+
return {
|
|
46
|
+
selected: this.selected,
|
|
47
|
+
name: this.name,
|
|
48
|
+
url: this.url,
|
|
49
|
+
// Round milliseconds because we don't need the precision
|
|
50
|
+
dur: this.duration === undefined ? undefined : Math.round(this.duration * 10) / 10,
|
|
51
|
+
self: this.selfDuration === undefined ? undefined : Math.round(this.selfDuration * 10) / 10,
|
|
52
|
+
children: this.children?.length ? this.children : undefined,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
static #fromTraceEvent(event) {
|
|
56
|
+
return new AINode(event);
|
|
58
57
|
}
|
|
59
58
|
/**
|
|
60
|
-
* Builds a TraceEntryNodeForAI tree from a
|
|
59
|
+
* Builds a TraceEntryNodeForAI tree from a node and marks the selected node. Primary entrypoint from EntriesFilter
|
|
61
60
|
*/
|
|
62
|
-
static
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
61
|
+
static fromEntryNode(selectedNode, entryIsVisibleInTimeline) {
|
|
62
|
+
/**
|
|
63
|
+
* Builds a AINode tree from a TraceEntryNode tree and marks the selected node.
|
|
64
|
+
*/
|
|
65
|
+
function fromEntryNodeAndTree(node) {
|
|
66
|
+
const aiNode = AINode.#fromTraceEvent(node.entry);
|
|
67
|
+
aiNode.id = node.id;
|
|
68
|
+
if (node === selectedNode) {
|
|
69
|
+
aiNode.selected = true;
|
|
70
|
+
}
|
|
71
|
+
aiNode.selfDuration = node.selfTime === undefined ? undefined : microSecondsToMilliseconds(node.selfTime);
|
|
72
|
+
for (const child of node.children) {
|
|
73
|
+
aiNode.children ??= [];
|
|
74
|
+
aiNode.children.push(fromEntryNodeAndTree(child));
|
|
75
|
+
}
|
|
76
|
+
return aiNode;
|
|
72
77
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
78
|
+
function findTopMostVisibleAncestor(node) {
|
|
79
|
+
const parentNodes = [node];
|
|
80
|
+
let parent = node.parent;
|
|
81
|
+
while (parent) {
|
|
82
|
+
parentNodes.unshift(parent);
|
|
83
|
+
parent = parent.parent;
|
|
79
84
|
}
|
|
80
|
-
return node;
|
|
85
|
+
return parentNodes.find(node => entryIsVisibleInTimeline(node.entry)) ?? node;
|
|
81
86
|
}
|
|
82
|
-
|
|
87
|
+
const topMostVisibleRoot = findTopMostVisibleAncestor(selectedNode);
|
|
88
|
+
const aiNode = fromEntryNodeAndTree(topMostVisibleRoot);
|
|
89
|
+
// If our root wasn't visible, this could return an array of multiple RunTasks.
|
|
90
|
+
// But with a visible root, we safely get back the exact same root, now with its descendent tree updated.
|
|
91
|
+
// Filter to ensure our tree here only has "visible" entries
|
|
92
|
+
const [filteredAiNodeRoot] = AINode.#filterRecursive([aiNode], node => {
|
|
93
|
+
if (node.event.name === 'V8.CompileCode' || node.event.name === 'UpdateCounters') {
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
return entryIsVisibleInTimeline(node.event);
|
|
97
|
+
});
|
|
98
|
+
return filteredAiNodeRoot;
|
|
83
99
|
}
|
|
84
|
-
static
|
|
100
|
+
static getSelectedNodeWithinTree(node) {
|
|
85
101
|
if (node.selected) {
|
|
86
102
|
return node;
|
|
87
103
|
}
|
|
@@ -89,13 +105,57 @@ export class TraceEntryNodeForAI {
|
|
|
89
105
|
return null;
|
|
90
106
|
}
|
|
91
107
|
for (const child of node.children) {
|
|
92
|
-
const returnedNode =
|
|
108
|
+
const returnedNode = AINode.getSelectedNodeWithinTree(child);
|
|
93
109
|
if (returnedNode) {
|
|
94
110
|
return returnedNode;
|
|
95
111
|
}
|
|
96
112
|
}
|
|
97
113
|
return null;
|
|
98
114
|
}
|
|
115
|
+
static #filterRecursive(list, predicate) {
|
|
116
|
+
let done;
|
|
117
|
+
do {
|
|
118
|
+
done = true;
|
|
119
|
+
const filtered = [];
|
|
120
|
+
for (const node of list) {
|
|
121
|
+
if (predicate(node)) {
|
|
122
|
+
// Keep it
|
|
123
|
+
filtered.push(node);
|
|
124
|
+
}
|
|
125
|
+
else if (node.children) {
|
|
126
|
+
filtered.push(...node.children);
|
|
127
|
+
done = false;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
list = filtered;
|
|
131
|
+
} while (!done);
|
|
132
|
+
for (const node of list) {
|
|
133
|
+
if (node.children) {
|
|
134
|
+
node.children = AINode.#filterRecursive(node.children, predicate);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return list;
|
|
138
|
+
}
|
|
139
|
+
static #removeInexpensiveNodesRecursively(list, options) {
|
|
140
|
+
const minDuration = options?.minDuration ?? 0;
|
|
141
|
+
const minSelf = options?.minSelf ?? 0;
|
|
142
|
+
const minJsDuration = options?.minJsDuration ?? 0;
|
|
143
|
+
const minJsSelf = options?.minJsSelf ?? 0;
|
|
144
|
+
const isJS = (node) => Boolean(node.url);
|
|
145
|
+
const longEnough = (node) => node.duration === undefined || node.duration >= (isJS(node) ? minJsDuration : minDuration);
|
|
146
|
+
const selfLongEnough = (node) => node.selfDuration === undefined || node.selfDuration >= (isJS(node) ? minJsSelf : minSelf);
|
|
147
|
+
return AINode.#filterRecursive(list, node => longEnough(node) && selfLongEnough(node));
|
|
148
|
+
}
|
|
149
|
+
// Invoked from DrJonesPerformanceAgent
|
|
150
|
+
sanitize() {
|
|
151
|
+
if (this.children) {
|
|
152
|
+
this.children = AINode.#removeInexpensiveNodesRecursively(this.children, {
|
|
153
|
+
minDuration: Types.Timing.MilliSeconds(1),
|
|
154
|
+
minJsDuration: Types.Timing.MilliSeconds(1),
|
|
155
|
+
minJsSelf: Types.Timing.MilliSeconds(0.1),
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
}
|
|
99
159
|
}
|
|
100
160
|
class TraceEntryNodeIdTag {
|
|
101
161
|
/* eslint-disable-next-line no-unused-private-class-members */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TreeHelpers.js","sourceRoot":"","sources":["../../../../../../../front_end/models/trace/helpers/TreeHelpers.ts"],"names":[],"mappings":"AAAA,4DAA4D;AAC5D,yEAAyE;AACzE,6BAA6B;AAE7B,OAAO,KAAK,KAAK,MAAM,mBAAmB,CAAC;AAE3C,OAAO,EAAC,eAAe,EAAE,0BAA0B,EAAC,MAAM,aAAa,CAAC;AAExE,IAAI,WAAW,GAAG,CAAC,CAAC;AACpB,MAAM,CAAC,MAAM,oBAAoB,GAAG,GAAqB,EAAE,CAAC,CAAC,EAAE,WAAW,CAAqB,CAAC;AAEhG,MAAM,CAAC,MAAM,uBAAuB,GAAG,GAAmB,EAAE,CAAC,CAAC;IAC5D,KAAK,EAAE,IAAI,GAAG,EAAE;IAChB,QAAQ,EAAE,CAAC;CACZ,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,KAAyB,EAAE,EAAoB,EAAkB,EAAE,CAAC,CAAC;IAC3G,KAAK;IACL,EAAE;IACF,MAAM,EAAE,IAAI;IACZ,QAAQ,EAAE,EAAE;IACZ,KAAK,EAAE,CAAC;CACT,CAAC,CAAC;AAgBH;;GAEG;AACH,MAAM,OAAO,mBAAmB;IAUnB;IAAqB;IAAyC;IAC9D;IAA8C;IAVzD,EAAE,CAAoB;IACtB,MAAM,CAAU;IAChB,IAAI,CAAU;IACd,MAAM,CAAU;IAChB,QAAQ,CAAU;IAClB,QAAQ,CAAyB;IACjC,QAAQ,CAAW;IAEnB,YACW,IAAY,EAAS,KAAgC,EAAS,GAA+B,EAC7F,SAAqC,EAAS,QAAoC;QADlF,SAAI,GAAJ,IAAI,CAAQ;QAAS,UAAK,GAAL,KAAK,CAA2B;QAAS,QAAG,GAAH,GAAG,CAA4B;QAC7F,cAAS,GAAT,SAAS,CAA4B;QAAS,aAAQ,GAAR,QAAQ,CAA4B;IAC7F,CAAC;IAED,MAAM,CAAC,eAAe,CAAC,KAAyB;QAC9C,MAAM,KAAK,GAAG,0BAA0B,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QACnD,MAAM,QAAQ,GAAG,KAAK,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,0BAA0B,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC7F,MAAM,SAAS,GAAG,IAAI,mBAAmB,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC;QACvE,IAAI,KAAK,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,EAAE,CAAC;YACtC,SAAS,CAAC,QAAQ,GAAG,KAAK,CAAC,SAAS,CAAC,YAAY,IAAI,aAAa,CAAC;YACnE,IAAI,CAAC;gBACH,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;gBACzC,SAAS,CAAC,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC;gBAC9B,SAAS,CAAC,IAAI,GAAG,KAAK,CAAC,SAAS,CAAC,UAAU,CAAC;gBAC5C,SAAS,CAAC,MAAM,GAAG,KAAK,CAAC,SAAS,CAAC,YAAY,CAAC;YAClD,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC;YACb,CAAC;QACH,CAAC;QACD,OAAO,SAAS,CAAC;IACnB,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,mBAAmB,CAAC,IAAoB,EAAE,iBAAiC;QAChF,MAAM,SAAS,GAAG,mBAAmB,CAAC,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAClE,SAAS,CAAC,EAAE,GAAG,IAAI,CAAC,EAAE,CAAC;QACvB,IAAI,IAAI,KAAK,iBAAiB,EAAE,CAAC;YAC/B,SAAS,CAAC,QAAQ,GAAG,IAAI,CAAC;QAC5B,CAAC;QACD,SAAS,CAAC,QAAQ,GAAG,IAAI,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,0BAA0B,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACzG,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAClC,SAAS,CAAC,QAAQ,KAAK,EAAE,CAAC;YAC1B,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,mBAAmB,CAAC,mBAAmB,CAAC,KAAK,EAAE,iBAAiB,CAAC,CAAC,CAAC;QAC7F,CAAC;QACD,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,MAAM,CAAC,qBAAqB,CAAC,iBAAiC;QAC5D,SAAS,OAAO,CAAC,IAAoB;YACnC,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;gBAChB,OAAO,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YAC9B,CAAC;YACD,OAAO,IAAI,CAAC;QACd,CAAC;QAED,OAAO,mBAAmB,CAAC,mBAAmB,CAAC,OAAO,CAAC,iBAAiB,CAAC,EAAE,iBAAiB,CAAC,CAAC;IAChG,CAAC;IAED,MAAM,CAAC,qCAAqC,CAAC,IAAyB;QACpE,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAClB,OAAO,IAAI,CAAC;QACd,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;YACnB,OAAO,IAAI,CAAC;QACd,CAAC;QACD,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAClC,MAAM,YAAY,GAAG,mBAAmB,CAAC,qCAAqC,CAAC,KAAK,CAAC,CAAC;YACtF,IAAI,YAAY,EAAE,CAAC;gBACjB,OAAO,YAAY,CAAC;YACtB,CAAC;QACH,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;CACF;AAED,MAAM,mBAAmB;IACvB,8DAA8D;IACrD,IAAI,CAAqB;CACnC;AAGD;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,MAAM,CAAC,OAA6B,EAAE,OAErD;IACC,wEAAwE;IACxE,yEAAyE;IACzE,wCAAwC;IACxC,MAAM,WAAW,GAAG,IAAI,GAAG,EAAsC,CAAC;IAElE,MAAM,KAAK,GAAG,EAAE,CAAC;IACjB,oDAAoD;IACpD,WAAW,GAAG,CAAC,CAAC,CAAC;IACjB,MAAM,IAAI,GAAG,uBAAuB,EAAE,CAAC;IAEvC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACxC,MAAM,KAAK,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;QACzB,2EAA2E;QAC3E,uBAAuB;QACvB,IAAI,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,IAAyB,CAAC,EAAE,CAAC;YACpE,SAAS;QACX,CAAC;QAED,MAAM,QAAQ,GAAG,KAAK,CAAC,GAAG,IAAI,CAAC,CAAC;QAChC,MAAM,MAAM,GAAG,oBAAoB,EAAE,CAAC;QACtC,MAAM,IAAI,GAAG,uBAAuB,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;QAEpD,2EAA2E;QAC3E,oEAAoE;QACpE,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACvB,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YACrB,IAAI,CAAC,QAAQ,GAAG,KAAK,CAAC,MAAM,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC;YACpD,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACjB,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;YACtD,WAAW,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;YAC7B,SAAS;QACX,CAAC;QAED,MAAM,UAAU,GAAG,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QAChC,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;YAC7B,MAAM,IAAI,KAAK,CAAC,+CAA+C,CAAC,CAAC;QACnE,CAAC;QAED,MAAM,WAAW,GAAG,UAAU,CAAC,KAAK,CAAC;QAErC,MAAM,KAAK,GAAG,KAAK,CAAC,EAAE,CAAC;QACvB,MAAM,WAAW,GAAG,WAAW,CAAC,EAAE,CAAC;QACnC,MAAM,cAAc,GAAG,WAAW,CAAC,GAAG,IAAI,CAAC,CAAC;QAC5C,MAAM,GAAG,GAAG,KAAK,GAAG,QAAQ,CAAC;QAC7B,MAAM,SAAS,GAAG,WAAW,GAAG,cAAc,CAAC;QAC/C,2EAA2E;QAC3E,mEAAmE;QACnE,0EAA0E;QAC1E,4EAA4E;QAC5E,yEAAyE;QACzE,yEAAyE;QACzE,oEAAoE;QAEpE,2EAA2E;QAC3E,2EAA2E;QAC3E,MAAM,kBAAkB,GAAG,KAAK,GAAG,WAAW,CAAC;QAC/C,IAAI,kBAAkB,EAAE,CAAC;YACvB,MAAM,IAAI,KAAK,CAAC,0DAA0D,CAAC,CAAC;QAC9E,CAAC;QAED,yEAAyE;QACzE,mDAAmD;QACnD,MAAM,iBAAiB,GAAG,KAAK,IAAI,SAAS,CAAC;QAC7C,IAAI,iBAAiB,EAAE,CAAC;YACtB,KAAK,CAAC,GAAG,EAAE,CAAC;YACZ,CAAC,EAAE,CAAC;YACJ,gEAAgE;YAChE,WAAW,EAAE,CAAC;YACd,SAAS;QACX,CAAC;QACD,mEAAmE;QACnE,kEAAkE;QAClE,mEAAmE;QACnE,wBAAwB;QACxB,MAAM,eAAe,GAAG,GAAG,GAAG,SAAS,CAAC;QACxC,IAAI,eAAe,EAAE,CAAC;YACpB,SAAS;QACX,CAAC;QAED,4EAA4E;QAC5E,sEAAsE;QACtE,2EAA2E;QAC3E,iBAAiB;QACjB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC;QAC1B,IAAI,CAAC,MAAM,GAAG,UAAU,CAAC;QACzB,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC/B,IAAI,CAAC,QAAQ,GAAG,KAAK,CAAC,MAAM,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC;QACpD,IAAI,UAAU,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;YACtC,UAAU,CAAC,QAAQ,GAAG,KAAK,CAAC,MAAM,CAAC,YAAY,CAAC,UAAU,CAAC,QAAQ,GAAG,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QAC1F,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACjB,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;QACtD,WAAW,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IAC/B,CAAC;IACD,OAAO,EAAC,IAAI,EAAE,WAAW,EAAC,CAAC;AAC7B,CAAC;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,UAAU,iBAAiB,CAC7B,WAAoD,EACpD,SAA6B,EAC7B,YAAiD,EACjD,UAA+C;IAEjD,MAAM,SAAS,GAAG,WAAW,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IAC7C,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,OAAO;IACT,CAAC;IACD,cAAc,CAAC,WAAW,EAAE,SAAS,EAAE,YAAY,EAAE,UAAU,CAAC,CAAC;AACnE,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,MAAM,UAAU,cAAc,CAC1B,WAAoD,EACpD,IAAoB,EACpB,YAAiD,EACjD,UAA+C,EAC/C,oBAA2D,EAC3D,WAAuC;IAEzC,KAAK,MAAM,QAAQ,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;QAClC,cAAc,CAAC,WAAW,EAAE,QAAQ,EAAE,YAAY,EAAE,UAAU,EAAE,oBAAoB,EAAE,WAAW,CAAC,CAAC;IACrG,CAAC;AACH,CAAC;AAED,SAAS,cAAc,CACnB,WAAoD,EACpD,QAAwB,EACxB,YAAiD,EACjD,UAA+C,EAC/C,oBAA2D,EAC3D,WAAuC;IAEzC,IAAI,oBAAoB,IAAI,CAAC,kBAAkB,CAAC,QAAQ,EAAE,oBAAoB,CAAC,EAAE,CAAC;QAChF,0EAA0E;QAC1E,2EAA2E;QAC3E,uBAAuB;QACvB,OAAO;IACT,CAAC;IAED,IAAI,OAAO,WAAW,KAAK,WAAW,EAAE,CAAC;QACvC,MAAM,QAAQ,GAAG,KAAK,CAAC,MAAM,CAAC,YAAY,CACtC,QAAQ,CAAC,KAAK,CAAC,EAAE,GAAG,KAAK,CAAC,MAAM,CAAC,YAAY,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,CAAC,CACzE,CAAC;QACF,IAAI,QAAQ,GAAG,WAAW,EAAE,CAAC;YAC3B,OAAO;QACT,CAAC;IACH,CAAC;IAED,YAAY,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC7B,KAAK,MAAM,KAAK,IAAI,QAAQ,CAAC,QAAQ,EAAE,CAAC;QACtC,cAAc,CAAC,WAAW,EAAE,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,oBAAoB,EAAE,WAAW,CAAC,CAAC;IAClG,CAAC;IACD,UAAU,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AAC7B,CAAC;AAED;;;;GAIG;AACH,SAAS,kBAAkB,CAAC,IAAoB,EAAE,WAAiD;IACjG,OAAO,eAAe,CAAC,IAAI,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC;AAClD,CAAC;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,uBAAuB,CAAC,MAAqC;IAC3E,MAAM,KAAK,GAAyB,EAAE,CAAC;IACvC,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,MAAM,SAAS,GAAG,KAAK,CAAC,EAAE,CAAC;QAC3B,MAAM,OAAO,GAAG,KAAK,CAAC,EAAE,GAAG,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;QAC5C,IAAI,MAAM,GAAG,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QAC1B,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YACzB,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAClB,SAAS;QACX,CAAC;QACD,IAAI,aAAa,GAAG,MAAM,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;QAClD,iEAAiE;QACjE,wDAAwD;QACxD,OAAO,KAAK,CAAC,MAAM,IAAI,SAAS,IAAI,aAAa,EAAE,CAAC;YAClD,KAAK,CAAC,GAAG,EAAE,CAAC;YACZ,MAAM,GAAG,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;YAEtB,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;gBACzB,MAAM;YACR,CAAC;YACD,aAAa,GAAG,MAAM,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;QAChD,CAAC;QACD,IAAI,KAAK,CAAC,MAAM,IAAI,OAAO,GAAG,aAAa,EAAE,CAAC;YAC5C,0DAA0D;YAC1D,0DAA0D;YAC1D,UAAU;YACV,OAAO,KAAK,CAAC;QACf,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACpB,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC","sourcesContent":["// Copyright 2023 The Chromium Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\nimport * as Types from '../types/types.js';\n\nimport {eventIsInBounds, microSecondsToMilliseconds} from './Timing.js';\n\nlet nodeIdCount = 0;\nexport const makeTraceEntryNodeId = (): TraceEntryNodeId => (++nodeIdCount) as TraceEntryNodeId;\n\nexport const makeEmptyTraceEntryTree = (): TraceEntryTree => ({\n roots: new Set(),\n maxDepth: 0,\n});\n\nexport const makeEmptyTraceEntryNode = (entry: Types.Events.Event, id: TraceEntryNodeId): TraceEntryNode => ({\n entry,\n id,\n parent: null,\n children: [],\n depth: 0,\n});\n\nexport interface TraceEntryTree {\n roots: Set<TraceEntryNode>;\n maxDepth: number;\n}\n\nexport interface TraceEntryNode {\n entry: Types.Events.Event;\n depth: number;\n selfTime?: Types.Timing.MicroSeconds;\n id: TraceEntryNodeId;\n parent: TraceEntryNode|null;\n children: TraceEntryNode[];\n}\n\n/**\n * Represents a node in a trace entry tree, simplified for AI Assistance processing.\n */\nexport class TraceEntryNodeForAI {\n id?: TraceEntryNodeId;\n domain?: string;\n line?: number;\n column?: number;\n function?: string;\n children?: TraceEntryNodeForAI[];\n selected?: boolean;\n\n constructor(\n public type: string, public start: Types.Timing.MilliSeconds, public end?: Types.Timing.MilliSeconds,\n public totalTime?: Types.Timing.MilliSeconds, public selfTime?: Types.Timing.MilliSeconds) {\n }\n\n static #fromTraceEvent(event: Types.Events.Event): TraceEntryNodeForAI {\n const start = microSecondsToMilliseconds(event.ts);\n const duration = event.dur === undefined ? undefined : microSecondsToMilliseconds(event.dur);\n const nodeForAI = new TraceEntryNodeForAI(event.name, start, duration);\n if (Types.Events.isProfileCall(event)) {\n nodeForAI.function = event.callFrame.functionName || '(anonymous)';\n try {\n const url = new URL(event.callFrame.url);\n nodeForAI.domain = url.origin;\n nodeForAI.line = event.callFrame.lineNumber;\n nodeForAI.column = event.callFrame.columnNumber;\n } catch (e) {\n }\n }\n return nodeForAI;\n }\n\n /**\n * Builds a TraceEntryNodeForAI tree from a TraceEntryNode tree and marks the selected node.\n */\n static #fromTraceEntryTree(node: TraceEntryNode, selectedEntryNode: TraceEntryNode): TraceEntryNodeForAI {\n const nodeForAI = TraceEntryNodeForAI.#fromTraceEvent(node.entry);\n nodeForAI.id = node.id;\n if (node === selectedEntryNode) {\n nodeForAI.selected = true;\n }\n nodeForAI.selfTime = node.selfTime === undefined ? undefined : microSecondsToMilliseconds(node.selfTime);\n for (const child of node.children) {\n nodeForAI.children ??= [];\n nodeForAI.children.push(TraceEntryNodeForAI.#fromTraceEntryTree(child, selectedEntryNode));\n }\n return nodeForAI;\n }\n\n static fromSelectedEntryNode(selectedEntryNode: TraceEntryNode): TraceEntryNodeForAI {\n function getRoot(node: TraceEntryNode): TraceEntryNode {\n if (node.parent) {\n return getRoot(node.parent);\n }\n return node;\n }\n\n return TraceEntryNodeForAI.#fromTraceEntryTree(getRoot(selectedEntryNode), selectedEntryNode);\n }\n\n static getSelectedNodeForTraceEntryTreeForAI(node: TraceEntryNodeForAI): TraceEntryNodeForAI|null {\n if (node.selected) {\n return node;\n }\n if (!node.children) {\n return null;\n }\n for (const child of node.children) {\n const returnedNode = TraceEntryNodeForAI.getSelectedNodeForTraceEntryTreeForAI(child);\n if (returnedNode) {\n return returnedNode;\n }\n }\n return null;\n }\n}\n\nclass TraceEntryNodeIdTag {\n /* eslint-disable-next-line no-unused-private-class-members */\n readonly #tag: (symbol|undefined);\n}\nexport type TraceEntryNodeId = number&TraceEntryNodeIdTag;\n\n/**\n * Builds a hierarchy of the entries (trace events and profile calls) in\n * a particular thread of a particular process, assuming that they're\n * sorted, by iterating through all of the events in order.\n *\n * The approach is analogous to how a parser would be implemented. A\n * stack maintains local context. A scanner peeks and pops from the data\n * stream. Various \"tokens\" (events) are treated as \"whitespace\"\n * (ignored).\n *\n * The tree starts out empty and is populated as the hierarchy is built.\n * The nodes are also assumed to be created empty, with no known parent\n * or children.\n *\n * Complexity: O(n), where n = number of events\n */\nexport function treify(entries: Types.Events.Event[], options?: {\n filter: {has: (name: Types.Events.Name) => boolean},\n}): {tree: TraceEntryTree, entryToNode: Map<Types.Events.Event, TraceEntryNode>} {\n // As we construct the tree, store a map of each entry to its node. This\n // means if you are iterating over a list of RendererEntry events you can\n // easily look up that node in the tree.\n const entryToNode = new Map<Types.Events.Event, TraceEntryNode>();\n\n const stack = [];\n // Reset the node id counter for every new renderer.\n nodeIdCount = -1;\n const tree = makeEmptyTraceEntryTree();\n\n for (let i = 0; i < entries.length; i++) {\n const event = entries[i];\n // If the current event should not be part of the tree, then simply proceed\n // with the next event.\n if (options && !options.filter.has(event.name as Types.Events.Name)) {\n continue;\n }\n\n const duration = event.dur || 0;\n const nodeId = makeTraceEntryNodeId();\n const node = makeEmptyTraceEntryNode(event, nodeId);\n\n // If the parent stack is empty, then the current event is a root. Create a\n // node for it, mark it as a root, then proceed with the next event.\n if (stack.length === 0) {\n tree.roots.add(node);\n node.selfTime = Types.Timing.MicroSeconds(duration);\n stack.push(node);\n tree.maxDepth = Math.max(tree.maxDepth, stack.length);\n entryToNode.set(event, node);\n continue;\n }\n\n const parentNode = stack.at(-1);\n if (parentNode === undefined) {\n throw new Error('Impossible: no parent node found in the stack');\n }\n\n const parentEvent = parentNode.entry;\n\n const begin = event.ts;\n const parentBegin = parentEvent.ts;\n const parentDuration = parentEvent.dur || 0;\n const end = begin + duration;\n const parentEnd = parentBegin + parentDuration;\n // Check the relationship between the parent event at the top of the stack,\n // and the current event being processed. There are only 4 distinct\n // possiblities, only 2 of them actually valid, given the assumed sorting:\n // 1. Current event starts before the parent event, ends whenever. (invalid)\n // 2. Current event starts after the parent event, ends whenever. (valid)\n // 3. Current event starts during the parent event, ends after. (invalid)\n // 4. Current event starts and ends during the parent event. (valid)\n\n // 1. If the current event starts before the parent event, then the data is\n // not sorted properly, messed up some way, or this logic is incomplete.\n const startsBeforeParent = begin < parentBegin;\n if (startsBeforeParent) {\n throw new Error('Impossible: current event starts before the parent event');\n }\n\n // 2. If the current event starts after the parent event, then it's a new\n // parent. Pop, then handle current event again.\n const startsAfterParent = begin >= parentEnd;\n if (startsAfterParent) {\n stack.pop();\n i--;\n // The last created node has been discarded, so discard this id.\n nodeIdCount--;\n continue;\n }\n // 3. If the current event starts during the parent event, but ends\n // after it, then the data is messed up some way, for example a\n // profile call was sampled too late after its start, ignore the\n // problematic event.\n const endsAfterParent = end > parentEnd;\n if (endsAfterParent) {\n continue;\n }\n\n // 4. The only remaining case is the common case, where the current event is\n // contained within the parent event. Create a node for the current\n // event, establish the parent/child relationship, then proceed with the\n // next event.\n node.depth = stack.length;\n node.parent = parentNode;\n parentNode.children.push(node);\n node.selfTime = Types.Timing.MicroSeconds(duration);\n if (parentNode.selfTime !== undefined) {\n parentNode.selfTime = Types.Timing.MicroSeconds(parentNode.selfTime - (event.dur || 0));\n }\n stack.push(node);\n tree.maxDepth = Math.max(tree.maxDepth, stack.length);\n entryToNode.set(event, node);\n }\n return {tree, entryToNode};\n}\n\n/**\n * Iterates events in a tree hierarchically, from top to bottom,\n * calling back on every event's start and end in the order\n * as it traverses down and then up the tree.\n *\n * For example, given this tree, the following callbacks\n * are expected to be made in the following order\n * |---------------A---------------|\n * |------B------||-------D------|\n * |---C---|\n *\n * 1. Start A\n * 3. Start B\n * 4. Start C\n * 5. End C\n * 6. End B\n * 7. Start D\n * 8. End D\n * 9. End A\n *\n */\nexport function walkTreeFromEntry(\n entryToNode: Map<Types.Events.Event, TraceEntryNode>,\n rootEntry: Types.Events.Event,\n onEntryStart: (entry: Types.Events.Event) => void,\n onEntryEnd: (entry: Types.Events.Event) => void,\n ): void {\n const startNode = entryToNode.get(rootEntry);\n if (!startNode) {\n return;\n }\n walkTreeByNode(entryToNode, startNode, onEntryStart, onEntryEnd);\n}\n\n/**\n * Given a Helpers.TreeHelpers.RendererTree, this will iterates events in hierarchically, visiting\n * each root node and working from top to bottom, calling back on every event's\n * start and end in the order as it traverses down and then up the tree.\n *\n * For example, given this tree, the following callbacks\n * are expected to be made in the following order\n * |------------- Task A -------------||-- Task E --|\n * |-- Task B --||-- Task D --|\n * |- Task C -|\n *\n * 1. Start A\n * 3. Start B\n * 4. Start C\n * 5. End C\n * 6. End B\n * 7. Start D\n * 8. End D\n * 9. End A\n * 10. Start E\n * 11. End E\n *\n */\n\nexport function walkEntireTree(\n entryToNode: Map<Types.Events.Event, TraceEntryNode>,\n tree: TraceEntryTree,\n onEntryStart: (entry: Types.Events.Event) => void,\n onEntryEnd: (entry: Types.Events.Event) => void,\n traceWindowToInclude?: Types.Timing.TraceWindowMicroSeconds,\n minDuration?: Types.Timing.MicroSeconds,\n ): void {\n for (const rootNode of tree.roots) {\n walkTreeByNode(entryToNode, rootNode, onEntryStart, onEntryEnd, traceWindowToInclude, minDuration);\n }\n}\n\nfunction walkTreeByNode(\n entryToNode: Map<Types.Events.Event, TraceEntryNode>,\n rootNode: TraceEntryNode,\n onEntryStart: (entry: Types.Events.Event) => void,\n onEntryEnd: (entry: Types.Events.Event) => void,\n traceWindowToInclude?: Types.Timing.TraceWindowMicroSeconds,\n minDuration?: Types.Timing.MicroSeconds,\n ): void {\n if (traceWindowToInclude && !treeNodeIsInWindow(rootNode, traceWindowToInclude)) {\n // If this node is not within the provided window, we can skip it. We also\n // can skip all its children too, as we know they won't be in the window if\n // their parent is not.\n return;\n }\n\n if (typeof minDuration !== 'undefined') {\n const duration = Types.Timing.MicroSeconds(\n rootNode.entry.ts + Types.Timing.MicroSeconds(rootNode.entry.dur ?? 0),\n );\n if (duration < minDuration) {\n return;\n }\n }\n\n onEntryStart(rootNode.entry);\n for (const child of rootNode.children) {\n walkTreeByNode(entryToNode, child, onEntryStart, onEntryEnd, traceWindowToInclude, minDuration);\n }\n onEntryEnd(rootNode.entry);\n}\n\n/**\n * Returns true if the provided node is partially or fully within the trace\n * window. The entire node does not have to fit inside the window, but it does\n * have to partially intersect it.\n */\nfunction treeNodeIsInWindow(node: TraceEntryNode, traceWindow: Types.Timing.TraceWindowMicroSeconds): boolean {\n return eventIsInBounds(node.entry, traceWindow);\n}\n\n/**\n * Determines if the given events, which are assumed to be ordered can\n * be organized into tree structures.\n * This condition is met if there is *not* a pair of async events\n * e1 and e2 where:\n *\n * e1.startTime < e2.startTime && e1.endTime > e2.startTime && e1.endTime < e2.endTime.\n * or, graphically:\n * |------- e1 ------|\n * |------- e2 --------|\n *\n * Because a parent-child relationship cannot be made from the example\n * above, a tree cannot be made from the set of events.\n *\n * Sync events from the same thread are tree-able by definition.\n *\n * Note that this will also return true if multiple trees can be\n * built, for example if none of the events overlap with each other.\n */\nexport function canBuildTreesFromEvents(events: readonly Types.Events.Event[]): boolean {\n const stack: Types.Events.Event[] = [];\n for (const event of events) {\n const startTime = event.ts;\n const endTime = event.ts + (event.dur ?? 0);\n let parent = stack.at(-1);\n if (parent === undefined) {\n stack.push(event);\n continue;\n }\n let parentEndTime = parent.ts + (parent.dur ?? 0);\n // Discard events that are not parents for this event. The parent\n // is one whose end time is after this event start time.\n while (stack.length && startTime >= parentEndTime) {\n stack.pop();\n parent = stack.at(-1);\n\n if (parent === undefined) {\n break;\n }\n parentEndTime = parent.ts + (parent.dur ?? 0);\n }\n if (stack.length && endTime > parentEndTime) {\n // If such an event exists but its end time is before this\n // event's end time, then a tree cannot be made using this\n // events.\n return false;\n }\n stack.push(event);\n }\n return true;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"TreeHelpers.js","sourceRoot":"","sources":["../../../../../../../front_end/models/trace/helpers/TreeHelpers.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2023 The Chromium Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\nimport * as Types from '../types/types.js';\n\nimport {eventIsInBounds, microSecondsToMilliseconds} from './Timing.js';\n\nlet nodeIdCount = 0;\nexport const makeTraceEntryNodeId = (): TraceEntryNodeId => (++nodeIdCount) as TraceEntryNodeId;\n\nexport const makeEmptyTraceEntryTree = (): TraceEntryTree => ({\n roots: new Set(),\n maxDepth: 0,\n});\n\nexport const makeEmptyTraceEntryNode = (entry: Types.Events.Event, id: TraceEntryNodeId): TraceEntryNode => ({\n entry,\n id,\n parent: null,\n children: [],\n depth: 0,\n});\n\nexport interface TraceEntryTree {\n roots: Set<TraceEntryNode>;\n maxDepth: number;\n}\n\n/** Node in the graph that defines all parent/child relationships. */\nexport interface TraceEntryNode {\n entry: Types.Events.Event;\n depth: number;\n selfTime?: Types.Timing.MicroSeconds;\n id: TraceEntryNodeId;\n parent: TraceEntryNode|null;\n children: TraceEntryNode[];\n}\n\nexport interface AINodeSerialized {\n name: string;\n dur?: number;\n self?: number;\n children?: AINodeSerialized[];\n url?: string;\n selected?: boolean;\n}\n\n/**\n * Node in a graph simplified for AI Assistance processing. The graph mirrors the TraceEntryNode one.\n * Huge tip of the hat to Victor Porof for prototyping this with some great work: https://crrev.com/c/5711249\n */\nexport class AINode {\n // event: Types.Events.Event; // Set in the constructor.\n name: string;\n duration?: Types.Timing.MilliSeconds;\n selfDuration?: Types.Timing.MilliSeconds;\n id?: TraceEntryNodeId;\n children?: AINode[];\n url?: string;\n selected?: boolean;\n\n constructor(public event: Types.Events.Event) {\n this.name = event.name;\n this.duration = event.dur === undefined ? undefined : microSecondsToMilliseconds(event.dur);\n\n if (Types.Events.isProfileCall(event)) {\n this.name = event.callFrame.functionName || '(anonymous)';\n this.url = event.callFrame.url;\n }\n }\n\n // Manually handle how nodes in this tree are serialized. We'll drop serveral properties that we don't need in the JSON string.\n // FYI: toJSON() is invoked implicitly via JSON.stringify()\n toJSON(): AINodeSerialized {\n return {\n selected: this.selected,\n name: this.name,\n url: this.url,\n // Round milliseconds because we don't need the precision\n dur: this.duration === undefined ? undefined : Math.round(this.duration * 10) / 10,\n self: this.selfDuration === undefined ? undefined : Math.round(this.selfDuration * 10) / 10,\n children: this.children?.length ? this.children : undefined,\n };\n }\n\n static #fromTraceEvent(event: Types.Events.Event): AINode {\n return new AINode(event);\n }\n\n /**\n * Builds a TraceEntryNodeForAI tree from a node and marks the selected node. Primary entrypoint from EntriesFilter\n */\n static fromEntryNode(selectedNode: TraceEntryNode, entryIsVisibleInTimeline: (event: Types.Events.Event) => boolean):\n AINode {\n /**\n * Builds a AINode tree from a TraceEntryNode tree and marks the selected node.\n */\n function fromEntryNodeAndTree(node: TraceEntryNode): AINode {\n const aiNode = AINode.#fromTraceEvent(node.entry);\n aiNode.id = node.id;\n if (node === selectedNode) {\n aiNode.selected = true;\n }\n aiNode.selfDuration = node.selfTime === undefined ? undefined : microSecondsToMilliseconds(node.selfTime);\n for (const child of node.children) {\n aiNode.children ??= [];\n aiNode.children.push(fromEntryNodeAndTree(child));\n }\n return aiNode;\n }\n\n function findTopMostVisibleAncestor(node: TraceEntryNode): TraceEntryNode {\n const parentNodes = [node];\n let parent = node.parent;\n while (parent) {\n parentNodes.unshift(parent);\n parent = parent.parent;\n }\n return parentNodes.find(node => entryIsVisibleInTimeline(node.entry)) ?? node;\n }\n\n const topMostVisibleRoot = findTopMostVisibleAncestor(selectedNode);\n const aiNode = fromEntryNodeAndTree(topMostVisibleRoot);\n\n // If our root wasn't visible, this could return an array of multiple RunTasks.\n // But with a visible root, we safely get back the exact same root, now with its descendent tree updated.\n // Filter to ensure our tree here only has \"visible\" entries\n const [filteredAiNodeRoot] = AINode.#filterRecursive([aiNode], node => {\n if (node.event.name === 'V8.CompileCode' || node.event.name === 'UpdateCounters') {\n return false;\n }\n return entryIsVisibleInTimeline(node.event);\n });\n return filteredAiNodeRoot;\n }\n\n static getSelectedNodeWithinTree(node: AINode): AINode|null {\n if (node.selected) {\n return node;\n }\n if (!node.children) {\n return null;\n }\n for (const child of node.children) {\n const returnedNode = AINode.getSelectedNodeWithinTree(child);\n if (returnedNode) {\n return returnedNode;\n }\n }\n return null;\n }\n\n static #filterRecursive(list: AINode[], predicate: (node: AINode) => boolean): AINode[] {\n let done;\n do {\n done = true;\n const filtered: AINode[] = [];\n for (const node of list) {\n if (predicate(node)) {\n // Keep it\n filtered.push(node);\n } else if (node.children) {\n filtered.push(...node.children);\n done = false;\n }\n }\n list = filtered;\n } while (!done);\n\n for (const node of list) {\n if (node.children) {\n node.children = AINode.#filterRecursive(node.children, predicate);\n }\n }\n return list;\n }\n\n static #removeInexpensiveNodesRecursively(\n list: AINode[],\n options?: {minDuration?: number, minSelf?: number, minJsDuration?: number, minJsSelf?: number}): AINode[] {\n const minDuration = options?.minDuration ?? 0;\n const minSelf = options?.minSelf ?? 0;\n const minJsDuration = options?.minJsDuration ?? 0;\n const minJsSelf = options?.minJsSelf ?? 0;\n\n const isJS = (node: AINode): boolean => Boolean(node.url);\n const longEnough = (node: AINode): boolean =>\n node.duration === undefined || node.duration >= (isJS(node) ? minJsDuration : minDuration);\n const selfLongEnough = (node: AINode): boolean =>\n node.selfDuration === undefined || node.selfDuration >= (isJS(node) ? minJsSelf : minSelf);\n\n return AINode.#filterRecursive(list, node => longEnough(node) && selfLongEnough(node));\n }\n\n // Invoked from DrJonesPerformanceAgent\n sanitize(): void {\n if (this.children) {\n this.children = AINode.#removeInexpensiveNodesRecursively(this.children, {\n minDuration: Types.Timing.MilliSeconds(1),\n minJsDuration: Types.Timing.MilliSeconds(1),\n minJsSelf: Types.Timing.MilliSeconds(0.1),\n });\n }\n }\n}\n\nclass TraceEntryNodeIdTag {\n /* eslint-disable-next-line no-unused-private-class-members */\n readonly #tag: (symbol|undefined);\n}\nexport type TraceEntryNodeId = number&TraceEntryNodeIdTag;\n\n/**\n * Builds a hierarchy of the entries (trace events and profile calls) in\n * a particular thread of a particular process, assuming that they're\n * sorted, by iterating through all of the events in order.\n *\n * The approach is analogous to how a parser would be implemented. A\n * stack maintains local context. A scanner peeks and pops from the data\n * stream. Various \"tokens\" (events) are treated as \"whitespace\"\n * (ignored).\n *\n * The tree starts out empty and is populated as the hierarchy is built.\n * The nodes are also assumed to be created empty, with no known parent\n * or children.\n *\n * Complexity: O(n), where n = number of events\n */\nexport function treify(entries: Types.Events.Event[], options?: {\n filter: {has: (name: Types.Events.Name) => boolean},\n}): {tree: TraceEntryTree, entryToNode: Map<Types.Events.Event, TraceEntryNode>} {\n // As we construct the tree, store a map of each entry to its node. This\n // means if you are iterating over a list of RendererEntry events you can\n // easily look up that node in the tree.\n const entryToNode = new Map<Types.Events.Event, TraceEntryNode>();\n\n const stack = [];\n // Reset the node id counter for every new renderer.\n nodeIdCount = -1;\n const tree = makeEmptyTraceEntryTree();\n\n for (let i = 0; i < entries.length; i++) {\n const event = entries[i];\n // If the current event should not be part of the tree, then simply proceed\n // with the next event.\n if (options && !options.filter.has(event.name as Types.Events.Name)) {\n continue;\n }\n\n const duration = event.dur || 0;\n const nodeId = makeTraceEntryNodeId();\n const node = makeEmptyTraceEntryNode(event, nodeId);\n\n // If the parent stack is empty, then the current event is a root. Create a\n // node for it, mark it as a root, then proceed with the next event.\n if (stack.length === 0) {\n tree.roots.add(node);\n node.selfTime = Types.Timing.MicroSeconds(duration);\n stack.push(node);\n tree.maxDepth = Math.max(tree.maxDepth, stack.length);\n entryToNode.set(event, node);\n continue;\n }\n\n const parentNode = stack.at(-1);\n if (parentNode === undefined) {\n throw new Error('Impossible: no parent node found in the stack');\n }\n\n const parentEvent = parentNode.entry;\n\n const begin = event.ts;\n const parentBegin = parentEvent.ts;\n const parentDuration = parentEvent.dur || 0;\n const end = begin + duration;\n const parentEnd = parentBegin + parentDuration;\n // Check the relationship between the parent event at the top of the stack,\n // and the current event being processed. There are only 4 distinct\n // possiblities, only 2 of them actually valid, given the assumed sorting:\n // 1. Current event starts before the parent event, ends whenever. (invalid)\n // 2. Current event starts after the parent event, ends whenever. (valid)\n // 3. Current event starts during the parent event, ends after. (invalid)\n // 4. Current event starts and ends during the parent event. (valid)\n\n // 1. If the current event starts before the parent event, then the data is\n // not sorted properly, messed up some way, or this logic is incomplete.\n const startsBeforeParent = begin < parentBegin;\n if (startsBeforeParent) {\n throw new Error('Impossible: current event starts before the parent event');\n }\n\n // 2. If the current event starts after the parent event, then it's a new\n // parent. Pop, then handle current event again.\n const startsAfterParent = begin >= parentEnd;\n if (startsAfterParent) {\n stack.pop();\n i--;\n // The last created node has been discarded, so discard this id.\n nodeIdCount--;\n continue;\n }\n // 3. If the current event starts during the parent event, but ends\n // after it, then the data is messed up some way, for example a\n // profile call was sampled too late after its start, ignore the\n // problematic event.\n const endsAfterParent = end > parentEnd;\n if (endsAfterParent) {\n continue;\n }\n\n // 4. The only remaining case is the common case, where the current event is\n // contained within the parent event. Create a node for the current\n // event, establish the parent/child relationship, then proceed with the\n // next event.\n node.depth = stack.length;\n node.parent = parentNode;\n parentNode.children.push(node);\n node.selfTime = Types.Timing.MicroSeconds(duration);\n if (parentNode.selfTime !== undefined) {\n parentNode.selfTime = Types.Timing.MicroSeconds(parentNode.selfTime - (event.dur || 0));\n }\n stack.push(node);\n tree.maxDepth = Math.max(tree.maxDepth, stack.length);\n entryToNode.set(event, node);\n }\n return {tree, entryToNode};\n}\n\n/**\n * Iterates events in a tree hierarchically, from top to bottom,\n * calling back on every event's start and end in the order\n * as it traverses down and then up the tree.\n *\n * For example, given this tree, the following callbacks\n * are expected to be made in the following order\n * |---------------A---------------|\n * |------B------||-------D------|\n * |---C---|\n *\n * 1. Start A\n * 3. Start B\n * 4. Start C\n * 5. End C\n * 6. End B\n * 7. Start D\n * 8. End D\n * 9. End A\n *\n */\nexport function walkTreeFromEntry(\n entryToNode: Map<Types.Events.Event, TraceEntryNode>,\n rootEntry: Types.Events.Event,\n onEntryStart: (entry: Types.Events.Event) => void,\n onEntryEnd: (entry: Types.Events.Event) => void,\n ): void {\n const startNode = entryToNode.get(rootEntry);\n if (!startNode) {\n return;\n }\n walkTreeByNode(entryToNode, startNode, onEntryStart, onEntryEnd);\n}\n\n/**\n * Given a Helpers.TreeHelpers.RendererTree, this will iterates events in hierarchically, visiting\n * each root node and working from top to bottom, calling back on every event's\n * start and end in the order as it traverses down and then up the tree.\n *\n * For example, given this tree, the following callbacks\n * are expected to be made in the following order\n * |------------- Task A -------------||-- Task E --|\n * |-- Task B --||-- Task D --|\n * |- Task C -|\n *\n * 1. Start A\n * 3. Start B\n * 4. Start C\n * 5. End C\n * 6. End B\n * 7. Start D\n * 8. End D\n * 9. End A\n * 10. Start E\n * 11. End E\n *\n */\n\nexport function walkEntireTree(\n entryToNode: Map<Types.Events.Event, TraceEntryNode>,\n tree: TraceEntryTree,\n onEntryStart: (entry: Types.Events.Event) => void,\n onEntryEnd: (entry: Types.Events.Event) => void,\n traceWindowToInclude?: Types.Timing.TraceWindowMicroSeconds,\n minDuration?: Types.Timing.MicroSeconds,\n ): void {\n for (const rootNode of tree.roots) {\n walkTreeByNode(entryToNode, rootNode, onEntryStart, onEntryEnd, traceWindowToInclude, minDuration);\n }\n}\n\nfunction walkTreeByNode(\n entryToNode: Map<Types.Events.Event, TraceEntryNode>,\n rootNode: TraceEntryNode,\n onEntryStart: (entry: Types.Events.Event) => void,\n onEntryEnd: (entry: Types.Events.Event) => void,\n traceWindowToInclude?: Types.Timing.TraceWindowMicroSeconds,\n minDuration?: Types.Timing.MicroSeconds,\n ): void {\n if (traceWindowToInclude && !treeNodeIsInWindow(rootNode, traceWindowToInclude)) {\n // If this node is not within the provided window, we can skip it. We also\n // can skip all its children too, as we know they won't be in the window if\n // their parent is not.\n return;\n }\n\n if (typeof minDuration !== 'undefined') {\n const duration = Types.Timing.MicroSeconds(\n rootNode.entry.ts + Types.Timing.MicroSeconds(rootNode.entry.dur ?? 0),\n );\n if (duration < minDuration) {\n return;\n }\n }\n\n onEntryStart(rootNode.entry);\n for (const child of rootNode.children) {\n walkTreeByNode(entryToNode, child, onEntryStart, onEntryEnd, traceWindowToInclude, minDuration);\n }\n onEntryEnd(rootNode.entry);\n}\n\n/**\n * Returns true if the provided node is partially or fully within the trace\n * window. The entire node does not have to fit inside the window, but it does\n * have to partially intersect it.\n */\nfunction treeNodeIsInWindow(node: TraceEntryNode, traceWindow: Types.Timing.TraceWindowMicroSeconds): boolean {\n return eventIsInBounds(node.entry, traceWindow);\n}\n\n/**\n * Determines if the given events, which are assumed to be ordered can\n * be organized into tree structures.\n * This condition is met if there is *not* a pair of async events\n * e1 and e2 where:\n *\n * e1.startTime < e2.startTime && e1.endTime > e2.startTime && e1.endTime < e2.endTime.\n * or, graphically:\n * |------- e1 ------|\n * |------- e2 --------|\n *\n * Because a parent-child relationship cannot be made from the example\n * above, a tree cannot be made from the set of events.\n *\n * Sync events from the same thread are tree-able by definition.\n *\n * Note that this will also return true if multiple trees can be\n * built, for example if none of the events overlap with each other.\n */\nexport function canBuildTreesFromEvents(events: readonly Types.Events.Event[]): boolean {\n const stack: Types.Events.Event[] = [];\n for (const event of events) {\n const startTime = event.ts;\n const endTime = event.ts + (event.dur ?? 0);\n let parent = stack.at(-1);\n if (parent === undefined) {\n stack.push(event);\n continue;\n }\n let parentEndTime = parent.ts + (parent.dur ?? 0);\n // Discard events that are not parents for this event. The parent\n // is one whose end time is after this event start time.\n while (stack.length && startTime >= parentEndTime) {\n stack.pop();\n parent = stack.at(-1);\n\n if (parent === undefined) {\n break;\n }\n parentEndTime = parent.ts + (parent.dur ?? 0);\n }\n if (stack.length && endTime > parentEndTime) {\n // If such an event exists but its end time is before this\n // event's end time, then a tree cannot be made using this\n // events.\n return false;\n }\n stack.push(event);\n }\n return true;\n}\n"]}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type * as Protocol from '../../../generated/protocol.js';
|
|
1
2
|
import * as Types from '../types/types.js';
|
|
2
3
|
import type { InsightResult, InsightSetContext, RequiredData } from './types.js';
|
|
3
4
|
export type CLSInsightResult = InsightResult<{
|
|
@@ -50,6 +51,7 @@ export interface LayoutShiftRootCausesData {
|
|
|
50
51
|
iframeIds: string[];
|
|
51
52
|
fontRequests: Types.Events.SyntheticNetworkRequest[];
|
|
52
53
|
nonCompositedAnimations: NoncompositedAnimationFailure[];
|
|
54
|
+
unsizedImages: Protocol.DOM.BackendNodeId[];
|
|
53
55
|
}
|
|
54
56
|
export declare function getNonCompositedFailure(animationEvent: Types.Events.SyntheticAnimationPair): NoncompositedAnimationFailure[];
|
|
55
57
|
export declare function generateInsight(parsedTrace: RequiredData<typeof deps>, context: InsightSetContext): CLSInsightResult;
|
|
@@ -91,10 +91,15 @@ const ACTIONABLE_FAILURE_REASONS = [
|
|
|
91
91
|
];
|
|
92
92
|
// 500ms window.
|
|
93
93
|
// Use this window to consider events and requests that may have caused a layout shift.
|
|
94
|
-
const
|
|
95
|
-
|
|
94
|
+
const ROOT_CAUSE_WINDOW = Helpers.Timing.secondsToMicroseconds(Types.Timing.Seconds(0.5));
|
|
95
|
+
/**
|
|
96
|
+
* Returns if an event happens within the root cause window, before the target event.
|
|
97
|
+
* ROOT_CAUSE_WINDOW v target event
|
|
98
|
+
* |------------------------|=======================
|
|
99
|
+
*/
|
|
100
|
+
function isInRootCauseWindow(event, targetEvent) {
|
|
96
101
|
const eventEnd = event.dur ? event.ts + event.dur : event.ts;
|
|
97
|
-
return eventEnd < targetEvent.ts && eventEnd >= targetEvent.ts -
|
|
102
|
+
return eventEnd < targetEvent.ts && eventEnd >= targetEvent.ts - ROOT_CAUSE_WINDOW;
|
|
98
103
|
}
|
|
99
104
|
export function getNonCompositedFailure(animationEvent) {
|
|
100
105
|
const failures = [];
|
|
@@ -133,13 +138,13 @@ function getNonCompositedFailureRootCauses(animationEvents, prePaintEvents, shif
|
|
|
133
138
|
continue;
|
|
134
139
|
}
|
|
135
140
|
allAnimationFailures.push(...failures);
|
|
136
|
-
const nextPrePaint =
|
|
141
|
+
const nextPrePaint = getNextEvent(prePaintEvents, animation);
|
|
137
142
|
// If no following prePaint, this is not a root cause.
|
|
138
143
|
if (!nextPrePaint) {
|
|
139
144
|
continue;
|
|
140
145
|
}
|
|
141
|
-
// If the animation event is outside the
|
|
142
|
-
if (!
|
|
146
|
+
// If the animation event is outside the ROOT_CAUSE_WINDOW, it could not be a root cause.
|
|
147
|
+
if (!isInRootCauseWindow(animation, nextPrePaint)) {
|
|
143
148
|
continue;
|
|
144
149
|
}
|
|
145
150
|
const shifts = shiftsByPrePaint.get(nextPrePaint);
|
|
@@ -186,16 +191,15 @@ function getShiftsByPrePaintEvents(layoutShifts, prePaintEvents) {
|
|
|
186
191
|
return shiftsByPrePaint;
|
|
187
192
|
}
|
|
188
193
|
/**
|
|
189
|
-
*
|
|
194
|
+
* Given a source event list, this returns the first event of that list that directly follows the target event.
|
|
190
195
|
*/
|
|
191
|
-
function
|
|
192
|
-
|
|
193
|
-
const nextPrePaintIndex = Platform.ArrayUtilities.nearestIndexFromBeginning(prePaintEvents, prePaint => prePaint.ts > targetEvent.ts + (targetEvent.dur || 0));
|
|
196
|
+
function getNextEvent(sourceEvents, targetEvent) {
|
|
197
|
+
const index = Platform.ArrayUtilities.nearestIndexFromBeginning(sourceEvents, source => source.ts > targetEvent.ts + (targetEvent.dur || 0));
|
|
194
198
|
// No PrePaint event registered after this event
|
|
195
|
-
if (
|
|
199
|
+
if (index === null) {
|
|
196
200
|
return undefined;
|
|
197
201
|
}
|
|
198
|
-
return
|
|
202
|
+
return sourceEvents[index];
|
|
199
203
|
}
|
|
200
204
|
/**
|
|
201
205
|
* An Iframe is considered a root cause if the iframe event occurs before a prePaint event
|
|
@@ -203,7 +207,7 @@ function getNextPrePaintEvent(prePaintEvents, targetEvent) {
|
|
|
203
207
|
*/
|
|
204
208
|
function getIframeRootCauses(iframeCreatedEvents, prePaintEvents, shiftsByPrePaint, rootCausesByShift, domLoadingEvents) {
|
|
205
209
|
for (const iframeEvent of iframeCreatedEvents) {
|
|
206
|
-
const nextPrePaint =
|
|
210
|
+
const nextPrePaint = getNextEvent(prePaintEvents, iframeEvent);
|
|
207
211
|
// If no following prePaint, this is not a root cause.
|
|
208
212
|
if (!nextPrePaint) {
|
|
209
213
|
continue;
|
|
@@ -231,20 +235,47 @@ function getIframeRootCauses(iframeCreatedEvents, prePaintEvents, shiftsByPrePai
|
|
|
231
235
|
}
|
|
232
236
|
return rootCausesByShift;
|
|
233
237
|
}
|
|
238
|
+
/**
|
|
239
|
+
* An unsized image is considered a root cause if its PaintImage can be correlated to a
|
|
240
|
+
* layout shift. We can correlate PaintImages with unsized images by their matching nodeIds.
|
|
241
|
+
* X <- layout shift
|
|
242
|
+
* |----------------|
|
|
243
|
+
* ^ PrePaint event |-----|
|
|
244
|
+
* ^ PaintImage
|
|
245
|
+
*/
|
|
246
|
+
function getUnsizedImageRootCauses(unsizedImageEvents, paintImageEvents, shiftsByPrePaint, rootCausesByShift) {
|
|
247
|
+
shiftsByPrePaint.forEach((shifts, prePaint) => {
|
|
248
|
+
const paintImage = getNextEvent(paintImageEvents, prePaint);
|
|
249
|
+
// The unsized image corresponds to this PaintImage.
|
|
250
|
+
const matchingNode = unsizedImageEvents.find(unsizedImage => unsizedImage.args.data.nodeId === paintImage?.args.data.nodeId);
|
|
251
|
+
if (!matchingNode) {
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
// The unsized image is a potential root cause of all the shifts of this prePaint.
|
|
255
|
+
for (const shift of shifts) {
|
|
256
|
+
const rootCausesForShift = rootCausesByShift.get(shift);
|
|
257
|
+
if (!rootCausesForShift) {
|
|
258
|
+
throw new Error('Unaccounted shift');
|
|
259
|
+
}
|
|
260
|
+
rootCausesForShift.unsizedImages.push(matchingNode.args.data.nodeId);
|
|
261
|
+
}
|
|
262
|
+
});
|
|
263
|
+
return rootCausesByShift;
|
|
264
|
+
}
|
|
234
265
|
/**
|
|
235
266
|
* A font request is considered a root cause if the request occurs before a prePaint event
|
|
236
267
|
* and within this prePaint event a layout shift(s) occurs. Additionally, this font request should
|
|
237
|
-
* happen within the
|
|
268
|
+
* happen within the ROOT_CAUSE_WINDOW of the prePaint event.
|
|
238
269
|
*/
|
|
239
270
|
function getFontRootCauses(networkRequests, prePaintEvents, shiftsByPrePaint, rootCausesByShift) {
|
|
240
271
|
const fontRequests = networkRequests.filter(req => req.args.data.resourceType === 'Font' && req.args.data.mimeType.startsWith('font'));
|
|
241
272
|
for (const req of fontRequests) {
|
|
242
|
-
const nextPrePaint =
|
|
273
|
+
const nextPrePaint = getNextEvent(prePaintEvents, req);
|
|
243
274
|
if (!nextPrePaint) {
|
|
244
275
|
continue;
|
|
245
276
|
}
|
|
246
|
-
// If the req is outside the
|
|
247
|
-
if (!
|
|
277
|
+
// If the req is outside the ROOT_CAUSE_WINDOW, it could not be a root cause.
|
|
278
|
+
if (!isInRootCauseWindow(req, nextPrePaint)) {
|
|
248
279
|
continue;
|
|
249
280
|
}
|
|
250
281
|
// Get the shifts that belong to this prepaint
|
|
@@ -270,21 +301,24 @@ export function generateInsight(parsedTrace, context) {
|
|
|
270
301
|
const iframeEvents = parsedTrace.LayoutShifts.renderFrameImplCreateChildFrameEvents.filter(isWithinContext);
|
|
271
302
|
const networkRequests = parsedTrace.NetworkRequests.byTime.filter(isWithinContext);
|
|
272
303
|
const domLoadingEvents = parsedTrace.LayoutShifts.domLoadingEvents.filter(isWithinContext);
|
|
304
|
+
const unsizedImageEvents = parsedTrace.LayoutShifts.layoutImageUnsizedEvents.filter(isWithinContext);
|
|
273
305
|
const clusterKey = context.navigation ? context.navigationId : Types.Events.NO_NAVIGATION;
|
|
274
306
|
const clusters = parsedTrace.LayoutShifts.clustersByNavigationId.get(clusterKey) ?? [];
|
|
275
307
|
const clustersByScore = clusters.toSorted((a, b) => b.clusterCumulativeScore - a.clusterCumulativeScore);
|
|
276
308
|
const worstCluster = clustersByScore.at(0);
|
|
277
309
|
const layoutShifts = clusters.flatMap(cluster => cluster.events);
|
|
278
310
|
const prePaintEvents = parsedTrace.LayoutShifts.prePaintEvents.filter(isWithinContext);
|
|
311
|
+
const paintImageEvents = parsedTrace.LayoutShifts.paintImageEvents.filter(isWithinContext);
|
|
279
312
|
// Get root causes.
|
|
280
313
|
const rootCausesByShift = new Map();
|
|
281
314
|
const shiftsByPrePaint = getShiftsByPrePaintEvents(layoutShifts, prePaintEvents);
|
|
282
315
|
for (const shift of layoutShifts) {
|
|
283
|
-
rootCausesByShift.set(shift, { iframeIds: [], fontRequests: [], nonCompositedAnimations: [] });
|
|
316
|
+
rootCausesByShift.set(shift, { iframeIds: [], fontRequests: [], nonCompositedAnimations: [], unsizedImages: [] });
|
|
284
317
|
}
|
|
285
318
|
// Populate root causes for rootCausesByShift.
|
|
286
319
|
getIframeRootCauses(iframeEvents, prePaintEvents, shiftsByPrePaint, rootCausesByShift, domLoadingEvents);
|
|
287
320
|
getFontRootCauses(networkRequests, prePaintEvents, shiftsByPrePaint, rootCausesByShift);
|
|
321
|
+
getUnsizedImageRootCauses(unsizedImageEvents, paintImageEvents, shiftsByPrePaint, rootCausesByShift);
|
|
288
322
|
const animationFailures = getNonCompositedFailureRootCauses(compositeAnimationEvents, prePaintEvents, shiftsByPrePaint, rootCausesByShift);
|
|
289
323
|
const relatedEvents = [...layoutShifts];
|
|
290
324
|
if (worstCluster) {
|