# How to Use Trace Filtering with Start and Stop Symbols in magic-trace

> Learn to use trace filtering with start and stop symbols in magic-trace. Reduce trace output and analysis complexity by capturing specific execution periods. Master -filter flag.

- Repository: [Jane Street/magic-trace](https://github.com/janestreet/magic-trace)
- Tags: how-to-guide
- Published: 2026-05-24

---

**Trace filtering in magic-trace allows you to capture only the execution period between two specified function symbols using the `-filter` flag, significantly reducing trace output size and analysis complexity.**

magic-trace, the high-resolution tracing tool from Jane Street, provides a powerful **trace filtering** mechanism that lets you isolate specific execution windows using start and stop symbols. By leveraging the `-filter` command-line flag, you can limit trace output to only the critical sections of your program's execution, making performance analysis more efficient. This feature is particularly valuable when debugging large applications where full traces would be prohibitively large or noisy.

## Understanding the `-filter` Flag Syntax

The `-filter` flag accepts a pair of symbols in the specific format `"( <START> <STOP> )"`, where both identifiers represent function entry points in your binary. According to the implementation in [`src/trace_filter.ml`](https://github.com/janestreet/magic-trace/blob/main/src/trace_filter.ml), this string is parsed into a `Trace_filter.Unevaluated.t` value containing `Symbol_selection.t` objects for both the start and stop positions.

When you execute a filtered trace, the symbols remain unevaluated until the target binary's ELF is loaded, enabling support for both static symbols and dynamic lookup via `fzf`. This lazy evaluation approach allows `magic-trace` to resolve ambiguous symbol names interactively if multiple matches exist.

## How Trace Filtering Works Internally

The implementation spans four core modules, each handling a distinct phase of the filtering pipeline.

### Symbol Parsing and Resolution

In [`src/trace_filter.ml`](https://github.com/janestreet/magic-trace/blob/main/src/trace_filter.ml), user input is parsed into unevaluated symbol selections. The function `evaluate_trace_filter` in [`src/trace.ml`](https://github.com/janestreet/magic-trace/blob/main/src/trace.ml) then converts these selections into concrete symbol name strings using `Symbol_selection.evaluate`. If symbols are ambiguous, this process invokes `fzf` for interactive disambiguation before returning a `Trace_filter.t` containing the resolved `start_symbol` and `stop_symbol` strings.

### Locating Symbol Hits in the Event Stream

The module [`src/for_range.ml`](https://github.com/janestreet/magic-trace/blob/main/src/for_range.ml) contains `range_hit_times`, which scans decoded perf events for `Trace` events whose destination matches either the start or stop symbol. Each match generates a `Symbol_hit.t` record tagged with `kind = Start | Stop` and its precise timestamp. The function `remove_unmatched_hits` then normalizes these sequences into clean alternating start-stop pairs, discarding unpaired starts or stops to prevent region calculation errors.

### Annotating and Filtering Events

Within `decode_events_and_annotate` in [`src/for_range.ml`](https://github.com/janestreet/magic-trace/blob/main/src/for_range.ml), the decoder folds over the event stream alongside the hit sequence. A Boolean flag `in_filtered_region` toggles each time a matched start-stop pair is encountered, and every event receives a `should_write` flag that is true only while inside the filtered region.

### Writing the Filtered Output

Finally, [`src/trace_writer.ml`](https://github.com/janestreet/magic-trace/blob/main/src/trace_writer.ml) implements `maybe_start_filtered_region` and `maybe_stop_filtered_region` to consume these annotations. The writer checks the `should_write` flag for each event and discards anything outside the filtered region, ensuring the final trace contains exclusively the events occurring between your specified start and stop symbols.

## Practical Command-Line Examples

Basic usage requires wrapping your symbol names in parentheses:

```bash

# Capture only execution between start_trigger and stop_trigger

magic-trace -filter "( start_trigger stop_trigger )" -- ./my_program

# Filter using the same symbol for both start and stop (requires additional flag)

magic-trace -filter "( foo foo )" -filter-same-symbol-jumps -- ./my_program

```

The `-filter-same-symbol-jumps` flag is essential when start and stop symbols are identical, as `magic-trace` defaults to discarding jumps from a symbol to itself.

## OCaml Implementation Example

The following test from [`test/test.ml`](https://github.com/janestreet/magic-trace/blob/main/test/test.ml) demonstrates the filtering mechanism programmatically:

```ocaml
(* test/test.ml – filtered trace expect test *)
let%expect_test "filtered trace" =
  let%bind.With _dirname = Expect_test_helpers_async.within_temp_dir in
  let events =
    Trace_helpers.(
      add Call 0 "base_fn";
      add Call 1 "pre_fn";
      add Call 4 "start_trigger";
      add Call 5 "fn0";
      add Return 6 "start_trigger";
      add Return 7 "container";
      add Call 8 "fn1";
      add Return 9 "container";
      add Return 10 "base_fn";
      add Call 13 "stop_trigger";
      add Return 14 "base_fn";
      events ())
  in
  let%bind () =
    dump_using_file
      ~range_symbols:
        { Trace_filter.start_symbol = "start_trigger"
        ; stop_symbol = "stop_trigger"
        }
      events
  in
  [%expect {| … output contains only events between start_trigger and stop_trigger … |}]

```

This test confirms that the output contains only events timestamped between the `start_trigger` and `stop_trigger` symbols.

## Handling Edge Cases

Understanding how `magic-trace` handles unusual scenarios ensures robust trace collection.

### Missing Start or Stop Symbols

If either symbol cannot be resolved, `evaluate_trace_filter` returns `None`. Consequently, the writer never enters a filtered region and produces no output, preventing the generation of misleading empty traces.

### Same-Symbol Filtering

By default, `magic-trace` discards jumps from a symbol to itself. To capture regions bounded by the same function entry, pass `-filter-same-symbol-jumps` or set the corresponding environment variable.

### Multiple and Overlapping Regions

When the start symbol appears multiple times before a stop, `remove_unmatched_hits` pairs each start with the next available stop. Multiple overlapping regions are supported—the `in_filtered_region` Boolean toggles on at each start and off at each subsequent stop, creating a union of all valid intervals.

### Ambiguous Symbol Resolution

When a symbol name matches multiple functions, `magic-trace` launches `fzf` (if installed) to prompt for the specific instance. This interactive resolution occurs for both start and stop symbols independently.

## Summary

- Use `-filter "( start_symbol stop_symbol )"` to limit traces to specific execution windows.
- The filtering pipeline spans [`src/trace_filter.ml`](https://github.com/janestreet/magic-trace/blob/main/src/trace_filter.ml), [`src/trace.ml`](https://github.com/janestreet/magic-trace/blob/main/src/trace.ml), [`src/for_range.ml`](https://github.com/janestreet/magic-trace/blob/main/src/for_range.ml), and [`src/trace_writer.ml`](https://github.com/janestreet/magic-trace/blob/main/src/trace_writer.ml).
- Symbol resolution is lazy and supports interactive `fzf` selection for ambiguous names.
- Unmatched starts or stops are automatically discarded to maintain valid region pairing.
- Enable `-filter-same-symbol-jumps` when filtering uses identical start and stop symbols.

## Frequently Asked Questions

### What happens if my start or stop symbol is not found in the binary?

If either symbol cannot be located, `evaluate_trace_filter` returns `None` and the trace writer never enters a filtered region. The tool produces no trace output for that filter configuration, effectively protecting you from capturing irrelevant data.

### Can I use the same function as both the start and stop symbol?

Yes, but you must pass the `-filter-same-symbol-jumps` flag. By default, `magic-trace` ignores self-jumps to prevent noise, so this explicit flag is required to capture regions where the same function marks both boundaries.

### How does magic-trace handle multiple calls to the start symbol before encountering a stop?

The `remove_unmatched_hits` function in [`src/for_range.ml`](https://github.com/janestreet/magic-trace/blob/main/src/for_range.ml) normalizes the hit sequence by pairing each start with the next available stop. This creates a union of all intervals, ensuring you capture every execution segment between matched pairs while discarding stray unpaired hits.

### Does trace filtering work with dynamically loaded symbols?

Yes. Because symbol evaluation is deferred until the target binary's ELF is loaded, `magic-trace` supports both static symbols and dynamic lookup. If symbols are ambiguous, the tool can invoke `fzf` for interactive selection regardless of whether the symbols come from the main binary or shared libraries.