File indexing completed on 2024-12-22 05:21:03

0001 #!/usr/bin/perl
0002 #
0003 # Redistribution and use in source and binary forms, with or without
0004 # modification, are permitted provided that the following conditions are met:
0005 # 
0006 # - Redistributions of source code must retain the above copyright notice,
0007 # this list of conditions and the following disclaimer.
0008 # 
0009 # - Redistributions in binary form must reproduce the above copyright
0010 # notice, this list of conditions and the following disclaimer in the
0011 # documentation and/or other materials provided with the distribution.
0012 # 
0013 # - All advertising materials mentioning features or use of this software
0014 # must display the following acknowledgement: This product includes software
0015 # developed by OmniTI Computer Consulting.
0016 # 
0017 # - Neither name of the company nor the names of its contributors may be
0018 # used to endorse or promote products derived from this software without
0019 # specific prior written permission.
0020 # 
0021 # THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS `AS IS'' AND ANY
0022 # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
0023 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
0024 # DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
0025 # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
0026 # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
0027 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
0028 # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
0029 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
0030 # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0031 #
0032 # Copyright (c) 2004 OmniTI Computer Consulting 
0033 # All rights reserved
0034 # The following code was written by George Schlossnagle <george@omniti.com>
0035 # and is provided completely free and without any warranty.
0036 #
0037 
0038 #
0039 # This script is designed to convert the tmon.out output emitted 
0040 # from Perl's Devel::DProf profiling package.  To use this:
0041 #
0042 # 1) Run your perl script as 
0043 #    > perl -d:DProf yoursript.pl
0044 #    This will create a file called tmon.out.  If you want to
0045 #    inspect it on the command line, look at the man page
0046 #    for dprofp for details.
0047 #
0048 # 2) Run  
0049 #    > dprof2calltree -f tmon.out
0050 #    or
0051 #    > dprof2calltree -f tmon.out -o cachegrind.out.foo
0052 #
0053 #    This creates a cachegrind-style file called cachgrind.out.tmon.out or
0054 #    cachegrind.out.foo, respecitvely.
0055 #
0056 # 3) Run kcachegrind cachegrind.out.foo
0057 #
0058 # 4) Enjoy!
0059 
0060 use strict;
0061 use Config;
0062 use Getopt::Std;
0063 use IO::File;
0064 
0065 my @callstack;
0066 my %function_info;
0067 my $tree = {};
0068 my $total_cost = 0;
0069 my %opts;
0070 
0071 getopt('f:o:', \%opts);
0072 
0073 my $infd;
0074 usage() unless ($opts{'f'} && ($infd = IO::File->new($opts{'f'}, "r")));
0075 
0076 my $outfd;
0077 my $outfile = $opts{'o'};
0078 unless($outfile) {
0079   $opts{'f'} =~ m!([^/]+)$!;
0080   $outfile = "cachegrind.out.$1";
0081 }
0082 $outfd = new IO::File $outfile, "w";
0083 usage() unless defined $outfd;
0084   
0085 while(<$infd>) {
0086   last if /^PART2/;
0087 }
0088 while(<$infd>) {
0089   chomp;
0090   my @args = split;
0091   if($args[0] eq '@') {
0092     # record timing event
0093     my $call_element = pop @callstack;
0094     if($call_element) {
0095       $call_element->{'cost'} += $args[3];
0096       $call_element->{'cumm_cost'} += $args[3];
0097       $total_cost += $args[3];
0098       push @callstack, $call_element;
0099     }
0100   }
0101   elsif($args[0] eq '&') {  
0102     # declare function
0103     $function_info{$args[1]}->{'package'} = $args[2];
0104     if($args[2] ne 'main') {
0105       $function_info{$args[1]}->{'name'} = $args[2]."::".$args[3];
0106     } else {
0107       $function_info{$args[1]}->{'name'} = $args[3];
0108     }
0109   }
0110   elsif($args[0] eq '+') {
0111     # push myself onto the stack
0112     my $call_element = { 'specifier' => $args[1], 'cost' => 0 };
0113     push @callstack, $call_element;
0114   }
0115   elsif($args[0] eq '-') {
0116     my $called = pop @callstack;
0117     my $called_id = $called->{'specifier'};
0118     my $caller = pop @callstack;
0119     if (exists $tree->{$called_id}) {
0120       $tree->{$called_id}->{'cost'} += $called->{'cost'};
0121     }
0122     else {
0123       $tree->{$called_id} = $called;
0124     } 
0125     if($caller) {
0126       $caller->{'child_calls'}++;
0127       my $caller_id = $caller->{'specifier'};
0128       if(! exists $tree->{$caller_id} ) {
0129         $tree->{$caller_id} = { 'specifier' => $caller_id, 'cost' => 0 };
0130 #        $tree->{$caller_id} = $caller;
0131       }
0132       $caller->{'cumm_cost'} += $called->{'cumm_cost'};
0133       $tree->{$caller_id}->{'called_funcs'}->[$tree->{$caller_id}->{'call_counter'}++]->{$called_id} += $called->{'cumm_cost'};
0134       push @callstack, $caller;
0135     }
0136   }
0137   elsif($args[0] eq '*') {
0138     # goto &func
0139     # replace last caller with self
0140     my $call_element = pop @callstack;
0141     $call_element->{'specifier'} = $args[1];
0142     push @callstack, $call_element;
0143   }
0144   else {print STDERR "Unexpected line: $_\n";}
0145 }
0146 
0147 #
0148 # Generate output
0149 #
0150 my $output = '';
0151 $output .= "events: Tick\n";
0152 $output .= "summary: $total_cost\n";
0153 $output .= "cmd: your script\n\n";
0154 foreach my $specifier ( keys %$tree ) {
0155   my $caller_package = $function_info{$specifier}->{'package'} || '???';
0156   my $caller_name = $function_info{$specifier}->{'name'} || '???';
0157   my $include = find_include($caller_package);
0158   $output .= "ob=\n";
0159   $output .= sprintf "fl=%s\n", find_include($caller_package);
0160   $output .= sprintf "fn=%s\n", $caller_name;
0161   $output .= sprintf "1 %d\n", $tree->{$specifier}->{'cost'};
0162   if(exists $tree->{$specifier}->{'called_funcs'}) {
0163     foreach my $items (@{$tree->{$specifier}->{'called_funcs'}}) {
0164       while(my ($child_specifier, $costs) = each %$items) {
0165         $output .= sprintf "cfn=%s\n", $function_info{$child_specifier}->{'name'};
0166         $output .= sprintf "cfi=%s\n", find_include($function_info{$child_specifier}->{'package'});
0167         $output .= "calls=1\n";
0168         $output .= sprintf "1 %d\n", $costs;
0169       }
0170     }
0171   }
0172   $output .= "\n";
0173 }
0174 print STDERR "Writing kcachegrind output to $outfile\n";
0175 $outfd->print($output);
0176 
0177 
0178 
0179 sub find_include {
0180   my $module = shift;
0181   $module =~ s!::!/!g;
0182   for (@INC) {
0183     if ( -f "$_/$module.pm" ) {
0184       return "$_/$module.pm";
0185     }
0186     if ( -f "$_/$module.so" ) {
0187       return "$_/$module.so";
0188     }
0189   }
0190   return "???";
0191 }
0192 
0193 sub usage() {
0194   print STDERR "dprof2calltree -f <tmon.out> [-o outfile]\n";
0195   exit -1;
0196 }
0197 
0198 
0199 # vim: set sts=2 ts=2 bs ai expandtab :