Skip to content

Commit 62e1652

Browse files
jackfranklinDevtools-frontend LUCI CQ
authored andcommitted
RPP: guard against CruxManager errors
[email protected] Bug: 441265851 Change-Id: I6b133db41dc6fcced066136fe47f7669b92b823d Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/6953785 Commit-Queue: Jack Franklin <[email protected]> Reviewed-by: Alex Rudenko <[email protected]> Auto-Submit: Jack Franklin <[email protected]> Commit-Queue: Alex Rudenko <[email protected]>
1 parent aa5f452 commit 62e1652

File tree

3 files changed

+152
-58
lines changed

3 files changed

+152
-58
lines changed

front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.snapshot.txt

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -919,6 +919,70 @@ Available insights:
919919
example question: How can I reduce the amount of legacy JavaScript on my page?
920920
=== end content
921921

922+
Title: PerformanceTraceFormatter formatTraceSummary deals with CrUX manager errors
923+
Content:
924+
URL: http://localhost/image-delivery-cases.html
925+
Bounds: {min: 59728641874, max: 59734400108}
926+
CPU throttling: 1x
927+
Network throttling: No throttling
928+
Metrics (lab / observed):
929+
- LCP: 663 ms, event: (eventKey: r-14753, ts: 59729312744)
930+
- LCP breakdown:
931+
- TTFB: 7 ms, bounds: {min: 59728649746, max: 59728656735}
932+
- Load delay: 45.6 ms, bounds: {min: 59728656735, max: 59728702313}
933+
- Load duration: 506.3 ms, bounds: {min: 59728702313, max: 59729208600}
934+
- Render delay: 104.1 ms, bounds: {min: 59729208600, max: 59729312744}
935+
- CLS: 0.31, event: (eventKey: s--1, ts: 59729100192)
936+
Metrics (field / real users): n/a – no data for this page in CrUX
937+
Available insights:
938+
- insight name: Cache
939+
description: A long cache lifetime can speed up repeat visits to your page. [Learn more](https://web.dev/uses-long-cache-ttl/).
940+
relevant trace bounds: {min: 59728702014, max: 59728867430}
941+
estimated metric savings: FCP 0 ms, LCP 150 ms
942+
estimated wasted bytes: 971.5 kB
943+
example question: What caching strategies can I apply to improve my page performance?
944+
- insight name: ImageDelivery
945+
description: Reducing the download time of images can improve the perceived load time of the page and LCP. [Learn more about optimizing image size](https://developer.chrome.com/docs/lighthouse/performance/uses-optimized-images/)
946+
relevant trace bounds: {min: 59728701403, max: 59729395208}
947+
estimated metric savings: FCP 0 ms, LCP 100 ms
948+
estimated wasted bytes: 2 MB
949+
example question: What should I do to improve and optimize the time taken to fetch and display images on the page?
950+
example question: Are all images on my site optimized?
951+
- insight name: LCPBreakdown
952+
description: Each [subpart has specific improvement strategies](https://web.dev/articles/optimize-lcp#lcp-breakdown). Ideally, most of the LCP time should be spent on loading the resources, not within delays.
953+
relevant trace bounds: {min: 59728649746, max: 59729312744}
954+
example question: Help me optimize my LCP score
955+
example question: Which LCP phase was most problematic?
956+
example question: What can I do to reduce the LCP time for this page load?
957+
- insight name: LCPDiscovery
958+
description: Optimize LCP by making the LCP image [discoverable](https://web.dev/articles/optimize-lcp#1_eliminate_resource_load_delay) from the HTML immediately, and [avoiding lazy-loading](https://web.dev/articles/lcp-lazy-loading)
959+
relevant trace bounds: {min: 59728656735, max: 59729209036}
960+
example question: Suggest fixes to reduce my LCP
961+
example question: What can I do to reduce my LCP discovery time?
962+
example question: Why is LCP discovery time important?
963+
- insight name: CLSCulprits
964+
description: Layout shifts occur when elements move absent any user interaction. [Investigate the causes of layout shifts](https://web.dev/articles/optimize-cls), such as elements being added, removed, or their fonts changing as the page loads.
965+
relevant trace bounds: {min: 59729100192, max: 59730317334}
966+
example question: Help me optimize my CLS score
967+
example question: How can I prevent layout shifts on this page?
968+
- insight name: DocumentLatency
969+
description: Your first network request is the most important. Reduce its latency by avoiding redirects, ensuring a fast server response, and enabling text compression.
970+
relevant trace bounds: {min: 59728651057, max: 59728790724}
971+
estimated metric savings: FCP 0 ms, LCP 0 ms
972+
estimated wasted bytes: 1.6 kB
973+
example question: How do I decrease the initial loading time of my page?
974+
example question: Did anything slow down the request for this document?
975+
- insight name: Viewport
976+
description: Tap interactions may be [delayed by up to 300 ms](https://developer.chrome.com/blog/300ms-tap-delay-gone-away/) if the viewport is not optimized for mobile.
977+
relevant trace bounds: {min: 59728649746, max: 59734400108}
978+
estimated metric savings: INP 0 ms
979+
example question: How do I make sure my page is optimized for mobile viewing?
980+
- insight name: ThirdParties
981+
description: 3rd party code can significantly impact load performance. [Reduce and defer loading of 3rd party code](https://web.dev/articles/optimizing-content-efficiency-loading-third-party-javascript/) to prioritize your page's content.
982+
relevant trace bounds: {min: 59728701403, max: 59729465969}
983+
example question: Which third parties are having the largest impact on my page performance?
984+
=== end content
985+
922986
Title: PerformanceTraceFormatter formatTraceSummary image-delivery.json.gz
923987
Content:
924988
URL: http://localhost/image-delivery-cases.html

front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.test.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
44

5+
import * as CrUXManager from '../../../models/crux-manager/crux-manager.js';
56
import * as Trace from '../../../models/trace/trace.js';
67
import {describeWithEnvironment} from '../../../testing/EnvironmentHelpers.js';
78
import {SnapshotTester} from '../../../testing/SnapshotTester.js';
@@ -42,6 +43,15 @@ describeWithEnvironment('PerformanceTraceFormatter', () => {
4243
snapshotTester.assert(this, output);
4344
});
4445

46+
it('deals with CrUX manager errors', async function() {
47+
const {formatter} = await createFormatter(this, 'image-delivery.json.gz');
48+
sinon.stub(CrUXManager.CrUXManager, 'instance').callsFake(() => {
49+
throw new Error('something went wrong with CrUX Manager');
50+
});
51+
const output = formatter.formatTraceSummary();
52+
snapshotTester.assert(this, output);
53+
});
54+
4555
// This one has field data.
4656
it('image-delivery.json.gz', async function() {
4757
const {formatter} = await createFormatter(this, 'image-delivery.json.gz');

front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.ts

Lines changed: 78 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,81 @@ export class PerformanceTraceFormatter {
3131
return `{min: ${bounds.min}, max: ${bounds.max}}`;
3232
}
3333

34+
/**
35+
* Fetching the Crux summary can error outside of DevTools, hence the
36+
* try-catch around it here.
37+
*/
38+
#getCruxTraceSummary(insightSet: Trace.Insights.Types.InsightSet|null): string[] {
39+
if (insightSet === null) {
40+
return [];
41+
}
42+
try {
43+
const cruxScope = CrUXManager.CrUXManager.instance().getSelectedScope();
44+
const parts: string[] = [];
45+
const fieldMetrics =
46+
Trace.Insights.Common.getFieldMetricsForInsightSet(insightSet, this.#parsedTrace.metadata, cruxScope);
47+
const fieldLcp = fieldMetrics?.lcp;
48+
const fieldInp = fieldMetrics?.inp;
49+
const fieldCls = fieldMetrics?.cls;
50+
51+
if (fieldLcp || fieldInp || fieldCls) {
52+
parts.push('Metrics (field / real users):');
53+
54+
const serializeFieldMetricTimingResult =
55+
(fieldMetric: Trace.Insights.Common.CrUXFieldMetricTimingResult): string => {
56+
return `${Math.round(fieldMetric.value / 1000)} ms (scope: ${fieldMetric.pageScope})`;
57+
};
58+
59+
const serializeFieldMetricNumberResult =
60+
(fieldMetric: Trace.Insights.Common.CrUXFieldMetricNumberResult): string => {
61+
return `${fieldMetric.value.toFixed(2)} (scope: ${fieldMetric.pageScope})`;
62+
};
63+
64+
if (fieldLcp) {
65+
parts.push(` - LCP: ${serializeFieldMetricTimingResult(fieldLcp)}`);
66+
67+
const fieldLcpBreakdown = fieldMetrics?.lcpBreakdown;
68+
if (fieldLcpBreakdown &&
69+
(fieldLcpBreakdown.ttfb || fieldLcpBreakdown.loadDelay || fieldLcpBreakdown.loadDuration ||
70+
fieldLcpBreakdown.renderDelay)) {
71+
parts.push(' - LCP breakdown:');
72+
if (fieldLcpBreakdown.ttfb) {
73+
parts.push(` - TTFB: ${serializeFieldMetricTimingResult(fieldLcpBreakdown.ttfb)}`);
74+
}
75+
if (fieldLcpBreakdown.loadDelay) {
76+
parts.push(` - Load delay: ${serializeFieldMetricTimingResult(fieldLcpBreakdown.loadDelay)}`);
77+
}
78+
if (fieldLcpBreakdown.loadDuration) {
79+
parts.push(` - Load duration: ${serializeFieldMetricTimingResult(fieldLcpBreakdown.loadDuration)}`);
80+
}
81+
if (fieldLcpBreakdown.renderDelay) {
82+
parts.push(` - Render delay: ${serializeFieldMetricTimingResult(fieldLcpBreakdown.renderDelay)}`);
83+
}
84+
}
85+
}
86+
if (fieldInp) {
87+
parts.push(` - INP: ${serializeFieldMetricTimingResult(fieldInp)}`);
88+
}
89+
if (fieldCls) {
90+
parts.push(` - CLS: ${serializeFieldMetricNumberResult(fieldCls)}`);
91+
}
92+
93+
parts.push(
94+
' - The above data is from CrUX–Chrome User Experience Report. It\'s how the page performs for real users.');
95+
parts.push(' - The values shown above are the p75 measure of all real Chrome users');
96+
parts.push(' - The scope indicates if the data came from the entire origin, or a specific url');
97+
parts.push(
98+
' - Lab metrics describe how this specific page load performed, while field metrics are an aggregation ' +
99+
'of results from real-world users. Best practice is to prioritize metrics that are bad in field data. ' +
100+
'Lab metrics may be better or worse than fields metrics depending on the developer\'s machine, network, or the ' +
101+
'actions performed while tracing.');
102+
}
103+
return parts;
104+
} catch {
105+
return [];
106+
}
107+
}
108+
34109
formatTraceSummary(): string {
35110
const parsedTrace = this.#parsedTrace;
36111
const insightSet = this.#insightSet;
@@ -78,64 +153,9 @@ export class PerformanceTraceFormatter {
78153
parts.push('Metrics (lab / observed): n/a');
79154
}
80155

81-
const fieldMetrics = insightSet &&
82-
Trace.Insights.Common.getFieldMetricsForInsightSet(
83-
insightSet, traceMetadata, CrUXManager.CrUXManager.instance().getSelectedScope());
84-
const fieldLcp = fieldMetrics?.lcp;
85-
const fieldInp = fieldMetrics?.inp;
86-
const fieldCls = fieldMetrics?.cls;
87-
88-
if (fieldLcp || fieldInp || fieldCls) {
89-
parts.push('Metrics (field / real users):');
90-
91-
const serializeFieldMetricTimingResult =
92-
(fieldMetric: Trace.Insights.Common.CrUXFieldMetricTimingResult): string => {
93-
return `${Math.round(fieldMetric.value / 1000)} ms (scope: ${fieldMetric.pageScope})`;
94-
};
95-
96-
const serializeFieldMetricNumberResult =
97-
(fieldMetric: Trace.Insights.Common.CrUXFieldMetricNumberResult): string => {
98-
return `${fieldMetric.value.toFixed(2)} (scope: ${fieldMetric.pageScope})`;
99-
};
100-
101-
if (fieldLcp) {
102-
parts.push(` - LCP: ${serializeFieldMetricTimingResult(fieldLcp)}`);
103-
104-
const fieldLcpBreakdown = fieldMetrics?.lcpBreakdown;
105-
if (fieldLcpBreakdown &&
106-
(fieldLcpBreakdown.ttfb || fieldLcpBreakdown.loadDelay || fieldLcpBreakdown.loadDuration ||
107-
fieldLcpBreakdown.renderDelay)) {
108-
parts.push(' - LCP breakdown:');
109-
if (fieldLcpBreakdown.ttfb) {
110-
parts.push(` - TTFB: ${serializeFieldMetricTimingResult(fieldLcpBreakdown.ttfb)}`);
111-
}
112-
if (fieldLcpBreakdown.loadDelay) {
113-
parts.push(` - Load delay: ${serializeFieldMetricTimingResult(fieldLcpBreakdown.loadDelay)}`);
114-
}
115-
if (fieldLcpBreakdown.loadDuration) {
116-
parts.push(` - Load duration: ${serializeFieldMetricTimingResult(fieldLcpBreakdown.loadDuration)}`);
117-
}
118-
if (fieldLcpBreakdown.renderDelay) {
119-
parts.push(` - Render delay: ${serializeFieldMetricTimingResult(fieldLcpBreakdown.renderDelay)}`);
120-
}
121-
}
122-
}
123-
if (fieldInp) {
124-
parts.push(` - INP: ${serializeFieldMetricTimingResult(fieldInp)}`);
125-
}
126-
if (fieldCls) {
127-
parts.push(` - CLS: ${serializeFieldMetricNumberResult(fieldCls)}`);
128-
}
129-
130-
parts.push(
131-
' - The above data is from CrUX–Chrome User Experience Report. It\'s how the page performs for real users.');
132-
parts.push(' - The values shown above are the p75 measure of all real Chrome users');
133-
parts.push(' - The scope indicates if the data came from the entire origin, or a specific url');
134-
parts.push(
135-
' - Lab metrics describe how this specific page load performed, while field metrics are an aggregation ' +
136-
'of results from real-world users. Best practice is to prioritize metrics that are bad in field data. ' +
137-
'Lab metrics may be better or worse than fields metrics depending on the developer\'s machine, network, or the ' +
138-
'actions performed while tracing.');
156+
const cruxParts = insightSet && this.#getCruxTraceSummary(insightSet);
157+
if (cruxParts?.length) {
158+
parts.push(...cruxParts);
139159
} else {
140160
parts.push('Metrics (field / real users): n/a – no data for this page in CrUX');
141161
}

0 commit comments

Comments
 (0)