skip to content

Profiling & Tracing Modes

Profile mode writes cachegrind files, trace mode logs every function call, and coverage mode feeds test suites. Interviewers ask what each mode costs and why Xdebug stays unloaded in production.

on this pageshow

explore

questions

6

With Xdebug 3, how does the xdebug.mode setting choose which features run, and how do comma-combined values and XDEBUG_MODE change it?

level: middleimportance: must knowfreq 40%

answer

  1. one setting replaced many switches
  2. default is develop, not off
  3. comma list: develop,trace
  4. read once at process start
  5. environment variable wins over php.ini

basics

~20 s

In Xdebug 3, xdebug.mode lists the features to enable (off, develop, coverage, debug, gcstats, profile, trace), comma-separated to combine them; it defaults to develop, is fixed when PHP starts, and the XDEBUG_MODE environment variable overrides it.

solid answer

~40 s

Xdebug 3 replaced Xdebug 2's per-feature switches (`xdebug.profiler_enable`, `xdebug.auto_trace`, `xdebug.coverage_enable`) with one setting, `xdebug.mode`. Its values are `off`, `develop`, `coverage`, `debug`, `gcstats`, `profile` and `trace`, and you combine them with commas, e.g. `xdebug.mode=develop,trace`. The default is `develop`, so an installed but unconfigured Xdebug is never truly off. The mode is locked in when the PHP process starts: it belongs in `php.ini` or a `99-xdebug.ini`, and `.htaccess`, `.user.ini`, `php_admin_value` and `ini_set()` cannot change it. For a one-off run you set the `XDEBUG_MODE` environment variable instead, e.g. `XDEBUG_MODE=profile php report.php`; it takes precedence over `xdebug.mode` without changing that setting's value. An invalid `XDEBUG_MODE` falls back to `xdebug.mode`, and an invalid `xdebug.mode` falls back to `develop`.

code

ini · 8 lines
ini
; /etc/php/8.5/cli/conf.d/99-xdebug.ini
zend_extension=xdebug

; develop helpers plus function traces; read once at startup
xdebug.mode=develop,trace

; trace only when XDEBUG_TRIGGER is present
xdebug.start_with_request=trigger

go deeper

for a junior

Recall that xdebug.mode names the features Xdebug enables, that its default is develop, and that you can list several modes with commas.

for a middle

Explain that the mode is read once at process start, why .user.ini and ini_set() cannot change it, and how XDEBUG_MODE overrides it for one command.

for a senior

Show you can audit a setup: spot Xdebug 2 directives that do nothing, the silent fallback to develop on an invalid value, and an environment variable stripped before PHP sees it.

for a principal

Weigh one shared image with xdebug.mode=off and per-command XDEBUG_MODE against separate dev and CI images, trading convenience against the risk of Xdebug reaching production.

## One setting instead of many switches **Xdebug** is a PHP extension (loaded with `zend_extension=xdebug`) that bundles several independent tools: development helpers, a step debugger, a profiler, a function tracer, code-coverage collection and garbage-collection statistics. Xdebug 2 switched each tool on with its own directive — `xdebug.remote_enable`, `xdebug.profiler_enable`, `xdebug.auto_trace`, `xdebug.coverage_enable`, `xdebug.default_enable`. Xdebug 3 replaced all of them with a single **mode** setting, `xdebug.mode`, so that Xdebug only pays overhead for the features you actually asked for. | Value | What it enables | |---|---| | `off` | Nothing; Xdebug only checks whether anything is enabled | | `develop` | Development helpers: the overloaded `var_dump()`, stack traces on errors, the recursion guard | | `coverage` | Code-coverage collection, mainly for PHPUnit | | `debug` | Step debugging with an IDE over DBGp | | `gcstats` | Garbage-collection statistics files | | `profile` | The profiler, writing Cachegrind-format files | | `trace` | Function traces and flame-graph data | ## Combining modes The value is a **comma-separated list**, so `xdebug.mode=develop,trace` gives you the nicer `var_dump()` and a function trace at the same time, and `xdebug.mode=debug,develop` is a common local setup. Two things to keep in mind: - Every mode you add costs something on every request, so combine only what you are using right now. Profiling and step debugging together make little sense: the time you spend paused at a breakpoint lands in the profile. - An unrecognised item makes the whole value invalid. Xdebug then logs a critical configuration message and falls back to `develop`, which is easy to miss. ## When the mode is decided `xdebug.mode` is a **system-level** setting that Xdebug reads once, while the PHP process starts up, and the features are wired into the engine at that moment. Consequences: 1. It must be set in `php.ini` or an extra ini file read at startup, such as `99-xdebug.ini` in the `conf.d` scan directory. 2. Per-directory files read per request — `.htaccess` and `.user.ini` — cannot set it, and neither can `php_admin_value` in an Apache vhost or a PHP-FPM pool. 3. `ini_set('xdebug.mode', 'profile')` inside a script cannot enable anything: by then the mode is locked in. 4. After editing the ini file you must restart PHP-FPM or the web server; a new request alone is not enough. On the command line, `php -d xdebug.mode=profile script.php` works, because `-d` is applied before the extension starts. ## The XDEBUG_MODE environment variable Setting `XDEBUG_MODE` in the environment of the PHP process **takes precedence** over `xdebug.mode` without changing that setting's value, so `ini_get('xdebug.mode')` can still report `develop` while profiling is actually running. This is the everyday way to switch a feature on for one command: ```bash XDEBUG_MODE=profile php bin/generate-report.php XDEBUG_MODE=coverage vendor/bin/phpunit --coverage-html build/coverage XDEBUG_MODE=off composer install ``` The variable is read at the same moment as the ini setting — process start — so for web requests it has to be present where the PHP-FPM service or web server itself starts. The Xdebug documentation also warns that servers can strip environment variables before PHP sees them; PHP-FPM's `clear_env` is on by default. If `XDEBUG_MODE` holds an invalid value, Xdebug logs a critical message and uses `xdebug.mode` instead. ## How the mode interacts with start_with_request The mode says which features *may* run; `xdebug.start_with_request` says *when* they activate. Its `default` value resolves per mode: `yes` for `profile` (every request is profiled), `trigger` for `trace` and `debug` (only requests carrying an `XDEBUG_TRIGGER` activate), and `no` for `gcstats`. Develop helpers and coverage do not use it: they are active whenever their mode is set. ## Why it matters in practice - **Installed means `develop`.** Installing the extension without writing `xdebug.mode` runs develop helpers on every request. - **Explicit `off` in shared images.** A container image used both locally and in CI often sets `xdebug.mode=off` in the ini file and turns features on per command with `XDEBUG_MODE`. - **CLI and web differ.** The CLI and PHP-FPM usually read different ini directories, so a mode set for one is not automatically set for the other. - **Replaced names are history.** Xdebug 2 directives such as `xdebug.profiler_enable` or `xdebug.remote_enable` do nothing in Xdebug 3; seeing them in a config is a sign the setup was never migrated.

  • You put xdebug.mode=profile in a project's .user.ini and nothing is profiled. Why?
    `.user.ini` files are read per request by the CGI/FPM SAPIs, but `xdebug.mode` is a system-level setting that Xdebug reads once at process start. It has to go into `php.ini` or a startup-scanned file such as `99-xdebug.ini`, followed by a restart of PHP-FPM, or be supplied through the `XDEBUG_MODE` environment variable of the process that starts PHP.
  • What happens if xdebug.mode contains a typo such as 'profiel'?
    The whole value is rejected: Xdebug logs a critical configuration message saying the mode is invalid and falls back to `develop`. So you get the development helpers and no profiler, with no fatal error to warn you. If the typo is in the `XDEBUG_MODE` environment variable instead, Xdebug logs it and uses the `xdebug.mode` setting.

saying these in an interview costs you the question

  • Xdebug does nothing until you set xdebug.mode, so installing it is harmless
  • ini_set('xdebug.mode', 'profile') at the top of a script starts the profiler
  • Xdebug 3 still reads xdebug.profiler_enable and xdebug.remote_enable
  • You can only run one Xdebug mode at a time
  • XDEBUG_MODE rewrites the xdebug.mode value that ini_get() reports
open as a page

In Xdebug 3's default develop mode, what changes about var_dump() output and PHP error messages?

level: juniorimportance: should knowfreq 28%

basics

~10 s

Develop mode overloads var_dump() to print the calling file and line with depth and size limits, adds a stack trace to error messages, and aborts runaway recursion at xdebug.max_nesting_level (512) with an Error.

open as a page

With Xdebug 3 installed, why does a PHPUnit coverage run collect nothing, and how do you enable coverage mode for that run only?

level: middleimportance: should knowfreq 42%

basics

~10 s

Xdebug 3 only collects coverage when coverage is in its mode, and the default mode is develop, so PHPUnit finds no active driver. Run XDEBUG_MODE=coverage vendor/bin/phpunit --coverage-html build/coverage to enable it for that run.

open as a page

A PHP report-generation request is slow; how do you set up Xdebug 3's profile mode to capture a cachegrind file for just that request?

level: middleimportance: should knowfreq 35%

basics

~10 s

Set xdebug.mode=profile with xdebug.start_with_request=trigger, then send XDEBUG_TRIGGER (query, POST, cookie or environment) with the report request; Xdebug writes a cachegrind.out file into xdebug.output_dir (default /tmp) for that request only.

open as a page

With Xdebug 3, what does trace mode record in a function trace, and when would you use it instead of profile mode?

level: middleimportance: should knowfreq 25%

basics

~20 s

Trace 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.

open as a page

Why do teams keep the Xdebug 3 extension unloaded on production PHP servers instead of relying on xdebug.mode=off?

level: seniorimportance: should knowfreq 38%

basics

~20 s

With xdebug.mode=off Xdebug costs little, but a missing or invalid mode means develop, XDEBUG_MODE can switch features on, and any active mode slows every call and can write request data to disk or screen. Not loading it removes those risks.

open as a page