PHP Code Profiling for 1C-Bitrix (Xdebug, Blackfire)
Page loads 4 seconds. Nginx responds quickly, MySQL is fine — but PHP spends three seconds on something unclear. Without a profiler, this is guessing. With a profiler — specific function, specific line, specific time. Let's cover two tools actually used in Bitrix projects.
Xdebug: Profiling via Cachegrind
Xdebug is a PHP extension that can record script execution traces in cachegrind format. The file opens in KCachegrind (Linux) or QCacheGrind (Windows/Mac).
Installation (PHP 8.x):
pecl install xdebug
# or via apt: apt-get install php8.2-xdebug
Configuration in php.ini or 99-xdebug.ini:
[xdebug]
zend_extension=xdebug.so
xdebug.mode=profile
xdebug.output_dir=/tmp/xdebug
xdebug.profiler_output_name=callgrind.out.%p.%r
xdebug.start_with_request=trigger
xdebug.start_with_request=trigger — profiling starts only with ?XDEBUG_PROFILE=1 GET parameter or cookie. Important: don't enable always on production — cachegrind generation kills performance.
Request with profiling:
curl "https://site.ru/catalog/product/?XDEBUG_PROFILE=1" -o /dev/null
File callgrind.out.12345.xyz appears in /tmp/xdebug. Open in KCachegrind — see call tree, time percentage for each function.
What to Look For in Bitrix Profile
Typical bottlenecks that appear in cachegrind:
-
CIBlockElement::GetList()— called dozens of times per page without cache. Search profile for totalGetListtime — if over 30% of PHP time, problem is caching. -
CSQLWhere::GetQuery()— complex iblock filters without indexes. -
CBitrixComponent::includeComponent()— if 20+ nested components, each loads templates and CSS, overhead accumulates. -
CUser::IsAuthorized()— on some configs called on every API access, not cached between calls in one request. -
\Bitrix\Main\ORM\Query\Query::exec()— D7 queries withfetchAll()on large selections without limit.
Blackfire: Profiling with Aggregation
Blackfire is SaaS. Agent is installed on server, PHP extension (blackfire.so) intercepts execution, data goes to blackfire.io, result is interactive call graph in browser.
Installing agent and extension:
# Agent
wget -O - https://packages.blackfire.io/gpg.key | apt-key add -
echo "deb http://packages.blackfire.io/debian any main" > /etc/apt/sources.list.d/blackfire.list
apt-get update && apt-get install blackfire
# PHP extension
apt-get install blackfire-php
# Or via PECL:
pecl install blackfire
In php.ini:
extension=blackfire.so
blackfire.agent_socket=tcp://127.0.0.1:8307
Agent authorization:
blackfire agent:config --server-id=XXXX --server-token=YYYY
blackfire agent:start
Running profiling via CLI:
blackfire run php artisan bitrix:some-command
# or via curl:
blackfire curl https://site.ru/catalog/
Via browser extension Blackfire (Chrome/Firefox) — Profile button right on page.
Xdebug vs Blackfire: When to Use What
| Criterion | Xdebug cachegrind | Blackfire |
|---|---|---|
| Profiling overhead | High (~10–20x slowdown) | Low (~2x) |
| Analysis ease | KCachegrind (desktop) | Web interface, graph |
| Profile comparison | Manual | Built-in diff |
| CLI script profiling | Yes | Yes |
| Production use | Not recommended | Possible (carefully) |
| Cost | Free | Freemium |
Xdebug — on dev server for deep diagnostics. Blackfire — when need to compare "before and after" optimization or profile staging.
Practical Optimization Algorithm
- Baseline measurement. Take a page, run profiler, fix top-5 functions by time.
-
Root analysis. For each "expensive" function look at call stack — who calls it and how many times. Often
GetListis expensive not itself, but because called 40 times in a row. -
Caching. Enable cache on component (
CACHE_TYPE=A,CACHE_TIME=3600). Or cacheGetListresult manually via\Bitrix\Main\Data\Cache. - Recheck. Profile again, compare with baseline.
-
Query optimization. If query is slow — add needed fields to
$select, remove unnecessaryIBLOCK_SECTION_IDwithout index, rewrite fromGetListto D7 ORM.
Profiling Agents and CLI
For background agents (\Bitrix\Main\Diag\ExceptionHandler), cron scripts, and CLI commands, Xdebug starts via environment variable:
XDEBUG_MODE=profile XDEBUG_OUTPUT_DIR=/tmp/xdebug php -d xdebug.start_with_request=yes /path/to/bitrix-script.php
Blackfire via CLI:
blackfire run php /path/to/bitrix-script.php
Timeline
| Task | Duration |
|---|---|
| Xdebug setup on dev server | 2–4 hours |
| Blackfire setup (agent + extension) | 4–6 hours |
| Profiling one page + report | 2–4 hours |
| Profiling + fixing 3–5 bottlenecks | 2–5 days |
Profiling without subsequent optimization is pointless. We don't just take a profile and hand over a report — we fix found issues and measure the result.







