With Xdebug 3, what does trace mode record in a function trace, and when would you use it instead of profile mode?
answer
- order of calls vs totals per function
- trace.%c plus .xt
- trace_format 0, 1, 2; 3 and 4 flame graphs
- collect_return and collect_assignments off by default
- xdebug_start_trace() and xdebug_stop_trace()
basics
~20 sTrace mode writes every function call in order, with time index, memory, arguments and optionally return values, to a .xt file; profile mode aggregates cost per function into a cachegrind file. Trace answers what happened; profile answers where time went.
solid answer
~40 s`xdebug.mode=trace` writes a **function trace**: one line per call, indented by depth, with a time index, memory usage, the function name, its arguments (`xdebug.collect_params`, on by default) and the file and line. `xdebug.collect_return` and `xdebug.collect_assignments` add return values and assignments. The file goes to `xdebug.output_dir` named by `xdebug.trace_output_name` (default `trace.%c`, plus `.xt`). `xdebug.trace_format` picks human-readable (0), tab-separated (1), HTML (2) or flame-graph data (3 cost, 4 memory). Tracing starts on `XDEBUG_TRIGGER` by default, or from code with `xdebug_start_trace()`. A profile instead sums inclusive and self cost per function. Use a trace to learn the order of calls and which arguments reached them in unfamiliar code; use a profile to find where the time goes.
code
ini · 6 linesxdebug.mode=trace
xdebug.start_with_request=trigger
xdebug.trace_output_name=trace.%t.%R
xdebug.collect_return=1
; 0 human, 1 tab-separated, 2 HTML, 3 cost flame graph, 4 memory flame graph
xdebug.trace_format=0go deeper
Recall that trace mode logs every call in order to a .xt file and profile mode sums cost per function into a cachegrind file.
Explain the trace line fields, what collect_return and trace_format change, how trace.%c names files, and how xdebug_start_trace() limits a trace to one slice.
Choose the tool from the symptom, keep traces small with triggers and slices, and treat trace files as sensitive because they carry argument values.
Judge when heavyweight tracing is worth it against lighter evidence such as logs or a profile, given how much disk and time a full trace of a big request costs.
## Two different questions Xdebug has two modes that both watch every function call, and they answer different questions: - A **function trace** (`xdebug.mode=trace`) is a **chronological log**: each call, in the order it happened, with its arguments and position in the call tree. It answers *"what did this request actually do, and in which order?"* - A **profile** (`xdebug.mode=profile`) is an **aggregate**: for every function, how often it was called and how much time and memory it cost, alone (**self**) and with its callees (**inclusive**). It answers *"where did the time go?"* | | Trace mode | Profile mode | |---|---|---| | Output | `.xt` file, one line per call | Cachegrind file, totals per function | | Order of calls | Preserved | Lost (summed) | | Arguments and return values | Available | Not recorded | | Best viewer | A text editor, `less`, a script | KCacheGrind, QCacheGrind, Webgrind, PhpStorm | | Default activation | On a trigger | Every request | ## What a trace line holds In the default human-readable format (`xdebug.trace_format=0`) each line shows the **time index** since the request started, the **memory** in use, an arrow indented by call depth, the **function** with its **arguments**, and the **file and line** of the call: ```text 0.0004 114272 -> str_split('Xdebug') /app/trace.php:8 0.0007 117424 -> ret_ord($c = 'X') /app/trace.php:10 0.0007 117584 -> ord('X') /app/trace.php:5 ``` What goes in is controlled by settings: - `xdebug.collect_params` (default `true`) — include arguments. Xdebug 3.0 removed this setting and 3.3 brought it back as an on/off switch. - `xdebug.collect_return` (default `false`) — add a `>=>` line with each return value. - `xdebug.collect_assignments` (default `false`) — record variable assignments too. - `xdebug.var_display_max_children`, `_data` and `_depth` — limit how much of each value is printed. ## Formats and file names `xdebug.trace_format` selects the layout: 1. `0` — human-readable, indented (default). 2. `1` — tab-separated entry and exit records, meant for scripts; Xdebug's source ships a `tracefile-analyser.php` for it. 3. `2` — a simple HTML table. 4. `3` and `4` — "cost" and "memory" **flame-graph** data, which an external tool turns into an SVG. Flame graphs are a special kind of trace, so they need `trace` in the mode. The file is written to `xdebug.output_dir` (default `/tmp`) with the name from `xdebug.trace_output_name`, default `trace.%c` — `%c` is a CRC32 of the working directory — plus `.xt`, plus `.gz` when `xdebug.use_compression` is on. Because every request from the same directory gets the same name, use `%t`, `%u` or `%R` to keep traces apart. `xdebug.trace_options` is a bitfield: `0x01` appends to an existing file, `0x08` stops the automatic `.xt` suffix. ## Starting and stopping a trace For trace mode, `xdebug.start_with_request` resolves to **`trigger`** by default, so a trace starts only when `XDEBUG_TRIGGER` (or the legacy `XDEBUG_TRACE`) is present in the query, POST data, a cookie or the environment. With `start_with_request=yes` every request is traced. You can also trace just a slice of code: ```php <?php xdebug_start_trace('/tmp/checkout'); // returns the file name, or false on failure $order = $checkout->place($cart); xdebug_stop_trace(); ``` `xdebug_start_trace()` works even with `start_with_request=no`, but only when `trace` is in the mode; otherwise Xdebug raises a "Functionality is not enabled" notice. ## Choosing between them - **Unfamiliar code path** ("which listener fired, with which arguments?") — trace. - **"Why is this request slow?"** — profile first; a trace of a slow request is often millions of lines. - **"Why did this value end up wrong?"** — a trace with `collect_return` shows each intermediate result without stopping the program, which suits code a step debugger disturbs, such as timeouts. - **Cost:** tracing writes a line for every call, so traces are large and slow the request more than people expect; and with `trace` in the mode Xdebug also switches off OPcache's optimizer so that every call stays visible. A trace also writes argument values to disk — passwords, tokens and personal data included — so treat trace files as sensitive and delete them after use.
- You traced two different requests but only one .xt file is left in /tmp. Why?The default `xdebug.trace_output_name` is `trace.%c`, where `%c` is a CRC32 of the current working directory. Requests served from the same directory produce the same name, so the second trace overwrote the first. Add `%t`, `%u`, `%r` or `%R` to the pattern.
- How do you produce a flame graph with Xdebug 3?Flame graphs are a variant of the function trace: set `xdebug.mode=trace` and `xdebug.trace_format=3` for a cost graph or `4` for a memory graph, trigger the request, then convert the output file to an SVG with an external flame-graph script. The docs advise tracing the whole request rather than a slice started with `xdebug_start_trace()`.
A trace is a till roll listing every item scanned in order; a profile is the end-of-day report totalling sales per product. The till roll shows what happened; the report shows where the money went.
saying these in an interview costs you the question
- A trace and a profile contain the same data in different formats
- Trace mode records return values by default
- Trace mode starts on every request by default, like profile mode
- Trace files are safe to share because they only hold function names