NeuroAgent

PHP Profiling Without xhprof and xdebug: Alternative Methods

Complete guide to alternative PHP profiling methods without xhprof and xdebug. Learn about built-in functions, commercial and open-source tools for performance analysis.

Question

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?

NeuroAgent

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

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.

php
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:

php
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

php
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

php
function startMemoryTracking() {
    return memory_get_usage();
}

function getMemoryUsage($startMemory) {
    $currentMemory = memory_get_usage();
    return [
        'absolute' => $currentMemory,
        'relative' => $currentMemory - $startMemory
    ];
}

Comprehensive Profiling

php
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

php
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

php
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

php
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

  1. Yes, profiling without xhprof and xdebug is possible - PHP provides built-in functions for basic profiling, and there are also alternative open source tools.

  2. For profiling with a known initial method, you can use automatic methods via register_shutdown_function or asynchronous profiling.

  3. php-spx is recommended as the “gold standard” for detailed benchmarking without complex setup.

  4. Commercial solutions (Tideways, Blackfire) offer the most powerful profiling capabilities but require licensing.

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

  1. PHP: debug_backtrace - Manual
  2. A Comprehensive Guide to PHP’s debug_backtrace() Function
  3. The 6 Best PHP Profilers – Tideways
  4. PHP profiling tools - Z-Ray, Blackfire, Tideways, XHProf, XHGui
  5. r/PHP on Reddit: Best Performance Profiler
  6. Profiling PHP applications with XHGui
  7. Seiden Group: Profiling PHP Applications with Xdebug and QCachegrind
  8. Debugging PHP Code with debug_backtrace - Stack Overflow
  9. Print PHP Call Stack - Stack Overflow
  10. PHP Error Handling debug_backtrace() Function