Reputation: 22324
I want the list of all functions executed to a certain point in code, somehow like debug_backtrace() but including functions not in the exact thread that leads to where debug_backtrace() is called. e.g :
a();
function a() {
b();
c();
d();
}
function b() { }
function c() { }
function d() { print all_trace(); }
would produce :
a(), b(), c(), d()
and not
a(), d()
like debug_backtrace() would
Upvotes: 3
Views: 1843
Reputation: 12935
need no Xdebug or dbg.so on server, return more detailed message, based on Paul Dixon's solution:
diyism_trace.php:
<?
define(TRACES_MODE, 'TEXTAREA');//'TEXTAREA' or 'FIREPHP' or 'TMPLOG'
$GLOBALS['traces.pre']=array();
function my_array_diff($arr1, $arr2)
{foreach ($arr1 as $k=>$v)
{if (in_array($v, $arr2, true))
{unset($arr1[$k]);
}
}
return $arr1;
}
function my_var_export($var, $is_str=false)
{$rtn=preg_replace(array('/Array\s+\(/', '/\[(\d+)\] => (.*)\n/', '/\[([^\d].*)\] => (.*)\n/'), array('array (', '\1 => \'\2\''."\n", '\'\1\' => \'\2\''."\n"), substr(print_r($var, true), 0, -1));
$rtn=strtr($rtn, array("=> 'array ('"=>'=> array ('));
$rtn=strtr($rtn, array(")\n\n"=>")\n"));
$rtn=strtr($rtn, array("'\n"=>"',\n", ")\n"=>"),\n"));
$rtn=preg_replace(array('/\n +/e'), array('strtr(\'\0\', array(\' \'=>\' \'))'), $rtn);
$rtn=strtr($rtn, array(" Object',"=>" Object'<-"));
if ($is_str)
{return $rtn;
}
else
{echo $rtn;
}
}
function tick_handler()
{$tmp=debug_backtrace();
$trace=my_array_diff($tmp, $GLOBALS['traces.pre']);
//echo '<pre>';var_export($trace);echo '</pre>';echo '<br/>'; //for debug diyism_trace.php
$trace=array_values($trace);
$GLOBALS['traces.pre']=$tmp;
$fun=@$tmp[1]['class'].@$tmp[1]['type'].@$tmp[1]['function'];
if (count($trace)>0 && $trace[0]['file'].'/'.$fun!==@$GLOBALS['traces'][count($GLOBALS['traces'])-1]['key']) //filter empty array and rearrange array_values(), because some lines will trigger two tick events per line, for example: 1.last line is "some code;questmark>" 2.error_reporting(...
{for ($i=count($trace)-1; $i>=0; --$i)
{$fun1=@$tmp[$i+1]['class'].@$tmp[$i+1]['type'].@$tmp[$i+1]['function'];
$fun2=$trace[$i]['class'].$trace[$i]['type'].$trace[$i]['function'];
$GLOBALS['traces'][]=$tmp_fb=array_merge(array('key'=>$trace[$i]['file'].'/'.$fun1),
$trace[$i],
array('function'=>strtr($fun2, array('tick_handler'=>'CONTINUE')), 'in_function'=>$fun1)
);
TRACES_MODE==='FIREPHP'?fb(trace_output($tmp_fb), 'diyism_trace:'.++$GLOBALS['diyism_trace_no']):'';
}
}
}
function trace_output($trace)
{$trace['in_function']=strtr(@$trace['in_function'], array('require'=>'', 'require_once'=>'', 'include'=>'', 'include_once'=>''));
$trace['args']=$trace['args']?strtr(preg_replace(array('/\n +/'), array(''), preg_replace(array('/\n \d+ => /'), array(''), substr(my_var_export($trace['args'], true), 7, -3))), array("\r"=>'\r', "\n"=>'\n')):'';
return $trace['file'].($trace['in_function']?'/'.$trace['in_function'].'()':'').'/'.$trace['line'].': '.$trace['function'].'('.$trace['args'].')';
}
function traces_output()
{$request_id=base_convert(time().substr(microtime(),2,6).mt_rand(), 10, 36);
TRACES_MODE==='TEXTAREA'
?(print '<textarea style="width:100%;height:300px;">')
:file_put_contents("/tmp/diyism_trace.{$request_id}.log", "==================".$_SERVER['REQUEST_URI'].":==================\n", FILE_APPEND);
$GLOBALS['traces']=array_slice($GLOBALS['traces'], 2);//remove registering tick line and requiring 'diyism_trace.php' line
foreach ($GLOBALS['traces'] as $k=>$trace)
{TRACES_MODE==='TEXTAREA'
?(print htmlentities($k.':'.trace_output($trace)."\n"))
:file_put_contents("/tmp/diyism_trace.{$request_id}.log", $k.':'.trace_output($trace)."\n", FILE_APPEND);
}
TRACES_MODE==='TEXTAREA'?(print '</textarea>'):'';
}
register_tick_function('tick_handler');
(TRACES_MODE==='TEXTAREA' || TRACES_MODE==='TMPLOG')?register_shutdown_function('traces_output'):'';
?>
test.php:
<?
declare(ticks=1);
require 'diyism_trace.php';
a('a', array('hello'));
1+2;
b();
function a()
{$d=1;
b();
$d=2;
cls::c('cccc');
$d=4;
d();
$d=5;
}
function b()
{1+1;
d();
}
class cls
{static function c()
{1+1;
}
}
function d()
{1+1;
}
?>
output:
/var/www/test/test.php/3: CONTINUE()
/var/www/test/test.php/4: a('a',array (0 => 'hello',))
/var/www/test/test.php/a()/8: CONTINUE()
/var/www/test/test.php/a()/9: b()
/var/www/test/test.php/b()/18: CONTINUE()
/var/www/test/test.php/b()/19: d()
/var/www/test/test.php/d()/27: CONTINUE()
/var/www/test/test.php/b()/19: CONTINUE()
/var/www/test/test.php/a()/9: CONTINUE()
/var/www/test/test.php/a()/11: c('cccc')
/var/www/test/test.php/c()/23: CONTINUE()
/var/www/test/test.php/a()/11: CONTINUE()
/var/www/test/test.php/a()/13: d()
/var/www/test/test.php/d()/27: CONTINUE()
/var/www/test/test.php/a()/13: CONTINUE()
/var/www/test/test.php/4: CONTINUE()
/var/www/test/test.php/6: b()
/var/www/test/test.php/b()/18: CONTINUE()
/var/www/test/test.php/b()/19: d()
/var/www/test/test.php/d()/27: CONTINUE()
/var/www/test/test.php/b()/19: CONTINUE()
/var/www/test/test.php/6: CONTINUE()
Upvotes: 0
Reputation: 300845
You could install a tick handler, and on every tick use debug_backtrace to find which function you are in and add it to a list. It will kill performance, but would give the list you desire.
$functions_called=array();
function tick_handler()
{
$trace=debug_backtrace();
//we're in tick_handler, let's eat that
array_shift($trace);
//if we're in a function, log it!
if (!empty($trace))
{
$function=$trace[0]['function'];
//log function - lets just remember it was called
global $functions_called;
$functions_called[$function]=1;
}
}
//install our tick handler...
declare(ticks=1);
register_tick_function('tick_handler');
Here's how you'd get the output you specify from this:
function all_trace()
{
global $functions_called;
$output='';
$separator='';
foreach($functions_called as $func=>$dummy)
{
$output.=$separator.$func.'()';
$separator=', ';
}
return $output;
}
Pretty nasty really - as others suggest, better to use tools like xdebug designed for the job if you can...
Upvotes: 1