<?php
/*
+--------------------------------------------------------------------------
|   IPB NULL EN Kernel [Application Profiling]
|	IPB NULL EN
|   =============================================
|   by IPB NULL EN
|   (c) 2001 - 2006 IPB NULL EN, Inc.
|   http://ipb-null.narod.ru/
|   =============================================
|   Web: http://www.ipb-null.narod.ru
+---------------------------------------------------------------------------
|   THIS IS NOT FREE / OPEN SOURCE SOFTWARE
+---------------------------------------------------------------------------
|
|   > Application Profiling
|   > Module written by Brandon Farber
|   > Date started: Monday 28th February 2005 16:46 
|
|	> Module Version Number: 1.0.0
+--------------------------------------------------------------------------
*/

/**
* IPS Kernel Pages: Application Profiling Layer [PHP5]
*
* @package		IPS_KERNEL
* @subpackage	ApplicationProfiling
* @author		Brandon Farber
* @copyright	IPB NULL EN, Inc.
* @version		1.0
*/

/**
 * Gateway for application profiling
 *
 *	$a = new class_profiling();
 *	$a->output_results( true, false );
 */
 
//-----------------------------------------
// Define KERNEL_PATH if not
// defined - i.e. installation
//-----------------------------------------
 
if ( ! defined('KERNEL_PATH') )
{
 	if( defined('INS_KERNEL_PATH') )
 	{
	 	define( 'KERNEL_PATH', INS_KERNEL_PATH );
 	}
 	else
 	{
	 	define( 'KERNEL_PATH', str_replace( "//", "/", str_replace( "\\", "/", dirname( __FILE__ ) ) ) . "/" );
 	}
}

//-----------------------------------------
// Define ROOT_PATH if not
//-----------------------------------------
 
if ( ! defined('ROOT_PATH') )
{
	$path 		= str_replace( "//", "/", str_replace( "\\", "/", dirname( __FILE__ ) ) );
	$path_bits	= explode( "/", $path );
	array_pop( $path_bits );
	
 	define( 'ROOT_PATH', implode( "/", $path_bits ) . "/" );
}


class class_profiling
{
	var $start_time			= array();
	var $end_time			= array();
	var $running_time		= array();
	
	var $start_memory		= array();
	var $end_memory			= array();
	
	var $descriptions		= array();
	
	var $timer_stack		= array();
	var $internal_count		= array();
	var $stack_trace		= null;
	
	var $cur_timer			= null;
	var $init_time			= 0;
	
	var $init_memory		= 0;
	var $total_memory		= 0;
	var $peak_memory		= 0;
	
	
	function __construct( $start_apd = false )
	{
		$this->start_time		= array();
		$this->end_time			= array();
		$this->running_time		= array();
		
		$this->start_memory		= array();
		$this->end_memory		= array();
		
		$this->descriptions		= array();
		
		$this->timer_stack		= array();
		$this->internal_count	= array();
		$this->stack_trace		= "Profiling Class Initialized\n";
		
		$this->cur_timer		= null;
		$this->init_time		= $this->get_micro_seconds();
		
		$this->init_memory		= $this->get_current_memory();
		$this->total_memory		= $this->init_memory;
		$this->peak_memory		= $this->init_memory;
		
		if( $start_apd == true && function_exists( 'apd_set_pprof_trace' ) )
		{
			apd_set_pprof_trace();
		}
		
		$this->start_session( 'Overall' );
	}
	
	
	function start_session( $name, $desc='' )
	{
		$x = array_push( $this->timer_stack, $this->cur_timer );
		
		if( isset($this->timer_stack[ $x - 1 ]) )
		{
			$this->_suspend_timer( $this->timer_stack[ $x - 1 ] );
		}
		
		$this->start_time[ $name ]		= $this->get_micro_seconds();
		$this->start_memory[ $name ]	= $this->get_current_memory();
		
		$this->cur_timer				= $name;
		
		$this->descriptions[ $name ]	= $desc;
		
		if( array_key_exists( $name, $this->internal_count ) )
		{
			$this->internal_count[ $name ]++;
		}
		else
		{
			$this->internal_count[ $name ] = 1;
		}
		
		$this->stack_trace				.= "Started Session '{$name}'\n";
		
		$backtrace				= debug_backtrace();
		
		if( is_array($backtrace) AND count($backtrace) )
		{
			$cnt = 0;
			
			foreach( array_reverse( $backtrace ) as $trace )
			{
				$cnt++;

				$this->stack_trace	.= "\t{$cnt}. Function '{$trace['function']}' in {$trace['file']} (line {$trace['line']})\n";
			}
		}
	}
	
	
	function stop_session( $name )
	{
		$this->end_time[ $name ]			= $this->get_micro_seconds();
		$this->end_memory[ $name ]			= $this->get_current_memory();
		
        if( !array_key_exists( $name, $this->running_time ) )
        {
            $this->running_time[ $name ] 	= $this->get_elapsed_time( $name );
        }
        else
        {
            $this->running_time[ $name ] 	+= $this->get_elapsed_time( $name );
        }
        
        $this->cur_timer					= array_pop( $this->timer_stack );
        
		$this->stack_trace				.= "Stopped Session '{$name}'\n";
		
        if( isset($this->cur_timer) )
        {
        	$this->_resume_timer( $this->cur_timer );
    	}
	}
	
	
	
	function get_elapsed_time( $name )
	{
		if( !array_key_exists( $name, $this->start_time ) )
		{
			return 0;
		}
		
		if( array_key_exists( $name, $this->end_time ) )
		{
			return ( $this->end_time[ $name ] - $this->start_time[ $name ] );
		}
		else
		{
			return( $this->get_micro_seconds() - $this->start_time[ $name ] );
		}
	}
	
	
	function get_total_time()
	{
		return ( $this->get_micro_seconds() - $this->init_time );
	}
	
	
	function get_peak_memory()
	{
		if( function_exists( 'memory_get_peak_usage' ) )
		{
			$this->peak_memory = memory_get_peak_usage();
		}
		else
		{
			if( count( $this->start_memory ) )
			{
				foreach( $this->start_memory as $mem_entry )
				{
					if( $mem_entry > $this->peak_memory )
					{
						$this->peak_memory = $mem_entry;
					}
				}
			}
			
			if( count( $this->end_memory ) )
			{
				foreach( $this->end_memory as $mem_entry )
				{
					if( $mem_entry > $this->peak_memory )
					{
						$this->peak_memory = $mem_entry;
					}
				}
			}
		}
		
		return $this->peak_memory;
	}

	
	function _resume_timer( $name )
	{
		$this->stack_trace			.= "Resumed Session '{$name}'\n";
		
		$this->start_time[ $name ]	= $this->get_micro_seconds();
	}
	
	
	function _suspend_timer( $name )
	{
		$this->stack_trace			.= "Suspended Session '{$name}'\n";
		
		$this->end_time[ $name ]	= $this->get_micro_seconds();
		
        if( !array_key_exists( $name, $this->running_time ) )
        {
            $this->running_time[ $name ] 	= $this->get_elapsed_time( $name );
        }
        else
        {
            $this->running_time[ $name ] 	+= $this->get_elapsed_time( $name );
        }
    }
	
	
	
	function get_micro_seconds()
	{
		$tmp = explode( " ", microtime() );
		
		return ( $tmp[0] + $tmp[1] );
	}
	
	
	function get_current_memory()
	{
		if( ! function_exists( 'memory_get_usage' ) )
		{
			return 0;
		}
		else
		{
			return memory_get_usage();
		}
	}
	
	
	function format_memory( $val=0 )
	{
		if( $val >= 1048576 )
		{
			return ( round( ( $val / 1048576 * 100 ), 2 ) / 100 ) . 'MB';
		}
		else if( $val >= 1024 )
		{
			return ( round( ( $val / 1024 * 100 ), 2 ) / 100 ) . 'KB';
		}
		else
		{
			return intval( $val ) . 'b';
		}
	}
	
	
    function output_results( $display_backtrace=true, $log=false )
    {
	    $this->stop_session( 'Overall' );
	    $this->total_memory	= $this->get_current_memory();
	    
	    $timed_total		= 0;
	    $percent_total		= 0;
	    $output				= null;
	    
	    $overall_time		= $this->get_total_time();
	    $overall_memory		= $this->get_peak_memory();
	    
	    if( $log == false )
	    {
		    print "<pre>\n";
	    }
	    
	    ksort($this->descriptions);
	    
	    $output  = "============================================================================\n";
	    $output .= "                              PROFILER OUTPUT\n";
	    $output .= "============================================================================\n";
	    $output .= "Calls			Time	Memory		Routine\n";
	    $output .= "----------------------------------------------------------------------------\n";
	    
	    foreach( $this->descriptions as $key => $val )
	    {
		    $this_total		= $this->running_time[$key];
		    $this_count		= $this->internal_count[$key];
		    $this_percent	= ( $this_total / $overall_time ) * 100;
		    $this_memory	= $this->format_memory( $this->end_memory[ $key ] - $this->start_memory[ $key ] );
		    
		    $timed_total	+= $this_total;
		    $percent_total	+= $this_percent;
		    $mem_total		+= $this->end_memory[ $key ] - $this->start_memory[ $key ];
		    
		    $output .= sprintf( "%3d	%3.4f ms (%3.2f %%)  %s	 %s\n", $this_count, $this_total * 1000, $this_percent, $this_memory, $key);
	    }
	    
	    $output .= "\n";
	    
	    $missed_time	= $overall_time - $timed_total;
	    $missed_percent	= ( $missed_time / $overall_time ) * 100;
	    $percent_total	+= $missed_percent;
	    $missed_memory	= $this->format_memory( $this->total_memory - $this->init_memory - $mem_total );
	    
		$output .= sprintf( "   	 %3.4f ms (%3.2f %%)  %s  %s\n", $missed_time * 1000, $missed_percent, $missed_memory, 'Leaked' );
		
		$output .= "============================================================================\n";
		$output .= "Start Memory: " . $this->format_memory( $this->init_memory ) . "\n";
		$output .= "Total Memory: " . $this->format_memory( $this->total_memory ) . "\n";
		$output .= "Peak Memory:  " . $this->format_memory( $this->peak_memory ) . "\n";
		$output .= "============================================================================\n";
		
		$output .= sprintf( "       %3.4f ms (%3.2f %%)  %s  %s\n", $timed_total * 1000, $pecent_total, $this->format_memory( $this->total_memory ), 'OVERALL TIME' );

		$output .= "============================================================================\n";

		if( $display_backtrace == true )
		{
			$output .= "Backtrace\n";
			$output .= $this->stack_trace;
		}
		
		if( $log == false )
		{
			print $output . '</pre>';
		}
		else
		{
			return $output;
		}
    }
	
	
}


?>