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

> Profile workerd performance with Perfetto tracing. Enable the --perfetto-trace flag, capture trace events, and analyze .perfetto files to find bottlenecks in C++ and JavaScript.

- Repository: [Cloudflare/workerd](https://github.com/cloudflare/workerd)
- Tags: performance
- Published: 2026-03-18

---

**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:

```bash

# 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`](https://github.com/cloudflare/workerd/blob/main/src/workerd/util/perfetto-tracing.h):

```cpp
#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`](https://github.com/cloudflare/workerd/blob/main/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`](https://github.com/cloudflare/workerd/blob/main/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`](https://github.com/cloudflare/workerd/blob/main/src/workerd/api/performance.h) and `src/workerd/api/performance.c++` that forwards calls to the underlying tracing system:

```javascript
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:

```bash

# 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`](https://github.com/cloudflare/workerd/blob/main/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`](https://github.com/cloudflare/workerd/blob/main/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`](https://github.com/cloudflare/workerd/blob/main/src/workerd/util/perfetto-tracing.h). You must include [`src/workerd/util/use-perfetto-categories.h`](https://github.com/cloudflare/workerd/blob/main/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.