Skip to content

Commit

Permalink
Merge branch 'main' into new-network-idle
Browse files Browse the repository at this point in the history
  • Loading branch information
soulgalore authored Sep 29, 2023
2 parents b5582c0 + 1273d4d commit 8f88b15
Show file tree
Hide file tree
Showing 7 changed files with 146 additions and 51 deletions.
64 changes: 20 additions & 44 deletions lib/chrome/webdriver/chromium.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,7 @@ import { pathToFolder } from '../../support/pathToFolder.js';
import { ChromeDevtoolsProtocol } from '../chromeDevtoolsProtocol.js';
import { NetworkManager } from '../networkManager.js';
import { Android, isAndroidConfigured } from '../../android/index.js';
import {
getFirstContentFulPaintEvent,
getLargestContentfulPaintEvent,
getRecalculateStyleElementsAndTimeBefore
} from './traceUtilities.js';
import { getRenderBlocking } from './traceUtilities.js';
const unlink = promisify(_unlink);
const rm = promisify(_rm);

Expand Down Expand Up @@ -161,7 +157,11 @@ export class Chromium {
await this.android.resetPowerUsage();
}

if (this.collectTracingEvents && !this.isTracing) {
if (
this.collectTracingEvents &&
!this.isTracing &&
this.options.chrome.timelineRecordingType !== 'custom'
) {
this.isTracing = true;
return this.cdpClient.startTrace();
}
Expand All @@ -175,7 +175,11 @@ export class Chromium {
*/
async afterPageCompleteCheck(runner, index, url, alias) {
const result = { url, alias };
if (this.collectTracingEvents && this.isTracing) {
if (
this.collectTracingEvents &&
this.isTracing &&
this.options.chrome.timelineRecordingType !== 'custom'
) {
// We are ready and can stop collecting events
this.isTracing = false;
this.events = await this.cdpClient.stopTrace();
Expand Down Expand Up @@ -335,7 +339,10 @@ export class Chromium {
await this.storageManager.gzip(filename, gzFilename, true);
}

if (this.collectTracingEvents) {
if (
this.collectTracingEvents &&
this.options.chrome.timelineRecordingType !== 'custom'
) {
const trace = parse(this.events, result.url);
const name = this.options.enableProfileRun
? `trace-${index}-extra-run.json`
Expand All @@ -346,50 +353,19 @@ export class Chromium {
result.cpu = cpu;

// Collect render blocking info
const renderBlockingInfo = {};
const urlsWithBlockingInfo = trace.traceEvents.filter(
task =>
task.cat === 'devtools.timeline' &&
task.name === 'ResourceSendRequest' &&
task.args.data.url &&
task.args.data.renderBlocking
);
for (let asset of urlsWithBlockingInfo) {
renderBlockingInfo[asset.args.data.url] =
asset.args.data.renderBlocking;
}

const fcpEvent = getFirstContentFulPaintEvent(trace.traceEvents);
const lcpEvent = getLargestContentfulPaintEvent(trace.traceEvents);

result.renderBlocking = { recalculateStyle: {}, requests: {} };

if (fcpEvent) {
const beforeFCP = getRecalculateStyleElementsAndTimeBefore(
trace.traceEvents,
fcpEvent.ts
);
result.renderBlocking.recalculateStyle.beforeFCP = beforeFCP;
}

if (lcpEvent) {
const beforeLCP = getRecalculateStyleElementsAndTimeBefore(
trace.traceEvents,
lcpEvent.ts
);
result.renderBlocking.recalculateStyle.beforeLCP = beforeLCP;
}
const render = await getRenderBlocking(trace);
result.renderBlocking = render.renderBlocking;

if (!this.options.skipHar) {
for (let harRequest of this.hars[index - 1].log.entries) {
if (renderBlockingInfo[harRequest.request.url]) {
if (render.renderBlockingInfo[harRequest.request.url]) {
harRequest._renderBlocking =
renderBlockingInfo[harRequest.request.url];
render.renderBlockingInfo[harRequest.request.url];
}
}
}

result.renderBlocking.requests = renderBlockingInfo;
result.renderBlocking.requests = render.renderBlockingInfo;
}

// Google Web Vitals hacksery
Expand Down
47 changes: 41 additions & 6 deletions lib/chrome/webdriver/traceUtilities.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import intel from 'intel';
const log = intel.getLogger('browsertime.chrome');

export function getLargestContentfulPaintEvent(traceEvents) {
function getLargestContentfulPaintEvent(traceEvents) {
const lcpCandidates = traceEvents.filter(
task => task.name === 'largestContentfulPaint::Candidate'
);
Expand All @@ -20,7 +20,7 @@ export function getLargestContentfulPaintEvent(traceEvents) {
}
}

export function getFirstContentFulPaintEvent(traceEvents) {
function getFirstContentFulPaintEvent(traceEvents) {
// Get first contentful paint
const fcpEvent = traceEvents.find(
task => task.name === 'firstContentfulPaint'
Expand All @@ -33,10 +33,7 @@ export function getFirstContentFulPaintEvent(traceEvents) {
}
}

export function getRecalculateStyleElementsAndTimeBefore(
traceEvents,
timestamp
) {
function getRecalculateStyleElementsAndTimeBefore(traceEvents, timestamp) {
const recalculatesBefore = traceEvents.filter(
task =>
task.cat === 'disabled-by-default-devtools.timeline' &&
Expand All @@ -62,3 +59,41 @@ export function getRecalculateStyleElementsAndTimeBefore(

return { elements, durationInMillis: duration / 1000 };
}

export async function getRenderBlocking(trace) {
const renderBlockingInfo = {};

const urlsWithBlockingInfo = trace.traceEvents.filter(
task =>
task.cat === 'devtools.timeline' &&
task.name === 'ResourceSendRequest' &&
task.args.data.url &&
task.args.data.renderBlocking
);
for (let asset of urlsWithBlockingInfo) {
renderBlockingInfo[asset.args.data.url] = asset.args.data.renderBlocking;
}

const fcpEvent = getFirstContentFulPaintEvent(trace.traceEvents);
const lcpEvent = getLargestContentfulPaintEvent(trace.traceEvents);

const renderBlocking = { recalculateStyle: {}, requests: {} };

if (fcpEvent) {
const beforeFCP = getRecalculateStyleElementsAndTimeBefore(
trace.traceEvents,
fcpEvent.ts
);
renderBlocking.recalculateStyle.beforeFCP = beforeFCP;
}

if (lcpEvent) {
const beforeLCP = getRecalculateStyleElementsAndTimeBefore(
trace.traceEvents,
lcpEvent.ts
);
renderBlocking.recalculateStyle.beforeLCP = beforeLCP;
}

return { renderBlockingInfo, renderBlocking };
}
63 changes: 63 additions & 0 deletions lib/core/engine/command/chromeTrace.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import intel from 'intel';

import { getRenderBlocking } from '../../../chrome/webdriver/traceUtilities.js';
import { parse } from '../../../chrome/traceCategoriesParser.js';
import { parseCPUTrace } from '../../../chrome/parseCpuTrace.js';
const log = intel.getLogger('browsertime.command.chrometrace');
export class ChromeTrace {
constructor(engineDelegate, index, options, result) {
this.engineDelegate = engineDelegate;
this.options = options;
this.result = result;
this.index = index;
}

async start() {
if (this.options.browser === 'chrome') {
if (this.options.chrome.timelineRecordingType === 'custom') {
return this.engineDelegate.getCDPClient().startTrace();
} else {
log.info(
'You need to set traceRecordingType to custom to turn on the profiler in scripting'
);
}
} else {
throw new Error('Trace only works in Chrome');
}
}

async stop() {
if (this.options.browser === 'chrome') {
if (this.options.chrome.timelineRecordingType === 'custom') {
let result = this.result[0];

this.events = [];
this.events = await this.engineDelegate.getCDPClient().stopTrace();
const trace = parse(this.events, result.url);
const name = this.options.enableProfileRun
? `trace-${this.index}-extra-run.json`
: `trace-${this.index}.json`;
result.extraJson[name] = trace;

const cpu = await parseCPUTrace(trace, result.url);
result.cpu = cpu;

// Collect render blocking info
const render = await getRenderBlocking(trace);

result.renderBlocking = render.renderBlocking;

if (!this.options.skipHar) {
for (let harRequest of this.hars[this.index - 1].log.entries) {
if (render.renderBlockingInfo[harRequest.request.url]) {
harRequest._renderBlocking =
render.renderBlockingInfo[harRequest.request.url];
}
}
}
}
} else {
throw new Error('Trace only works in Chrome');
}
}
}
3 changes: 3 additions & 0 deletions lib/core/engine/iteration.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { Select } from './command/select.js';
import { Debug } from './command/debug.js';
import { AndroidCommand } from './command/android.js';
import { ChromeDevelopmentToolsProtocol } from './command/chromeDevToolsProtocol.js';
import { ChromeTrace } from './command/chromeTrace.js';
import {
addConnectivity,
removeConnectivity
Expand Down Expand Up @@ -164,10 +165,12 @@ export class Iteration {
engineDelegate,
options.browser
);
const trace = new ChromeTrace(engineDelegate, index, options, result);
const android = new Android(options);
const debug = new Debug(browser, options);
const commands = {
profiler: profiler,
trace: trace,
click: new Click(browser, this.pageCompleteCheck),
scroll: new Scroll(browser, options),
addText: new AddText(browser),
Expand Down
8 changes: 8 additions & 0 deletions lib/support/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,14 @@ export function parseCommandLine() {
type: 'boolean',
group: 'chrome'
})
.option('chrome.timelineRecordingType', {
alias: 'chrome.traceRecordingType',
describe: 'Expose the start/stop commands for the chrome trace',
default: 'pageload',
choices: ['pageload', 'custom'],
type: 'string',
group: 'chrome'
})
.option('chrome.collectPerfLog', {
type: 'boolean',
describe:
Expand Down
7 changes: 6 additions & 1 deletion test/commandtests/chromeTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,12 @@ after.always('Stop the HTTP server', () => {

serial.beforeEach('Start the browser', async t => {
t.timeout(timeout);
engine = getEngine({ browser: 'chrome' });
engine = getEngine({
browser: 'chrome',
chrome: {
timelineRecordingType: 'custom'
}
});
return engine.start();
});

Expand Down
5 changes: 5 additions & 0 deletions test/data/commandscripts/chrome.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,10 @@ module.exports = async function (context, commands) {
});
await commands.measure.start('http://127.0.0.1:3000/simple/');
await commands.cdp.send('Network.clearBrowserCookies');

await commands.chromeTrace.start();
await commands.measure.start('http://127.0.0.1:3000/dimple/');
await commands.chromeTrace.stop();

return commands.cdp.sendAndGet('Memory.getDOMCounters');
};

0 comments on commit 8f88b15

Please sign in to comment.