tracing.md

Tracing Turbopack

turbo-tasks comes with a tracing feature that allows to keep track of executions and their runtime and memory consumption. This is useful to debug and optimize the performance of Turbopack.

Logging

Inside of Next.js one can enable tracing with the NEXT_TURBOPACK_TRACING environment variable.

It supports the following special preset values:

  • 1 or overview: Basic user level tracing is enabled. (This is the only preset available in a published Next.js release)
  • next: Same as overview, but with lower-level debug and trace logs for Next.js's own crates
  • turbopack: Same as next, but with lower-level debug and trace logs for Turbopack's own crates
  • turbo-tasks: Same as turbopack, but also with verbose tracing of every Turbo-Engine function execution.

Alternatively, any directives syntax supported by tracing_subscriber::filter::EnvFilter can be used.

Additionally, these flags can be added to the comma-separated list (e.g. NEXT_TURBOPACK_TRACING=1,no-memory,zstd):

  • zstd: Compress the trace file with zstd (level 3). Recommended: it needs about the same CPU time as gz, but produces files about 25% smaller.
  • zstd-fast: Compress the trace file with zstd (level 1). Needs the least CPU time of all compression options, and the files are still smaller than with gz.
  • zstd-best: Compress the trace file with zstd (level 19).
  • gz: Compress the trace file with gzip (fast compression level).
  • gz-best: Compress the trace file with gzip (best compression level).
  • no-memory: Don't track memory. Skips the per-thread allocation counters and the process memory samples, which make up a large part of the trace file size. Allocation and memory value modes in the viewer will be empty.

When multiple compression flags are given, the last one is used. Compression runs on the background thread that writes the trace file. zstd-best and gz-best compress much slower than the other options and can fall behind on large traces (e.g. the turbo-tasks preset). The trace data waiting to be compressed is kept in memory, and the process waits for the remaining data to be written before it exits.

[!WARNING] A normal Next.js canary/stable release only includes the info level tracing. This is the tracing level intended for user-facing tracing.

For the more detailed tracing a custom Next.js build is required. See Developing for more information on how to create one.

With this environment variable, Next.js will write a .next-profiles/trace-turbopack.bin file with the tracing information in a binary format.

Viewer

To visualize the content of .next-profiles/trace-turbopack.bin, use the turbo-trace-viewer.

A video showing how to use the tool is available here.

This tool connects a WebSocket on port 57475 on localhost to connect to the trace-server. You can start the trace-server with the following command:

cargo run --bin turbo-trace-server --release -- /path/to/your/trace-turbopack.bin

# or
pnpm next internal trace .next-profiles/trace-turbopack.bin

Once the server is started, open https://trace.nextjs.org/ in your browser.

[!TIP] Make sure you're using a --release build when running the trace server. The trace server is very slow and this can make a very significant (10x) difference in performance.

The trace viewer allows to switch between multiple different visualization modes:

  • Aggregated spans: Spans with the same name in the same parent are grouped together.
  • Individual spans: Every span is shown individually.
  • ... in order: Spans are shown in the order they occur.
  • ... by value: Spans are sorted and spans with the largest value are shown first.
  • Bottom-up view: Instead of showing the total value, the self value is shown.

And there are different value modes:

  • Duration: The CPU time of each span is shown.
  • Allocated Memory: How much memory was allocated during the span.
  • Allocations: How many allocations were made during the span.
  • Deallocated Memory: How much memory was deallocated during the span.
  • Persistently Allocated Memory: How much memory was allocated but not deallocated during the span. It survives the span.

Trace file size

To see what takes up the space in a trace file, use turbo-trace-size. It breaks the file down by row type (Start, Enter, Exit, Record, ...), by span name, by attribute key and shows how many bytes are spent on repeated strings. Raw, gzip and zstd compressed files are supported. All sizes refer to the decompressed trace stream.

cargo run --bin turbo-trace-size --release -- /path/to/your/trace-turbopack.bin

# show more entries in the per span name and per attribute key tables
cargo run --bin turbo-trace-size --release -- /path/to/your/trace-turbopack.bin --top 100

Omitted Exit and Enter rows

To keep trace files small, an Exit of a span that is followed by an Enter of the same span on the same thread shortly after is omitted together with that Enter. That's common for async spans, which are exited and entered again on every poll. This only happens when nothing was (de)allocated in between and the gap is at most 1µs or 0.1% of the time since the last Enter row written on that thread (omitted Enter rows don't count; for nested spans, it can be later than the span's own Enter, which only makes the allowed gap smaller). The viewer shows the span as entered during these gaps, so its self time can be slightly too large.

Rendered from the public repository vercel/next.js on GitHub; content belongs to its authors. Cached for up to an hour.

contributing/turbopack/tracing.md · vercel/next.js | Docs MD