diff options
author | oetting <jacob@oettinger.dk> | 2008-04-17 12:38:08 +0000 |
---|---|---|
committer | oetting <jacob@oettinger.dk> | 2008-04-17 12:38:08 +0000 |
commit | 9ec6bb9e1c87dd564c9c449a69a6763815610dbd (patch) | |
tree | 86d67484b8023f76ef86a06950ed10a68ccf1263 /lib | |
parent | 1c2db8c05c843ae3f0bd3309d20919204b40434b (diff) | |
download | webgrind-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.php | 239 | ||||
-rw-r--r-- | lib/Reader.php | 96 |
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); + } } |