Created
May 11, 2012 15:35
-
-
Save hpbuniat/2660487 to your computer and use it in GitHub Desktop.
Find memory intensive functions in a bunch of xhprof-results (e.g. phpunit run with xhprof-listener)
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
<?php | |
class xhparser { | |
/** | |
* Content | |
* | |
* @var string | |
*/ | |
private $_aFiles = array(); | |
/** | |
* Functions to filter | |
* | |
* @var array | |
*/ | |
private $_aFilter = array( | |
'main()', | |
'PHPUnit_Framework_TestCase', | |
'PHPUnit_Framework_MockObject' | |
); | |
/** | |
* The result | |
* | |
* @var array | |
*/ | |
private $_aResult = array(); | |
/** | |
* Order by | |
* | |
* @var string | |
*/ | |
private $_sOrder = 'mu'; | |
/** | |
* Load a file | |
* | |
* @param string $sFile | |
* | |
* @return xhparser | |
*/ | |
public function load($sFile) { | |
if (file_exists($sFile) === true) { | |
$this->_aFiles[] = unserialize(file_get_contents($sFile)); | |
} | |
return $this; | |
} | |
/** | |
* Run | |
* | |
* @return array | |
*/ | |
public function run() { | |
$aResult = array(); | |
foreach ($this->_aFiles as $aTrace) { | |
foreach ($aTrace as $sFunction => $aData) { | |
$bAdd = true; | |
foreach ($this->_aFilter as $sFilter) { | |
if (strpos($sFunction, $sFilter) !== false) { | |
$bAdd = false; | |
break; | |
} | |
} | |
if ($bAdd === true) { | |
if (isset($aResult[$sFunction]) === false) { | |
$aResult[$sFunction] = array(); | |
} | |
$this->_merge($aResult[$sFunction], $aData); | |
} | |
} | |
} | |
$this->_order($aResult); | |
$this->_aResult = $aResult; | |
return $this; | |
} | |
/** | |
* Limit the result | |
* | |
* @param int $iLimit | |
* | |
* @return xhparser | |
*/ | |
public function limit($iLimit = 0) { | |
$iLimit = (int) $iLimit; | |
if ($iLimit > 0) { | |
$this->_aResult = array_slice($this->_aResult, 0, $iLimit); | |
} | |
return $this; | |
} | |
/** | |
* Return the result-string | |
* | |
* @return string | |
*/ | |
public function get() { | |
$sResult = ''; | |
foreach ($this->_aResult as $sFunction => $aResult) { | |
$sResult .= PHP_EOL . sprintf('Result: %s', $sFunction) . PHP_EOL . "Num\tCalls\t(avg)\t"; | |
$sResult .= str_pad("Wall", 10, " "); | |
$sResult .= str_pad("(avg)", 10, " "); | |
$sResult .= str_pad("CPU", 10, " "); | |
$sResult .= str_pad("(avg)", 10, " "); | |
$sResult .= str_pad("MEM", 10, " "); | |
$sResult .= str_pad("(avg)", 10, " "); | |
$sResult .= PHP_EOL; | |
$sResult .= ($aResult['count'] . "\t"); | |
$sResult .= ($aResult['ct'] . "\t"); | |
$sResult .= (ceil($aResult['ct_avg']) . "\t"); | |
$sResult .= str_pad($aResult['wt'], 10, " "); | |
$sResult .= str_pad(ceil($aResult['wt_avg']), 10, " "); | |
$sResult .= str_pad($aResult['cpu'], 10, " "); | |
$sResult .= str_pad(ceil($aResult['cpu_avg']), 10, " "); | |
$sResult .= str_pad(round($aResult['mu'] / 1024, 2), 10, " "); | |
$sResult .= str_pad(round($aResult['mu_avg'] / 1024, 2), 10, " "); | |
$sResult .= (PHP_EOL); | |
} | |
return $sResult; | |
} | |
/** | |
* Merge data of a function | |
* | |
* @param array $aBase | |
* @param array $aData | |
* | |
* @return xhparser | |
*/ | |
protected function _merge(array &$aBase, array $aData) { | |
$aData['count'] = 1; | |
if (empty($aBase) === true) { | |
$aBase = $aData; | |
} | |
else { | |
foreach ($aData as $sKey => $mValue) { | |
$aBase[$sKey] += $mValue; | |
} | |
} | |
foreach ($aData as $sKey => $mValue) { | |
$aBase[$sKey . '_avg'] = ($aBase[$sKey] / $aBase['count']); | |
} | |
return $this; | |
} | |
/** | |
* Order the traces of a file | |
* | |
* @param array $aTrace | |
* | |
* @return xhparser | |
*/ | |
protected function _order(&$aTrace) { | |
foreach ($aTrace as $sKey => $aRow) { | |
$aMemory[$sKey] = $aRow[$this->_sOrder]; | |
} | |
array_multisort($aMemory, SORT_DESC, $aTrace); | |
return $this; | |
} | |
/** | |
* Set order by | |
* | |
* @param string $sOrder | |
* | |
* @return xhparser | |
*/ | |
public function order($sOrder) { | |
$this->_sOrder = $sOrder; | |
return $this; | |
} | |
/** | |
* Add a filter | |
* | |
* @param string $sFilter | |
* | |
* @return xhparser | |
*/ | |
public function filter($sFilter) { | |
$this->_aFilter[] = trim($sFilter); | |
return $this; | |
} | |
} | |
$o = new xhparser(); | |
$aArgs = getopt('f:d:l:o:'); | |
if (isset($aArgs['d']) !== true) { | |
$aArgs['d'] = ini_get('xhprof.output_dir'); | |
} | |
if (isset($aArgs['o']) === true) { | |
$o->order($aArgs['o']); | |
} | |
if (empty($aArgs['f']) !== true) { | |
$aFilter = explode(',', $aArgs['f']); | |
foreach ($aFilter as $sFilter) { | |
$o->filter($sFilter); | |
} | |
} | |
$oIterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator(realpath($aArgs['d']))); | |
$aFiles = array(); | |
foreach ($oIterator as $sName => $oFile) { | |
$aFiles[] = $oFile->getPath() . DIRECTORY_SEPARATOR . $oFile->getFilename(); | |
} | |
foreach ($aFiles as $sFile) { | |
$o->load($sFile); | |
} | |
$o->run(); | |
if (empty($aArgs['l']) !== true) { | |
$o->limit($aArgs['l']); | |
} | |
print_r($o->get()); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment