public function displayDebug() { global $start_time; $this->display(); $this->_memory['display'] = memory_get_usage(); $this->_mempeak['display'] = memory_get_peak_usage(); $this->_time['display'] = microtime(true); $memory_peak_usage = memory_get_peak_usage(); $hr = '<hr>'; $totalSize = 0; foreach (get_included_files() as $file) { $totalSize += filesize($file); } $totalQueryTime = 0; foreach (Db::getInstance()->queries as $data) { $totalQueryTime += $data['time']; } $executedModules = Hook::getExecutedModules(); $hooktime = Hook::getHookTime(); arsort($hooktime); $totalHookTime = 0; foreach ($hooktime as $time) { $totalHookTime += $time; } $hookMemoryUsage = Hook::getHookMemoryUsage(); arsort($hookMemoryUsage); $totalHookMemoryUsage = 0; foreach ($hookMemoryUsage as $usage) { $totalHookMemoryUsage += $usage; } $globalSize = array(); $totalGlobalSize = 0; foreach ($GLOBALS as $key => $value) { if ($key != 'GLOBALS') { $totalGlobalSize += $size = $this->sizeofvar($value); if ($size > 1024) { $globalSize[$key] = round($size / 1024); } } } arsort($globalSize); $cache = Cache::retrieveAll(); $totalCacheSize = $this->sizeofvar($cache); echo ' <style> #ps_profiling{ padding: 20px; } .ps_back-office.page-sidebar #ps_profiling{ margin-left: 210px; } .ps_back-office.page-sidebar-closed #ps_profiling{ margin-left: 50px; } .ps_back-office #ps_profiling{ clear: both; padding: 10px; margin-bottom: 50px; } #ps_profiling *{ box-sizing:border-box; -moz-box-sizing:border-box; color: #888; } #ps_profiling .ps_profiling_title{ font-size: 20px; display: inline-block; padding-bottom: 15px; } #ps_profiling ul{ margin: 0; padding: 0; list-style: none; } #ps_profiling hr{ margin: 5px 0; padding: 0; border: none; border-bottom: solid 1px #ccc; } #ps_profiling td pre{ padding: 6px; margin-right: 10px; border-radius: 5px; 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; } #ps_profiling td .qbt{ max-height: 140px; overflow: auto; } #ps_profiling table{ width: 100%; margin-bottom: 10px; background-color: white; } #ps_profiling table th{ font-weight: normal; border-bottom: 1px solid #999; color: #888; padding: 5px 0; } #ps_profiling table td{ border-bottom: 1px solid #eee; padding: 6px; } .sortable thead th{ cursor:pointer; } #ps_profiling table .text-right{ text-align: right } #ps_profiling table .text-left{ text-align: left } #ps_profiling table .text-center{ text-align: center } #ps_profiling .ps_profiling_row{ clear: both; margin-bottom: 60px; } #ps_profiling .ps_profiling_col4{ float: left; padding: 0 10px; border-right: 1px solid #ccc; width: 25%; } @media (max-width: 1200px) { #ps_profiling .ps_profiling_col4 { width: 50%; } } @media (max-width: 600px) { #ps_profiling .ps_profiling_col4 { width: 100%; } } #ps_profiling .ps_profiling_col4:last-child{ border-right: none; } #ps_profiling .ps_profiling_infobox{ background-color: white; padding: 5px 10px; border: 1px solid #ccc; margin-bottom: 10px; } #ps_profiling .text-muted{ color: #bbb; } </style>'; echo '<div id="ps_profiling">'; if (!empty($this->redirect_after)) { echo '<div class="ps_profiling_row"><div class="ps_profiling_col12"><h2>Caught redirection to <a href="' . htmlspecialchars($this->redirect_after) . '">' . htmlspecialchars($this->redirect_after) . '</a></h2></div></div>'; } echo ' <div class="ps_profiling_row"> <div class="ps_profiling_col4"> <div class="ps_profiling_infobox"><b>Load time</b>: ' . $this->displayLoadTimeColor($this->_time['display'] - $start_time, true) . '</div>'; if (self::$_footer) { echo '<table>'; echo '<thead><tr><th class="text-left">Execution</th><th class="text-right">Load time (ms)</th></tr><thead><tbody>'; $last_time = $start_time; foreach ($this->_time as $k => $time) { echo '<tr><td>' . $k . '</td><td class="text-right">' . $this->displayLoadTimeColor($time - $last_time) . '</td></tr>'; $last_time = $time; } echo '</tbody></table>'; } echo '</div> <div class="ps_profiling_col4"> <div class="ps_profiling_infobox"><b>Hook processing</b>: ' . $this->displayLoadTimeColor($totalHookTime) . ' ms / ' . $this->displayMemoryColor($totalHookMemoryUsage) . ' Mb<br> ' . (int) count($executedModules) . ' methods called in ' . (int) count(array_unique($executedModules)) . ' modules</div>'; echo '<table>'; echo '<thead><tr><th class="text-left">Hook</th><th class="text-right">Processing</th></tr><thead><tbody>'; foreach ($hooktime as $hook => $time) { echo '<tr><td>' . $hook . '</td><td class="text-right">' . $this->displayMemoryColor($hookMemoryUsage[$hook]) . ' Mb in ' . $this->displayLoadTimeColor($time) . ' ms</td></tr>'; } echo '</table> </div> <div class="ps_profiling_col4"> <div class="ps_profiling_infobox"><b>Memory peak usage</b>: ' . $this->displayPeakMemoryColor($memory_peak_usage) . ' Mb</div>'; if (self::$_footer) { echo '<table>'; echo '<thead><tr><th class="text-left">Execution</th><th class="text-right">Memory (Mb)</th><th class="text-right">Total (Mb)</th></tr><thead><tbody>'; $last_memory = 0; foreach ($this->_memory as $k => $memory) { echo '<tr><td>' . $k . '</td><td class="text-right">' . $this->displayMemoryColor($memory - $last_memory) . '</td><td class="text-right">' . $this->displayPeakMemoryColor($this->_mempeak[$k]) . '</td></tr>'; $last_memory = $memory; } echo '<tbody></table>'; } echo ' </div>'; $compile = array(0 => 'green">never recompile', 1 => 'orange">auto', 2 => 'red">force compile'); echo ' <div class="ps_profiling_col4"> <div class="ps_profiling_infobox"><b>Total cache size in Cache class</b>: ' . $this->displayMemoryColor($totalCacheSize) . ' Mb</div> <div class="ps_profiling_infobox"><b>Smarty cache</b>: <span style="color:' . (Configuration::get('PS_SMARTY_CACHE') ? 'green">enabled' : 'red">disabled') . '</span></div> <div class="ps_profiling_infobox"><b>Smarty compilation</b>: <span style="color:' . $compile[Configuration::get('PS_SMARTY_FORCE_COMPILE')] . '</span></div> <div class="ps_profiling_infobox"><b>SQL Queries</b>: ' . $this->displaySQLQueries(count(Db::getInstance()->queries)) . ' in ' . $this->displayLoadTimeColor($totalQueryTime) . ' ms</div> <div class="ps_profiling_infobox"><b>Included files</b>: ' . sizeof(get_included_files()) . ' (' . $this->displayMemoryColor($totalSize) . ' Mb)</div> <div class="ps_profiling_infobox"><b>Global vars</b> : ' . $this->displayMemoryColor($totalGlobalSize) . ' Mb <ul>'; foreach ($globalSize as $global => $size) { echo '<li>$' . $global . ' ≈ ' . $size . 'k</li>'; } echo '</ul> </div> </div>'; $array_queries = array(); $queries = Db::getInstance()->queries; uasort($queries, 'prestashop_querytime_sort'); foreach ($queries as $data) { $query_row = array('time' => $data['time'], 'query' => $data['query'], 'location' => $data['stack'][0]['file'] . ':' . $data['stack'][0]['line'], 'filesort' => false, 'rows' => 1, 'group_by' => false, 'stack' => $data['stack']); 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_queries[] = $query_row; } echo '</div>'; echo ' <div class="ps_profiling_row"> <ul> <li><a href="#stopwatch">Stopwatch</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">Files included</a></li> </ul> </div> <div class="ps_profiling_row"> <span class="ps_profiling_title"><a name="stopwatch">Stopwatch (with SQL_NO_CACHE) (total = ' . count(Db::getInstance()->queries) . ')</a></span>'; $i = 1; echo ' <script type="text/javascript" src="https://raw.githubusercontent.com/drvic10k/bootstrap-sortable/master/Scripts/bootstrap-sortable.js"></script> <table class="table table-striped table-condensed sortable"> <thead> <tr> <th class="text-left col-lg-6">Query</th> <th class="text-left col-lg-1">Time (ms)</th> <th class="text-left col-lg-1">Rows</th> <th class="text-left col-lg-1">Filesort</th> <th class="text-left col-lg-1">Group By</th> <th class="text-left col-lg-2">Location</th> </tr> <thead> <tbody>'; foreach ($array_queries as $data) { $echo_stack = ''; array_shift($data['stack']); foreach ($data['stack'] as $call) { $echo_stack .= 'from ' . str_replace('\\', '/', substr($call['file'], strlen(_PS_ROOT_DIR_))) . ':' . $call['line'] . '<br />'; } echo ' <tr> <td><pre>' . preg_replace("/(^[\\s]*)/m", "", htmlspecialchars($data['query'], ENT_NOQUOTES, 'utf-8', false)) . '</pre></td> <td><span ' . $this->getTimeColor($data['time'] * 1000) . '>' . (round($data['time'] * 1000, 1) < 0.1 ? '< 1' : round($data['time'] * 1000, 1)) . '</span></td> <td>' . $data['rows'] . '</td> <td>' . ($data['filesort'] ? '<span style="color:red">Yes</span>' : '') . '</td> <td>' . ($data['group_by'] ? '<span style="color:red">Yes</span>' : '') . '</td> <td>in ' . $data['location'] . '<br /><div class="qbt" id="qbt' . $i++ . '">' . $echo_stack . '</div></td> </tr>'; } echo '</table>'; $queries = Db::getInstance()->uniqQueries; arsort($queries); $count = count(Db::getInstance()->uniqQueries); foreach ($queries as $q => &$nb) { if ($nb == 1) { $count--; } } if ($count) { echo '</div> <div class="ps_profiling_row"> <span class="ps_profiling_title"><a name="doubles">Doubles (IDs replaced by "XX") (total = ' . $count . ')</a></span> <table>'; } foreach ($queries as $q => $nb) { if ($nb > 1) { echo '<tr><td><span ' . $this->getQueryColor($nb) . '>' . $nb . '</span> ' . $q . '</td></tr>'; } } echo '</table></div> <div class="ps_profiling_row"> <span class="ps_profiling_title"><a name="tables">Tables stress</a></span> <table>'; $tables = Db::getInstance()->tables; arsort($tables); foreach ($tables as $table => $nb) { echo '<tr><td><span ' . $this->getTableColor($nb) . '>' . $nb . '</span> ' . $table . '</td></tr>'; } echo '</table></div>'; if (isset(ObjectModel::$debug_list)) { echo '<div class="ps_profiling_row"> <span class="ps_profiling_title"><a name="objectModels">ObjectModel instances</a></span>'; $list = ObjectModel::$debug_list; uasort($list, create_function('$a,$b', 'return (count($a) < count($b)) ? 1 : -1;')); $i = 0; echo '<table><thead><tr><th class="text-left">Name</th><th class="text-left">Instance</th><th class="text-left">Source</th></tr></thead><tbody>'; foreach ($list as $class => $info) { echo '<tr><td>' . $class . '</td>'; echo '<td><span ' . $this->getObjectModelColor(count($info)) . '>' . count($info) . '</span></td>'; echo '<td><div id="object_model_' . $i . '">'; foreach ($info as $trace) { echo ltrim(str_replace(array(_PS_ROOT_DIR_, '\\'), array('', '/'), $trace['file']), '/') . ' [' . $trace['line'] . ']<br />'; } echo '</div></td></tr>'; $i++; } echo '</tbody></table></div>'; } // List of included files echo '<div class="ps_profiling_row"> <span class="ps_profiling_title"><a name="includedFiles">Included files</a></span> <table>'; $i = 1; echo '<thead><tr><th class="text-left">#</th><th class="text-left">Filename</th></tr></thead><tbody>'; foreach (get_included_files() as $file) { $file = ltrim(str_replace('\\', '/', str_replace(_PS_ROOT_DIR_, '', $file)), '/'); $file = '<span class="text-muted">' . dirname($file) . '/</span><span>' . basename($file) . '</span>'; echo '<tr><td>' . $i . '</td><td>' . $file . '</td></tr>'; $i++; } echo '</tbody></table></div></div>'; }
public function displayDebug() { global $start_time; $this->display(); $this->_memory['display'] = memory_get_usage(); $this->_mempeak['display'] = memory_get_peak_usage(); $this->_time['display'] = microtime(true); if (!$this->ini_get_display_errors()) { return; } $memory_peak_usage = memory_get_peak_usage(); $hr = '<hr style="color:#F5F5F5;margin:2px" />'; $totalSize = 0; foreach (get_included_files() as $file) { $totalSize += filesize($file); } $totalQueryTime = 0; foreach (Db::getInstance()->queries as $data) { $totalQueryTime += $data['time']; } $executedModules = Hook::getExecutedModules(); $hooktime = Hook::getHookTime(); arsort($hooktime); $totalHookTime = 0; foreach ($hooktime as $time) { $totalHookTime += $time; } $hookMemoryUsage = Hook::getHookMemoryUsage(); arsort($hookMemoryUsage); $totalHookMemoryUsage = 0; foreach ($hookMemoryUsage as $usage) { $totalHookMemoryUsage += $usage; } $globalSize = array(); $totalGlobalSize = 0; foreach ($GLOBALS as $key => $value) { if ($key != 'GLOBALS') { $totalGlobalSize += $size = $this->sizeofvar($value); if ($size > 1024) { $globalSize[$key] = round($size / 1024, 1); } } } arsort($globalSize); $cache = Cache::retrieveAll(); $totalCacheSize = $this->sizeofvar($cache); echo ' <div style="clear:both;height:20px;line-height:20px"> </div> <div style="margin:50px;background-color:#FFFFFF"> <div class="rte" style="text-align:left;padding:8px;float:left"> <b>Load time</b>: ' . $this->displayLoadTimeColor($this->_time['display'] - $start_time, true) . ''; if (self::$_footer) { echo '<ul>'; } $last_time = $start_time; foreach ($this->_time as $k => $time) { echo '<li>' . $k . ': ' . $this->displayLoadTimeColor($time - $last_time) . '</li>'; $last_time = $time; } echo '</ul>'; echo '</div> <div class="rte" style="text-align:left;padding:8px;float:left;margin-left:20px"> <b>Hook processing</b>: ' . $this->displayLoadTimeColor($totalHookTime) . ' / ' . $this->displayMemoryColor($totalHookMemoryUsage) . '<br /> ' . (int) count($executedModules) . ' methods called in ' . (int) count(array_unique($executedModules)) . ' modules <ul>'; foreach ($hooktime as $hook => $time) { echo '<li>' . $hook . ': ' . $this->displayLoadTimeColor($time) . ' / ' . $this->displayMemoryColor($hookMemoryUsage[$hook]) . '</li>'; } echo '</ul> </div> <div class="rte" style="text-align:left;padding:8px;float:left;margin-left:20px"> <b>Memory peak usage</b>: ' . $this->displayPeakMemoryColor($memory_peak_usage); if (self::$_footer) { echo '<ul>'; $last_memory = 0; foreach ($this->_memory as $k => $memory) { echo '<li>' . $k . ': ' . $this->displayMemoryColor($memory - $last_memory) . ' (' . $this->displayPeakMemoryColor($this->_mempeak[$k]) . ')</li>'; $last_memory = $memory; } echo '</ul>'; } echo '<br /><br /> <b>Total cache size (in Cache class)</b>: ' . $this->displayMemoryColor($totalCacheSize) . ' </div>'; echo ' <div class="rte" style="text-align:left;padding:8px;float:left;margin-left:20px"> <b>DB type</b>: ' . get_class(Db::getInstance()) . ' <br /><b>SQL Queries</b>: ' . $this->displaySQLQueries(count(Db::getInstance()->queries)) . ' <br /><b>Time spent querying</b>: ' . $this->displayLoadTimeColor($totalQueryTime) . ' </div> <div class="rte" style="text-align:left;padding:8px;float:left;margin-left:20px"> <b>Included files</b>: ' . sizeof(get_included_files()) . '<br /> <b>Size of included files</b>: ' . $this->displayMemoryColor($totalSize) . ' </div> <div class="rte" style="text-align:left;padding:8px;float:left;margin-left:20px"> <b>Globals (> 1 Ko only): ' . round($totalGlobalSize / 1024) . ' Ko</b> <ul>'; foreach ($globalSize as $global => $size) { echo '<li>' . $global . ' ≈ ' . $size . ' Ko</li>'; } echo '</ul> </div>'; $array_queries = array(); $queries = Db::getInstance()->queries; uasort($queries, 'prestashop_querytime_sort'); foreach ($queries as $data) { $query_row = array('time' => $data['time'], 'query' => $data['query'], 'location' => $data['stack'][0]['file'] . ':' . $data['stack'][0]['line'], 'filesort' => false, 'rows' => 1, 'group_by' => false, 'stack' => $data['stack']); 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_queries[] = $query_row; } echo ' <div class="rte" style="text-align:left;padding:8px;clear:both;margin-top:20px"> <ul> <li><a href="#stopwatch">Go to Stopwatch</a></li> <li><a href="#doubles">Go to Doubles</a></li> <li><a href="#tables">Go to Tables</a></li> ' . (isset(ObjectModel::$debug_list) ? '<li><a href="#objectModels">Go to ObjectModels</a></li>' : '') . ' <li><a onclick="$(\'#queries_table\').toggle();" style="cursor:pointer">Display queries table</a></li> <li><a href="#includedFiles">Go to files</a></li> </ul> </div> <div id="queries_table" style="display:none;margin:4px"> <table class="table std"> <tr><th>Time (ms)</th><th>Rows</th><th>Query</th><th>Location</th><th>Filesort</th><th>Group By</th></tr>'; foreach ($array_queries as &$data) { $data['location'] = str_replace('\\', '/', substr($data['location'], strlen(_PS_ROOT_DIR_))); $data['query'] = str_replace('SQL_NO_CACHE ', '', $data['query']); echo '<tr><td>' . round(1000 * $data['time'], 3) . '</td><td>' . $data['rows'] . '</td><td>' . $data['query'] . '</td><td>' . $data['location'] . '</td><td>' . ($data['filesort'] ? 'Yes' : '') . '</td><td>' . ($data['group_by'] ? 'Yes' : '') . '</td></tr>'; } echo ' </table> </div> <div class="rte" style="text-align:left;padding:8px"> <h3><a name="stopwatch">Stopwatch (with SQL_NO_CACHE) (total = ' . count(Db::getInstance()->queries) . ')</a></h3>'; $i = 1; foreach ($array_queries as $data) { $echo_stack = ''; array_shift($data['stack']); foreach ($data['stack'] as $call) { $echo_stack .= 'from ' . str_replace('\\', '/', substr($call['file'], strlen(_PS_ROOT_DIR_))) . ':' . $call['line'] . '<br />'; } echo $hr . '<div onclick="$(\'#qbt' . $i . '\').toggle();"><b ' . $this->getTimeColor($data['time'] * 1000) . '>' . round($data['time'] * 1000, 3) . ' ms</b> ' . htmlspecialchars($data['query'], ENT_NOQUOTES, 'utf-8', false) . '<br /> in ' . $data['location'] . '<br /> <div id="qbt' . $i++ . '" style="display:none">' . $echo_stack . '</div>'; if (preg_match('/^\\s*select\\s+/i', $data['query'])) { if ($data['filesort']) { echo '<b ' . $this->getTimeColor($data['time'] * 1000) . '>USING FILESORT</b> - '; } echo $this->displayRowsBrowsed($data['rows']); if ($data['group_by']) { echo '<br /><b>Useless GROUP BY need to be removed</b>'; } } echo '</div>'; } $queries = Db::getInstance()->uniqQueries; arsort($queries); $count = count(Db::getInstance()->uniqQueries); foreach ($queries as $q => &$nb) { if ($nb == 1) { $count--; } } if ($count) { echo '</div> <div class="rte" style="text-align:left;padding:8px"> <h3><a name="doubles">Doubles (IDs replaced by "XX") (total = ' . $count . ')</a></h3>'; } foreach ($queries as $q => $nb) { if ($nb > 1) { echo $hr . '<b ' . $this->getQueryColor($nb) . '>' . $nb . '</b> ' . $q; } } echo '</div> <div class="rte" style="text-align:left;padding:8px"> <h3><a name="tables">Tables stress</a></h3>'; $tables = Db::getInstance()->tables; arsort($tables); foreach ($tables as $table => $nb) { echo $hr . '<b ' . $this->getTableColor($nb) . '>' . $nb . '</b> ' . $table; } echo '</div>'; if (isset(ObjectModel::$debug_list)) { echo '<div class="rte" style="text-align:left;padding:8px"> <h3><a name="objectModels">ObjectModel instances</a></h3>'; $list = ObjectModel::$debug_list; uasort($list, create_function('$a,$b', 'return (count($a) < count($b)) ? 1 : -1;')); $i = 0; foreach ($list as $class => $info) { echo $hr . '<b ' . $this->getObjectModelColor(count($info)) . '>' . count($info) . '</b> '; echo '<a href="#" onclick="$(\'#object_model_' . $i . '\').css(\'display\', $(\'#object_model_' . $i . '\').css(\'display\') == \'none\' ? \'block\' : \'none\'); return false">' . $class . '</a>'; echo '<div id="object_model_' . $i . '" style="display: none">'; foreach ($info as $trace) { echo ltrim(str_replace(array(_PS_ROOT_DIR_, '\\'), array('', '/'), $trace['file']), '/') . ' [' . $trace['line'] . ']<br />'; } echo '</div>'; $i++; } echo '</div>'; } // List of included files echo '<div class="rte" style="text-align:left;padding:8px"> <h3><a name="includedFiles">Included files</a></h3>'; $i = 1; foreach (get_included_files() as $file) { $file = ltrim(str_replace('\\', '/', str_replace(_PS_ROOT_DIR_, '', $file)), '/'); $file = '<b style="color: red">' . dirname($file) . '/</b><b style="color: blue">' . basename($file) . '</b>'; echo $i . ' ' . $file . '<br />'; $i++; } echo '</div> <div style="clear:both;height:20px;line-height:20px"> </div> </div>'; }