diff --git a/src/McpContext.ts b/src/McpContext.ts index a77bc06bc..2916163cd 100644 --- a/src/McpContext.ts +++ b/src/McpContext.ts @@ -481,7 +481,7 @@ export class McpContext implements Context { page.networkConditions, ); page.pptrPage.setDefaultNavigationTimeout( - NAVIGATION_TIMEOUT * networkMultiplier, + NAVIGATION_TIMEOUT * networkMultiplier * cpuMultiplier, ); } diff --git a/src/tools/ToolDefinition.ts b/src/tools/ToolDefinition.ts index 34cdd435e..9737f5eff 100644 --- a/src/tools/ToolDefinition.ts +++ b/src/tools/ToolDefinition.ts @@ -257,6 +257,8 @@ export type Context = Readonly<{ */ export type ContextPage = Readonly<{ readonly pptrPage: Page; + readonly cpuThrottlingRate: number; + readonly networkConditions: string | null; getAXNodeByUid(uid: string): TextSnapshotNode | undefined; getElementByUid(uid: string): Promise>; diff --git a/src/tools/performance.ts b/src/tools/performance.ts index 39f9b3186..edd1bdf83 100644 --- a/src/tools/performance.ts +++ b/src/tools/performance.ts @@ -8,7 +8,6 @@ import zlib from 'node:zlib'; import {logger} from '../logger.js'; import {zod, DevTools} from '../third_party/index.js'; -import type {Page} from '../third_party/index.js'; import type {InsightName, TraceResult} from '../trace-processing/parse.js'; import { parseRawTraceBuffer, @@ -16,7 +15,7 @@ import { } from '../trace-processing/parse.js'; import {ToolCategory} from './categories.js'; -import type {Context, Response} from './ToolDefinition.js'; +import type {Context, Response, ContextPage} from './ToolDefinition.js'; import {definePageTool} from './ToolDefinition.js'; const filePathSchema = zod @@ -103,7 +102,7 @@ export const startTrace = definePageTool({ if (request.params.autoStop) { await new Promise(resolve => setTimeout(resolve, 5_000)); await stopTracingAndAppendOutput( - page.pptrPage, + page, response, context, request.params.filePath, @@ -135,7 +134,7 @@ export const stopTrace = definePageTool({ } const page = request.page; await stopTracingAndAppendOutput( - page.pptrPage, + page, response, context, request.params.filePath, @@ -182,13 +181,13 @@ export const analyzeInsight = definePageTool({ }); async function stopTracingAndAppendOutput( - page: Page, + page: ContextPage, response: Response, context: Context, filePath?: string, ): Promise { try { - const traceEventsBuffer = await page.tracing.stop(); + const traceEventsBuffer = await page.pptrPage.tracing.stop(); if (filePath && traceEventsBuffer) { let dataToWrite: Uint8Array = traceEventsBuffer; if (filePath.endsWith('.gz')) { @@ -211,7 +210,10 @@ async function stopTracingAndAppendOutput( `The raw trace data was saved to ${file.filename}.`, ); } - const result = await parseRawTraceBuffer(traceEventsBuffer); + const result = await parseRawTraceBuffer(traceEventsBuffer, { + cpuThrottling: page.cpuThrottlingRate, + networkThrottling: page.networkConditions ?? undefined, + }); response.appendResponseLine('The performance trace has been stopped.'); if (traceResultIsSuccess(result)) { if (context.isCruxEnabled()) { diff --git a/src/trace-processing/parse.ts b/src/trace-processing/parse.ts index 7b152d853..f0ede38db 100644 --- a/src/trace-processing/parse.ts +++ b/src/trace-processing/parse.ts @@ -26,6 +26,10 @@ export interface TraceParseError { export async function parseRawTraceBuffer( buffer: Uint8Array | undefined, + metadata?: { + cpuThrottling?: number; + networkThrottling?: string; + }, ): Promise { engine.resetProcessor(); if (!buffer) { @@ -47,7 +51,7 @@ export async function parseRawTraceBuffer( | DevTools.TraceEngine.Types.Events.Event[]; const events = Array.isArray(data) ? data : data.traceEvents; - await engine.parse(events); + await engine.parse(events, {metadata}); const parsedTrace = engine.parsedTrace(); if (!parsedTrace) { return { diff --git a/tests/e2e/chrome-devtools-commands.test.ts b/tests/e2e/chrome-devtools-commands.test.ts index 4884d4f82..97d23fb7d 100644 --- a/tests/e2e/chrome-devtools-commands.test.ts +++ b/tests/e2e/chrome-devtools-commands.test.ts @@ -106,4 +106,41 @@ describe('chrome-devtools', () => { 'restart command suggestion is miss: ' + result.stdout, ); }); + + it('can record a performance trace', async () => { + const startResult = await runCli( + ['start', '--performanceCrux=false'], + sessionId, + ); + assert.strictEqual( + startResult.status, + 0, + `start command failed: ${startResult.stderr}`, + ); + + const emulateResult = await runCli( + ['emulate', '--cpuThrottlingRate', '2'], + sessionId, + ); + assert.strictEqual( + emulateResult.status, + 0, + `emulate command failed: ${emulateResult.stderr}`, + ); + + const result = await runCli(['performance_start_trace'], sessionId); + assert.strictEqual( + result.status, + 0, + `performance_start_trace command failed: ${result.stderr}`, + ); + assert( + result.stdout.includes('The performance trace has been stopped.'), + 'performance_start_trace output is unexpected: ' + result.stdout, + ); + assert( + result.stdout.includes('CPU throttling: 2x'), + 'performance_start_trace output is unexpected: ' + result.stdout, + ); + }); });