<?php
/**
* Detailed mysql server profiling
* Logs all page view times in the Profile schema.
*
* records, when the request was made and from where,
* how long the request took un user, system and wait times,
* memory usage, all sql queries along with detailed profile
* counters on joins, file sorts, etc.
*
* All queries must be run though DB.php with ROGUE_PROFILE
* set to true.
*
* To use the profiler, after your page is rendered call:
* SqlProfile::getInstance()->logProfile();
*
* @author Cory Marsh
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
*/
class SqlProfile
{
protected static $_instance = null;
private $_time;
private $_queries;
private $_qtime;
private $_profile;
private function __construct()
{
$this->_time = 0;
$this->_queries = array();
$this->_qtime = array();
$this->_profile = array();
}
/**
* @return SqlProfile singleton
*/
public static function getInstance()
{
if (self::$_instance == null)
{
self::$_instance = new SqlProfile();
}
return self::$_instance;
}
public function profileQuery($sql, $time, $profile)
{
$this->_queries[] = $sql;
$this->_qtime[] = $time;
$this->_qprofile[] = $profile;
$this->_time += $time;
}
public function logProfile()
{
$wtime = microtime(true) - $GLOBALS['START'];
$usage = getrusage();
$usageText = '';
foreach ($usage as $key => $value)
{
if ($value > 0)
$usageText .= "$key = $value, ";
}
$queryText = '';
$qcounts = array();
// create the full profile text
for ($i=0,$m=count($this->_queries);$i<$m;$i++)
{
// remove extra white space
$q = preg_replace('/\s+/', ' ', $this->_queries[$i]);
// count the number of times the query is executed
$qcounts[$q] = (isset($qcounts)) ? $qcounts + 1 : 1;
$queryText .= $q . '\nTIME: ' . $this->_qtime . '\nPROFILE: ' . $this->_qprofile . '\n';
}
$ref = (isset($_SERVER['HTTP_REFERER'])) ? $_SERVER['HTTP_REFERER'] : '';
$db = DB::getConnection('Profile', true);
$insert = array('ip' => "!inet_aton('{$_SERVER['REMOTE_ADDR']}')",
'url' => $_SERVER['REQUEST_URI'],
'urlCRC' => crc32($_SERVER['REQUEST_URI']),
'utime' => $usage['ru_utime.tv_sec'] + $usage['ru_utime.tv_usec'] * 0.000001,
'stime' => $usage['ru_stime.tv_sec'] + $usage['ru_stime.tv_usec'] * 0.000001,
'wtime' => $wtime,
'rusage' => $usageText,
'sqltime' => $this->_time,
'memory' => memory_get_peak_usage(true),
'numQueries' => count($this->_queries),
'cache' => LIB_CACHE_ENABLE,
'agent' => $_SERVER['HTTP_USER_AGENT'],
'referer' => $ref,
'queries' => $queryText
);
try
{
$db->insert('profile-page', 'current', $insert);
}
catch (SyntaxException $ex)
{
if ($db->getLastErrorNo() == 1146)
{
$db->sqlStmt('clone-profile', "CREATE TABLE current LIKE template", null);
$db->insert('profile-page', 'current', $insert);
}
else
Logger::getLogger('exception')->error($ex);
}
foreach ($this->_queries as $q)
{
if (preg_match('/^\s*select /i', $q)) {
$q = preg_replace('/\s+/', ' ', $this->_queries[$i]);
if ($qcounts[$q] > 1) {
$db->insert('log-query-dups', 'QueryDups',
array('url' => $_SERVER['REQUEST_URI'],
'urlCRC' => crc32($_SERVER['REQUEST_URI']),
'ip' => "!inet_aton('{$_SERVER['REMOTE_ADDR']}')",
'query' => $q,
'queryCRC' => crc32($q),
'executed' => $qcounts[$q]));
}
}
}
}
}