How to Perform workerd Performance Profiling Using Built-In Perfetto Tracing

Enable the --perfetto-trace flag when starting workerd to capture low-overhead C++ and JavaScript trace events, then analyze the resulting .perfetto file in the Perfetto UI or CLI to identify bottlenecks across native and application layers.

Cloudflare's workerd runtime ships with built-in Perfetto tracing support that captures detailed timing data from both the native C++ layer and JavaScript worker code. By leveraging the --perfetto-trace command-line option implemented in src/workerd/server/workerd.c++, you can generate comprehensive profiles that correlate native request handling with performance.mark() calls in your application logic. This guide covers the complete workflow from enabling traces to analyzing request latency.

Enabling Perfetto Tracing at Startup

workerd exposes tracing through the -p or --perfetto-trace flag. The syntax requires a file path followed by an equals sign and a comma-separated list of categories:


# Capture all default categories in the workerd::traces namespace

workerd -p /tmp/trace.perfetto=*

# Capture only request handling and async I/O categories

workerd -p /tmp/trace.perfetto=request,async_io

The flag parsing logic resides in src/workerd/server/workerd.c++ around line 802, where addOptionWithArg processes the input and stores values in perfettoTraceDestination and perfettoTraceCategories. These members initialize a PerfettoSession at lines 1416-1418 when the server starts. Use * to enable every category, or leave the category list empty after the equals sign to disable all categories.

Instrumenting C++ Code with TRACE_EVENT Macros

To emit custom events from native code, include the convenience header and use the standard Perfetto macros defined in src/workerd/util/perfetto-tracing.h:

#include <workerd/util/use-perfetto-categories.h>

void handleRequest(jsg::Lock& js) {
  TRACE_EVENT("request", "handle_start", "method", request.method.cStr());
  
  // ... request handling logic ...
  
  TRACE_EVENT_END("request");
}

When WORKERD_USE_PERFETTO is undefined, these macros expand to no-ops (see the #else branch in perfetto-tracing.h), making them safe to retain in production builds without impacting performance. To add custom categories, extend the PERFETTO_DEFINE_CATEGORIES_IN_NAMESPACE(workerd::traces, ...) block in perfetto-tracing.h with new perfetto::Category("your_name") entries.

Using the JavaScript Performance API

workerd implements a minimal Performance object in src/workerd/api/performance.h and src/workerd/api/performance.c++ that forwards calls to the underlying tracing system:

export default {
  async fetch(request) {
    performance.mark('fetch_start');
    
    const response = await fetch(request);
    
    performance.measure('fetch_duration', 'fetch_start');
    console.log('Duration:', performance.getEntriesByName('fetch_duration')[0].duration);
    return response;
  }
};

When tracing is active, performance.mark() and performance.measure() automatically generate PerformanceMark and PerformanceMeasure entries that internally call TRACE_EVENT. This allows seamless correlation between JavaScript timing markers and native C++ execution phases in a single trace file.

Analyzing Trace Output

Web Interface

Open the trace file generated by src/workerd/util/perfetto-tracing.c++ in the Perfetto UI:

  1. Navigate to https://ui.perfetto.dev
  2. Drag and drop the .perfetto file onto the window
  3. In the Tracks panel, enable the workerd group to view native events
  4. Use the Search bar to filter by category (e.g., request or custom names)
  5. Hover over events to inspect timestamps, durations, and attached arguments

Command Line

Use the perfetto CLI to process traces programmatically:


# Human-readable text output

perfetto -i /tmp/trace.perfetto --txt

# Generate an SVG flamegraph

perfetto -i /tmp/trace.perfetto --flamegraph > profile.svg

The trace contains nanosecond-resolution timestamps for all enabled categories, enabling precise analysis of event-loop latency and async I/O patterns.

Summary

  • Enable tracing by passing -p /path/to/file.perfetto=categories when starting workerd; the parser in src/workerd/server/workerd.c++ handles the category list and initializes a PerfettoSession
  • Instrument C++ code using TRACE_EVENT macros from src/workerd/util/perfetto-tracing.h, which safely compile to no-ops when tracing is disabled
  • Instrument JavaScript via the standard performance.mark() and performance.measure() API implemented in src/workerd/api/performance.c++
  • Analyze results using the Perfetto web UI or CLI tools to view synchronized timelines of native and JavaScript execution

Frequently Asked Questions

Does Perfetto tracing impact production performance?

When disabled, the TRACE_EVENT macros defined in src/workerd/util/perfetto-tracing.h expand to no-ops, resulting in zero runtime overhead. When enabled, Perfetto uses lock-free, memory-mapped ring buffers designed for minimal latency, though you should target specific categories rather than using * in high-traffic production environments to reduce serialization overhead.

Can I define custom trace categories?

Yes. Add new categories to the PERFETTO_DEFINE_CATEGORIES_IN_NAMESPACE(workerd::traces, ...) declaration in src/workerd/util/perfetto-tracing.h. You must include src/workerd/util/use-perfetto-categories.h in any translation unit that emits events with your custom category names.

How do I correlate JavaScript and C++ events in the same trace?

The JavaScript Performance API implementation in src/workerd/api/performance.c++ internally calls the same TRACE_EVENT infrastructure used by C++ code. When you open the trace in Perfetto, events from both layers appear on synchronized timelines. Use distinct category names (e.g., "js_handler" vs "request") to filter and compare execution phases across language boundaries.

What file format does workerd use for traces?

workerd generates standard Perfetto trace files (conventionally .perfetto) that follow the ProtoTrace format. These files can be opened directly in the Perfetto UI, processed with the perfetto CLI, or converted to other formats like JSON or flamegraphs using the standard Perfetto toolchain.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →