summaryrefslogtreecommitdiffstats
path: root/library
diff options
context:
space:
mode:
Diffstat (limited to 'library')
-rw-r--r--library/FileHandler.php376
-rw-r--r--library/Preprocessor.php277
-rw-r--r--library/Reader.php492
-rw-r--r--library/gprof2dot.py210
4 files changed, 673 insertions, 682 deletions
diff --git a/library/FileHandler.php b/library/FileHandler.php
index 7bcce61..05c756c 100644
--- a/library/FileHandler.php
+++ b/library/FileHandler.php
@@ -7,200 +7,200 @@ require 'Preprocessor.php';
* @author Jacob Oettinger
* @author Joakim Nygård
*/
-class Webgrind_FileHandler{
-
- private static $singleton = null;
-
-
- /**
- * @return Singleton instance of the filehandler
- */
- public static function getInstance(){
- if(self::$singleton==null)
- self::$singleton = new self();
- return self::$singleton;
- }
-
- private function __construct(){
- // Get list of files matching the defined format
- $files = $this->getFiles(Webgrind_Config::xdebugOutputFormat(), Webgrind_Config::xdebugOutputDir());
-
- // Get list of preprocessed files
+class Webgrind_FileHandler
+{
+
+ private static $singleton = null;
+
+
+ /**
+ * @return Singleton instance of the filehandler
+ */
+ public static function getInstance() {
+ if (self::$singleton==null)
+ self::$singleton = new self();
+ return self::$singleton;
+ }
+
+ private function __construct() {
+ // Get list of files matching the defined format
+ $files = $this->getFiles(Webgrind_Config::xdebugOutputFormat(), Webgrind_Config::xdebugOutputDir());
+
+ // Get list of preprocessed files
$prepFiles = $this->getPrepFiles('/\\'.Webgrind_Config::$preprocessedSuffix.'$/', Webgrind_Config::storageDir());
- // Loop over the preprocessed files.
- foreach($prepFiles as $fileName=>$prepFile){
- $fileName = str_replace(Webgrind_Config::$preprocessedSuffix,'',$fileName);
-
- // If it is older than its corrosponding original: delete it.
- // If it's original does not exist: delete it
- if(!isset($files[$fileName]) || $files[$fileName]['mtime']>$prepFile['mtime'] )
- unlink($prepFile['absoluteFilename']);
- else
- $files[$fileName]['preprocessed'] = true;
- }
- // Sort by mtime
- uasort($files,array($this,'mtimeCmp'));
-
- $this->files = $files;
- }
-
- /**
- * Get the value of the cmd header in $file
- *
- * @return void string
- */
- private function getInvokeUrl($file){
- if (preg_match('/.webgrind$/', $file))
- return 'Webgrind internal';
-
- // Grab name of invoked file.
- $fp = fopen($file, 'r');
+ // Loop over the preprocessed files.
+ foreach ($prepFiles as $fileName=>$prepFile) {
+ $fileName = str_replace(Webgrind_Config::$preprocessedSuffix,'',$fileName);
+
+ // If it is older than its corrosponding original: delete it.
+ // If it's original does not exist: delete it
+ if (!isset($files[$fileName]) || $files[$fileName]['mtime']>$prepFile['mtime'])
+ unlink($prepFile['absoluteFilename']);
+ else
+ $files[$fileName]['preprocessed'] = true;
+ }
+ // Sort by mtime
+ uasort($files,array($this,'mtimeCmp'));
+
+ $this->files = $files;
+ }
+
+ /**
+ * Get the value of the cmd header in $file
+ *
+ * @return void string
+ */
+ private function getInvokeUrl($file) {
+ if (preg_match('/.webgrind$/', $file))
+ return 'Webgrind internal';
+
+ // Grab name of invoked file.
+ $fp = fopen($file, 'r');
$invokeUrl = '';
- while ((($line = fgets($fp)) !== FALSE) && !strlen($invokeUrl)){
- if (preg_match('/^cmd: (.*)$/', $line, $parts)){
+ while ((($line = fgets($fp)) !== FALSE) && !strlen($invokeUrl)) {
+ if (preg_match('/^cmd: (.*)$/', $line, $parts)) {
$invokeUrl = isset($parts[1]) ? $parts[1] : '';
}
}
fclose($fp);
- if (!strlen($invokeUrl))
+ if (!strlen($invokeUrl))
$invokeUrl = 'Unknown!';
- return $invokeUrl;
- }
-
- /**
- * List of files in $dir whose filename has the format $format
- *
- * @return array Files
- */
- private function getFiles($format, $dir){
- $list = preg_grep($format,scandir($dir));
- $files = array();
-
- $scriptFilename = $_SERVER['SCRIPT_FILENAME'];
-
- # Moved this out of loop to run faster
- if (function_exists('xdebug_get_profiler_filename'))
- $selfFile = realpath(xdebug_get_profiler_filename());
- else
- $selfFile = '';
-
- foreach($list as $file){
- $absoluteFilename = $dir.$file;
-
- // Exclude webgrind preprocessed files
- if (false !== strstr($absoluteFilename, Webgrind_Config::$preprocessedSuffix))
- continue;
-
- // Make sure that script never parses the profile currently being generated. (infinite loop)
- if ($selfFile == realpath($absoluteFilename))
- continue;
-
- $invokeUrl = rtrim($this->getInvokeUrl($absoluteFilename));
- if (Webgrind_Config::$hideWebgrindProfiles && $invokeUrl == dirname(dirname(__FILE__)).DIRECTORY_SEPARATOR.'index.php')
- continue;
-
-
- $files[$file] = array('absoluteFilename' => $absoluteFilename,
- 'mtime' => filemtime($absoluteFilename),
- 'preprocessed' => false,
- 'invokeUrl' => $invokeUrl,
- 'filesize' => $this->bytestostring(filesize($absoluteFilename))
- );
- }
- return $files;
- }
-
- /**
- * List of files in $dir whose filename has the format $format
- *
- * @return array Files
- */
- private function getPrepFiles($format, $dir){
- $list = preg_grep($format,scandir($dir));
- $files = array();
-
- $scriptFilename = $_SERVER['SCRIPT_FILENAME'];
-
- foreach($list as $file){
- $absoluteFilename = $dir.$file;
-
- // Make sure that script does not include the profile currently being generated. (infinite loop)
- if (function_exists('xdebug_get_profiler_filename') && realpath(xdebug_get_profiler_filename())==realpath($absoluteFilename))
- continue;
-
- $files[$file] = array('absoluteFilename' => $absoluteFilename,
- 'mtime' => filemtime($absoluteFilename),
- 'preprocessed' => true,
- 'filesize' => $this->bytestostring(filesize($absoluteFilename))
- );
- }
- return $files;
- }
- /**
- * Get list of available trace files. Optionally including traces of the webgrind script it self
- *
- * @return array Files
- */
- public function getTraceList(){
- $result = array();
- foreach($this->files as $fileName=>$file){
- $result[] = array('filename' => $fileName,
- 'invokeUrl' => str_replace($_SERVER['DOCUMENT_ROOT'].'/', '', $file['invokeUrl']),
- 'filesize' => $file['filesize'],
- 'mtime' => date(Webgrind_Config::$dateFormat, $file['mtime'])
- );
- }
- return $result;
- }
-
- /**
- * Get a trace reader for the specific file.
- *
- * If the file has not been preprocessed yet this will be done first.
- *
- * @param string File to read
- * @param Cost format for the reader
- * @return Webgrind_Reader Reader for $file
- */
- public function getTraceReader($file, $costFormat){
- $prepFile = Webgrind_Config::storageDir().$file.Webgrind_Config::$preprocessedSuffix;
- try{
- $r = new Webgrind_Reader($prepFile, $costFormat);
- } catch (Exception $e){
- // Preprocessed file does not exist or other error
- Webgrind_Preprocessor::parse(Webgrind_Config::xdebugOutputDir().$file, $prepFile);
- $r = new Webgrind_Reader($prepFile, $costFormat);
- }
- return $r;
- }
-
- /**
- * Comparison function for sorting
- *
- * @return boolean
- */
- private function mtimeCmp($a, $b){
- if ($a['mtime'] == $b['mtime'])
- return 0;
-
- return ($a['mtime'] > $b['mtime']) ? -1 : 1;
- }
-
- /**
- * Present a size (in bytes) as a human-readable value
- *
- * @param int $size size (in bytes)
- * @param int $precision number of digits after the decimal point
- * @return string
- */
- private function bytestostring($size, $precision = 0) {
- $sizes = array('YB', 'ZB', 'EB', 'PB', 'TB', 'GB', 'MB', 'KB', 'B');
- $total = count($sizes);
-
- while($total-- && $size > 1024) {
- $size /= 1024;
- }
- return round($size, $precision).$sizes[$total];
+ return $invokeUrl;
+ }
+
+ /**
+ * List of files in $dir whose filename has the format $format
+ *
+ * @return array Files
+ */
+ private function getFiles($format, $dir) {
+ $list = preg_grep($format,scandir($dir));
+ $files = array();
+
+ $scriptFilename = $_SERVER['SCRIPT_FILENAME'];
+
+ # Moved this out of loop to run faster
+ if (function_exists('xdebug_get_profiler_filename'))
+ $selfFile = realpath(xdebug_get_profiler_filename());
+ else
+ $selfFile = '';
+
+ foreach ($list as $file) {
+ $absoluteFilename = $dir.$file;
+
+ // Exclude webgrind preprocessed files
+ if (false !== strstr($absoluteFilename, Webgrind_Config::$preprocessedSuffix))
+ continue;
+
+ // Make sure that script never parses the profile currently being generated. (infinite loop)
+ if ($selfFile == realpath($absoluteFilename))
+ continue;
+
+ $invokeUrl = rtrim($this->getInvokeUrl($absoluteFilename));
+ if (Webgrind_Config::$hideWebgrindProfiles && $invokeUrl == dirname(dirname(__FILE__)).DIRECTORY_SEPARATOR.'index.php')
+ continue;
+
+ $files[$file] = array('absoluteFilename' => $absoluteFilename,
+ 'mtime' => filemtime($absoluteFilename),
+ 'preprocessed' => false,
+ 'invokeUrl' => $invokeUrl,
+ 'filesize' => $this->bytestostring(filesize($absoluteFilename))
+ );
+ }
+ return $files;
+ }
+
+ /**
+ * List of files in $dir whose filename has the format $format
+ *
+ * @return array Files
+ */
+ private function getPrepFiles($format, $dir) {
+ $list = preg_grep($format,scandir($dir));
+ $files = array();
+
+ $scriptFilename = $_SERVER['SCRIPT_FILENAME'];
+
+ foreach ($list as $file) {
+ $absoluteFilename = $dir.$file;
+
+ // Make sure that script does not include the profile currently being generated. (infinite loop)
+ if (function_exists('xdebug_get_profiler_filename') && realpath(xdebug_get_profiler_filename())==realpath($absoluteFilename))
+ continue;
+
+ $files[$file] = array('absoluteFilename' => $absoluteFilename,
+ 'mtime' => filemtime($absoluteFilename),
+ 'preprocessed' => true,
+ 'filesize' => $this->bytestostring(filesize($absoluteFilename))
+ );
+ }
+ return $files;
+ }
+ /**
+ * Get list of available trace files. Optionally including traces of the webgrind script it self
+ *
+ * @return array Files
+ */
+ public function getTraceList() {
+ $result = array();
+ foreach ($this->files as $fileName=>$file) {
+ $result[] = array('filename' => $fileName,
+ 'invokeUrl' => str_replace($_SERVER['DOCUMENT_ROOT'].'/', '', $file['invokeUrl']),
+ 'filesize' => $file['filesize'],
+ 'mtime' => date(Webgrind_Config::$dateFormat, $file['mtime'])
+ );
+ }
+ return $result;
+ }
+
+ /**
+ * Get a trace reader for the specific file.
+ *
+ * If the file has not been preprocessed yet this will be done first.
+ *
+ * @param string File to read
+ * @param Cost format for the reader
+ * @return Webgrind_Reader Reader for $file
+ */
+ public function getTraceReader($file, $costFormat) {
+ $prepFile = Webgrind_Config::storageDir().$file.Webgrind_Config::$preprocessedSuffix;
+ try {
+ $r = new Webgrind_Reader($prepFile, $costFormat);
+ } catch (Exception $e) {
+ // Preprocessed file does not exist or other error
+ Webgrind_Preprocessor::parse(Webgrind_Config::xdebugOutputDir().$file, $prepFile);
+ $r = new Webgrind_Reader($prepFile, $costFormat);
+ }
+ return $r;
+ }
+
+ /**
+ * Comparison function for sorting
+ *
+ * @return boolean
+ */
+ private function mtimeCmp($a, $b) {
+ if ($a['mtime'] == $b['mtime'])
+ return 0;
+
+ return ($a['mtime'] > $b['mtime']) ? -1 : 1;
+ }
+
+ /**
+ * Present a size (in bytes) as a human-readable value
+ *
+ * @param int $size size (in bytes)
+ * @param int $precision number of digits after the decimal point
+ * @return string
+ */
+ private function bytestostring($size, $precision = 0) {
+ $sizes = array('YB', 'ZB', 'EB', 'PB', 'TB', 'GB', 'MB', 'KB', 'B');
+ $total = count($sizes);
+
+ while ($total-- && $size > 1024) {
+ $size /= 1024;
+ }
+ return round($size, $precision).$sizes[$total];
}
-} \ No newline at end of file
+}
diff --git a/library/Preprocessor.php b/library/Preprocessor.php
index b29581c..9205a62 100644
--- a/library/Preprocessor.php
+++ b/library/Preprocessor.php
@@ -1,70 +1,68 @@
<?php
/**
* Class for preprocessing callgrind files.
- *
- * Information from the callgrind file is extracted and written in a binary format for
+ *
+ * Information from the callgrind file is extracted and written in a binary format for
* fast random access.
- *
+ *
* @see http://code.google.com/p/webgrind/wiki/PreprocessedFormat
* @see http://valgrind.org/docs/manual/cl-format.html
* @package Webgrind
* @author Jacob Oettinger
- **/
+ */
class Webgrind_Preprocessor
{
- /**
- * Fileformat version. Embedded in the output for parsers to use.
- */
- const FILE_FORMAT_VERSION = 7;
-
- /**
- * Binary number format used.
- * @see http://php.net/pack
- */
- const NR_FORMAT = 'V';
-
- /**
- * Size, in bytes, of the above number format
- */
- const NR_SIZE = 4;
-
- /**
- * String name of main function
- */
- const ENTRY_POINT = '{main}';
-
-
- /**
- * Extract information from $inFile and store in preprocessed form in $outFile
- *
- * @param string $inFile Callgrind file to read
- * @param string $outFile File to write preprocessed data to
- * @return void
- **/
- static function parse($inFile, $outFile)
- {
- $in = @fopen($inFile, 'rb');
- if(!$in)
- throw new Exception('Could not open '.$inFile.' for reading.');
- $out = @fopen($outFile, 'w+b');
- if(!$out)
- throw new Exception('Could not open '.$outFile.' for writing.');
-
- $nextFuncNr = 0;
- $functions = array();
- $headers = array();
- $calls = array();
-
-
- // Read information into memory
- while(($line = fgets($in))){
- if(substr($line,0,3)==='fl='){
- // Found invocation of function. Read functionname
- list($function) = fscanf($in,"fn=%[^\n\r]s");
- if(!isset($functions[$function])){
- $functions[$function] = array(
- 'filename' => substr(trim($line),3),
+ /**
+ * Fileformat version. Embedded in the output for parsers to use.
+ */
+ const FILE_FORMAT_VERSION = 7;
+
+ /**
+ * Binary number format used.
+ * @see http://php.net/pack
+ */
+ const NR_FORMAT = 'V';
+
+ /**
+ * Size, in bytes, of the above number format
+ */
+ const NR_SIZE = 4;
+
+ /**
+ * String name of main function
+ */
+ const ENTRY_POINT = '{main}';
+
+
+ /**
+ * Extract information from $inFile and store in preprocessed form in $outFile
+ *
+ * @param string $inFile Callgrind file to read
+ * @param string $outFile File to write preprocessed data to
+ * @return void
+ */
+ static function parse($inFile, $outFile) {
+ $in = @fopen($inFile, 'rb');
+ if(!$in)
+ throw new Exception('Could not open '.$inFile.' for reading.');
+ $out = @fopen($outFile, 'w+b');
+ if(!$out)
+ throw new Exception('Could not open '.$outFile.' for writing.');
+
+ $nextFuncNr = 0;
+ $functions = array();
+ $headers = array();
+ $calls = array();
+
+ // Read information into memory
+ while (($line = fgets($in))) {
+ if (substr($line,0,3)==='fl=') {
+ // Found invocation of function. Read functionname
+ list($function) = fscanf($in,"fn=%[^\n\r]s");
+ if (!isset($functions[$function])) {
+ $functions[$function] = array(
+ 'filename' => substr(trim($line),3),
'invocationCount' => 0,
'nr' => $nextFuncNr++,
'count' => 0,
@@ -72,90 +70,85 @@ class Webgrind_Preprocessor
'summedInclusiveCost' => 0,
'calledFromInformation' => array(),
'subCallInformation' => array()
- );
- }
- $functions[$function]['invocationCount']++;
- // Special case for ENTRY_POINT - it contains summary header
- if(self::ENTRY_POINT == $function){
- fgets($in);
- $headers[] = fgets($in);
- fgets($in);
- }
- // Cost line
- list($lnr, $cost) = fscanf($in,"%d %d");
- $functions[$function]['line'] = $lnr;
- $functions[$function]['summedSelfCost'] += $cost;
- $functions[$function]['summedInclusiveCost'] += $cost;
- } else if(substr($line,0,4)==='cfn=') {
-
- // Found call to function. ($function should contain function call originates from)
- $calledFunctionName = substr(trim($line),4);
- // Skip call line
- fgets($in);
- // Cost line
- list($lnr, $cost) = fscanf($in,"%d %d");
-
- $functions[$function]['summedInclusiveCost'] += $cost;
-
- if(!isset($functions[$calledFunctionName]['calledFromInformation'][$function.':'.$lnr]))
- $functions[$calledFunctionName]['calledFromInformation'][$function.':'.$lnr] = array('functionNr'=>$functions[$function]['nr'],'line'=>$lnr,'callCount'=>0,'summedCallCost'=>0);
-
- $functions[$calledFunctionName]['calledFromInformation'][$function.':'.$lnr]['callCount']++;
- $functions[$calledFunctionName]['calledFromInformation'][$function.':'.$lnr]['summedCallCost'] += $cost;
-
- if(!isset($functions[$function]['subCallInformation'][$calledFunctionName.':'.$lnr])){
- $functions[$function]['subCallInformation'][$calledFunctionName.':'.$lnr] = array('functionNr'=>$functions[$calledFunctionName]['nr'],'line'=>$lnr,'callCount'=>0,'summedCallCost'=>0);
- }
-
- $functions[$function]['subCallInformation'][$calledFunctionName.':'.$lnr]['callCount']++;
- $functions[$function]['subCallInformation'][$calledFunctionName.':'.$lnr]['summedCallCost'] += $cost;
-
-
- } else if(strpos($line,': ')!==false){
- // Found header
- $headers[] = $line;
- }
- }
-
-
- // Write output
- $functionCount = sizeof($functions);
- fwrite($out, pack(self::NR_FORMAT.'*', self::FILE_FORMAT_VERSION, 0, $functionCount));
- // Make room for function addresses
- fseek($out,self::NR_SIZE*$functionCount, SEEK_CUR);
- $functionAddresses = array();
- foreach($functions as $functionName => $function){
- $functionAddresses[] = ftell($out);
- $calledFromCount = sizeof($function['calledFromInformation']);
- $subCallCount = sizeof($function['subCallInformation']);
- fwrite($out, pack(self::NR_FORMAT.'*', $function['line'], $function['summedSelfCost'], $function['summedInclusiveCost'], $function['invocationCount'], $calledFromCount, $subCallCount));
- // Write called from information
- foreach((array)$function['calledFromInformation'] as $call){
- fwrite($out, pack(self::NR_FORMAT.'*', $call['functionNr'], $call['line'], $call['callCount'], $call['summedCallCost']));
- }
- // Write sub call information
- foreach((array)$function['subCallInformation'] as $call){
- fwrite($out, pack(self::NR_FORMAT.'*', $call['functionNr'], $call['line'], $call['callCount'], $call['summedCallCost']));
- }
-
- fwrite($out, $function['filename']."\n".$functionName."\n");
- }
- $headersPos = ftell($out);
- // Write headers
- foreach($headers as $header){
- fwrite($out,$header);
- }
-
- // Write addresses
- fseek($out,self::NR_SIZE, SEEK_SET);
- fwrite($out, pack(self::NR_FORMAT, $headersPos));
- // Skip function count
- fseek($out,self::NR_SIZE, SEEK_CUR);
- // Write function addresses
- foreach($functionAddresses as $address){
- fwrite($out, pack(self::NR_FORMAT, $address));
- }
-
- }
-
+ );
+ }
+ $functions[$function]['invocationCount']++;
+ // Special case for ENTRY_POINT - it contains summary header
+ if (self::ENTRY_POINT == $function) {
+ fgets($in);
+ $headers[] = fgets($in);
+ fgets($in);
+ }
+ // Cost line
+ list($lnr, $cost) = fscanf($in,"%d %d");
+ $functions[$function]['line'] = $lnr;
+ $functions[$function]['summedSelfCost'] += $cost;
+ $functions[$function]['summedInclusiveCost'] += $cost;
+ } elseif (substr($line,0,4)==='cfn=') {
+ // Found call to function. ($function should contain function call originates from)
+ $calledFunctionName = substr(trim($line),4);
+ // Skip call line
+ fgets($in);
+ // Cost line
+ list($lnr, $cost) = fscanf($in,"%d %d");
+
+ $functions[$function]['summedInclusiveCost'] += $cost;
+
+ if (!isset($functions[$calledFunctionName]['calledFromInformation'][$function.':'.$lnr]))
+ $functions[$calledFunctionName]['calledFromInformation'][$function.':'.$lnr] = array('functionNr'=>$functions[$function]['nr'],'line'=>$lnr,'callCount'=>0,'summedCallCost'=>0);
+
+ $functions[$calledFunctionName]['calledFromInformation'][$function.':'.$lnr]['callCount']++;
+ $functions[$calledFunctionName]['calledFromInformation'][$function.':'.$lnr]['summedCallCost'] += $cost;
+
+ if (!isset($functions[$function]['subCallInformation'][$calledFunctionName.':'.$lnr])){
+ $functions[$function]['subCallInformation'][$calledFunctionName.':'.$lnr] = array('functionNr'=>$functions[$calledFunctionName]['nr'],'line'=>$lnr,'callCount'=>0,'summedCallCost'=>0);
+ }
+
+ $functions[$function]['subCallInformation'][$calledFunctionName.':'.$lnr]['callCount']++;
+ $functions[$function]['subCallInformation'][$calledFunctionName.':'.$lnr]['summedCallCost'] += $cost;
+ } elseif (strpos($line,': ')!==false) {
+ // Found header
+ $headers[] = $line;
+ }
+ }
+
+ // Write output
+ $functionCount = sizeof($functions);
+ fwrite($out, pack(self::NR_FORMAT.'*', self::FILE_FORMAT_VERSION, 0, $functionCount));
+ // Make room for function addresses
+ fseek($out,self::NR_SIZE*$functionCount, SEEK_CUR);
+ $functionAddresses = array();
+ foreach ($functions as $functionName => $function) {
+ $functionAddresses[] = ftell($out);
+ $calledFromCount = sizeof($function['calledFromInformation']);
+ $subCallCount = sizeof($function['subCallInformation']);
+ fwrite($out, pack(self::NR_FORMAT.'*', $function['line'], $function['summedSelfCost'], $function['summedInclusiveCost'], $function['invocationCount'], $calledFromCount, $subCallCount));
+ // Write called from information
+ foreach ((array)$function['calledFromInformation'] as $call) {
+ fwrite($out, pack(self::NR_FORMAT.'*', $call['functionNr'], $call['line'], $call['callCount'], $call['summedCallCost']));
+ }
+ // Write sub call information
+ foreach ((array)$function['subCallInformation'] as $call) {
+ fwrite($out, pack(self::NR_FORMAT.'*', $call['functionNr'], $call['line'], $call['callCount'], $call['summedCallCost']));
+ }
+
+ fwrite($out, $function['filename']."\n".$functionName."\n");
+ }
+ $headersPos = ftell($out);
+ // Write headers
+ foreach ($headers as $header) {
+ fwrite($out,$header);
+ }
+
+ // Write addresses
+ fseek($out,self::NR_SIZE, SEEK_SET);
+ fwrite($out, pack(self::NR_FORMAT, $headersPos));
+ // Skip function count
+ fseek($out,self::NR_SIZE, SEEK_CUR);
+ // Write function addresses
+ foreach ($functionAddresses as $address) {
+ fwrite($out, pack(self::NR_FORMAT, $address));
+ }
+ }
+
}
diff --git a/library/Reader.php b/library/Reader.php
index 35c5113..cb306b5 100644
--- a/library/Reader.php
+++ b/library/Reader.php
@@ -1,269 +1,267 @@
<?php
/**
* Class for reading datafiles generated by Webgrind_Preprocessor
- *
+ *
* @package Webgrind
* @author Jacob Oettinger
- **/
+ */
class Webgrind_Reader
{
- /**
- * File format version that this reader understands
- */
- const FILE_FORMAT_VERSION = 7;
-
- /**
- * Binary number format used.
- * @see http://php.net/pack
- */
- const NR_FORMAT = 'V';
-
- /**
- * Size, in bytes, of the above number format
- */
- const NR_SIZE = 4;
-
- /**
- * Length of a call information block
- */
- const CALLINFORMATION_LENGTH = 4;
-
/**
- * Length of a function information block
- */
- const FUNCTIONINFORMATION_LENGTH = 6;
-
+ * File format version that this reader understands
+ */
+ const FILE_FORMAT_VERSION = 7;
+
+ /**
+ * Binary number format used.
+ * @see http://php.net/pack
+ */
+ const NR_FORMAT = 'V';
+
+ /**
+ * Size, in bytes, of the above number format
+ */
+ const NR_SIZE = 4;
+
+ /**
+ * Length of a call information block
+ */
+ const CALLINFORMATION_LENGTH = 4;
+
+ /**
+ * Length of a function information block
+ */
+ const FUNCTIONINFORMATION_LENGTH = 6;
+
+ /**
+ * Address of the headers in the data file
+ *
+ * @var int
+ */
+ private $headersPos;
+
+ /**
+ * Array of addresses pointing to information about functions
+ *
+ * @var array
+ */
+ private $functionPos;
+
+ /**
+ * Array of headers
+ *
+ * @var array
+ */
+ private $headers=null;
+
+ /**
+ * Format to return costs in
+ *
+ * @var string
+ */
+ private $costFormat;
+
+
+ /**
+ * Constructor
+ * @param string Data file to read
+ * @param string Format to return costs in
+ */
+ function __construct($dataFile, $costFormat) {
+ $this->fp = @fopen($dataFile,'rb');
+ if (!$this->fp)
+ throw new Exception('Error opening file!');
+
+ $this->costFormat = $costFormat;
+ $this->init();
+ }
+
+ /**
+ * Initializes the parser by reading initial information.
+ *
+ * Throws an exception if the file version does not match the readers version
+ *
+ * @return void
+ * @throws Exception
+ */
+ private function init() {
+ list($version, $this->headersPos, $functionCount) = $this->read(3);
+ if ($version!=self::FILE_FORMAT_VERSION)
+ throw new Exception('Datafile not correct version. Found '.$version.' expected '.self::FILE_FORMAT_VERSION);
+ $this->functionPos = $this->read($functionCount);
+ }
+
+ /**
+ * Returns number of functions
+ * @return int
+ */
+ function getFunctionCount(){
+ return count($this->functionPos);
+ }
+
/**
- * Address of the headers in the data file
- *
- * @var int
- */
- private $headersPos;
-
- /**
- * Array of addresses pointing to information about functions
- *
- * @var array
- */
- private $functionPos;
-
- /**
- * Array of headers
- *
- * @var array
- */
- private $headers=null;
-
- /**
- * Format to return costs in
- *
- * @var string
- */
- private $costFormat;
-
-
- /**
- * Constructor
- * @param string Data file to read
- * @param string Format to return costs in
- **/
- function __construct($dataFile, $costFormat){
- $this->fp = @fopen($dataFile,'rb');
- if(!$this->fp)
- throw new Exception('Error opening file!');
-
- $this->costFormat = $costFormat;
- $this->init();
- }
-
- /**
- * Initializes the parser by reading initial information.
- *
- * Throws an exception if the file version does not match the readers version
- *
- * @return void
- * @throws Exception
- */
- private function init(){
- list($version, $this->headersPos, $functionCount) = $this->read(3);
- if($version!=self::FILE_FORMAT_VERSION)
- throw new Exception('Datafile not correct version. Found '.$version.' expected '.self::FILE_FORMAT_VERSION);
- $this->functionPos = $this->read($functionCount);
- }
-
- /**
- * Returns number of functions
- * @return int
- */
- function getFunctionCount(){
- return count($this->functionPos);
- }
-
- /**
- * Returns information about function with nr $nr
- *
- * @param $nr int Function number
- * @return array Function information
- */
- function getFunctionInfo($nr){
- $this->seek($this->functionPos[$nr]);
-
- list($line, $summedSelfCost, $summedInclusiveCost, $invocationCount, $calledFromCount, $subCallCount) = $this->read(self::FUNCTIONINFORMATION_LENGTH);
-
- $this->seek(self::NR_SIZE*self::CALLINFORMATION_LENGTH*($calledFromCount+$subCallCount), SEEK_CUR);
- $file = $this->readLine();
- $function = $this->readLine();
-
- $result = array(
- 'file'=>$file,
- 'line'=>$line,
- 'functionName'=>$function,
- 'summedSelfCost'=>$summedSelfCost,
- 'summedInclusiveCost'=>$summedInclusiveCost,
- 'invocationCount'=>$invocationCount,
- 'calledFromInfoCount'=>$calledFromCount,
- 'subCallInfoCount'=>$subCallCount
- );
+ * Returns information about function with nr $nr
+ *
+ * @param $nr int Function number
+ * @return array Function information
+ */
+ function getFunctionInfo($nr) {
+ $this->seek($this->functionPos[$nr]);
+
+ list($line, $summedSelfCost, $summedInclusiveCost, $invocationCount, $calledFromCount, $subCallCount) = $this->read(self::FUNCTIONINFORMATION_LENGTH);
+
+ $this->seek(self::NR_SIZE*self::CALLINFORMATION_LENGTH*($calledFromCount+$subCallCount), SEEK_CUR);
+ $file = $this->readLine();
+ $function = $this->readLine();
+
+ $result = array(
+ 'file'=>$file,
+ 'line'=>$line,
+ 'functionName'=>$function,
+ 'summedSelfCost'=>$summedSelfCost,
+ 'summedInclusiveCost'=>$summedInclusiveCost,
+ 'invocationCount'=>$invocationCount,
+ 'calledFromInfoCount'=>$calledFromCount,
+ 'subCallInfoCount'=>$subCallCount
+ );
$result['summedSelfCost'] = $this->formatCost($result['summedSelfCost']);
$result['summedInclusiveCost'] = $this->formatCost($result['summedInclusiveCost']);
- return $result;
- }
-
- /**
- * Returns information about positions where a function has been called from
- *
- * @param $functionNr int Function number
- * @param $calledFromNr int Called from position nr
- * @return array Called from information
- */
- function getCalledFromInfo($functionNr, $calledFromNr){
- $this->seek(
- $this->functionPos[$functionNr]
- + self::NR_SIZE
- * (self::CALLINFORMATION_LENGTH * $calledFromNr + self::FUNCTIONINFORMATION_LENGTH)
- );
-
- $data = $this->read(self::CALLINFORMATION_LENGTH);
-
- $result = array(
- 'functionNr'=>$data[0],
- 'line'=>$data[1],
- 'callCount'=>$data[2],
- 'summedCallCost'=>$data[3]
- );
-
+ return $result;
+ }
+
+ /**
+ * Returns information about positions where a function has been called from
+ *
+ * @param $functionNr int Function number
+ * @param $calledFromNr int Called from position nr
+ * @return array Called from information
+ */
+ function getCalledFromInfo($functionNr, $calledFromNr){
+ $this->seek(
+ $this->functionPos[$functionNr]
+ + self::NR_SIZE
+ * (self::CALLINFORMATION_LENGTH * $calledFromNr + self::FUNCTIONINFORMATION_LENGTH)
+ );
+
+ $data = $this->read(self::CALLINFORMATION_LENGTH);
+
+ $result = array(
+ 'functionNr'=>$data[0],
+ 'line'=>$data[1],
+ 'callCount'=>$data[2],
+ 'summedCallCost'=>$data[3]
+ );
+
$result['summedCallCost'] = $this->formatCost($result['summedCallCost']);
- return $result;
- }
-
- /**
- * Returns information about functions called by a function
- *
- * @param $functionNr int Function number
- * @param $subCallNr int Sub call position nr
- * @return array Sub call information
- */
- function getSubCallInfo($functionNr, $subCallNr){
- // Sub call count is the second last number in the FUNCTION_INFORMATION block
- $this->seek($this->functionPos[$functionNr] + self::NR_SIZE * (self::FUNCTIONINFORMATION_LENGTH - 2));
- $calledFromInfoCount = $this->read();
- $this->seek( ( ($calledFromInfoCount+$subCallNr) * self::CALLINFORMATION_LENGTH + 1 ) * self::NR_SIZE,SEEK_CUR);
- $data = $this->read(self::CALLINFORMATION_LENGTH);
-
- $result = array(
- 'functionNr'=>$data[0],
- 'line'=>$data[1],
- 'callCount'=>$data[2],
- 'summedCallCost'=>$data[3]
- );
-
+ return $result;
+ }
+
+ /**
+ * Returns information about functions called by a function
+ *
+ * @param $functionNr int Function number
+ * @param $subCallNr int Sub call position nr
+ * @return array Sub call information
+ */
+ function getSubCallInfo($functionNr, $subCallNr) {
+ // Sub call count is the second last number in the FUNCTION_INFORMATION block
+ $this->seek($this->functionPos[$functionNr] + self::NR_SIZE * (self::FUNCTIONINFORMATION_LENGTH - 2));
+ $calledFromInfoCount = $this->read();
+ $this->seek( ( ($calledFromInfoCount+$subCallNr) * self::CALLINFORMATION_LENGTH + 1 ) * self::NR_SIZE,SEEK_CUR);
+ $data = $this->read(self::CALLINFORMATION_LENGTH);
+
+ $result = array(
+ 'functionNr'=>$data[0],
+ 'line'=>$data[1],
+ 'callCount'=>$data[2],
+ 'summedCallCost'=>$data[3]
+ );
+
$result['summedCallCost'] = $this->formatCost($result['summedCallCost']);
- return $result;
- }
-
- /**
- * Returns array of defined headers
- *
- * @return array Headers in format array('header name'=>'header value')
- */
- function getHeaders(){
- if($this->headers==null){ // Cache headers
- $this->seek($this->headersPos);
- $this->headers['runs'] = 0;
- while($line=$this->readLine()){
- $parts = explode(': ',$line);
- if ($parts[0] == 'summary') {
- $this->headers['runs']++;
- if(isset($this->headers['summary']))
+ return $result;
+ }
+
+ /**
+ * Returns array of defined headers
+ *
+ * @return array Headers in format array('header name'=>'header value')
+ */
+ function getHeaders() {
+ if ($this->headers==null) { // Cache headers
+ $this->seek($this->headersPos);
+ $this->headers['runs'] = 0;
+ while ($line=$this->readLine()) {
+ $parts = explode(': ',$line);
+ if ($parts[0] == 'summary') {
+ $this->headers['runs']++;
+ if (isset($this->headers['summary']))
$this->headers['summary'] += $parts[1];
else
$this->headers['summary'] = $parts[1];
- } else {
+ } else {
$this->headers[$parts[0]] = $parts[1];
}
- }
- }
- return $this->headers;
- }
-
- /**
- * Returns value of a single header
- *
- * @return string Header value
- */
- function getHeader($header){
- $headers = $this->getHeaders();
- return isset($headers[$header]) ? $headers[$header] : '';
- }
-
- /**
- * Formats $cost using the format in $this->costFormat or optionally the format given as input
- *
- * @param int $cost Cost
- * @param string $format 'percent', 'msec' or 'usec'
- * @return int Formatted cost
- */
- function formatCost($cost, $format=null)
- {
- if($format==null)
- $format = $this->costFormat;
-
- if ($format == 'percent') {
- $total = $this->getHeader('summary');
- $result = ($total==0) ? 0 : ($cost*100)/$total;
- return number_format($result, 2, '.', '');
- }
-
- if ($format == 'msec') {
- return round($cost/1000, 0);
- }
-
- // Default usec
- return $cost;
-
- }
-
- private function read($numbers=1){
- $values = unpack(self::NR_FORMAT.$numbers,fread($this->fp,self::NR_SIZE*$numbers));
- if($numbers==1)
- return $values[1];
- else
- return array_values($values); // reindex and return
- }
-
- private function readLine(){
- $result = fgets($this->fp);
- if($result)
- return trim($result);
- else
- return $result;
- }
-
- private function seek($offset, $whence=SEEK_SET){
- return fseek($this->fp, $offset, $whence);
- }
-
+ }
+ }
+ return $this->headers;
+ }
+
+ /**
+ * Returns value of a single header
+ *
+ * @return string Header value
+ */
+ function getHeader($header) {
+ $headers = $this->getHeaders();
+ return isset($headers[$header]) ? $headers[$header] : '';
+ }
+
+ /**
+ * Formats $cost using the format in $this->costFormat or optionally the format given as input
+ *
+ * @param int $cost Cost
+ * @param string $format 'percent', 'msec' or 'usec'
+ * @return int Formatted cost
+ */
+ function formatCost($cost, $format=null) {
+ if ($format==null)
+ $format = $this->costFormat;
+
+ if ($format == 'percent') {
+ $total = $this->getHeader('summary');
+ $result = ($total==0) ? 0 : ($cost*100)/$total;
+ return number_format($result, 2, '.', '');
+ }
+
+ if ($format == 'msec') {
+ return round($cost/1000, 0);
+ }
+
+ // Default usec
+ return $cost;
+ }
+
+ private function read($numbers=1) {
+ $values = unpack(self::NR_FORMAT.$numbers,fread($this->fp,self::NR_SIZE*$numbers));
+ if ($numbers==1)
+ return $values[1];
+ else
+ return array_values($values); // reindex and return
+ }
+
+ private function readLine() {
+ $result = fgets($this->fp);
+ if ($result)
+ return trim($result);
+ else
+ return $result;
+ }
+
+ private function seek($offset, $whence=SEEK_SET) {
+ return fseek($this->fp, $offset, $whence);
+ }
+
}
diff --git a/library/gprof2dot.py b/library/gprof2dot.py
index dc7bc8d..99a8c1b 100644
--- a/library/gprof2dot.py
+++ b/library/gprof2dot.py
@@ -79,7 +79,7 @@ def ratio(numerator, denominator):
class UndefinedEvent(Exception):
"""Raised when attempting to get an event which is undefined."""
-
+
def __init__(self, event):
Exception.__init__(self)
self.event = event
@@ -111,7 +111,7 @@ class Event(object):
assert val1 is not None
assert val2 is not None
return self._aggregator(val1, val2)
-
+
def format(self, val):
"""Format an event value."""
assert val is not None
@@ -145,13 +145,13 @@ class Object(object):
def __contains__(self, event):
return event in self.events
-
+
def __getitem__(self, event):
try:
return self.events[event]
except KeyError:
raise UndefinedEvent(event)
-
+
def __setitem__(self, event, value):
if value is None:
if event in self.events:
@@ -162,7 +162,7 @@ class Object(object):
class Call(Object):
"""A call between functions.
-
+
There should be at most one call object for every pair of functions.
"""
@@ -186,7 +186,7 @@ class Function(Object):
self.called = None
self.weight = None
self.cycle = None
-
+
def add_call(self, call):
if call.callee_id in self.calls:
sys.stderr.write('warning: overwriting call from function %s to %s\n' % (str(self.id), str(call.callee_id)))
@@ -270,7 +270,7 @@ class Profile(Object):
sys.stderr.write("Cycle:\n")
for member in cycle.functions:
sys.stderr.write("\tFunction %s\n" % member.name)
-
+
def _tarjan(self, function, order, stack, orders, lowlinks, visited):
"""Tarjan's strongly connected components algorithm.
@@ -349,7 +349,7 @@ class Profile(Object):
if call.callee_id != function.id:
assert call.ratio is not None
- # Aggregate the input for each cycle
+ # Aggregate the input for each cycle
for cycle in self.cycles:
total = inevent.null()
for function in self.functions.itervalues():
@@ -374,7 +374,7 @@ class Profile(Object):
total += self._integrate_call(call, outevent, inevent)
function[outevent] = total
return function[outevent]
-
+
def _integrate_call(self, call, outevent, inevent):
assert outevent not in call
assert call.ratio is not None
@@ -396,7 +396,7 @@ class Profile(Object):
subtotal += self._integrate_call(call, outevent, inevent)
total += subtotal
cycle[outevent] = total
-
+
# Compute the time propagated to callers of this cycle
callees = {}
for function in self.functions.itervalues():
@@ -408,7 +408,7 @@ class Profile(Object):
callees[callee] += call.ratio
except KeyError:
callees[callee] = call.ratio
-
+
for member in cycle.functions:
member[outevent] = outevent.null()
@@ -509,11 +509,11 @@ class Profile(Object):
if TOTAL_TIME_RATIO in call:
# handle exact cases first
- call.weight = call[TOTAL_TIME_RATIO]
+ call.weight = call[TOTAL_TIME_RATIO]
else:
try:
# make a safe estimate
- call.weight = min(function[TOTAL_TIME_RATIO], callee[TOTAL_TIME_RATIO])
+ call.weight = min(function[TOTAL_TIME_RATIO], callee[TOTAL_TIME_RATIO])
except UndefinedEvent:
pass
@@ -530,7 +530,7 @@ class Profile(Object):
call = function.calls[callee_id]
if callee_id not in self.functions or call.weight is not None and call.weight < edge_thres:
del function.calls[callee_id]
-
+
def dump(self):
for function in self.functions.itervalues():
sys.stderr.write('Function %s:\n' % (function.name,))
@@ -557,7 +557,7 @@ class Struct:
if attrs is None:
attrs = {}
self.__dict__['_attrs'] = attrs
-
+
def __getattr__(self, name):
try:
return self._attrs[name]
@@ -572,7 +572,7 @@ class Struct:
def __repr__(self):
return repr(self._attrs)
-
+
class ParseError(Exception):
"""Raised when parsing to signal mismatches."""
@@ -595,7 +595,7 @@ class Parser:
def parse(self):
raise NotImplementedError
-
+
class LineParser(Parser):
"""Base class for parsers that read line-based formats."""
@@ -664,21 +664,21 @@ class XmlTokenizer:
self.index = 0
self.final = False
self.skip_ws = skip_ws
-
+
self.character_pos = 0, 0
self.character_data = ''
-
+
self.parser = xml.parsers.expat.ParserCreate()
self.parser.StartElementHandler = self.handle_element_start
self.parser.EndElementHandler = self.handle_element_end
self.parser.CharacterDataHandler = self.handle_character_data
-
+
def handle_element_start(self, name, attributes):
self.finish_character_data()
line, column = self.pos()
token = XmlToken(XML_ELEMENT_START, name, attributes, line, column)
self.tokens.append(token)
-
+
def handle_element_end(self, name):
self.finish_character_data()
line, column = self.pos()
@@ -689,15 +689,15 @@ class XmlTokenizer:
if not self.character_data:
self.character_pos = self.pos()
self.character_data += data
-
+
def finish_character_data(self):
if self.character_data:
- if not self.skip_ws or not self.character_data.isspace():
+ if not self.skip_ws or not self.character_data.isspace():
line, column = self.character_pos
token = XmlToken(XML_CHARACTER_DATA, self.character_data, None, line, column)
self.tokens.append(token)
self.character_data = ''
-
+
def next(self):
size = 16*1024
while self.index >= len(self.tokens) and not self.final:
@@ -742,13 +742,13 @@ class XmlParser(Parser):
Parser.__init__(self)
self.tokenizer = XmlTokenizer(fp)
self.consume()
-
+
def consume(self):
self.token = self.tokenizer.next()
def match_element_start(self, name):
return self.token.type == XML_ELEMENT_START and self.token.name_or_data == name
-
+
def match_element_end(self, name):
return self.token.type == XML_ELEMENT_END and self.token.name_or_data == name
@@ -762,7 +762,7 @@ class XmlParser(Parser):
attrs = self.token.attrs
self.consume()
return attrs
-
+
def element_end(self, name):
while self.token.type == XML_CHARACTER_DATA:
self.consume()
@@ -840,20 +840,20 @@ class GprofParser(Parser):
)
_cg_primary_re = re.compile(
- r'^\[(?P<index>\d+)\]?' +
- r'\s+(?P<percentage_time>\d+\.\d+)' +
- r'\s+(?P<self>\d+\.\d+)' +
- r'\s+(?P<descendants>\d+\.\d+)' +
- r'\s+(?:(?P<called>\d+)(?:\+(?P<called_self>\d+))?)?' +
+ r'^\[(?P<index>\d+)\]?' +
+ r'\s+(?P<percentage_time>\d+\.\d+)' +
+ r'\s+(?P<self>\d+\.\d+)' +
+ r'\s+(?P<descendants>\d+\.\d+)' +
+ r'\s+(?:(?P<called>\d+)(?:\+(?P<called_self>\d+))?)?' +
r'\s+(?P<name>\S.*?)' +
r'(?:\s+<cycle\s(?P<cycle>\d+)>)?' +
r'\s\[(\d+)\]$'
)
_cg_parent_re = re.compile(
- r'^\s+(?P<self>\d+\.\d+)?' +
- r'\s+(?P<descendants>\d+\.\d+)?' +
- r'\s+(?P<called>\d+)(?:/(?P<called_total>\d+))?' +
+ r'^\s+(?P<self>\d+\.\d+)?' +
+ r'\s+(?P<descendants>\d+\.\d+)?' +
+ r'\s+(?P<called>\d+)(?:/(?P<called_total>\d+))?' +
r'\s+(?P<name>\S.*?)' +
r'(?:\s+<cycle\s(?P<cycle>\d+)>)?' +
r'\s\[(?P<index>\d+)\]$'
@@ -862,19 +862,19 @@ class GprofParser(Parser):
_cg_child_re = _cg_parent_re
_cg_cycle_header_re = re.compile(
- r'^\[(?P<index>\d+)\]?' +
- r'\s+(?P<percentage_time>\d+\.\d+)' +
- r'\s+(?P<self>\d+\.\d+)' +
- r'\s+(?P<descendants>\d+\.\d+)' +
- r'\s+(?:(?P<called>\d+)(?:\+(?P<called_self>\d+))?)?' +
+ r'^\[(?P<index>\d+)\]?' +
+ r'\s+(?P<percentage_time>\d+\.\d+)' +
+ r'\s+(?P<self>\d+\.\d+)' +
+ r'\s+(?P<descendants>\d+\.\d+)' +
+ r'\s+(?:(?P<called>\d+)(?:\+(?P<called_self>\d+))?)?' +
r'\s+<cycle\s(?P<cycle>\d+)\sas\sa\swhole>' +
r'\s\[(\d+)\]$'
)
_cg_cycle_member_re = re.compile(
- r'^\s+(?P<self>\d+\.\d+)?' +
- r'\s+(?P<descendants>\d+\.\d+)?' +
- r'\s+(?P<called>\d+)(?:\+(?P<called_self>\d+))?' +
+ r'^\s+(?P<self>\d+\.\d+)?' +
+ r'\s+(?P<descendants>\d+\.\d+)?' +
+ r'\s+(?P<called>\d+)(?:\+(?P<called_self>\d+))?' +
r'\s+(?P<name>\S.*?)' +
r'(?:\s+<cycle\s(?P<cycle>\d+)>)?' +
r'\s\[(?P<index>\d+)\]$'
@@ -892,7 +892,7 @@ class GprofParser(Parser):
line = lines.pop(0)
if line.startswith('['):
break
-
+
# read function parent line
mo = self._cg_parent_re.match(line)
if not mo:
@@ -913,7 +913,7 @@ class GprofParser(Parser):
while lines:
line = lines.pop(0)
-
+
# read function subroutine line
mo = self._cg_child_re.match(line)
if not mo:
@@ -923,7 +923,7 @@ class GprofParser(Parser):
else:
child = self.translate(mo)
children.append(child)
-
+
function.parents = parents
function.children = children
@@ -948,7 +948,7 @@ class GprofParser(Parser):
continue
call = self.translate(mo)
cycle.functions.append(call)
-
+
self.cycles[cycle.cycle] = cycle
def parse_cg_entry(self, lines):
@@ -975,16 +975,16 @@ class GprofParser(Parser):
self.parse_cg_entry(entry_lines)
entry_lines = []
else:
- entry_lines.append(line)
+ entry_lines.append(line)
line = self.readline()
-
+
def parse(self):
self.parse_cg()
self.fp.close()
profile = Profile()
profile[TIME] = 0.0
-
+
cycles = {}
for index in self.cycles.iterkeys():
cycles[index] = Cycle()
@@ -999,16 +999,16 @@ class GprofParser(Parser):
call = Call(entry.index)
call[CALLS] = entry.called_self
function.called += entry.called_self
-
+
# populate the function calls
for child in entry.children:
call = Call(child.index)
-
+
assert child.called is not None
call[CALLS] = child.called
if child.index not in self.functions:
- # NOTE: functions that were never called but were discovered by gprof's
+ # NOTE: functions that were never called but were discovered by gprof's
# static call graph analysis dont have a call graph entry so we need
# to add them here
missing = Function(child.index, child.name)
@@ -1024,7 +1024,7 @@ class GprofParser(Parser):
try:
cycle = cycles[entry.cycle]
except KeyError:
- sys.stderr.write('warning: <cycle %u as a whole> entry missing\n' % entry.cycle)
+ sys.stderr.write('warning: <cycle %u as a whole> entry missing\n' % entry.cycle)
cycle = Cycle()
cycles[entry.cycle] = cycle
cycle.add_function(function)
@@ -1046,7 +1046,7 @@ class GprofParser(Parser):
class CallgrindParser(LineParser):
"""Parser for valgrind's callgrind tool.
-
+
See also:
- http://valgrind.org/docs/manual/cl-format.html
"""
@@ -1153,7 +1153,7 @@ class CallgrindParser(LineParser):
self.parse_association_spec()
__subpos_re = r'(0x[0-9a-fA-F]+|\d+|\+\d+|-\d+|\*)'
- _cost_re = re.compile(r'^' +
+ _cost_re = re.compile(r'^' +
__subpos_re + r'( +' + __subpos_re + r')*' +
r'( +\d+)*' +
'$')
@@ -1188,12 +1188,12 @@ class CallgrindParser(LineParser):
events = map(float, events)
if calls is None:
- function[SAMPLES] += events[0]
+ function[SAMPLES] += events[0]
self.profile[SAMPLES] += events[0]
else:
callee = self.get_callee()
callee.called += calls
-
+
try:
call = function.calls[callee.id]
except KeyError:
@@ -1255,7 +1255,7 @@ class CallgrindParser(LineParser):
def parse_position_spec(self):
line = self.lookahead()
-
+
if line.startswith('jump=') or line.startswith('jcnd='):
self.consume()
return True
@@ -1338,20 +1338,20 @@ class CallgrindParser(LineParser):
def get_function(self):
module = self.positions.get('ob', '')
- filename = self.positions.get('fl', '')
- function = self.positions.get('fn', '')
+ filename = self.positions.get('fl', '')
+ function = self.positions.get('fn', '')
return self.make_function(module, filename, function)
def get_callee(self):
module = self.positions.get('cob', '')
- filename = self.positions.get('cfi', '')
- function = self.positions.get('cfn', '')
+ filename = self.positions.get('cfi', '')
+ function = self.positions.get('cfn', '')
return self.make_function(module, filename, function)
class OprofileParser(LineParser):
"""Parser for oprofile callgraph output.
-
+
See also:
- http://oprofile.sourceforge.net/doc/opreport.html#opreport-callgraph
"""
@@ -1380,7 +1380,7 @@ class OprofileParser(LineParser):
self.update_subentries_dict(callers_total, callers)
function_total.samples += function.samples
self.update_subentries_dict(callees_total, callees)
-
+
def update_subentries_dict(self, totals, partials):
for partial in partials.itervalues():
try:
@@ -1389,7 +1389,7 @@ class OprofileParser(LineParser):
totals[partial.id] = partial
else:
total.samples += partial.samples
-
+
def parse(self):
# read lookahead
self.readline()
@@ -1401,7 +1401,7 @@ class OprofileParser(LineParser):
profile = Profile()
reverse_call_samples = {}
-
+
# populate the profile
profile[SAMPLES] = 0
for _callers, _function, _callees in self.entries.itervalues():
@@ -1424,7 +1424,7 @@ class OprofileParser(LineParser):
call = Call(_callee.id)
call[SAMPLES2] = _callee.samples
function.add_call(call)
-
+
# compute derived data
profile.validate()
profile.find_cycles()
@@ -1510,7 +1510,7 @@ class OprofileParser(LineParser):
def match_primary(self):
line = self.lookahead()
return not line[:1].isspace()
-
+
def match_secondary(self):
line = self.lookahead()
return line[:1].isspace()
@@ -1518,7 +1518,7 @@ class OprofileParser(LineParser):
class HProfParser(LineParser):
"""Parser for java hprof output
-
+
See also:
- http://java.sun.com/developer/technicalArticles/Programming/HPROF.html
"""
@@ -1679,7 +1679,7 @@ class SysprofParser(XmlParser):
def build_profile(self, objects, nodes):
profile = Profile()
-
+
profile[SAMPLES] = 0
for id, object in objects.iteritems():
# Ignore fake objects (process names, modules, "Everything", "kernel", etc.)
@@ -1753,7 +1753,7 @@ class SharkParser(LineParser):
else:
function_total, callees_total = entry
function_total.samples += function.samples
-
+
def add_callee(self, function, callee):
func, callees = self.entries[function.id]
try:
@@ -1762,7 +1762,7 @@ class SharkParser(LineParser):
callees[callee.id] = callee
else:
entry.samples += callee.samples
-
+
def parse(self):
self.readline()
self.readline()
@@ -1800,9 +1800,9 @@ class SharkParser(LineParser):
# if the callstack has had an entry, it's this functions caller
if prefix > 0:
self.add_callee(self.stack[prefix - 1], entry)
-
+
self.add_entry(entry)
-
+
profile = Profile()
profile[SAMPLES] = 0
for _function, _callees in self.entries.itervalues():
@@ -1818,7 +1818,7 @@ class SharkParser(LineParser):
call = Call(_callee.id)
call[SAMPLES] = _callee.samples
function.add_call(call)
-
+
# compute derived data
profile.validate()
profile.find_cycles()
@@ -1843,7 +1843,7 @@ class XPerfParser(Parser):
def parse(self):
import csv
reader = csv.reader(
- self.stream,
+ self.stream,
delimiter = ',',
quotechar = None,
escapechar = None,
@@ -1856,7 +1856,7 @@ class XPerfParser(Parser):
self.parse_header(row)
for row in it:
self.parse_row(row)
-
+
# compute derived data
self.profile.validate()
self.profile.find_cycles()
@@ -1884,7 +1884,7 @@ class XPerfParser(Parser):
else:
break
fields[name] = value
-
+
process = fields['Process Name']
symbol = fields['Module'] + '!' + fields['Function']
weight = fields['Weight']
@@ -1950,12 +1950,12 @@ class SleepyParser(Parser):
self.calls = {}
self.profile = Profile()
-
+
_symbol_re = re.compile(
- r'^(?P<id>\w+)' +
- r'\s+"(?P<module>[^"]*)"' +
- r'\s+"(?P<procname>[^"]*)"' +
- r'\s+"(?P<sourcefile>[^"]*)"' +
+ r'^(?P<id>\w+)' +
+ r'\s+"(?P<module>[^"]*)"' +
+ r'\s+"(?P<procname>[^"]*)"' +
+ r'\s+"(?P<sourcefile>[^"]*)"' +
r'\s+(?P<sourceline>\d+)$'
)
@@ -1965,7 +1965,7 @@ class SleepyParser(Parser):
mo = self._symbol_re.match(line)
if mo:
symbol_id, module, procname, sourcefile, sourceline = mo.groups()
-
+
function_id = ':'.join([module, procname])
try:
@@ -1991,7 +1991,7 @@ class SleepyParser(Parser):
callee[SAMPLES] += samples
self.profile[SAMPLES] += samples
-
+
for caller in callstack[1:]:
try:
call = caller.calls[callee.id]
@@ -2059,7 +2059,7 @@ class AQtimeParser(XmlParser):
self.parse_headers()
results = self.parse_results()
self.element_end('AQtime_Results')
- return self.build_profile(results)
+ return self.build_profile(results)
def parse_headers(self):
self.element_start('HEADERS')
@@ -2163,7 +2163,7 @@ class AQtimeParser(XmlParser):
profile[TOTAL_TIME] = profile[TIME]
profile.ratio(TOTAL_TIME_RATIO, TOTAL_TIME)
return profile
-
+
def build_function(self, fields):
function = Function(self.build_id(fields), self.build_name(fields))
function[TIME] = fields['Time']
@@ -2263,7 +2263,7 @@ class PstatsParser:
class Theme:
- def __init__(self,
+ def __init__(self,
bgcolor = (0.0, 0.0, 1.0),
mincolor = (0.0, 0.0, 0.0),
maxcolor = (0.0, 0.0, 1.0),
@@ -2320,10 +2320,10 @@ class Theme:
def color(self, weight):
weight = min(max(weight, 0.0), 1.0)
-
+
hmin, smin, lmin = self.mincolor
hmax, smax, lmax = self.maxcolor
-
+
if self.skew < 0:
raise ValueError("Skew must be greater than 0")
elif self.skew == 1.0:
@@ -2446,10 +2446,10 @@ class DotWriter:
weight = 0.0
label = '\n'.join(labels)
- self.node(function.id,
- label = label,
- color = self.color(theme.node_bgcolor(weight)),
- fontcolor = self.color(theme.node_fgcolor(weight)),
+ self.node(function.id,
+ label = label,
+ color = self.color(theme.node_bgcolor(weight)),
+ fontcolor = self.color(theme.node_fgcolor(weight)),
fontsize = "%.2f" % theme.node_fontsize(weight),
)
@@ -2471,13 +2471,13 @@ class DotWriter:
label = '\n'.join(labels)
- self.edge(function.id, call.callee_id,
- label = label,
- color = self.color(theme.edge_color(weight)),
+ self.edge(function.id, call.callee_id,
+ label = label,
+ color = self.color(theme.edge_color(weight)),
fontcolor = self.color(theme.edge_color(weight)),
- fontsize = "%.2f" % theme.edge_fontsize(weight),
- penwidth = "%.2f" % theme.edge_penwidth(weight),
- labeldistance = "%.2f" % theme.edge_penwidth(weight),
+ fontsize = "%.2f" % theme.edge_fontsize(weight),
+ penwidth = "%.2f" % theme.edge_penwidth(weight),
+ labeldistance = "%.2f" % theme.edge_penwidth(weight),
arrowsize = "%.2f" % theme.edge_arrowsize(weight),
)
@@ -2621,7 +2621,7 @@ class Main:
self.theme = self.themes[self.options.theme]
except KeyError:
parser.error('invalid colormap \'%s\'' % self.options.theme)
-
+
# set skew on the theme now that it has been picked.
if self.options.theme_skew:
self.theme.skew = self.options.theme_skew
@@ -2655,7 +2655,7 @@ class Main:
fp = sys.stdin
else:
fp = open(self.args[0], 'rt')
- parser = HProfParser(fp)
+ parser = HProfParser(fp)
elif self.options.format == 'pstats':
if not self.args:
parser.error('at least a file must be specified for pstats input')
@@ -2686,7 +2686,7 @@ class Main:
parser.error('invalid format \'%s\'' % self.options.format)
self.profile = parser.parse()
-
+
if self.options.output is None:
self.output = sys.stdout
else:
@@ -2760,4 +2760,4 @@ class Main:
if __name__ == '__main__':
- Main().main() \ No newline at end of file
+ Main().main()