How to Use Trace Filtering with Start and Stop Symbols in magic-trace
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, 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, user input is parsed into unevaluated symbol selections. The function evaluate_trace_filter in 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 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, 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 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:
# 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 demonstrates the filtering mechanism programmatically:
(* 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,src/trace.ml,src/for_range.ml, andsrc/trace_writer.ml. - Symbol resolution is lazy and supports interactive
fzfselection for ambiguous names. - Unmatched starts or stops are automatically discarded to maintain valid region pairing.
- Enable
-filter-same-symbol-jumpswhen 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 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.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →