view Lib/IMPL/Profiler.pm @ 59:0f3e369553bd

Rewritten property implementation (probably become slower but more flexible) Configuration infrastructure in progress (in the aspect of the lazy activation) Initial concept for the code generator
author wizard
date Tue, 09 Mar 2010 02:50:45 +0300
parents 16ada169ca75
children 44977efed303
line wrap: on
line source

package IMPL::Profiler;

our $Enabled;
our %TrappedModules;
our %InvokeInfo;
our $InvokeTime = 0;
my $level;

BEGIN {
    $level = 0;
    if ($Enabled) {
        warn "profiler enabled";
        no warnings 'once';
        *CORE::GLOBAL::caller = sub {
            my $target = (shift || 0)+1;
            my $realFrame = 1;
            
            for (my $i = 1; $i<$target; $i++) {
                $realFrame ++;
                my $caller = CORE::caller($realFrame-1) or return;
                $realFrame ++ if $caller eq 'IMPL::Profiler::Proxy'; #current frame is proxy
            }
            
            my @frame = CORE::caller($realFrame) or return;
            if ( $frame[0] eq 'IMPL::Profiler::Proxy' ) {
                my @next = CORE::caller($realFrame+1) or return;
                @frame[0..2] = @next[0..2];
            }
            
            #warn "\t"x$level,"$frame[0] - $frame[3]";
            return wantarray ? @frame : $frame[0];
        };
    }
}
use strict;
use warnings;
use Time::HiRes;
require Scalar::Util;



sub trap_all {    
    return if not $Enabled;
    no strict 'refs';
    foreach my $class (@_) {
        next if $TrappedModules{$class};
        $TrappedModules{$class} = 1;
        
        eval "warn 'load $class'; require $class;" if not %{"${class}::"};
        die $@ if $@;
        
        no strict 'refs';
        my $table = \%{"${class}::"};
        trap($class,$_) foreach (grep *{$table->{$_}}{CODE}, keys %$table);
    }
}

sub trap {
    my ($class,$method) = @_;
    
    return if not $Enabled;
    
    no strict 'refs';
    my $prevCode = \&{"${class}::${method}"};
    my $proto = prototype $prevCode;
    
    if (defined $proto and not $proto) {
        return;
    }
    {
    package IMPL::Profiler::Proxy;
    no warnings 'redefine';
    my $sub = sub {
        my $t0 = [Time::HiRes::gettimeofday];
        my @arr;
        my $scalar;
        my $entry = $prevCode;
        my ($timeOwn,$timeTotal);
        my $context = wantarray;
        {
            local $InvokeTime = 0;
            #warn "\t"x$level,"enter ${class}::$method";
            $level ++;
            if ($context) {
                @arr = &$entry(@_);
            } else {
                if (defined $context) {
                    $scalar = &$entry(@_);
                } else {
                    &$entry(@_);
                }
            }
            $timeTotal = Time::HiRes::tv_interval($t0);
            $timeOwn = $timeTotal - $InvokeTime;
        }
        $InvokeInfo{"${class}::${method}"}{Count} ++;
        $InvokeInfo{"${class}::${method}"}{Total} += $timeTotal;
        $InvokeInfo{"${class}::${method}"}{Own} += $timeOwn;
        $InvokeTime += $timeTotal;
        $level --;
        #warn "\t"x$level,"leave ${class}::$method";
        return $context ? @arr : $scalar;
    };
    if ($proto) {
        Scalar::Util::set_prototype($sub => $proto);
    }
    *{"${class}::${method}"} = $sub;
    }
    
}

sub PrintStatistics {
    my $hout = shift || *STDERR;
    print $hout "-- modules --\n";
    print $hout "$_\n" foreach sort keys %TrappedModules;
    print $hout "\n-- stats --\n";
    print $hout
        pad($_,50),
        pad("$InvokeInfo{$_}{Count}",10),
        pad(sprintf("%.3f",$InvokeInfo{$_}{Own}),10),
        pad(sprintf("%.3f",$InvokeInfo{$_}{Total}),10),
        "\n"
        foreach sort { $InvokeInfo{$b}{Own} <=> $InvokeInfo{$a}{Own} } keys %InvokeInfo;
}

sub ResetStatistics {
    $InvokeTime = 0;
    %InvokeInfo = ();
}

sub pad {
    my ($str,$len) = @_;
    if (length $str < $len) {
        return $str.(' 'x ($len- length $str));
    } else {
        return $str;
    }
}
1;