How to Profile PHP Application Performance with Xdebug
Xdebug is the standard profiling tool for PHP applications, providing detailed execution traces and cachegrind-format profiles that reveal exactly where your application spends its time. On your Breeze server, Xdebug profiling helps identify slow functions, excessive database calls, and memory-hungry operations.
Installing Xdebug
sudo apt install -y php-xdebug
Verify the installation:
php -v
# Should show "with Xdebug" in the output
php -m | grep xdebug
Configuring Xdebug for Profiling
Edit the Xdebug configuration file (typically at /etc/php/8.2/mods-available/xdebug.ini):
; Enable profiling mode
xdebug.mode = profile
; Trigger profiling only when requested (recommended)
xdebug.start_with_request = trigger
; Output directory for profile files
xdebug.output_dir = /tmp/xdebug-profiles
; Profile filename format
xdebug.profiler_output_name = cachegrind.out.%t.%p
Create the output directory:
sudo mkdir -p /tmp/xdebug-profiles
sudo chmod 777 /tmp/xdebug-profiles
Restart PHP-FPM to apply changes:
sudo systemctl restart php8.2-fpm
Triggering a Profile
With start_with_request = trigger, profiling only activates when you add a special parameter:
# Via browser - add XDEBUG_PROFILE to the URL
https://yourdomain.com/slow-page?XDEBUG_PROFILE=1
# Via curl
curl -b "XDEBUG_PROFILE=1" https://yourdomain.com/api/endpoint
# Via cookie (install a browser extension like Xdebug Helper)
Analyzing Profile Output
Profile files are in cachegrind format. Use these tools to visualize them:
# Install KCacheGrind (Linux GUI)
sudo apt install -y kcachegrind
# Open a profile file
kcachegrind /tmp/xdebug-profiles/cachegrind.out.1234.5678
# For command-line analysis, use webgrind or qcachegrind
Alternatively, use Webgrind, a web-based viewer you can run directly on your Breeze server:
cd /var/www
git clone https://github.com/jokkedk/webgrind.git
# Access via browser at http://yourdomain.com/webgrind/
Understanding the Profile Data
- Self time — time spent executing the function itself, excluding calls to other functions
- Inclusive time — total time including all child function calls
- Call count — how many times a function was invoked (high counts indicate loops or recursion)
- Callers/Callees — the call graph showing which functions call which
Common Performance Patterns to Look For
- N+1 queries — a database fetch function called hundreds of times inside a loop; fix with eager loading or JOINs
- Excessive autoloading —
spl_autoloadappearing high in the profile; use Composer's classmap optimization - String concatenation in loops — build arrays and implode instead of repeated concatenation
- Unoptimized regex —
preg_matchwith backtracking-heavy patterns on large inputs - Missing opcode cache — ensure OPcache is enabled in production
Production Safety
Never leave Xdebug profiling enabled on a production server. It adds significant overhead:
# Disable Xdebug in production
sudo phpdismod xdebug
sudo systemctl restart php8.2-fpm
# Or keep Xdebug installed but set mode to off
xdebug.mode = off
For production profiling, consider lighter alternatives like tideways or SPX that have lower overhead. For quick one-off profiling, use the trigger mode and remove it immediately after collecting data.
Quick Profiling with SPX
SPX is a lighter alternative built into PHP:
sudo pecl install spx
echo "extension=spx.so" | sudo tee /etc/php/8.2/mods-available/spx.ini
sudo phpenmod spx
Access the built-in web UI at /?SPX_KEY=dev&SPX_UI_URI=/ for real-time flame graphs without cachegrind files.