Skip to content

Commit 8b013be

Browse files
[wptrunner] Collect per-test trace events from Chromium
When `wpt run --trace-categories` is specified, the trace events [0] are retrieved from an internal ChromeDriver buffer and logged as an `extra` test result field. The trace will be consumed by an event listener on the Chromium side (https://crrev.com/c/6820247) and can be viewed in https://ui.perfetto.dev/. Partially fulfills crbug.com/434017035 [0]: https://developer.chrome.com/docs/chromedriver/logging/performance-log
1 parent b343309 commit 8b013be

File tree

3 files changed

+81
-4
lines changed

3 files changed

+81
-4
lines changed

tools/wptrunner/wptrunner/browsers/chrome.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,15 @@ def executor_kwargs(logger, test_type, test_environment, run_info_data, subsuite
8686
# fail to create a session if they don't recognize this capability.
8787
chrome_options["quitGracefully"] = True
8888

89+
if trace_categories := kwargs.get("trace_categories"):
90+
executor_kwargs["enable_tracing"] = True
91+
capabilities["goog:loggingPrefs"] = {
92+
"performance": "INFO",
93+
}
94+
chrome_options["perfLoggingPrefs"] = {
95+
"traceCategories": trace_categories,
96+
}
97+
8998
# Here we set a few Chrome flags that are always passed.
9099
# ChromeDriver's "acceptInsecureCerts" capability only controls the current
91100
# browsing context, whereas the CLI flag works for workers, too.

tools/wptrunner/wptrunner/executors/executorchrome.py

Lines changed: 66 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -185,13 +185,46 @@ def execute_cdp_command(self, command, params=None):
185185
body=body)
186186

187187

188+
class ChromeDriverTracingProtocolPart(ProtocolPart):
189+
name = "tracing"
190+
191+
def setup(self):
192+
self.webdriver = self.parent.webdriver
193+
194+
def get_trace(self):
195+
"""Retrieve trace events accumulated by ChromeDriver.
196+
197+
This also clears ChromeDriver's internal buffer of logged events.
198+
199+
Returns:
200+
JSON in the trace array format [0].
201+
202+
[0]: https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/preview?tab=t.0#heading=h.f2f0yd51wi15
203+
"""
204+
# Not a standard WebDriver method.
205+
perf_data = self.webdriver.send_session_command("POST", "se/log", {
206+
"type": "performance",
207+
})
208+
events = []
209+
for entry in perf_data:
210+
# Unwrap the inner trace event and discard the unnecessary
211+
# ChromeDriver-added fields.
212+
data_collected_event = json.loads(entry["message"]).get("message", {})
213+
if data_collected_event.get("method") != "Tracing.dataCollected":
214+
continue
215+
if trace_event := data_collected_event.get("params"):
216+
events.append(trace_event)
217+
return events
218+
219+
188220
class ChromeDriverProtocol(WebDriverProtocol):
189221
implements = [
190222
ChromeDriverBaseProtocolPart,
191223
ChromeDriverDevToolsProtocolPart,
192224
ChromeDriverFedCMProtocolPart,
193225
ChromeDriverTestDriverProtocolPart,
194226
ChromeDriverTestharnessProtocolPart,
227+
ChromeDriverTracingProtocolPart,
195228
]
196229
for base_part in WebDriverProtocol.implements:
197230
if base_part.name not in {part.name for part in implements}:
@@ -258,24 +291,39 @@ def convert_result(self, test, result, **kwargs):
258291
class ChromeDriverCrashTestExecutor(WebDriverCrashtestExecutor):
259292
protocol_cls = ChromeDriverProtocol
260293

261-
def __init__(self, *args, sanitizer_enabled=False, **kwargs):
294+
def __init__(self, *args, sanitizer_enabled=False, enable_tracing=False, **kwargs):
262295
super().__init__(*args, **kwargs)
263296
self.sanitizer_enabled = sanitizer_enabled
297+
self.enable_tracing = enable_tracing
298+
299+
def do_test(self, test):
300+
file_result, subtest_results = super().do_test(test)
301+
if self.enable_tracing:
302+
file_result.extra["trace"] = self.protocol.tracing.get_trace()
303+
return file_result, subtest_results
264304

265305

266306
@_evaluate_sanitized_result
267307
class ChromeDriverRefTestExecutor(WebDriverRefTestExecutor):
268308
protocol_cls = ChromeDriverProtocol
269309

270-
def __init__(self, *args, sanitizer_enabled=False, **kwargs):
310+
def __init__(self, *args, sanitizer_enabled=False, enable_tracing=False, **kwargs):
271311
super().__init__(*args, **kwargs)
272312
self.sanitizer_enabled = sanitizer_enabled
313+
self.enable_tracing = enable_tracing
314+
315+
def do_test(self, test):
316+
file_result, subtest_results = super().do_test(test)
317+
if self.enable_tracing:
318+
file_result.extra["trace"] = self.protocol.tracing.get_trace()
319+
return file_result, subtest_results
273320

274321

275322
@_evaluate_sanitized_result
276323
class ChromeDriverTestharnessExecutor(WebDriverTestharnessExecutor):
277324

278-
def __init__(self, *args, sanitizer_enabled=False, reuse_window=False, **kwargs):
325+
def __init__(self, *args, sanitizer_enabled=False, enable_tracing=False, reuse_window=False,
326+
**kwargs):
279327
require_webdriver_bidi = kwargs.get("browser_settings", {}).get(
280328
"require_webdriver_bidi", None)
281329
if require_webdriver_bidi:
@@ -285,6 +333,7 @@ def __init__(self, *args, sanitizer_enabled=False, reuse_window=False, **kwargs)
285333

286334
super().__init__(*args, **kwargs)
287335
self.sanitizer_enabled = sanitizer_enabled
336+
self.enable_tracing = enable_tracing
288337
self.reuse_window = reuse_window
289338

290339
def create_test_window(self, protocol):
@@ -312,11 +361,24 @@ def create_test_window(self, protocol):
312361
self.protocol.testharness.persistent_test_window = test_window
313362
return test_window
314363

364+
def do_test(self, test):
365+
file_result, subtest_results = super().do_test(test)
366+
if self.enable_tracing:
367+
file_result.extra["trace"] = self.protocol.tracing.get_trace()
368+
return file_result, subtest_results
369+
315370

316371
@_evaluate_sanitized_result
317372
class ChromeDriverPrintRefTestExecutor(WebDriverPrintRefTestExecutor):
318373
protocol_cls = ChromeDriverProtocol
319374

320-
def __init__(self, *args, sanitizer_enabled=False, **kwargs):
375+
def __init__(self, *args, sanitizer_enabled=False, enable_tracing=False, **kwargs):
321376
super().__init__(*args, **kwargs)
322377
self.sanitizer_enabled = sanitizer_enabled
378+
self.enable_tracing = enable_tracing
379+
380+
def do_test(self, test):
381+
file_result, subtest_results = super().do_test(test)
382+
if self.enable_tracing:
383+
file_result.extra["trace"] = self.protocol.tracing.get_trace()
384+
return file_result, subtest_results

tools/wptrunner/wptrunner/wptcommandline.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,12 @@ def create_parser(product_choices=None):
381381
help=("Reuse a window across `testharness.js` tests where possible, "
382382
"which can speed up testing. Also useful for ensuring that the "
383383
"renderer process has a stable PID for a debugger to attach to."))
384+
chrome_group.add_argument(
385+
"--trace-categories",
386+
metavar="CATEGORIES",
387+
nargs="?",
388+
const="blink,blink.bindings",
389+
help="Record traces under the given categories for each test.")
384390

385391
sauce_group = parser.add_argument_group("Sauce Labs-specific")
386392
sauce_group.add_argument("--sauce-browser", help="Sauce Labs browser name")

0 commit comments

Comments
 (0)