Is it possible to profile PHP without using xhprof and xdebug?
Are there alternative PHP profiling methods that can provide a complete list of all methods and functions executing during page or script execution? Profiling with standard tools has proven difficult to configure, especially on Windows in combination with OpenServer.
I’m aware of the debug_backtrace() function, but this method is only effective when you have information about the last method in the call chain, as the function outputs a backtrace of the call stack.
Could you suggest if there are alternative approaches or analogs for profiling when only the first method in the execution chain is known?
Yes, PHP profiling without using xhprof and xdebug is possible. There are built-in PHP functions and alternative tools that allow you to track method and function execution, even if only the first method in the call chain is known.
Table of Contents
- Built-in PHP Profiling Methods
- Commercial Alternatives
- Open Source Profiling Tools
- Manual Profiling Methods
- Approaches for Profiling with Known Initial Method
- Comparison of Available Solutions
Built-in PHP Profiling Methods
PHP provides several built-in functions for profiling without the need to install extensions. The primary tool is the debug_backtrace() function, which returns an array with information about the current call stack.
The debug_backtrace() function returns an associative array containing information about function and method calls that led to the current execution point. This allows you to track the entire execution path of the script.
function debugExecution() {
$trace = debug_backtrace();
$result = "Current call stack:\n";
foreach ($trace as $item) {
if (isset($item['file']) && isset($item['line'])) {
$result .= basename($item['file']) . " (line {$item['line']}): ";
}
if (isset($item['function'])) {
$result .= $item['function'] . "()";
}
$result .= "\n";
}
return $result;
}
Another useful function is debug_print_backtrace(), which outputs the call stack directly to the output stream. This is convenient for quick debugging without the need for additional data processing.
To get a complete picture of execution, you can combine these functions with tracking of execution time and memory usage:
function profileFunction($functionName) {
$startTime = microtime(true);
$startMemory = memory_get_usage();
$result = $functionName();
$endTime = microtime(true);
$endMemory = memory_get_usage();
$executionTime = ($endTime - $startTime) * 1000; // in milliseconds
$memoryUsed = ($endMemory - $startMemory) / 1024; // in KB
$trace = debug_backtrace();
$callStack = [];
foreach ($trace as $item) {
if (isset($item['function']) && $item['function'] != 'profileFunction') {
$callStack[] = $item['function'];
}
}
return [
'result' => $result,
'execution_time' => $executionTime,
'memory_used' => $memoryUsed,
'call_stack' => array_reverse($callStack)
];
}
Commercial Alternatives
If you need more advanced profiling capabilities, there are commercial alternatives that don’t require complex setup:
Tideways
Tideways is a modern PHP profiler that is compatible with XHProf and provides low-level profiling with minimal overhead. Tideways is available in a free version for small projects and has a cloud version tideways.io that is compatible with Xhprof.
The cloud service tideways.io provides a profiler extension compatible with Xhprof and available for free use.
Blackfire
SensioLabs Blackfire is a commercial profiler with powerful performance analysis capabilities. Blackfire provides profiling visualization and deep analysis of application performance.
Zend Server Z-Ray
Zend Server Z-Ray is a profiling tool from Zend, integrated into Zend Server. Z-Ray provides real-time profiling and performance monitoring.
Open Source Profiling Tools
There are several open source alternatives that can be used for profiling PHP applications:
php-spx
According to discussions on Reddit, php-spx is considered the “gold standard” for detailed benchmarking. It’s an open source profiling tool that doesn’t require complex setup.
XHGui
XHGui is a web interface for analyzing XHProf profiling data. Although XHProf doesn’t have a version for PHP 7.x, Tideways provides a compatible extension.
Webgrind
Webgrind is a web interface for analyzing Xdebug profiling data, which perfectly complements Xdebug and provides convenient viewing of profiling results.
KCachegrind
KCachegrind is a profiling visualization tool that works in conjunction with Xdebug to provide detailed performance analysis.
Manual Profiling Methods
For basic profiling, you can use PHP’s built-in functions to measure time and memory:
Measuring Execution Time
function startTimer() {
return microtime(true);
}
function endTimer($startTime) {
return (microtime(true) - $startTime) * 1000; // in milliseconds
}
// Usage
$timer = startTimer();
// code to execute
$executionTime = endTimer($timer);
Monitoring Memory Usage
function startMemoryTracking() {
return memory_get_usage();
}
function getMemoryUsage($startMemory) {
$currentMemory = memory_get_usage();
return [
'absolute' => $currentMemory,
'relative' => $currentMemory - $startMemory
];
}
Comprehensive Profiling
class Profiler {
private $startTime;
private $startMemory;
private $traces = [];
public function start($functionName) {
$this->startTime = microtime(true);
$this->startMemory = memory_get_usage();
$this->traces[] = [
'function' => $functionName,
'start_time' => $this->startTime,
'start_memory' => $this->startMemory,
'backtrace' => debug_backtrace()
];
}
public function end() {
$endTime = microtime(true);
$endMemory = memory_get_usage();
return [
'execution_time' => ($endTime - $this->startTime) * 1000,
'memory_used' => $endMemory - $this->startMemory,
'traces' => $this->traces
];
}
}
Approaches for Profiling with Known Initial Method
If only the first method in the execution chain is known, you can use the following approaches:
Automatic Profiling with register_shutdown_function
function globalProfiler() {
$startTime = $_SERVER['REQUEST_TIME_FLOAT'] ?? microtime(true);
$startMemory = memory_get_peak_usage(true);
register_shutdown_function(function() use ($startTime, $startMemory) {
$endTime = microtime(true);
$endMemory = memory_get_peak_usage(true);
$profileData = [
'execution_time' => ($endTime - $startTime) * 1000,
'peak_memory' => ($endMemory - $startMemory) / 1024,
'included_files' => get_included_files(),
'defined_functions' => get_defined_functions(),
'backtrace' => debug_backtrace()
];
// Save profiling data
file_put_contents('profile_' . date('Y-m-d_H-i-s') . '.log', json_encode($profileData));
});
}
// Initialize at start
globalProfiler();
Asynchronous Profiling
class AsyncProfiler {
private static $enabled = true;
private static $profiles = [];
public static function enable() {
self::$enabled = true;
}
public static function disable() {
self::$enabled = false;
}
public static function profile($functionName, $callable) {
if (!self::$enabled) {
return $callable();
}
$startTime = microtime(true);
$startMemory = memory_get_usage();
$backtrace = debug_backtrace();
$result = $callable();
$endTime = microtime(true);
$endMemory = memory_get_usage();
self::$profiles[] = [
'function' => $functionName,
'execution_time' => ($endTime - $startTime) * 1000,
'memory_used' => $endMemory - $startMemory,
'backtrace' => $backtrace
];
return $result;
}
public static function getProfiles() {
return self::$profiles;
}
public static function saveProfile($filename = null) {
if (!$filename) {
$filename = 'profile_' . date('Y-m-d_H-i-s') . '.json';
}
file_put_contents($filename, json_encode(self::$profiles, JSON_PRETTY_PRINT));
}
}
// Usage
AsyncProfiler::profile('processData', function() {
// code to execute
return processData();
});
Profiling with Function Interception
class FunctionProfiler {
private static $profiles = [];
private static $originalFunctions = [];
public static function start() {
$functionsToProfile = ['file_get_contents', 'include', 'require', 'mysql_query', 'curl_exec'];
foreach ($functionsToProfile as $function) {
if (function_exists($function)) {
self::$originalFunctions[$function] = $function;
$profiler = self::class;
$wrapped = "
\$startTime = microtime(true);
\$startMemory = memory_get_usage();
\$result = call_user_func_array('{$profiler}::original_{$function}', func_get_args());
\$endTime = microtime(true);
\$endMemory = memory_get_usage();
{$profiler}::recordProfile('{$function}', \$startTime, \$endTime, \$startMemory, \$endMemory);
return \$result;
";
$lambda = create_function('', $wrapped);
self::overrideFunction($function, $lambda);
}
}
}
private static function overrideFunction($name, $newFunction) {
// Function override implementation should go here
// This can be achieved through runkit or similar extensions
}
public static function recordProfile($function, $startTime, $endTime, $startMemory, $endMemory) {
self::$profiles[] = [
'function' => $function,
'execution_time' => ($endTime - $startTime) * 1000,
'memory_used' => $endMemory - $startMemory,
'timestamp' => microtime(true),
'backtrace' => debug_backtrace()
];
}
}
Comparison of Available Solutions
| Solution | Type | Setup Requirements | Ease of Use | Profiling Detail |
|---|---|---|---|---|
| debug_backtrace() | Built-in | Minimal | Low | Medium |
| Manual profiling | Built-in | Minimal | Medium | Low |
| Tideways | Commercial | Medium | Medium | High |
| Blackfire | Commercial | Medium | Low | High |
| php-spx | Open Source | Minimal | Low | High |
| XHGui | Open Source | Medium | Medium | High |
| Webgrind | Open Source | Minimal (with Xdebug) | Low | Medium |
Conclusion
-
Yes, profiling without xhprof and xdebug is possible - PHP provides built-in functions for basic profiling, and there are also alternative open source tools.
-
For profiling with a known initial method, you can use automatic methods via register_shutdown_function or asynchronous profiling.
-
php-spx is recommended as the “gold standard” for detailed benchmarking without complex setup.
-
Commercial solutions (Tideways, Blackfire) offer the most powerful profiling capabilities but require licensing.
-
PHP’s built-in functions allow you to create your own profiling system with minimal setup costs, especially useful for projects with limited budgets or complex infrastructure.
For beginners and projects with limited resources, I recommend starting with PHP’s built-in functions and php-spx, which don’t require complex setup and provide sufficient information for performance analysis.
Sources
- PHP: debug_backtrace - Manual
- A Comprehensive Guide to PHP’s
debug_backtrace()Function - The 6 Best PHP Profilers – Tideways
- PHP profiling tools - Z-Ray, Blackfire, Tideways, XHProf, XHGui
- r/PHP on Reddit: Best Performance Profiler
- Profiling PHP applications with XHGui
- Seiden Group: Profiling PHP Applications with Xdebug and QCachegrind
- Debugging PHP Code with debug_backtrace - Stack Overflow
- Print PHP Call Stack - Stack Overflow
- PHP Error Handling debug_backtrace() Function