Skip to content

Commit

Permalink
Merge branch 'master' of https://github.com/tmaiaroto/li3_perf
Browse files Browse the repository at this point in the history
  • Loading branch information
greut committed Jan 7, 2012
2 parents b1780b5 + a306cbe commit 2567ae7
Show file tree
Hide file tree
Showing 20 changed files with 1,318 additions and 18 deletions.
4 changes: 4 additions & 0 deletions config/bootstrap/dispatcher.php
Expand Up @@ -36,6 +36,10 @@

// Apply a filter that will render the toolbar and mark some timers.
Dispatcher::applyFilter('run', function($self, $params, $chain) {
if(substr($params['request']->url, 0, 17) == '/li3_perf/profile') {
return $chain->next($self, $params, $chain);
}

Data::append('timers', array('li3_perf_start_dispatch' => microtime(true)));

$result = $chain->next($self, $params, $chain);
Expand Down
138 changes: 120 additions & 18 deletions config/routes.php
Expand Up @@ -3,6 +3,8 @@
use lithium\action\Response;
use lithium\core\Environment;
use li3_perf\extensions\util\Asset;
use li3_perf\extensions\webgrind\library\Webgrind;
use li3_perf\extensions\webgrind\library\FileHandler;

// Assets (can't symlink in the VM because Windows host)
Router::connect("/{:library}/{:asset_type:js|img|css}/{:args}", array(), function($request) {
Expand All @@ -14,28 +16,128 @@
// This one is cool. It outputs the last few lines of a log file.
// If CCZE is installed the log output will be in HTML and styles can be altered easily with CSS.
Router::connect('/li3_perf/tail/{:file}/{:lines}', array('lines' => 25, 'file' => LITHIUM_APP_PATH . '/resources/tmp/logs/debug.log'), function($request) {
$lines = $request->params['lines'];
$logfile = $request->params['file'];

header("Content-type: text/html");
// echo `tail -n 50 /var/log/php-fpm/www-error.log | ccze -h`;
$lines = $request->params['lines'];
$logfile = $request->params['file'];

header("Content-type: text/html");
// echo `tail -n 50 /var/log/php-fpm/www-error.log | ccze -h`;

$options = '-n ' . $lines;
$command = 'tail ' . $options . ' ' . $logfile . ' | ccze -h';
$output = shell_exec($command);

if($output == null) {
$output = '';
$command = 'tail ' . $options . ' ' . $logfile;
$lines = explode("\n", shell_exec($command));
if(!empty($lines)) {
foreach($lines as $line) {
$output .= $line . "<br />";
}
}
}

echo $output;
exit();
});

Router::connect('/{:library}/profile/trace/{:file}', array('file' => 'latest'), function($request) {
// If for some reason a profile session is active, do NOT run the code in this route.
// It would cause some pretty big issues =)
if(isset($_GET['XDEBUG_PROFILE']) || isset($_COOKIE['XDEBUG_PROFILE'])) {
echo 'You can\'t attempt to get the latest profile information while the profiler is active.';
return false;
}

// webgrind bootstrap/config process
require LITHIUM_APP_PATH . '/libraries/li3_perf/extensions/webgrind/library/bootstrap.php';

$trace_files = FileHandler::getInstance()->getTraceList(1);
if(is_array($trace_files)) {
$dataFile = $trace_files[0]['filename'];
// I've seen this work before and then sometimes not...Sometimes it needs the slash. Weird.
if(!file_exists(Webgrind::$config->xdebugOutputDir . $dataFile)){
$dataFile = '/' . $dataFile;
}
$costFormat = Webgrind::$config->defaultCostformat;
$reader = FileHandler::getInstance()->getTraceReader(
$dataFile, $costFormat
);

$options = '-n ' . $lines;
$command = 'tail ' . $options . ' ' . $logfile . ' | ccze -h';
$output = shell_exec($command);
$result = array();
$functions = array();
$shownTotal = 0;
$breakdown = array('internal' => 0, 'procedural' => 0, 'class' => 0, 'include' => 0);
$functionCount = $reader->getFunctionCount();

if($output == null) {
$output = '';
$command = 'tail ' . $options . ' ' . $logfile;
$lines = explode("\n", shell_exec($command));
if(!empty($lines)) {
foreach($lines as $line) {
$output .= $line . "<br />";
$result['moodpik'] = array('function_calls' => 0, 'total_cost' => 0);
for ($i = 0; $i < $functionCount; $i++) {
$functionInfo = $reader->getFunctionInfo($i);
//var_dump($functionInfo['functionName']);
if(strstr($functionInfo['functionName'], 'moodpik\\')) {
$result['moodpik']['function_calls']++;
$result['moodpik']['total_cost'] += $functionInfo['summedSelfCost'];
}

$isInternal = (strpos($functionInfo['functionName'], 'php::') !== false);

if ($isInternal) {
if (get('hideInternals', false)) {
continue;
}

$breakdown['internal'] += $functionInfo['summedSelfCost'];
$humanKind = 'internal';
} elseif (false !== strpos($functionInfo['functionName'], 'require_once::') ||
false !== strpos($functionInfo['functionName'], 'require::') ||
false !== strpos($functionInfo['functionName'], 'include_once::') ||
false !== strpos($functionInfo['functionName'], 'include::')) {
$breakdown['include'] += $functionInfo['summedSelfCost'];
$humanKind = 'include';
} elseif (false !== strpos($functionInfo['functionName'], '->') ||
false !== strpos($functionInfo['functionName'], '::')) {
$breakdown['class'] += $functionInfo['summedSelfCost'];
$humanKind = 'class';
} else {
$breakdown['procedural'] += $functionInfo['summedSelfCost'];
$humanKind = 'procedural';
}

$shownTotal += $functionInfo['summedSelfCost'];
$functions[$i] = $functionInfo;
$functions[$i]['nr'] = $i;
$functions[$i]['humanKind'] = $humanKind;
}

echo $output;

usort($functions, 'costCmp');

$remainingCost = $shownTotal * get('showFraction');

$result['functions'] = array();
foreach ($functions as $function) {

$remainingCost -= $function['summedSelfCost'];
$function['file'] = urlencode($function['file']);
$result['functions'][] = $function;
if ($remainingCost < 0) {
break;
}
}

$result['summedInvocationCount'] = $reader->getFunctionCount();
$result['summedRunTime'] = $reader->formatCost($reader->getHeader('summary'), 'msec');
$result['dataFile'] = $dataFile;
$result['invokeUrl'] = $reader->getHeader('cmd');
$result['runs'] = $reader->getHeader('runs');
$result['breakdown'] = $breakdown;
$result['mtime'] = date(Webgrind::$config->dateFormat, filemtime(Webgrind::$config->xdebugOutputDir.'/'.$dataFile));

$creator = preg_replace('/[^0-9\.]/', '', $reader->getHeader('creator'));
$result['linkToFunctionLine'] = version_compare($creator, '2.1') > 0;


var_dump($result);
exit();
});
}
});
?>
13 changes: 13 additions & 0 deletions extensions/webgrind/config.php
@@ -0,0 +1,13 @@
<?php

/**
* See Webgrind_Config::$_defaults in 'library/Config.php'
* for complete list of options
*/

return array(
'checkVersion' => false,
'defaultTimezone' => 'Europe/Kiev',
'defaultCostformat' => 'usec',
'defaultFunctionPercentage' => 100
);
79 changes: 79 additions & 0 deletions extensions/webgrind/index.php
@@ -0,0 +1,79 @@
<?php
/**
* @author Jacob Oettinger
* @author Joakim Nygård
*/

require './library/common.php';


switch(get('op')) {
case 'fileviewer':
$file = get('file');
$line = get('line');

if ($file && $file != '') {
$message = '';
if (!file_exists($file)) {
$message = $file.' does not exist.';
} elseif (!is_readable($file)) {
$message = $file.' is not readable.';
} elseif (is_dir($file)) {
$message = $file.' is a directory.';
}
} else {
$message = 'No file to view';
}
require 'templates/fileviewer.phtml';
break;

case 'function_graph':
$dataFile = get('dataFile');
$showFraction = 100 - intval(get('showFraction') * 100);

if ($dataFile == '0') {
$files = Webgrind_FileHandler::getInstance()->getTraceList(1);
$dataFile = $files[0]['filename'];
}

if (!is_executable(Webgrind::$config->pythonExecutable)) {
die("can't find python interpreter");
}

if (!is_executable(Webgrind::$config->dotExecutable)) {
die("can't find drawing tool");
}

$filename = Webgrind::$config->storageDir.'/'.$dataFile.'-'.$showFraction.Webgrind::$config->preprocessedSuffix.'.png';
if (!file_exists($filename)) {
if (!createGraph(
Webgrind::$config->xdebugOutputDir.'/'.$dataFile,
$filename,
$showFraction
)) {
die("can't generate graph");
}
}

header("Content-Type: image/png");
readfile($filename);
break;

default:
$welcome = '';
$storageDir = Webgrind::$config->storageDir;
$xdebugOutputDir = Webgrind::$config->xdebugOutputDir;

if (!file_exists($storageDir) || !is_writable($storageDir)) {
$welcome .= "Webgrind storageDir ${storageDir} does not exist or is not writeable.<br>";
}
if (!file_exists($xdebugOutputDir) || !is_readable($xdebugOutputDir)) {
$welcome .= "Webgrind profilerDir ${xdebugOutputDir} does not exist or is not readable.<br>";
}

if ($welcome == '') {
$welcome = "Select a cachegrind file above<br>(looking in <code>${xdebugOutputDir}</code> for files matching <code>".
Webgrind::$config->xdebugOutputFormat.'</code>)';
}
require 'templates/index.phtml';
}

0 comments on commit 2567ae7

Please sign in to comment.