776 lines
26 KiB
PHP
776 lines
26 KiB
PHP
<?php
|
|
/*
|
|
* 2007-2016 PrestaShop
|
|
*
|
|
* NOTICE OF LICENSE
|
|
*
|
|
* This source file is subject to the Open Software License (OSL 3.0)
|
|
* that is bundled with this package in the file LICENSE.txt.
|
|
* It is also available through the world-wide-web at this URL:
|
|
* http://opensource.org/licenses/osl-3.0.php
|
|
* If you did not receive a copy of the license and are unable to
|
|
* obtain it through the world-wide-web, please send an email
|
|
* to license@prestashop.com so we can send you a copy immediately.
|
|
*
|
|
* DISCLAIMER
|
|
*
|
|
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
|
|
* versions in the future. If you wish to customize PrestaShop for your
|
|
* needs please refer to http://www.prestashop.com for more information.
|
|
*
|
|
* @author PrestaShop SA <contact@prestashop.com>
|
|
* @copyright 2007-2016 PrestaShop SA
|
|
* @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
|
|
* International Registered Trademark & Property of PrestaShop SA
|
|
*/
|
|
|
|
abstract class Controller extends ControllerCore
|
|
{
|
|
protected $total_filesize = 0;
|
|
protected $total_query_time = 0;
|
|
protected $total_global_var_size = 0;
|
|
protected $total_modules_time = 0;
|
|
protected $total_modules_memory = 0;
|
|
protected $global_var_size = array();
|
|
|
|
protected $modules_perfs = array();
|
|
protected $hooks_perfs = array();
|
|
|
|
protected $array_queries = array();
|
|
|
|
protected $profiler = array();
|
|
|
|
private function getMemoryColor($n)
|
|
{
|
|
$n /= 1048576;
|
|
if ($n > 3) {
|
|
return '<span style="color:red">'.sprintf('%0.2f', $n).'</span>';
|
|
} elseif ($n > 1) {
|
|
return '<span style="color:#EF8B00">'.sprintf('%0.2f', $n).'</span>';
|
|
} elseif (round($n, 2) > 0) {
|
|
return '<span style="color:green">'.sprintf('%0.2f', $n).'</span>';
|
|
}
|
|
return '<span style="color:green">-</span>';
|
|
}
|
|
|
|
private function getPeakMemoryColor($n)
|
|
{
|
|
$n /= 1048576;
|
|
if ($n > 16) {
|
|
return '<span style="color:red">'.sprintf('%0.1f', $n).'</span>';
|
|
}
|
|
if ($n > 12) {
|
|
return '<span style="color:#EF8B00">'.sprintf('%0.1f', $n).'</span>';
|
|
}
|
|
return '<span style="color:green">'.sprintf('%0.1f', $n).'</span>';
|
|
}
|
|
|
|
private function displaySQLQueries($n)
|
|
{
|
|
if ($n > 150) {
|
|
return '<span style="color:red">'.$n.' queries</span>';
|
|
}
|
|
if ($n > 100) {
|
|
return '<span style="color:#EF8B00">'.$n.' queries</span>';
|
|
}
|
|
return '<span style="color:green">'.$n.' quer'.($n == 1 ? 'y' : 'ies').'</span>';
|
|
}
|
|
|
|
private function displayRowsBrowsed($n)
|
|
{
|
|
if ($n > 400) {
|
|
return '<span style="color:red">'.$n.' rows browsed</span>';
|
|
}
|
|
if ($n > 100) {
|
|
return '<span style="color:#EF8B00">'.$n.' rows browsed</span>';
|
|
}
|
|
return '<span style="color:green">'.$n.' row'.($n == 1 ? '' : 's').' browsed</span>';
|
|
}
|
|
|
|
private function getPhpVersionColor($version)
|
|
{
|
|
if (version_compare($version, '5.3') < 0) {
|
|
return '<span style="color:red">'.$version.' (Upgrade strongly recommended)</span>';
|
|
} elseif (version_compare($version, '5.4') < 0) {
|
|
return '<span style="color:#EF8B00">'.$version.' (Consider upgrading)</span>';
|
|
}
|
|
return '<span style="color:green">'.$version.' (OK)</span>';
|
|
}
|
|
|
|
private function getMySQLVersionColor($version)
|
|
{
|
|
if (version_compare($version, '5.5') < 0) {
|
|
return '<span style="color:red">'.$version.' (Upgrade strongly recommended)</span>';
|
|
} elseif (version_compare($version, '5.6') < 0) {
|
|
return '<span style="color:#EF8B00">'.$version.' (Consider upgrading)</span>';
|
|
}
|
|
return '<span style="color:green">'.$version.' (OK)</span>';
|
|
}
|
|
|
|
private function getLoadTimeColor($n, $kikoo = false)
|
|
{
|
|
if ($n > 1.6) {
|
|
return '<span style="color:red">'.round($n * 1000).'</span>'.($kikoo ? ' ms - You\'d better run your shop on a toaster' : '');
|
|
} elseif ($n > 0.8) {
|
|
return '<span style="color:#EF8B00">'.round($n * 1000).'</span>'.($kikoo ? ' ms - OK... for a shared hosting' : '');
|
|
} elseif ($n > 0) {
|
|
return '<span style="color:green">'.round($n * 1000).'</span>'.($kikoo ? ' ms - Unicorn powered webserver!' : '');
|
|
}
|
|
return '<span style="color:green">-</span>'.($kikoo ? ' ms - Faster than light' : '');
|
|
}
|
|
|
|
private function getTotalQueriyingTimeColor($n)
|
|
{
|
|
if ($n >= 100) {
|
|
return '<span style="color:red">'.$n.'</span>';
|
|
} elseif ($n >= 50) {
|
|
return '<span style="color:#EF8B00">'.$n.'</span>';
|
|
}
|
|
return '<span style="color:green">'.$n.'</span>';
|
|
}
|
|
|
|
private function getNbQueriesColor($n)
|
|
{
|
|
if ($n >= 100) {
|
|
return '<span style="color:red">'.$n.'</span>';
|
|
} elseif ($n >= 50) {
|
|
return '<span style="color:#EF8B00">'.$n.'</span>';
|
|
}
|
|
return '<span style="color:green">'.$n.'</span>';
|
|
}
|
|
|
|
private function getTimeColor($n)
|
|
{
|
|
if ($n > 4) {
|
|
return 'style="color:red"';
|
|
}
|
|
if ($n > 2) {
|
|
return 'style="color:#EF8B00"';
|
|
}
|
|
return 'style="color:green"';
|
|
}
|
|
|
|
private function getQueryColor($n)
|
|
{
|
|
if ($n > 5) {
|
|
return 'style="color:red"';
|
|
}
|
|
if ($n > 2) {
|
|
return 'style="color:#EF8B00"';
|
|
}
|
|
return 'style="color:green"';
|
|
}
|
|
|
|
private function getTableColor($n)
|
|
{
|
|
if ($n > 30) {
|
|
return 'style="color:red"';
|
|
}
|
|
if ($n > 20) {
|
|
return 'style="color:#EF8B00"';
|
|
}
|
|
return 'style="color:green"';
|
|
}
|
|
|
|
private function getObjectModelColor($n)
|
|
{
|
|
if ($n > 50) {
|
|
return 'style="color:red"';
|
|
}
|
|
if ($n > 10) {
|
|
return 'style="color:#EF8B00"';
|
|
}
|
|
return 'style="color:green"';
|
|
}
|
|
|
|
protected function stamp($block)
|
|
{
|
|
return array('block' => $block, 'memory_usage' => memory_get_usage(), 'peak_memory_usage' => memory_get_peak_usage(), 'time' => microtime(true));
|
|
}
|
|
|
|
public function __construct()
|
|
{
|
|
$this->profiler[] = $this->stamp('config');
|
|
|
|
parent::__construct();
|
|
$this->profiler[] = $this->stamp('__construct');
|
|
}
|
|
|
|
public function run()
|
|
{
|
|
$this->init();
|
|
$this->profiler[] = $this->stamp('init');
|
|
|
|
if ($this->checkAccess()) {
|
|
$this->profiler[] = $this->stamp('checkAccess');
|
|
|
|
if (!$this->content_only && ($this->display_header || (isset($this->className) && $this->className))) {
|
|
$this->setMedia();
|
|
$this->profiler[] = $this->stamp('setMedia');
|
|
}
|
|
|
|
$this->postProcess();
|
|
$this->profiler[] = $this->stamp('postProcess');
|
|
|
|
if (!$this->content_only && ($this->display_header || (isset($this->className) && $this->className))) {
|
|
$this->initHeader();
|
|
$this->profiler[] = $this->stamp('initHeader');
|
|
}
|
|
|
|
$this->initContent();
|
|
$this->profiler[] = $this->stamp('initContent');
|
|
|
|
if (!$this->content_only && ($this->display_footer || (isset($this->className) && $this->className))) {
|
|
$this->initFooter();
|
|
$this->profiler[] = $this->stamp('initFooter');
|
|
}
|
|
|
|
if ($this->ajax) {
|
|
$action = Tools::toCamelCase(Tools::getValue('action'), true);
|
|
if (!empty($action) && method_exists($this, 'displayAjax'.$action)) {
|
|
$this->{'displayAjax'.$action}();
|
|
} elseif (method_exists($this, 'displayAjax')) {
|
|
$this->displayAjax();
|
|
}
|
|
return;
|
|
}
|
|
} else {
|
|
$this->initCursedPage();
|
|
}
|
|
|
|
$this->displayProfiling();
|
|
}
|
|
|
|
private function getVarSize($var)
|
|
{
|
|
$start_memory = memory_get_usage();
|
|
try {
|
|
$tmp = Tools::unSerialize(serialize($var));
|
|
} catch (Exception $e) {
|
|
$tmp = $this->getVarData($var);
|
|
}
|
|
$size = memory_get_usage() - $start_memory;
|
|
return $size;
|
|
}
|
|
|
|
private function getVarData($var)
|
|
{
|
|
if (is_object($var)) {
|
|
return $var;
|
|
}
|
|
return (string)$var;
|
|
}
|
|
|
|
protected function processProfilingData()
|
|
{
|
|
global $start_time;
|
|
|
|
// Including a lot of files uses memory
|
|
foreach (get_included_files() as $file) {
|
|
$this->total_filesize += filesize($file);
|
|
}
|
|
|
|
// Sum querying time
|
|
foreach (Db::getInstance()->queries as $data) {
|
|
$this->total_query_time += $data['time'];
|
|
}
|
|
|
|
foreach ($GLOBALS as $key => $value) {
|
|
if ($key != 'GLOBALS') {
|
|
$this->total_global_var_size += ($size = $this->getVarSize($value));
|
|
if ($size > 1024) {
|
|
$this->global_var_size[$key] = round($size / 1024);
|
|
}
|
|
}
|
|
}
|
|
arsort($this->global_var_size);
|
|
|
|
$cache = Cache::retrieveAll();
|
|
$this->total_cache_size = $this->getVarSize($cache);
|
|
|
|
// Retrieve module perfs
|
|
$result = Db::getInstance()->ExecuteS('
|
|
SELECT *
|
|
FROM '._DB_PREFIX_.'modules_perfs
|
|
WHERE session = '.(int)Module::$_log_modules_perfs_session.'
|
|
AND time_start >= '.(float)$start_time.'
|
|
AND time_end <= '.(float)$this->profiler[count($this->profiler) - 1]['time']
|
|
);
|
|
|
|
foreach ($result as $row) {
|
|
$tmp_time = $row['time_end'] - $row['time_start'];
|
|
$tmp_memory = $row['memory_end'] - $row['memory_start'];
|
|
$this->total_modules_time += $tmp_time;
|
|
$this->total_modules_memory += $tmp_memory;
|
|
|
|
if (!isset($this->modules_perfs[$row['module']])) {
|
|
$this->modules_perfs[$row['module']] = array('time' => 0, 'memory' => 0, 'methods' => array());
|
|
}
|
|
$this->modules_perfs[$row['module']]['time'] += $tmp_time;
|
|
$this->modules_perfs[$row['module']]['methods'][$row['method']]['time'] = $tmp_time;
|
|
$this->modules_perfs[$row['module']]['memory'] += $tmp_memory;
|
|
$this->modules_perfs[$row['module']]['methods'][$row['method']]['memory'] = $tmp_memory;
|
|
|
|
if (!isset($this->hooks_perfs[$row['method']])) {
|
|
$this->hooks_perfs[$row['method']] = array('time' => 0, 'memory' => 0, 'modules' => array());
|
|
}
|
|
$this->hooks_perfs[$row['method']]['time'] += $tmp_time;
|
|
$this->hooks_perfs[$row['method']]['modules'][$row['module']]['time'] = $tmp_time;
|
|
$this->hooks_perfs[$row['method']]['memory'] += $tmp_memory;
|
|
$this->hooks_perfs[$row['method']]['modules'][$row['module']]['memory'] = $tmp_memory;
|
|
}
|
|
uasort($this->modules_perfs, 'prestashop_querytime_sort');
|
|
uasort($this->hooks_perfs, 'prestashop_querytime_sort');
|
|
|
|
$queries = Db::getInstance()->queries;
|
|
uasort($queries, 'prestashop_querytime_sort');
|
|
foreach ($queries as $data) {
|
|
$query_row = array(
|
|
'time' => $data['time'],
|
|
'query' => $data['query'],
|
|
'location' => str_replace('\\', '/', substr($data['stack'][0]['file'], strlen(_PS_ROOT_DIR_))).':'.$data['stack'][0]['line'],
|
|
'filesort' => false,
|
|
'rows' => 1,
|
|
'group_by' => false,
|
|
'stack' => array(),
|
|
);
|
|
if (preg_match('/^\s*select\s+/i', $data['query'])) {
|
|
$explain = Db::getInstance()->executeS('explain '.$data['query']);
|
|
if (stristr($explain[0]['Extra'], 'filesort')) {
|
|
$query_row['filesort'] = true;
|
|
}
|
|
foreach ($explain as $row) {
|
|
$query_row['rows'] *= $row['rows'];
|
|
}
|
|
if (stristr($data['query'], 'group by') && !preg_match('/(avg|count|min|max|group_concat|sum)\s*\(/i', $data['query'])) {
|
|
$query_row['group_by'] = true;
|
|
}
|
|
}
|
|
|
|
array_shift($data['stack']);
|
|
foreach ($data['stack'] as $call) {
|
|
$query_row['stack'][] = str_replace('\\', '/', substr($call['file'], strlen(_PS_ROOT_DIR_))).':'.$call['line'];
|
|
}
|
|
|
|
$this->array_queries[] = $query_row;
|
|
}
|
|
|
|
uasort(ObjectModel::$debug_list, create_function('$a,$b', 'return (count($a) < count($b)) ? 1 : -1;'));
|
|
arsort(Db::getInstance()->tables);
|
|
arsort(Db::getInstance()->uniqQueries);
|
|
}
|
|
|
|
protected function displayProfilingLinks()
|
|
{
|
|
echo '
|
|
<div class="col-4">
|
|
<ol>
|
|
<li><a href="#stopwatch">Stopwatch SQL</a></li>
|
|
<li><a href="#doubles">Doubles</a></li>
|
|
<li><a href="#tables">Tables stress</a></li>
|
|
'.(isset(ObjectModel::$debug_list) ? '<li><a href="#objectModels">ObjectModel instances</a></li>' : '').'
|
|
<li><a href="#includedFiles">Included Files</a></li>
|
|
</ol>
|
|
</div>';
|
|
}
|
|
|
|
protected function displayProfilingStyle()
|
|
{
|
|
echo '
|
|
<style>
|
|
#prestashop_profiling {
|
|
padding: 20px;
|
|
}
|
|
|
|
.ps_back-office.page-sidebar #prestashop_profiling {
|
|
margin-left: 210px;
|
|
}
|
|
.ps_back-office.page-sidebar-closed #prestashop_profiling {
|
|
margin-left: 50px;
|
|
}
|
|
.ps_back-office #prestashop_profiling {
|
|
clear: both;
|
|
padding: 10px;
|
|
margin-bottom: 50px;
|
|
}
|
|
|
|
#prestashop_profiling * {
|
|
box-sizing:border-box;
|
|
-moz-box-sizing:border-box;
|
|
color: #888;
|
|
}
|
|
|
|
#prestashop_profiling td .pre {
|
|
padding: 6px;
|
|
margin-right: 10px;
|
|
overflow: auto;
|
|
display: block;
|
|
color: #777;
|
|
font-size: 12px;
|
|
line-height: 1.42857;
|
|
word-break: break-all;
|
|
word-wrap: break-word;
|
|
background-color: whitesmoke;
|
|
border: 1px solid #cccccc;
|
|
max-width: 960px;
|
|
}
|
|
.sortable thead th {
|
|
cursor:pointer;
|
|
}
|
|
|
|
#prestashop_profiling .row {
|
|
clear: both;
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
#prestashop_profiling .col-4 {
|
|
float: left;
|
|
padding: 0 10px;
|
|
width: 33%;
|
|
}
|
|
@media (max-width: 1200px) {
|
|
#prestashop_profiling .col-4 {
|
|
width: 50%;
|
|
}
|
|
}
|
|
@media (max-width: 600px) {
|
|
#prestashop_profiling .col-4 {
|
|
width: 100%;
|
|
}
|
|
}
|
|
</style>
|
|
<script type="text/javascript" src="https://cdn.rawgit.com/drvic10k/bootstrap-sortable/1.11.2/Scripts/bootstrap-sortable.js"></script>';
|
|
}
|
|
|
|
protected function displayProfilingSummary()
|
|
{
|
|
global $start_time;
|
|
|
|
echo '
|
|
<div class="col-4">
|
|
<table class="table table-condensed">
|
|
<tr><td>Load Time</td><td>'.$this->getLoadTimeColor($this->profiler[count($this->profiler) - 1]['time'] - $start_time, true).'</td></tr>
|
|
<tr><td>Querying Time</td><td>'.$this->getTotalQueriyingTimeColor(round(1000 * $this->total_query_time)).' ms</span>
|
|
<tr><td>Queries</td><td>'.$this->getNbQueriesColor(count($this->array_queries)).'</td></tr>
|
|
<tr><td>Memory Peak Usage</td><td>'.$this->getPeakMemoryColor($this->profiler[count($this->profiler) - 1]['peak_memory_usage']).' Mb</td></tr>
|
|
<tr><td>Included Files</td><td>'.count(get_included_files()).' files - '.$this->getMemoryColor($this->total_filesize).' Mb</td></tr>
|
|
<tr><td>PrestaShop Cache</td><td>'.$this->getMemoryColor($this->total_cache_size).' Mb</td></tr>
|
|
<tr><td><a href="javascript:void(0);" onclick="$(\'.global_vars_detail\').toggle();">Global vars</a></td><td>'.$this->getMemoryColor($this->total_global_var_size).' Mb</td></tr>';
|
|
foreach ($this->global_var_size as $global => $size) {
|
|
echo '<tr class="global_vars_detail" style="display:none"><td>- global $'.$global.'</td><td>'.$size.'k</td></tr>';
|
|
}
|
|
echo '
|
|
</table>
|
|
</div>';
|
|
}
|
|
|
|
protected function displayProfilingConfiguration()
|
|
{
|
|
echo '
|
|
<div class="col-4">
|
|
<table class="table table-condensed">
|
|
<tr><td>PrestaShop Version</td><td>'._PS_VERSION_.'</td></tr>
|
|
<tr><td>PHP Version</td><td>'.$this->getPhpVersionColor(phpversion()).'</td></tr>
|
|
<tr><td>MySQL Version</td><td>'.$this->getMySQLVersionColor(Db::getInstance()->getVersion()).'</td></tr>
|
|
<tr><td>Memory Limit</td><td>'.ini_get('memory_limit').'</td></tr>
|
|
<tr><td>Max Execution Time</td><td>'.ini_get('max_execution_time').'s</td></tr>
|
|
<tr><td>Smarty Cache</td><td><span style="color:'.(Configuration::get('PS_SMARTY_CACHE') ? 'green">enabled' : 'red">disabled').'</td></tr>
|
|
<tr><td>Smarty Compilation</td><td><span style="color:'.(Configuration::get('PS_SMARTY_FORCE_COMPILE') == 0 ? 'green">never recompile' : (Configuration::get('PS_SMARTY_FORCE_COMPILE') == 1 ? '#EF8B00">auto' : 'red">force compile')).'</td></tr>
|
|
</table>
|
|
</div>';
|
|
}
|
|
|
|
protected function displayProfilingRun()
|
|
{
|
|
global $start_time;
|
|
|
|
echo '
|
|
<div class="col-4">
|
|
<table class="table table-condensed">
|
|
<tr><th> </th><th>Time</th><th>Cumulated Time</th><th>Memory Usage</th><th>Memory Peak Usage</th></tr>';
|
|
$last = array('time' => $start_time, 'memory_usage' => 0);
|
|
foreach ($this->profiler as $row) {
|
|
if ($row['block'] == 'checkAccess' && $row['time'] == $last['time']) {
|
|
continue;
|
|
}
|
|
echo '<tr>
|
|
<td>'.$row['block'].'</td>
|
|
<td>'.$this->getLoadTimeColor($row['time'] - $last['time']).' ms</td>
|
|
<td>'.$this->getLoadTimeColor($row['time'] - $start_time).' ms</td>
|
|
<td>'.$this->getMemoryColor($row['memory_usage'] - $last['memory_usage']).' Mb</td>
|
|
<td>'.$this->getMemoryColor($row['peak_memory_usage']).' Mb</td>
|
|
</tr>';
|
|
$last = $row;
|
|
}
|
|
echo '
|
|
</table>
|
|
</div>';
|
|
}
|
|
|
|
protected function displayProfilingHooks()
|
|
{
|
|
$count_hooks = count($this->hooks_perfs);
|
|
|
|
echo '
|
|
<div class="col-4">
|
|
<table class="table table-condensed">
|
|
<tr>
|
|
<th>Hook</th>
|
|
<th>Time</th>
|
|
<th>Memory Usage</th>
|
|
</tr>';
|
|
foreach ($this->hooks_perfs as $hook => $hooks_perfs) {
|
|
echo '
|
|
<tr>
|
|
<td>
|
|
<a href="javascript:void(0);" onclick="$(\'.'.$hook.'_modules_details\').toggle();">'.$hook.'</a>
|
|
</td>
|
|
<td>
|
|
'.$this->getLoadTimeColor($hooks_perfs['time']).' ms
|
|
</td>
|
|
<td>
|
|
'.$this->getMemoryColor($hooks_perfs['memory']).' Mb
|
|
</td>
|
|
</tr>';
|
|
foreach ($hooks_perfs['modules'] as $module => $perfs) {
|
|
echo '
|
|
<tr class="'.$hook.'_modules_details" style="background-color:#EFEFEF;display:none">
|
|
<td>
|
|
=> '.$module.'
|
|
</td>
|
|
<td>
|
|
'.$this->getLoadTimeColor($perfs['time']).' ms
|
|
</td>
|
|
<td>
|
|
'.$this->getMemoryColor($perfs['memory']).' Mb
|
|
</td>
|
|
</tr>';
|
|
}
|
|
}
|
|
echo ' <tr>
|
|
<th><b>'.($count_hooks == 1 ? '1 hook' : (int)$count_hooks.' hooks').'</b></th>
|
|
<th>'.$this->getLoadTimeColor($this->total_modules_time).' ms</th>
|
|
<th>'.$this->getMemoryColor($this->total_modules_memory).' Mb</th>
|
|
</tr>
|
|
</table>
|
|
</div>';
|
|
}
|
|
|
|
protected function displayProfilingModules()
|
|
{
|
|
$count_modules = count($this->modules_perfs);
|
|
|
|
echo '
|
|
<div class="col-4">
|
|
<table class="table table-condensed">
|
|
<tr>
|
|
<th>Module</th>
|
|
<th>Time</th>
|
|
<th>Memory Usage</th>
|
|
</tr>';
|
|
foreach ($this->modules_perfs as $module => $modules_perfs) {
|
|
echo '
|
|
<tr>
|
|
<td>
|
|
<a href="javascript:void(0);" onclick="$(\'.'.$module.'_hooks_details\').toggle();">'.$module.'</a>
|
|
</td>
|
|
<td>
|
|
'.$this->getLoadTimeColor($modules_perfs['time']).' ms
|
|
</td>
|
|
<td>
|
|
'.$this->getMemoryColor($modules_perfs['memory']).' Mb
|
|
</td>
|
|
</tr>';
|
|
foreach ($modules_perfs['methods'] as $hook => $perfs) {
|
|
echo '
|
|
<tr class="'.$module.'_hooks_details" style="background-color:#EFEFEF;display:none">
|
|
<td>
|
|
=> '.$hook.'
|
|
</td>
|
|
<td>
|
|
'.$this->getLoadTimeColor($perfs['time']).' ms
|
|
</td>
|
|
<td>
|
|
'.$this->getMemoryColor($perfs['memory']).' Mb
|
|
</td>
|
|
</tr>';
|
|
}
|
|
}
|
|
echo ' <tr>
|
|
<th><b>'.($count_modules == 1 ? '1 module' : (int)$count_modules.' modules').'</b></th>
|
|
<th>'.$this->getLoadTimeColor($this->total_modules_time).' ms</th>
|
|
<th>'.$this->getMemoryColor($this->total_modules_memory).' Mb</th>
|
|
</tr>
|
|
</table>
|
|
</div>';
|
|
}
|
|
|
|
protected function displayProfilingStopwatch()
|
|
{
|
|
echo '
|
|
<div class="row">
|
|
<h2><a name="stopwatch">Stopwatch SQL - '.count($this->array_queries).' queries</a></h2>
|
|
<table class="table table-condensed table-bordered sortable">
|
|
<thead>
|
|
<tr>
|
|
<th>Query</th>
|
|
<th>Time (ms)</th>
|
|
<th>Rows</th>
|
|
<th>Filesort</th>
|
|
<th>Group By</th>
|
|
<th>Location</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>';
|
|
foreach ($this->array_queries as $data) {
|
|
$callstack = implode('<br>', $data['stack']);
|
|
$callstack_md5 = md5($callstack);
|
|
|
|
echo '
|
|
<tr>
|
|
<td class="pre"><pre>'.preg_replace("/(^[\s]*)/m", "", htmlspecialchars($data['query'], ENT_NOQUOTES, 'utf-8', false)).'</pre></td>
|
|
<td data-value="'.$data['time'].'"><span '.$this->getTimeColor($data['time'] * 1000).'>'.(round($data['time'] * 1000, 1) < 0.1 ? '< 1' : round($data['time'] * 1000, 1)).'</span></td>
|
|
<td>'.(int)$data['rows'].'</td>
|
|
<td data-value="'.$data['filesort'].'">'.($data['filesort'] ? '<span style="color:red">Yes</span>' : '').'</td>
|
|
<td data-value="'.$data['group_by'].'">'.($data['group_by'] ? '<span style="color:red">Yes</span>' : '').'</td>
|
|
<td data-value="'.$data['location'].'">
|
|
<a href="javascript:void(0);" onclick="$(\'#callstack_'.$callstack_md5.'\').toggle();">'.$data['location'].'</a>
|
|
<div id="callstack_'.$callstack_md5.'" style="display:none">'.implode('<br>', $data['stack']).'</div>
|
|
</td>
|
|
</tr>';
|
|
}
|
|
echo '</table>
|
|
</div>';
|
|
}
|
|
|
|
protected function displayProfilingDoubles()
|
|
{
|
|
echo '<div class="row">
|
|
<h2><a name="doubles">Doubles</a></h2>
|
|
<table class="table table-condensed">';
|
|
foreach (Db::getInstance()->uniqQueries as $q => $nb) {
|
|
if ($nb > 1) {
|
|
echo '<tr><td><span '.$this->getQueryColor($nb).'>'.$nb.'</span></td><td class="pre"><pre>'.$q.'</pre></td></tr>';
|
|
}
|
|
}
|
|
echo '</table>
|
|
</div>';
|
|
}
|
|
|
|
protected function displayProfilingTableStress()
|
|
{
|
|
echo '<div class="row">
|
|
<h2><a name="tables">Tables stress</a></h2>
|
|
<table class="table table-condensed">';
|
|
foreach (Db::getInstance()->tables as $table => $nb) {
|
|
echo '<tr><td><span '.$this->getTableColor($nb).'>'.$nb.'</span> '.$table.'</td></tr>';
|
|
}
|
|
echo '</table>
|
|
</div>';
|
|
}
|
|
|
|
protected function displayProfilingObjectModel()
|
|
{
|
|
echo '
|
|
<div class="row">
|
|
<h2><a name="objectModels">ObjectModel instances</a></h2>
|
|
<table class="table table-condensed">
|
|
<tr><th>Name</th><th>Instances</th><th>Source</th></tr>';
|
|
foreach (ObjectModel::$debug_list as $class => $info) {
|
|
echo '<tr>
|
|
<td>'.$class.'</td>
|
|
<td><span '.$this->getObjectModelColor(count($info)).'>'.count($info).'</span></td>
|
|
<td>';
|
|
foreach ($info as $trace) {
|
|
echo str_replace(array(_PS_ROOT_DIR_, '\\'), array('', '/'), $trace['file']).' ['.$trace['line'].']<br />';
|
|
}
|
|
echo ' </td>
|
|
</tr>';
|
|
}
|
|
echo '</table>
|
|
</div>';
|
|
}
|
|
|
|
protected function displayProfilingFiles()
|
|
{
|
|
$i = 0;
|
|
|
|
echo '<div class="row">
|
|
<h2><a name="includedFiles">Included Files</a></h2>
|
|
<table class="table table-condensed">
|
|
<tr><th>#</th><th>Filename</th></tr>';
|
|
foreach (get_included_files() as $file) {
|
|
$file = str_replace('\\', '/', str_replace(_PS_ROOT_DIR_, '', $file));
|
|
if (strpos($file, '/tools/profiling/') === 0) {
|
|
continue;
|
|
}
|
|
echo '<tr><td>'.(++$i).'</td><td>'.$file.'</td></tr>';
|
|
}
|
|
echo '</table>
|
|
</div>';
|
|
}
|
|
|
|
public function displayProfiling()
|
|
{
|
|
if (!empty($this->redirect_after)) {
|
|
echo '
|
|
<html>
|
|
<head>
|
|
<meta charset="utf-8" />
|
|
<script src="//code.jquery.com/jquery-1.11.2.min.js"></script>
|
|
<script src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.2/js/bootstrap.min.js"></script>
|
|
<link href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css" rel="stylesheet" type="text/css"/>
|
|
</head>
|
|
<body>
|
|
<div class="container" style="width:100%">
|
|
<div class="row">
|
|
<div class="col-lg-12">
|
|
<h2>Caught redirection to <a href="'.htmlspecialchars($this->redirect_after).'"> '.htmlspecialchars($this->redirect_after).' </a></h2>
|
|
</div>
|
|
</div>';
|
|
} else {
|
|
// Call original display method
|
|
$this->display();
|
|
$this->profiler[] = $this->stamp('display');
|
|
}
|
|
|
|
// Process all profiling data
|
|
$this->processProfilingData();
|
|
|
|
// Add some specific style for profiling information
|
|
$this->displayProfilingStyle();
|
|
|
|
echo '<div id="prestashop_profiling" class="bootstrap">';
|
|
|
|
echo '<div class="row">';
|
|
$this->displayProfilingSummary();
|
|
$this->displayProfilingConfiguration();
|
|
$this->displayProfilingRun();
|
|
echo '</div><div class="row">';
|
|
$this->displayProfilingHooks();
|
|
$this->displayProfilingModules();
|
|
$this->displayProfilingLinks();
|
|
echo '</div>';
|
|
|
|
$this->displayProfilingStopwatch();
|
|
$this->displayProfilingDoubles();
|
|
$this->displayProfilingTableStress();
|
|
if (isset(ObjectModel::$debug_list)) {
|
|
$this->displayProfilingObjectModel();
|
|
}
|
|
$this->displayProfilingFiles();
|
|
|
|
if (!empty($this->redirect_after)) {
|
|
echo '</div></body></html>';
|
|
}
|
|
}
|
|
}
|
|
|
|
function prestashop_querytime_sort($a, $b)
|
|
{
|
|
if ($a['time'] == $b['time']) {
|
|
return 0;
|
|
}
|
|
return ($a['time'] > $b['time']) ? -1 : 1;
|
|
}
|