Skip to content

core(lantern): add network timings to debug traces #14571

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

Merged
merged 5 commits into from
Dec 1, 2022
Merged

Conversation

brendankenny
Copy link
Contributor

@brendankenny brendankenny commented Nov 30, 2022

Adds network request timing breakdowns to lantern debug traces. When debugging simulator results (e.g. for debugging #14166), it can be really helpful to see time for DNS, connection, to get the first byte back, etc.

Because the changes are kind of thread through four different files, it might be helpful to look at individual commits (some general trace fixes, getting full node timings to the trace saver, and adding new timings to network nodes).

Changes have no effect on simulator functionality or results. Timings available to audits are unchanged, it's only the trace generator that has access to the full node timings.


Example

Trace from main:
A Chrome DevTools performance trace, showing simulated network requests blocks in a timeline

Trace from this branch:
The same performance trace, where network requests now have additional timing annotations

For reference, the "real" (devtools-throttled) trace:

A real devtools trace showing similar timing annotations

@brendankenny brendankenny requested a review from a team as a code owner November 30, 2022 18:22
@brendankenny brendankenny requested review from connorjclark and removed request for a team November 30, 2022 18:22
Comment on lines +191 to +194
// TODO: timing information currently difficult to model for warm h2 connections.
connectionTiming = {
timeToFirstByte,
};
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the way warm h2 connections are simulated means there's not really anything meaningful going on here (timeToFirstByte will actually always be 0), but that's something I'd like to iterate on

* @param {CompleteNodeTiming} timing
* @return {LH.TraceEvent}
*/
function createWillSendRequestEvent(requestId, record, timing) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

adding a ResourceWillSendRequest trace event for the navigation for completeness, though the perf panel doesn't seem to depend on it

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Heh yeah found the same thing (in rum-trace)

image

proxyEnd: -1,
pushStart: 0,
pushEnd: 0,
},
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@paulirish you might have lots of thoughts. Only so much we can do with the timings we have, so it's a bit handwavey (especially sendStart/sendEnd/receiveHeadersEnd), but maybe you have ideas

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah this looks great

* @param {CompleteNodeTiming} timing
* @return {LH.TraceEvent}
*/
function createWillSendRequestEvent(requestId, record, timing) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Heh yeah found the same thing (in rum-trace)

image

@@ -31,7 +31,11 @@ function convertNodeTimingsToTrace(nodeTimings) {
} else {
// Ignore data URIs as they don't really add much value
if (/^data/.test(node.record.url)) continue;
traceEvents.push(...createFakeNetworkEvents(node.record, timing));
if (requestId === 1) {
traceEvents.push(createWillSendRequestEvent(requestId, node.record, timing));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you could do this conditional within createFakeNetworkEvents instead.
IMO that'd be a bit cleaner :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you could do this conditional within createFakeNetworkEvents instead.

yeah, I was trying to keep createFakeNetworkEvents ignorant of the requestId, but it's already pretty hacky, so that's fine. I suspect this needs to be reworked for redirects and timespans anyway.

@@ -481,8 +490,9 @@ class Simulator {
}
}

const nodeTimings = this._computeFinalNodeTimings();
ALL_SIMULATION_NODE_TIMINGS.set(options.label || 'unlabeled', nodeTimings);
// `nodeTimings` are used for simulator consumers, `completeNodeTimings` kept for debugging.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thank you!

const receiveResponseData = {
...requestData,
statusCode: record.statusCode,
mimeType: record.mimeType,
encodedDataLength: record.transferSize,
fromCache: record.fromDiskCache,
fromServiceWorker: record.fetchedViaServiceWorker,
timing: {
requestTime: toMicroseconds(startTime) / (1000 * 1000),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might wanna leave a comment that requestTime is seconds whereas the rest of the timings are ms.

proxyEnd: -1,
pushStart: 0,
pushEnd: 0,
},
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah this looks great

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants