-
Notifications
You must be signed in to change notification settings - Fork 9.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
misc: lantern trace collection script #9662
Merged
Merged
Changes from 108 commits
Commits
Show all changes
109 commits
Select commit
Hold shift + click to select a range
a261919
init
connorjclark 92ea971
first pass
connorjclark f4441aa
tracePath
connorjclark 20c2709
remove task stuff
connorjclark 419151a
progress stuff
connorjclark 1423a95
ProgressLogger
connorjclark 6565644
urls. 9
connorjclark 80b0e58
move to folder, readme, urls.json
connorjclark a4c7b58
comments
connorjclark 8badb3f
eslint
connorjclark 4104f8b
archive
connorjclark c25babd
readme
connorjclark 475c6eb
comments
connorjclark f937608
pr
connorjclark 2738755
log skipped
connorjclark 22bb27a
wpt/unthrottled
connorjclark ae5481e
use while
connorjclark 1b8d529
Update lighthouse-core/scripts/lantern/collect/collect.js
connorjclark 32e9c41
comment
connorjclark 0e23c7b
remove path join
connorjclark 7012e86
pr
connorjclark d62c64b
exec async
connorjclark c4a5bc1
pr
connorjclark a9f48c9
pr
connorjclark 1f02506
archive
connorjclark 5d82760
lhr
connorjclark ef1566e
devtools log
connorjclark 71c100f
max buffer. filestub is insecure
connorjclark 68b06d7
readme
connorjclark f9fe259
sorta organize
connorjclark 1ee49a2
update list
connorjclark d134c52
urls
connorjclark a9d2b7e
10s
connorjclark 23e4306
403
connorjclark 0992893
urls
connorjclark f221897
delete bad urls
connorjclark dc0331b
birds arent real
connorjclark 9e47e8e
sort
connorjclark b4982d2
cool sites
connorjclark 4b49fe8
sites
connorjclark 53a91cc
banjo
connorjclark 3207ac1
chrome beta
connorjclark 89dae69
remove stale comment
connorjclark c52b519
better waiting
connorjclark b50784f
download link
connorjclark 68503ba
summary
connorjclark de61ec2
remove bad urls
connorjclark 0acf633
golden
connorjclark e07e3df
common.js
connorjclark 60a2576
golden
connorjclark a7c2d36
logger
connorjclark 415199a
fix emptry trace event
connorjclark 2e9a4c7
fix collect
connorjclark dce6140
tsc
connorjclark 1f4f51a
add metrics dump to golden.json
connorjclark a664425
update zips
connorjclark 9c84f91
changes
connorjclark 2a6bcd3
use public drive links
connorjclark 5a8fea2
fix wpt query
connorjclark a8b3bf7
rm custom connectivity params
connorjclark 71a6a87
update location
connorjclark 09319e2
fix
connorjclark ecb3c06
Merge remote-tracking branch 'origin/master' into lantern-collect
connorjclark ebe0cf9
rm url that always timesout wpt
connorjclark e5b2915
use golden format that lantern expects
connorjclark af618eb
fix metrics
connorjclark c604758
Merge remote-tracking branch 'origin/master' into lantern-collect
connorjclark 336b1d2
links
connorjclark 3cae321
moto4
connorjclark 20206d9
Merge remote-tracking branch 'origin/master' into lantern-collect
connorjclark aab7f40
oopif flag
connorjclark 06e14fc
mkdirp
connorjclark 75bdecd
use stable
connorjclark 3fe1f2b
Merge remote-tracking branch 'origin/master' into lantern-collect
connorjclark ae65e32
use beta
connorjclark 7182a26
disable throttling
connorjclark 36f3432
fix 302s
connorjclark 01ddf2f
retry if no metrics. use fmp instead of tti for median
connorjclark 97bea1a
tweak percentiles
connorjclark 161ef23
wait for wpt before doing local
connorjclark 12b0d43
mkdirp
connorjclark 0337100
don't hit the api so hard
connorjclark 30d7ef9
make all wpt requests at same time
connorjclark 270676c
Merge remote-tracking branch 'origin/master' into lantern-collect
connorjclark 90725b5
dont wait to poll more than 10 min
connorjclark 4fa8939
Merge branch 'lantern-collect' of github.com:GoogleChrome/lighthouse …
connorjclark e8b04d0
remove url that never finishes
connorjclark 1063c4d
no oopifs by default
connorjclark 9b3675d
Merge branch 'master' into lantern-collect
patrickhulce 43efd9f
Revert "make all wpt requests at same time"
patrickhulce 4c7a63f
patrick's tweaks to lantern collection script (#9980)
patrickhulce e7afdec
check metrics, use Chrome stable, record screenshots
connorjclark 46a3804
nit
connorjclark 3809a24
tweaks
connorjclark 472b977
tmp
connorjclark 4345fe5
persist warnings in summary
connorjclark b16f6b2
docs on gcp
connorjclark 7000578
Merge remote-tracking branch 'origin/master' into lantern-collect
connorjclark 75ef38c
pipefail
connorjclark 210b614
repeat until works
connorjclark 0ca1fee
add --project
connorjclark da264dd
gsutil
connorjclark 8f6b889
travis just lantern
connorjclark b7e6661
update
connorjclark c94beda
works now
connorjclark 39b5f6c
fix logic
connorjclark 17b6478
fix bash
connorjclark 7c1518e
curl
connorjclark 04e8b5a
delete prompt
connorjclark File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
# Lantern Collect Traces | ||
|
||
Collects many traces using a local machine and mobile devices via WPT. | ||
|
||
There are 9 runs for each URL in the big zip. The golden zip contains just the median runs (by performance score), along with a dump of the `metrics` collected by Lighthouse. | ||
|
||
[Download all](https://drive.google.com/open?id=17WsQ3CU0R1072sezXw5Np2knV_NvGAfO) traces (3.2GB zipped, 19GB unzipped). | ||
[Download golden](https://drive.google.com/open?id=1aQp-oqX7jeFq9RFwNik6gkEZ0FLtjlHp) traces (363MB zipped, 2.1GB unzipped). | ||
|
||
Note: Only 45/80 of the URLs in `./urls.js` have been processed. | ||
|
||
## Get a WPT key | ||
|
||
This is how you get a regular key: | ||
|
||
http://www.webpagetest.org/getkey.php -> "Login with Google" -> fill form. Key will be emailed to you. | ||
|
||
But you'll really need a privileged key to run the collection in a reasonable amount of time. | ||
|
||
Note: to actually run this, you want a better key than the default. Ask @connorjclark for it. | ||
|
||
## Lighthouse Version | ||
|
||
Check what version of Lighthouse WPT is using. You should use the same version of lighthouse for the desktop collection. | ||
|
||
## Verify URLs | ||
|
||
```sh | ||
node -e "console.log(require('./urls.js').join('\n'))" |\ | ||
xargs -P 10 -I{} curl -A 'Mozilla/5.0 (Linux; Android 6.0.1; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3694.0 Mobile Safari/537.36 Chrome-Lighthouse' -o /dev/null -s --write-out '%{http_code} {} (if redirect: %{redirect_url})\n' {} |\ | ||
sort | ||
``` | ||
|
||
Note: some good URLs will 4xx b/c the site blocks such usages of `curl`. | ||
|
||
## Run | ||
|
||
```sh | ||
DEBUG=1 WPT_KEY=... NUM_SAMPLES=3 node --max-old-space-size=4096 collect.js | ||
``` | ||
|
||
Output will be in `dist/collect-lantern-traces`, and zipped at `dist/collect-lantern-traces.zip`. | ||
|
||
```sh | ||
node golden.js | ||
``` | ||
|
||
Output will be in `dist/golden-lantern-traces`, and zipped at `dist/golden-lantern-traces.zip`. | ||
|
||
Update the zips on Google Drive and `download-traces.sh`. | ||
|
||
|
||
## Run in GCP | ||
|
||
```sh | ||
WPT_KEY=... /usr/local/google/home/cjamcl/code/lighthouse/lighthouse-core/scripts/lantern/collect/gcp-create-and-run.sh | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,331 @@ | ||
/** | ||
* @license Copyright 2019 Google Inc. All Rights Reserved. | ||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 | ||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. | ||
*/ | ||
'use strict'; | ||
|
||
/** @typedef {import('./common.js').Result} Result */ | ||
/** @typedef {import('./common.js').Summary} Summary */ | ||
|
||
const fs = require('fs'); | ||
const fetch = require('isomorphic-fetch'); | ||
const {execFile} = require('child_process'); | ||
const {promisify} = require('util'); | ||
const execFileAsync = promisify(execFile); | ||
const common = require('./common.js'); | ||
|
||
const LH_ROOT = `${__dirname}/../../../..`; | ||
const SAMPLES = process.env.SAMPLES ? Number(process.env.SAMPLES) : 9; | ||
const TEST_URLS = process.env.TEST_URLS ? process.env.TEST_URLS.split(' ') : require('./urls.js'); | ||
|
||
if (!process.env.WPT_KEY) throw new Error('missing WPT_KEY'); | ||
const WPT_KEY = process.env.WPT_KEY; | ||
const DEBUG = process.env.DEBUG; | ||
|
||
/** @type {typeof common.ProgressLogger['prototype']} */ | ||
let log; | ||
|
||
/** @type {Summary} */ | ||
let summary; | ||
|
||
/** | ||
* @param {string} message | ||
*/ | ||
function warn(message) { | ||
summary.warnings.push(message); | ||
log.log(message); | ||
} | ||
|
||
/** | ||
* @param {string} filename | ||
* @param {string} data | ||
*/ | ||
function saveData(filename, data) { | ||
fs.mkdirSync(common.collectFolder, {recursive: true}); | ||
fs.writeFileSync(`${common.collectFolder}/${filename}`, data); | ||
return filename; | ||
} | ||
|
||
/** | ||
* @param {string} url | ||
* @return {Promise<string>} | ||
*/ | ||
async function fetchString(url) { | ||
const response = await fetch(url); | ||
if (response.ok) return response.text(); | ||
throw new Error(`error fetching ${url}: ${response.status} ${response.statusText}`); | ||
} | ||
|
||
/** | ||
* @param {string} url | ||
*/ | ||
async function startWptTest(url) { | ||
const apiUrl = new URL('https://www.webpagetest.org/runtest.php'); | ||
apiUrl.search = new URLSearchParams({ | ||
k: WPT_KEY, | ||
f: 'json', | ||
url, | ||
location: 'Dulles_MotoG4:Motorola G (gen 4) - Chrome.3G', | ||
runs: '1', | ||
lighthouse: '1', | ||
// Make the trace file available over /getgzip.php. | ||
lighthouseTrace: '1', | ||
lighthouseScreenshots: '1', | ||
// Disable some things that WPT does, such as a "repeat view" analysis. | ||
type: 'lighthouse', | ||
}).toString(); | ||
const wptResponseJson = await fetchString(apiUrl.href); | ||
const wptResponse = JSON.parse(wptResponseJson); | ||
if (wptResponse.statusCode !== 200) { | ||
throw new Error(`unexpected status code ${wptResponse.statusCode} ${wptResponse.statusText}`); | ||
} | ||
|
||
return { | ||
testId: wptResponse.data.testId, | ||
jsonUrl: wptResponse.data.jsonUrl, | ||
}; | ||
} | ||
|
||
/** | ||
* @param {string} url | ||
* @return {Promise<Result>} | ||
*/ | ||
async function runUnthrottledLocally(url) { | ||
const artifactsFolder = `${LH_ROOT}/.tmp/collect-traces-artifacts`; | ||
const {stdout} = await execFileAsync('node', [ | ||
`${LH_ROOT}/lighthouse-cli`, | ||
url, | ||
'--throttling-method=provided', | ||
'--output=json', | ||
`-AG=${artifactsFolder}`, | ||
process.env.OOPIFS === '1' ? '' : '--chrome-flags=--disable-features=site-per-process', | ||
], { | ||
// Default (1024 * 1024) is too small. | ||
maxBuffer: 10 * 1024 * 1024, | ||
}); | ||
const lhr = JSON.parse(stdout); | ||
assertLhr(lhr); | ||
const devtoolsLog = fs.readFileSync(`${artifactsFolder}/defaultPass.devtoolslog.json`, 'utf-8'); | ||
const trace = fs.readFileSync(`${artifactsFolder}/defaultPass.trace.json`, 'utf-8'); | ||
patrickhulce marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return { | ||
devtoolsLog, | ||
lhr: JSON.stringify(lhr), | ||
trace, | ||
}; | ||
} | ||
|
||
/** | ||
* @param {string} url | ||
* @return {Promise<Result>} | ||
*/ | ||
async function runForWpt(url) { | ||
const {testId, jsonUrl} = await startWptTest(url); | ||
if (DEBUG) log.log({testId, jsonUrl}); | ||
|
||
// Poll for the results every x seconds, where x = position in queue. | ||
connorjclark marked this conversation as resolved.
Show resolved
Hide resolved
|
||
let lhr; | ||
// eslint-disable-next-line no-constant-condition | ||
while (true) { | ||
connorjclark marked this conversation as resolved.
Show resolved
Hide resolved
|
||
const responseJson = await fetchString(jsonUrl); | ||
const response = JSON.parse(responseJson); | ||
|
||
if (response.statusCode === 200) { | ||
lhr = response.data.lighthouse; | ||
assertLhr(lhr); | ||
break; | ||
} | ||
|
||
if (response.statusCode >= 100 && response.statusCode < 200) { | ||
// If behindCount doesn't exist, the test is currently running. | ||
// * Wait 30 seconds if the test is currently running. | ||
// * Wait an additional 10 seconds for every test ahead of this one. | ||
// * Don't wait for more than 10 minutes. | ||
const secondsToWait = Math.min(30 + 10 * (response.data.behindCount || 0), 10 * 1000); | ||
if (DEBUG) log.log('poll wpt in', secondsToWait); | ||
await new Promise((resolve) => setTimeout(resolve, secondsToWait * 1000)); | ||
} else { | ||
throw new Error(`unexpected response: ${response.statusCode} ${response.statusText}`); | ||
} | ||
} | ||
|
||
const traceUrl = new URL('https://www.webpagetest.org/getgzip.php'); | ||
traceUrl.searchParams.set('test', testId); | ||
traceUrl.searchParams.set('file', 'lighthouse_trace.json'); | ||
const traceJson = await fetchString(traceUrl.href); | ||
|
||
/** @type {LH.Trace} */ | ||
const trace = JSON.parse(traceJson); | ||
// For some reason, the first trace event is an empty object. | ||
trace.traceEvents = trace.traceEvents.filter(e => Object.keys(e).length > 0); | ||
|
||
return { | ||
lhr: JSON.stringify(lhr), | ||
trace: JSON.stringify(trace), | ||
}; | ||
} | ||
|
||
/** | ||
* Repeats the ascyn function a maximum of maxAttempts times until it passes. | ||
* The empty object ({}) is returned when maxAttempts is reached. | ||
* @param {() => Promise<Result>} asyncFn | ||
* @param {number} [maxAttempts] | ||
* @return {Promise<Result|null>} | ||
*/ | ||
async function repeatUntilPassOrNull(asyncFn, maxAttempts = 3) { | ||
for (let i = 0; i < maxAttempts; i++) { | ||
try { | ||
return await asyncFn(); | ||
} catch (err) { | ||
warn('Error: ' + err.toString()); | ||
} | ||
} | ||
|
||
return null; | ||
} | ||
|
||
/** | ||
* @param {LH.Result=} lhr | ||
*/ | ||
function assertLhr(lhr) { | ||
if (!lhr) throw new Error('missing lhr'); | ||
if (lhr.runtimeError) throw new Error(`runtime error: ${lhr.runtimeError}`); | ||
const metrics = common.getMetrics(lhr); | ||
if (metrics && | ||
metrics.estimatedInputLatency && | ||
metrics.firstContentfulPaint && | ||
metrics.firstCPUIdle && | ||
metrics.firstMeaningfulPaint && | ||
metrics.interactive && | ||
// WPT won't have this, we'll just get from the trace. | ||
// metrics.largestContentfulPaint && | ||
metrics.maxPotentialFID && | ||
metrics.speedIndex | ||
) return; | ||
throw new Error('run failed to get metrics'); | ||
} | ||
|
||
async function main() { | ||
log = new common.ProgressLogger(); | ||
|
||
// Resume state from previous invocation of script. | ||
summary = common.loadSummary(); | ||
|
||
// Remove data if no longer in TEST_URLS. | ||
summary.results = summary.results | ||
.filter(urlSet => TEST_URLS.includes(urlSet.url)); | ||
|
||
fs.mkdirSync(common.collectFolder, {recursive: true}); | ||
|
||
// Traces are collected for one URL at a time, in series, so all traces are from a small time | ||
// frame, reducing the chance of a site change affecting results. | ||
for (const url of TEST_URLS) { | ||
// This URL has been done on a previous script invocation. Skip it. | ||
if (summary.results.find((urlResultSet) => urlResultSet.url === url)) { | ||
log.log(`already collected traces for ${url}`); | ||
continue; | ||
} | ||
log.log(`collecting traces for ${url}`); | ||
|
||
const sanitizedUrl = url.replace(/[^a-z0-9]/gi, '-'); | ||
/** @type {Result[]} */ | ||
const wptResults = []; | ||
/** @type {Result[]} */ | ||
const unthrottledResults = []; | ||
|
||
let wptResultsDone = 0; | ||
let unthrottledResultsDone = 0; | ||
|
||
// The closure this makes is too convenient to decompose. | ||
// eslint-disable-next-line no-inner-declarations | ||
function updateProgress() { | ||
const index = TEST_URLS.indexOf(url); | ||
const wptDone = wptResultsDone === SAMPLES; | ||
const unthrottledDone = unthrottledResultsDone === SAMPLES; | ||
log.progress([ | ||
`${url} (${index + 1} / ${TEST_URLS.length})`, | ||
'wpt', | ||
'(' + (wptDone ? 'DONE' : `${wptResultsDone + 1} / ${SAMPLES}`) + ')', | ||
'unthrottledResults', | ||
'(' + (unthrottledDone ? 'DONE' : `${unthrottledResultsDone + 1} / ${SAMPLES}`) + ')', | ||
].join(' ')); | ||
} | ||
|
||
updateProgress(); | ||
|
||
// Can run in parallel. | ||
const wptResultsPromises = []; | ||
for (let i = 0; i < SAMPLES; i++) { | ||
const resultPromise = repeatUntilPassOrNull(() => runForWpt(url)); | ||
// Push to results array as they finish, so the progress indicator can track progress. | ||
resultPromise.then((result) => result && wptResults.push(result)).finally(() => { | ||
wptResultsDone += 1; | ||
updateProgress(); | ||
}); | ||
wptResultsPromises.push(resultPromise); | ||
} | ||
|
||
// Wait for the first WPT result to finish because we can sit in the queue for a while before we start | ||
// and we want to avoid seeing totally different content locally. | ||
await Promise.race(wptResultsPromises); | ||
|
||
// Must run in series. | ||
for (let i = 0; i < SAMPLES; i++) { | ||
const result = await repeatUntilPassOrNull(() => runUnthrottledLocally(url)); | ||
if (result) { | ||
unthrottledResults.push(result); | ||
} | ||
unthrottledResultsDone += 1; | ||
updateProgress(); | ||
} | ||
|
||
// Wait for *all* WPT runs to finish since we just waited on the first one earlier. | ||
await Promise.all(wptResultsPromises); | ||
|
||
const urlResultSet = { | ||
url, | ||
wpt: wptResults | ||
.map((result, i) => { | ||
const prefix = `${sanitizedUrl}-mobile-wpt-${i + 1}`; | ||
return { | ||
lhr: saveData(`${prefix}-lhr.json`, result.lhr), | ||
trace: saveData(`${prefix}-trace.json`, result.trace), | ||
}; | ||
}), | ||
unthrottled: unthrottledResults | ||
.filter(result => result.lhr && result.trace && result.devtoolsLog) | ||
.map((result, i) => { | ||
// Unthrottled runs will have devtools logs, so this should never happen. | ||
if (!result.devtoolsLog) throw new Error('expected devtools log'); | ||
|
||
const prefix = `${sanitizedUrl}-mobile-unthrottled-${i + 1}`; | ||
return { | ||
devtoolsLog: saveData(`${prefix}-devtoolsLog.json`, result.devtoolsLog), | ||
lhr: saveData(`${prefix}-lhr.json`, result.lhr), | ||
trace: saveData(`${prefix}-trace.json`, result.trace), | ||
}; | ||
}), | ||
}; | ||
|
||
// Too many attempts (with 3 retries) failed, so don't both saving results for this URL. | ||
if (urlResultSet.wpt.length < SAMPLES / 2 || urlResultSet.unthrottled.length < SAMPLES / 2) { | ||
warn(`too many results for ${url} failed, skipping.`); | ||
continue; | ||
} | ||
|
||
// We just collected NUM_SAMPLES * 2 traces, so let's save our progress. | ||
log.log(`collected results for ${url}, saving progress.`); | ||
summary.results.push(urlResultSet); | ||
common.saveSummary(summary); | ||
} | ||
|
||
log.progress('archiving ...'); | ||
await common.archive(common.collectFolder); | ||
log.closeProgress(); | ||
} | ||
|
||
main().catch(err => { | ||
if (log) log.closeProgress(); | ||
process.stderr.write(`Fatal error in collect:\n\n ${err.stack}`); | ||
process.exit(1); | ||
}); |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this important? WPT uses LH 5.2.0 rn.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
shouldn't be a big deal as long as we're not turning on different trace categories or something between the versions. observed metric definitions haven't changed in forever.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
actually I take it back, it'll be a big deal because we want LCP and layout stability to be computed for these that's the whole point of getting new ones. I guess we'll have to derive them after the fact from the trace if we want to avoid delaying this an entire release :/
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
so: i'll collect the LHR, devtoolslog, and trace for local unthrottled desktop runs. And the LHR and trace for WPT runs. put them all in a giant zip and upload somewhere.
then make the new golden lantern set by taking the median runs for the local runs. and just run trace-processor on the WPT traces to get the metrics.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
right-o, but since we don't have LCP metric yet, I would triple check that the traces you're getting have the LCP and layout stability events in them at least and we don't need to enable some other category to get them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yup they do