You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdesdk/kcachegrind/converters/pprof2calltree

219 lines
6.4 KiB

#!/usr/bin/env php
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# - Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
#
# - Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# - All advertising materials mentioning features or use of this software
# must display the following acknowledgement: This product includes software
# developed by OmniTI Computer Consulting.
#
# - Neither name of the company nor the names of its contributors may be
# used to endorse or promote products derived from this software without
# specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS `AS IS'' AND ANY
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# Copyright (c) 2004 OmniTI Computer Consulting
# All rights reserved
# The following code was written by George Schlossnagle <george@omniti.com>
# and is provided completely free and without any warranty.
#
# This script is designed to convert the pprof output from
# APD (http://pecl.php.net/apd/) to one readable by kcachegrind. To use
# this script:
#
# 1) Install APD.
# 2) Profile your script with APD accordingto the directions in it's
# README file.
# 3) Take the pprof trace file for your script (pprof.XXXXX.Y) and run it
# through this script as follows:
# > pprof2calltree -f pprof.12345.1
# This creates a new file cachegrind.out.12345.1
# 4) View your trace with pprof2calltree cachegrind.out.12345.1
<?php
require "Console/Getopt.php";
$con = new Console_Getopt;
$args = $con->readPHPArgv();
array_shift($args);
$shortoptions = 'f:';
$retval = $con->getopt( $args, $shortoptions);
if(is_object($retval)) {
usage();
}
foreach ($retval[0] as $kv_array) {
$opt[$kv_array[0]] = $kv_array[1];
}
if(!$opt['f']) {
usage();
}
if(!file_exists($opt['f'])) {
print "Trace file ${opt['f']} does not exist\n";
exit;
}
$IN = fopen($opt['f'], "r");
if(!$IN) {
print "Trace file ${opt['f']} could not be opened\n";
exit;
}
$path_parts = pathinfo($opt['f']);
$outfile = "cachegrind.out.".$path_parts['basename'];
$OUT = fopen($outfile, "w");
if(!$OUT) {
print "Destination file $outfile could not be opened.\n";
exit;
}
while(($line = fgets($IN)) !== false) {
$line = rtrim($line);
if($line == "END_HEADER") {
break;
}
}
$tree = array();
$callstack = array();
while(($line = fgets($IN)) !== false) {
$line = rtrim($line);
$args = explode(" ", $line);
if($args[0] == '!') {
$file_lookup[$args[1]] = $args[2];
}
else if($args[0] == '&') {
$function_lookup[$args[1]] = $args[2];
$function_type[$args[1]] = ($args[3] == 2)?"USER":"INTERNAL";
}
else if($args[0] == '+') {
$val = array(function_id => $args[1],
file_id => $args[2],
line => $args[3],
cost => 0);
array_push($callstack, $val);
}
else if($args[0] == '-') {
// retrieve $called to discard
$called = array_pop($callstack);
// retrieve $caller for reference
$caller = array_pop($callstack);
$called_id = $called['function_id'];
// Set meta data if not already set'
if(!array_key_exists($called_id, $tree)) {
$tree[$called_id] = $called;
// initialize these to 0
$tree[$called_id]['cost_per_line'] = array();
}
if($caller !== null) {
$caller['child_calls']++;
$caller_id = $caller['function_id'];
if(!array_key_exists($caller_id, $tree)) {
$tree[$caller_id] = $caller;
}
$caller['cost'] += $called['cost'];
$tree[$caller_id]['called_funcs'][$tree[$caller_id]['call_counter']++][$called_id][$called['file_id']][$called['line']] += $called['cost'];
array_push($callstack, $caller);
}
if(is_array($called['cost_per_line'])) {
foreach($called[cost_per_line] as $file => $lines) {
foreach($lines as $line => $cost) {
$tree[$called_id]['cost_per_line'][$file][$line] += $cost;
}
}
}
}
else if($args[0] == '@') {
$called = array_pop($callstack);
switch(count($args)) {
// support new and old-style pprof data
case 6:
$file = $args[1];
$line = $args[2];
$real_tm = $args[5];
break;
case 4:
$file = $called['file_id'];
$line = $called['line'];
$real_tm = $args[3];
break;
}
$called['cost_per_line'][$file][$line] += $real_tm;
$called['cost'] += $real_tm;
$total_cost += $real_tm;
array_push($callstack, $called);
}
}
ob_start();
print "events: Tick\n";
print "summary: $total_cost\n";
printf("cmd: %s\n", $file_lookup[1]);
print "\n";
foreach($tree as $caller => $data) {
$filename = $file_lookup[$data['file_id']]?$file_lookup[$data['file_id']]:"???";
printf("ob=%s\n", $function_type[$caller]);
printf("fl=%s\n", $filename);
printf("fn=%s\n", $function_lookup[$caller]);
if(is_array($data['cost_per_line'])) {
foreach($data['cost_per_line'] as $file => $lines) {
foreach($lines as $line => $cost) {
print "$line $cost\n";
}
}
}
else if ($data['cost']) {
printf("COST %s %s\n", $items['line'], $items['cost']);
}
else {
print_r($items);
}
if(is_array($data['called_funcs'])) {
foreach($data['called_funcs'] as $counter => $items) {
foreach($items as $called_id => $costs) {
if(is_array($costs)) {
printf("cfn=%s\n", $function_lookup[$called_id]);
foreach($costs as $file => $lines) {
printf("cfi=%s\ncalls=1\n", $file_lookup[$file]);
foreach($lines as $line => $cost) {
print "$line $cost\n";
}
}
}
}
}
}
print "\n";
}
print "\ntotals=$total_cost\n";
$buffer = ob_get_clean();
print "Writing kcachegrind compatible output to $outfile\n";
fwrite($OUT, $buffer);
function usage()
{
print <<<EOD
pprof2calltree -f <tracefile>
EOD;
exit(1);
}
?>