Skip to content
DenoaxPublic

About

Stream Chrome performance traces and find main-thread stalls.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

1 Commit

Folders and files

Repository files navigation

janktrace

Find the main-thread stalls hiding in a Chrome performance trace.

janktrace reads DevTools trace JSON as a stream, selects the renderer main thread, and reports long tasks plus the work that tends to cause them. It has no runtime dependencies and does not load the whole trace into memory.

Example output from the test fixture:

janktrace  small-trace.json
main thread  CrRendererMain (pid 1, tid 10)
trace span   179.0 ms · 8 complete events · 3 tasks
long tasks   2 at or above 50.00 ms · 140.0 ms blocked
worst task   80.00 ms

top stalls
    80.00 ms  at +19.00 ms    RunTask
    60.00 ms  at +119.0 ms    RunTask

Use

Node 20 or newer is required.

git clone https://github.com/Denoax/janktrace.git
cd janktrace
npm install
node ./bin/janktrace.js path/to/profile.trace.json

Gzipped traces and stdin work too:

janktrace profile.trace.json.gz
cat profile.trace.json | janktrace -

Set a different long-task threshold, emit JSON, or enforce a budget in CI:

janktrace --threshold 40 --top 12 profile.trace.json
janktrace --json profile.trace.json > report.json
janktrace --budget 100 profile.trace.json  # exits 1 if the worst task is over 100 ms

Record a trace in Chrome DevTools under Performance, then use Save profile. Traces recorded through Puppeteer or the Chrome DevTools Protocol work when they contain complete (ph: "X") timeline events.

Library

import { createReadStream } from 'node:fs';
import { analyzeTrace, parseTraceEvents } from 'janktrace';

const events = parseTraceEvents(createReadStream('profile.trace.json'));
const report = await analyzeTrace(events, { thresholdMs: 50, top: 8 });

parseTraceEvents() also accepts a raw array of trace events. A single event is limited to 8 MiB by default so malformed input cannot quietly consume the machine. Override maxEventBytes only for traces that genuinely contain larger events.

What the numbers mean

  • A task is a complete event named RunTask or one of Chromium's equivalent task-runner names.
  • A long task lasts at least --threshold milliseconds. The default is 50 ms.
  • Activity totals are inclusive durations for recognized scripting, rendering, painting, and garbage-collection events. Nested work can overlap, so the totals should not be added together.
  • If thread metadata is missing, the busiest thread containing task events is used and the report says so.

This is a trace summary, not a replacement for the DevTools flame chart. It does not attribute every long task to a call frame, interpret async events, or claim that all time over a budget was visible jank. Chromium's trace vocabulary also changes; unknown events are intentionally ignored.

Why it streams

Performance traces become large quickly. The parser scans only far enough to find the top-level traceEvents array, buffers one event, and hands that event to the analyzer. JSON.parse() still handles each event, which keeps the small parser concerned with boundaries rather than reimplementing JSON.

Development

npm test
npm run check

Tests cover hostile chunk boundaries, escaped strings, malformed input, thread selection, reporting, and CLI exit codes.

MIT

About

Stream Chrome performance traces and find main-thread stalls.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages