summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authoroetting <jacob@oettinger.dk>2008-04-17 12:38:08 +0000
committeroetting <jacob@oettinger.dk>2008-04-17 12:38:08 +0000
commit9ec6bb9e1c87dd564c9c449a69a6763815610dbd (patch)
tree86d67484b8023f76ef86a06950ed10a68ccf1263 /lib
parent1c2db8c05c843ae3f0bd3309d20919204b40434b (diff)
downloadwebgrind-9ec6bb9e1c87dd564c9c449a69a6763815610dbd.zip
webgrind-9ec6bb9e1c87dd564c9c449a69a6763815610dbd.tar.gz
webgrind-9ec6bb9e1c87dd564c9c449a69a6763815610dbd.tar.bz2
Showing summed invocations
New simpler preprocessed format (v5)
Diffstat (limited to 'lib')
-rw-r--r--lib/CallgrindPreprocessor.php239
-rw-r--r--lib/Reader.php96
2 files changed, 120 insertions, 215 deletions
diff --git a/lib/CallgrindPreprocessor.php b/lib/CallgrindPreprocessor.php
index 92afa37..7beddd5 100644
--- a/lib/CallgrindPreprocessor.php
+++ b/lib/CallgrindPreprocessor.php
@@ -1,53 +1,41 @@
<?php
/*
-Preprocessed file format v4:
+Preprocessed file format v5:
-file_contents: version_number header_address function_count function_addresses functions subcalls headers
+file_contents: version_number header_address function_count function_addresses functions headers
version_number: number
header_address: number
function_count: number
function_addresses: {number}
-functions: {total_self_cost total_inclusive_self_cost total_call_cost invocation_count invocations file_name function_name}
-total_self_cost: number
-total_inclusive_self_cost: number
-total_call_cost: number
+functions: {summed_self_cost summed_inclusive_cost invocation_count called_from_count call_information file_name function_name}
+summed_self_cost: number
+summed_inclusive_cost: number
+summed_call_cost: number
invocation_count: number
-invocations: self_cost inclusive_self_cost called_from subcall_count subcall_address
+called_from_count: number
+call_information: {function_number line call_count summed_call_cost}
+function_number: number
+line: number
+call_count: number
+summed_call_cost: number
file_name: string_newline
function_name: string_newline
-self_cost: number
-inclusive_self_cost: number
-called_from: call_cost function invocation line_number
-call_cost: number
-function: number
-invocation: number
-line_number: number
-subcall_count: number
-subcall_address: number
-subcalls: {function invocation line_number call_cost}
headers: {string_newline}
string_newline: any string terminated by a newline character
number: unsigned long (always 32 bit, little endian byte order)
*/
-
-
class CallgrindPreprocessor{
- const FILE_FORMAT_VERSION = 4;
+ const FILE_FORMAT_VERSION = 5;
const NR_FORMAT = 'V';
const NR_SIZE = 4;
- const INVOCATION_LENGTH = 8;
- const SUBCALL_LENGTH = 4;
const ENTRY_POINT = '{main}';
private $in, $out;
- private $nextFuncNr = 0;
- private $functions = array();
- private $headers = array();
function __construct($inFile, $outFile){
$this->in = fopen($inFile, 'rb');
@@ -56,153 +44,84 @@ class CallgrindPreprocessor{
function parse(){
- /*
- * Pass 1
- * Find function information and count function invocations
- */
- while(($line = fgets($this->in))){
- if(substr($line,0,3)==='fl='){
- list($function) = fscanf($this->in,"fn=%s");
- if(!isset($this->functions[$function])){
- // New function
- $this->functions[$function] = array('filename'=>substr(trim($line),3), 'invocations'=>1,'nr'=>$this->nextFuncNr++,'stack'=>array(),'count'=>0,'selfCost'=>0,'inclusiveSelfCost'=>0,'callCost'=>0);
- } else {
- $this->functions[$function]['invocations']++;
- }
- } else if(strpos($line,': ')!==false){
- $this->headerFound($line);
- }
- }
- // Write function information
- $functionCount = sizeof($this->functions);
- fwrite($this->out,pack(self::NR_FORMAT.'*',self::FILE_FORMAT_VERSION,0,$functionCount));
- // Position where function addresses must be written
- $funcAddrMark = ftell($this->out);
- fseek($this->out,self::NR_SIZE*$functionCount, SEEK_CUR);
- foreach($this->functions as $function=>&$data){
- $data['position'] = ftell($this->out);
- fwrite($this->out,pack(self::NR_FORMAT.'*',0,0,0,$data['invocations']));
- $data['nextInvocationPosition'] = ftell($this->out);
- fseek($this->out,self::NR_SIZE*self::INVOCATION_LENGTH*$data['invocations'], SEEK_CUR);
- fwrite($this->out,$data['filename']."\n".$function."\n");
- }
- $callAddress = ftell($this->out);
- fseek($this->out,$funcAddrMark,SEEK_SET);
- foreach($this->functions as $func){
- fwrite($this->out,pack(self::NR_FORMAT,$func['position']));
- }
- fseek($this->out,$callAddress,SEEK_SET);
- rewind($this->in);
+ // Make local for faster access
+ $in = $this->in;
+ $out = $this->out;
- /*
- * Pass 2
- * Parse invocations and insert address information in data created above
- */
- while(($line = fgets($this->in))){
+ $nextFuncNr = 0;
+ $functions = array();
+ $headers = array();
+
+
+ // Read information into memory
+ while(($line = fgets($in))){
if(substr($line,0,3)==='fl='){
- list($function) = fscanf($this->in,"fn=%s");
-
- if($function==self::ENTRY_POINT){
- // The entry point function has a summary header in the middle of no where. Header has been read above
- fgets($this->in);
- fgets($this->in);
- fgets($this->in);
- }
-
- $stackSize = sizeof($this->functions[$function]['stack']);
- $count = $this->functions[$function]['count'];
-
- if($stackSize>0 && $this->functions[$function]['stack'][$stackSize-1]['nr']==$count-1)
- $this->functions[$function]['stack'][$stackSize-1]['nr']++;
- else
- $this->functions[$function]['stack'][] = array('min'=>$count, 'nr'=>$count);
-
- $this->functions[$function]['count']++;
- list($lnr, $selfCost) = fscanf($this->in,"%d %d");
- $inclusiveSelfCost = $selfCost;
- $this->functions[$function]['selfCost'] += $selfCost;
- $callAddr = ftell($this->out);
- $callCount = 0;
-
-
- $callsPos = ftell($this->in);
- $functionCalls = array();
- while(($line=fgets($this->in))!="\n"){
- if(substr($line,0,4)==='cfn='){
- $calledFunctionName = substr(trim($line),4);
- if(!isset($functionCalls[$calledFunctionName]))
- $functionCalls[$calledFunctionName]=0;
- $functionCalls[$calledFunctionName]++;
- }
+ list($function) = fscanf($in,"fn=%s");
+ if(!isset($functions[$function])){
+ $functions[$function] = array('filename'=>substr(trim($line),3), 'invocationCount'=>0,'nr'=>$nextFuncNr++,'count'=>0,'summedSelfCost'=>0,'summedInclusiveCost'=>0,'callInformation'=>array());
+ }
+ $functions[$function]['invocationCount']++;
+ // Special case for {main} - 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]['summedSelfCost'] += $cost;
+ $functions[$function]['summedInclusiveCost'] += $cost;
+ } else if(substr($line,0,4)==='cfn=') {
+ $calledFunctionName = substr(trim($line),4);
+ // Skip call line
+ fgets($in);
+ // Cost line
+ list($lnr, $cost) = fscanf($in,"%d %d");
- fseek($this->in, $callsPos, SEEK_SET);
- while(($line=fgets($this->in))!="\n"){
- if(substr($line,0,4)==='cfn='){
- // Skip call line
- fgets($this->in);
- // Cost line
- list($lnr, $cost) = fscanf($this->in,"%d %d");
- $calledFunctionName = substr(trim($line),4);
- $inclusiveSelfCost += $cost;
- $this->functions[$calledFunctionName]['callCost'] += $cost;
-
- $stackTop = sizeof($this->functions[$calledFunctionName]['stack'])-1;
-
- $invocationNr = $this->functions[$calledFunctionName]['stack'][$stackTop]['nr']--;
- if($invocationNr == $this->functions[$calledFunctionName]['stack'][$stackTop]['min'])
- array_pop($this->functions[$calledFunctionName]['stack']);
-
- //echo 'Function '.$function.' calls '.$calledFunctionName.' invocation '.$invocationNr."\n";
-
- $here = ftell($this->out);
- $pos = $this->functions[$calledFunctionName]['position'];
- // Seek past total selfcost, total invocation cost, total callcost, invocationcount and invocations and self cost and inclusive self cost
- $pos = $pos+(6+self::INVOCATION_LENGTH*$invocationNr)*self::NR_SIZE;
- fseek($this->out, $pos, SEEK_SET);
- fwrite($this->out, pack(self::NR_FORMAT.'*', $cost, $this->functions[$function]['nr'], $this->functions[$function]['count']-1, $lnr));
- fseek($this->out, $here, SEEK_SET);
-
- fwrite($this->out, pack(self::NR_FORMAT.'*',$this->functions[$calledFunctionName]['nr'],$invocationNr,$lnr, $cost));
- $callCount++;
- }
- }
+ $functions[$function]['summedInclusiveCost'] += $cost;
- $this->functions[$function]['inclusiveSelfCost'] += $inclusiveSelfCost;
+ if(!isset($functions[$calledFunctionName]['callInformation'][$function.':'.$lnr]))
+ $functions[$calledFunctionName]['callInformation'][$function.':'.$lnr] = array('functionNr'=>$functions[$function]['nr'],'line'=>$lnr,'callCount'=>0,'summedCallCost'=>0);
+ $functions[$calledFunctionName]['callInformation'][$function.':'.$lnr]['callCount']++;
+ $functions[$calledFunctionName]['callInformation'][$function.':'.$lnr]['summedCallCost'] += $cost;
- $this->addInvocationInfo($function, $selfCost, $inclusiveSelfCost, $callCount, $callAddr);
+ } else if(strpos($line,': ')!==false){
+ $headers[] = $line;
}
}
-
- foreach($this->functions as $func){
- fseek($this->out, $func['position'], SEEK_SET);
- fwrite($this->out,pack(self::NR_FORMAT.'*',$func['selfCost'],$func['inclusiveSelfCost'],$func['callCost']));
+
+
+ // 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['callInformation']);
+ fwrite($out, pack(self::NR_FORMAT.'*', $function['summedSelfCost'], $function['summedInclusiveCost'], $function['invocationCount'], $calledFromCount));
+ // Write call information
+ foreach($function['callInformation'] as $call){
+ fwrite($out, pack(self::NR_FORMAT.'*', $call['functionNr'], $call['line'], $call['callCount'], $call['summedCallCost']));
+ }
+ fwrite($out, $function['filename']."\n".$functionName."\n");
}
-
- fseek($this->out,0,SEEK_END);
$headersPos = ftell($this->out);
// Write headers
- foreach($this->headers as $header){
- fwrite($this->out,$header);
+ foreach($headers as $header){
+ fwrite($out,$header);
}
- fseek($this->out,self::NR_SIZE, SEEK_SET);
- fwrite($this->out, pack('L',$headersPos));
- }
-
- private function addInvocationInfo($function, $selfCost, $inclusiveSelfCost, $subCallCount, $subCallsAddr){
- $here = ftell($this->out);
-
- $pos = &$this->functions[$function]['nextInvocationPosition'];
- fseek($this->out, $pos, SEEK_SET);
- fwrite($this->out, pack(self::NR_FORMAT.'*',$selfCost, $inclusiveSelfCost, 0, -1, 0, 0, $subCallCount, $subCallsAddr));
- $pos = ftell($this->out);
-
- fseek($this->out, $here, SEEK_SET);
+
+ // 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);
+ foreach($functionAddresses as $address){
+ fwrite($out, pack(self::NR_FORMAT, $address));
+ }
+
}
-
- private function headerFound($line){
- $this->headers[] = $line;
- }
}
-
diff --git a/lib/Reader.php b/lib/Reader.php
index acefc07..26401b7 100644
--- a/lib/Reader.php
+++ b/lib/Reader.php
@@ -1,14 +1,11 @@
<?php
-// TODO Error handling
class Reader{
- const FILE_FORMAT_VERSION = 4;
+ const FILE_FORMAT_VERSION = 5;
const NR_FORMAT = 'V';
const NR_SIZE = 4;
- const INVOCATION_LENGTH = 8;
- const SUBCALL_LENGTH = 4;
-
+ const CALLINFORMATION_LENGTH = 4;
const ENTRY_POINT = '{main}';
@@ -21,27 +18,7 @@ class Reader{
throw new Exception('Error opening file!');
$this->init();
}
-
- 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);
- }
-
+
// Read initial information from datafile
private function init(){
list($version, $this->headersPos, $functionCount) = $this->read(3);
@@ -56,55 +33,45 @@ class Reader{
function getFunctionInfo($nr, $costFormat = 'absolute'){
$this->seek($this->functionPos[$nr]);
- list($totalSelfCost, $totalInclusiveSelfCost, $totalCallCost, $invocationCount) = $this->read(4);
- $this->seek(self::NR_SIZE*self::INVOCATION_LENGTH*$invocationCount, SEEK_CUR);
+
+ list($summedSelfCost, $summedInclusiveCost, $invocationCount, $calledFromCount) = $this->read(4);
+
+ $this->seek(self::NR_SIZE*self::CALLINFORMATION_LENGTH*$calledFromCount, SEEK_CUR);
$file = $this->readLine();
$function = $this->readLine();
$result = array(
'file'=>$file,
'functionName'=>$function,
- 'totalSelfCost'=>$totalSelfCost,
- 'totalInclusiveSelfCost'=>$totalInclusiveSelfCost,
- 'totalCallCost'=>$totalCallCost,
- 'invocationCount'=>$invocationCount
+ 'summedSelfCost'=>$summedSelfCost,
+ 'summedInclusiveCost'=>$summedInclusiveCost,
+ 'invocationCount'=>$invocationCount,
+ 'callInfoCount'=>$calledFromCount
);
if ($costFormat == 'percentual') {
- $result['totalSelfCost'] = $this->percentCost($result['totalSelfCost']);
- $result['totalInclusiveSelfCost'] = $this->percentCost($result['totalInclusiveSelfCost']);
- $result['totalCallCost'] = $this->percentCost($result['totalCallCost']);
+ $result['summedSelfCost'] = $this->percentCost($result['summedSelfCost']);
+ $result['summedInclusiveCost'] = $this->percentCost($result['summedInclusiveCost']);
}
return $result;
}
- function getInvocation($functionNr, $invocationNr, $includeSubCalls, $costFormat = 'absolute'){
- $this->seek($this->functionPos[$functionNr]+self::NR_SIZE*(self::INVOCATION_LENGTH*$invocationNr+4));
- $data = $this->read(self::INVOCATION_LENGTH);
+ function getCallInfo($functionNr, $calledFromNr, $costFormat = 'absolute'){
+ // 4 = number of numbers beforea call information
+ $this->seek($this->functionPos[$functionNr]+self::NR_SIZE*(self::CALLINFORMATION_LENGTH*$calledFromNr+4));
+ $data = $this->read(self::CALLINFORMATION_LENGTH);
$result = array(
- 'selfCost'=>$data[0],
- 'inclusiveSelfCost'=>$data[1],
- 'callCost'=>$data[2],
- 'calledFromFunction'=>$data[3],
- 'calledFromInvocation'=>$data[4],
- 'calledFromLine'=>$data[5],
+ 'functionNr'=>$data[0],
+ 'line'=>$data[1],
+ 'callCount'=>$data[2],
+ 'summedCallCost'=>$data[3]
);
if ($costFormat == 'percentual') {
- $result['selfCost'] = $this->percentCost($result['selfCost']);
- $result['inclusiveSelfCost'] = $this->percentCost($result['inclusiveSelfCost']);
- $result['callCost'] = $this->percentCost($result['callCost']);
+ $result['summedCallCost'] = $this->percentCost($result['summedCallCost']);
}
- if($includeSubCalls){
- $result['subCalls'] = array();
- $this->seek($data[7]);
- for($i=0;$i<$data[6];$i++){
- $scData = $this->read(self::SUBCALL_LENGTH);
- $result['subCalls'][] = array('functionNr'=>$scData[0], 'invocationNr'=>$scData[1], 'line'=>$scData[2], 'cost'=>$scData[3]);
- }
- }
return $result;
}
@@ -130,5 +97,24 @@ class Reader{
return number_format($result, 3, '.', '');
}
+ 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);
+ }
}