forked from jimrubenstein/php-profiler
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
initial commit of the php-profiler. inspired by the beautiful people …
…@ stack exchange.
- Loading branch information
0 parents
commit 99ae728
Showing
2 changed files
with
373 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,339 @@ | ||
<? | ||
|
||
class Profiler | ||
{ | ||
protected static | ||
$init = false, | ||
$enabled = true, | ||
$currentNode = null, | ||
$depthCount = 0, | ||
|
||
$topNodes = array(), | ||
|
||
$globalStart = 0, | ||
$globalEnd = 0, | ||
$globalDuration = 0, | ||
|
||
$profilerKey = null; | ||
|
||
public static function init() | ||
{ | ||
if (self::$init) return; | ||
|
||
self::$globalStart = microtime(true); | ||
self::$profilerKey = md5(rand(1,1000) . 'louddoor!' . time()); | ||
self::$init = true; | ||
} | ||
|
||
public static function isEnabled() | ||
{ | ||
return self::$enabled; | ||
} | ||
|
||
public static function enable() | ||
{ | ||
self::$enabled = true; | ||
} | ||
|
||
public static function disable() | ||
{ | ||
if (self::$currentNode == null && count(self::$topNodes) == 0) | ||
{ | ||
self::$enabled = false; | ||
} | ||
else | ||
{ | ||
throw new exception("Can not disable profiling once it has begun."); | ||
} | ||
} | ||
|
||
public static function start($nodeName, array $params = array()) | ||
{ | ||
if (!self::isEnabled()) return; | ||
|
||
$newNode = new ProfilerNode($nodeName, ++self::$depthCount, self::$currentNode, self::$profilerKey); | ||
|
||
if (self::$currentNode) | ||
{ | ||
self::$currentNode->addChild($newNode); | ||
} | ||
else | ||
{ | ||
self::$topNodes []= $newNode; | ||
} | ||
|
||
self::$currentNode = $newNode; | ||
|
||
return self::$currentNode; | ||
} | ||
|
||
public static function end($nodeName, $nuke = false) | ||
{ | ||
if (!self::isEnabled()) return; | ||
|
||
if (self::$currentNode == null) | ||
{ | ||
return; | ||
} | ||
|
||
while (self::$currentNode && self::$currentNode->getName() != $nodeName) | ||
{ | ||
if (!$nuke) | ||
{ | ||
trigger_error("Ending profile node '" . self::$currentNode->getName() . "' out of order (Requested end: '{$nodeName}')", E_USER_WARNING); | ||
} | ||
|
||
self::$currentNode = self::$currentNode->end(self::$profilerKey); | ||
self::$depthCount --; | ||
} | ||
|
||
if (self::$currentNode && self::$currentNode->getName() == $nodeName) | ||
{ | ||
self::$currentNode = self::$currentNode->end(self::$profilerKey); | ||
self::$depthCount --; | ||
} | ||
|
||
return self::$currentNode; | ||
} | ||
|
||
public static function sqlStart($query) | ||
{ | ||
if (!self::isEnabled()) return; | ||
|
||
$sqlProfile = new ProfilerSQLNode($query, self::$currentNode); | ||
|
||
self::$currentNode->sqlStart($sqlProfile); | ||
|
||
return $sqlProfile; | ||
} | ||
|
||
public static function sqlEnd($query) | ||
{ | ||
if (!self::isEnabled()) return; | ||
|
||
return self::$currentNode->sqlEnd($query); | ||
} | ||
|
||
public function render($show_depth = 2) | ||
{ | ||
if (!self::isEnabled()) return; | ||
|
||
self::end("___GLOBAL_END_PROFILER___", true); | ||
|
||
self::$globalEnd = microtime(true); | ||
self::$globalDuration = self::$globalEnd - self::$globalStart; | ||
|
||
require_once dirname(__FILE__) . '/profiler_tpl.tpl.php'; | ||
} | ||
|
||
public static function getGlobalStart() | ||
{ | ||
return round(self::$globalStart * 1000, 1); | ||
} | ||
|
||
public function getGlobalDuration() | ||
{ | ||
return round(self::$globalDuration * 1000, 1); | ||
} | ||
} | ||
//initialize this as soon as we include it. should be minimal overhead, so it's okay to do this all the time. | ||
profiler::init(); | ||
|
||
class ProfilerNode | ||
{ | ||
protected | ||
$name, | ||
$depth = 0, | ||
|
||
$started = null, | ||
$ended = null, | ||
$totalDuration = null, | ||
$selfDuration = null, | ||
$childDuration = 0, | ||
|
||
$parentNode = null, | ||
$childNodes = array(), | ||
|
||
$sqlQueryCount = 0, | ||
$sqlQueries = array(), | ||
$totalSQLQueryDuration = 0, | ||
|
||
$mcacheRequests = array(), | ||
|
||
$profilerKey = null; | ||
|
||
public function __construct($name, $depth, $parentNode, $profilerKey) | ||
{ | ||
$this->started = microtime(true); | ||
|
||
$this->name = $name; | ||
$this->depth = $depth; | ||
|
||
$this->parentNode = $parentNode; | ||
|
||
$this->profilerKey = $profilerKey; | ||
} | ||
|
||
public function end($profilerKey = null) | ||
{ | ||
if (!$profilerKey || $profilerKey != $this->profilerKey) | ||
{ | ||
profiler::end($this->name); | ||
|
||
return $this->parentNode; | ||
} | ||
|
||
if (null == $this->ended) | ||
{ | ||
$this->ended = microtime(true); | ||
$this->totalDuration = $this->ended - $this->started; | ||
$this->selfDuration = $this->totalDuration - $this->childDuration; | ||
|
||
if ($this->parentNode) | ||
{ | ||
$this->parentNode->increaseChildDuration($this->totalDuration); | ||
} | ||
} | ||
|
||
return $this->parentNode; | ||
} | ||
|
||
public function sqlStart($sqlProfile) | ||
{ | ||
$this->sqlQueries []= $sqlProfile; | ||
$this->sqlQueryCount ++; | ||
|
||
return $sqlProfile; | ||
} | ||
|
||
public function sqlEnd($query) | ||
{ | ||
foreach ($this->sqlQueries as $queryProfile) | ||
{ | ||
if ($queryProfile->getQuery() == $query) | ||
{ | ||
$this->totalSQLQueryDuration += $queryProfile->end()->getDuration(); | ||
return $queryProfile; | ||
} | ||
} | ||
|
||
return null; | ||
} | ||
|
||
public function getName() | ||
{ | ||
return $this->name; | ||
} | ||
|
||
public function getDepth() | ||
{ | ||
return $this->depth; | ||
} | ||
|
||
public function getParent() | ||
{ | ||
return $this->parentNode; | ||
} | ||
|
||
public function addChild($childNode) | ||
{ | ||
$this->childNodes []= $childNode; | ||
} | ||
|
||
public function hasChildren() | ||
{ | ||
return count($this->childNodes) > 0? true : false; | ||
} | ||
|
||
public function getChildren() | ||
{ | ||
return $this->childNodes; | ||
} | ||
|
||
public function increaseChildDuration($time) | ||
{ | ||
$this->childDuration += $time; | ||
|
||
return $this->childDuration; | ||
} | ||
|
||
public function hasSQLQueries() | ||
{ | ||
return $this->sqlQueryCount > 0? true : false; | ||
} | ||
|
||
public function getSQLQueries() | ||
{ | ||
return $this->sqlQueries; | ||
} | ||
|
||
public function getSQLQueryCount() | ||
{ | ||
return $this->sqlQueryCount; | ||
} | ||
|
||
public function addQueryDuration($time) | ||
{ | ||
$this->totalSQLQueryDuration += $time; | ||
} | ||
|
||
public function getTotalSQLQueryDuration() | ||
{ | ||
return round($this->totalSQLQueryDuration * 1000, 1); | ||
} | ||
|
||
public function getStart() | ||
{ | ||
return round($this->started * 1000, 1); | ||
} | ||
|
||
public function getEnd() | ||
{ | ||
return round($this->ended * 1000, 1); | ||
} | ||
|
||
public function getTotalDuration() | ||
{ | ||
return round($this->totalDuration * 1000, 1); | ||
} | ||
|
||
public function getSelfDuration() | ||
{ | ||
return round($this->selfDuration * 1000, 1); | ||
} | ||
} | ||
|
||
class ProfilerSQLNode | ||
{ | ||
protected | ||
$query, | ||
$profileNode, | ||
|
||
$started = null, | ||
$ended = null, | ||
$duration = null; | ||
|
||
public function __construct($query, $profileNode = null) | ||
{ | ||
$this->started = microtime(true); | ||
$this->query = $query; | ||
$this->profileNode = $profileNode; | ||
} | ||
|
||
public function end() | ||
{ | ||
if (null == $this->ended) | ||
{ | ||
$this->ended = microtime(true); | ||
$this->duration = $this->ended - $this->started; | ||
$this->profileNode->addQueryDuration($this->duration); | ||
} | ||
|
||
return $this; | ||
} | ||
|
||
public function getDuration() | ||
{ | ||
return round($this->duration * 1000, 1); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
<table> | ||
<tr> | ||
<th>Node Identifier</th> | ||
<th>duration (ms)</th> | ||
<th>with children (ms)</th> | ||
<th>from start (ms)</th> | ||
<th colspan="2">query time (ms)</th> | ||
</tr> | ||
|
||
<? foreach (self::$topNodes as $node): renderNode($node, $show_depth); endforeach; ?> | ||
|
||
<tr> | ||
<th> </th> | ||
<td><?= self::getGlobalDuration(); ?></td> | ||
</tr> | ||
</table> | ||
|
||
<? function renderNode($node, $max_depth = -1) { ?> | ||
<tr class="depth_<?= $node->getDepth(); ?>"> | ||
<td><?= str_repeat(' ', $node->getDepth() - 1); ?><?= $node->getName(); ?></td> | ||
<td><?= $node->getSelfDuration(); ?></td> | ||
<td><?= $node->getTotalDuration(); ?></td> | ||
<td><?= round($node->getStart() - profiler::getGlobalStart(), 1); ?></td> | ||
<td><?= $node->getSQLQueryCount() . " sql"; ?></td> | ||
<td><?= $node->getTotalSQLQueryDuration(); ?></td> | ||
</tr> | ||
|
||
<? if ($node->hasChildren() && ($max_depth == -1 || $max_depth > $node->getDepth())): ?> | ||
<? foreach ($node->getChildren() as $childNode): renderNode($childNode, $max_depth); endforeach; ?> | ||
<? endif; ?> | ||
<? } ?> | ||
|
||
|
||
|