#!/usr/bin/perl


package Monitor::PerfLib;

@ISA = qw(Exporter Monitor::Monitor);

use Win32::PerfLib;
use strict;

my %countertype_fields = (
    "size" => 0x300,
    "type" => 0xC00,
    "numberdisplay" => 0x70000,
    "countertype" => 0x70000,
    "texttype" => 0x70000,
    "timer_subtype" => 0x300000,
    "calculation" => 0x3C00000,
    "display" => 0x70000000
);


my %countertypes = (
    "size" => {
	0 => "size_dword",
	0x100 => "size_large",
	0x200 => "size_zero",
	0x300 => "size_variable"
    },

    "type" => {
	0=> "type_number" => 0,
	0x400 => "type_counter",
	0x800 => "type_text",
	0xc00 => "type_zero"
    },

	"numberdisplay" => {
	    0 => "number_hex",
	    0x10000 => "number_decimal",
	    0x20000 => "number_dec_1000"
	},

	"countertype" => {
	    0 => "counter_value",
	    0x10000 => "counter_rate",
	    0x20000 => "counter_fraction",
	    0x30000 => "counter_base",
	    0x40000 => "counter_elapsed",
	    0x50000 => "counter_queuelen",
	    0x60000 => "counter_histogram"
	},

	"texttype" => {
	    0 => "text_unicode",
	    0x10000 => "text_ascii"
	},

    "timer_subtype" => {
	0 => "timer_tick",
	0x100000 => "timer_100ns",
	0x200000 => "timer_object"
    },

    "calculation" => {
	0x400000 => "delta_counter",
	0x800000 => "delta_base",
	0x1000000 => "inverse_counter",
	0x2000000 => "multi_counter"
    },

    "display" => {
	0 => "display_no_suffix",
	0x10000000 => "display_per_sec",
	0x20000000 => "display_percent",
	0x30000000 => "display_seconds",
	0x40000000 => "display_noshow"
    }
);


my $i;
foreach $i (keys %countertypes) {
    my $j;
    foreach $j (keys %{$countertypes{$i}}) {
	$countertypes{$i}->{$countertypes{$i}->{$j}} = $j;
    }
}


sub test {
    my( @objs ) = @_;
    my %counternames;
    my %helptexts;
    Win32::PerfLib::GetCounterNames(undef,\%counternames);
    Win32::PerfLib::GetCounterHelp(undef,\%helptexts); 
    my %r_counternames = map { $counternames{$_} => $_ } keys %counternames;

    my $i;
    foreach $i (keys %countertypes) {
	my $j;
	foreach $j (keys %{$countertypes{$i}}) {
	    $countertypes{$i}->{$countertypes{$i}->{$j}} = $j;
	}
    }

    #my $key;
    #foreach $key (sort { $a <=> $b } keys %counternames) {
    #    print "Counter $key: ".($counternames{$key})."\n";
    #}

    unless( @objs ) {
	my @objs = ( "Processor",
		    "Redirector",
		    "Server",
		    "Server Work Queues",
		    "System",
		    "TCP",
		    "Telephony",
		    "Thread",
		    "UDP",
		    "WINS Server",
		    "Process",
		    "PhysicalDisk",
		    "Paging File",
		    "Objects",
		    "Network Segment",
		    "Network Interface",
		    "NBT Connection",
		    "Memory",
		    "LogicalDisk",
		    "IP",
		    "ICMP",
		    "Cache",
		    "Browser" );

	@objs = ("LogicalDisk");
    }
    my $perflib = new Win32::PerfLib(undef);

    my $i;
    my $proc_ref = {};
    my $lib = Monitor::PerfLib->new();
    for( $i=0; $i<5; $i++ ) {
       print "$i\n";

	my $previous = $proc_ref;
	$proc_ref = {};
	my $obj;
	my $ref;

	$ref = $proc_ref;
	foreach $obj ( @objs ) {
	    my $id = $r_counternames{$obj};
	    $ref->{$obj} = {};
	$perflib->GetObjectList($id, $ref->{$obj});
	}
	my %var=("a","x");
	foreach $obj (keys %$proc_ref) {
	    my $inst;
	    my $obid;
	    if( $previous->{$obj} ) {
		my $timebase = $proc_ref->{$obj}->{"PerfTime100nSec"};
		my $timebase0 = $previous->{$obj}->{"PerfTime100nSec"};
		print "time base in 100nsec: ".($timebase-$timebase0)."\n";
		my $timebase = $proc_ref->{$obj}->{"PerfTime"};
		my $timebase0 = $previous->{$obj}->{"PerfTime"};
		print "time base in sec: ".($timebase-$timebase0)."\n";
	    }
	    foreach $obid (keys %{$proc_ref->{$obj}->{"Objects"}}) {
		foreach $inst (keys %{$proc_ref->{$obj}->{"Objects"}->{$obid}->{"Instances"}}) {
		    $lib->compute( $proc_ref->{$obj}, $previous->{$obj}, $obid, $inst, $obj, \%var );
		}
	    }
    print "\n\n\n\nObject: $obj\n\n";
    $lib->print_hash( $proc_ref->{$obj} );
	}
	sleep 1;
    }

    $perflib->Close();
}



sub new
{
    my ($this, $host) = @_;
    my  $class = ref($this) || $this;
    my  $self = {};
    my ($pos,$time,$ctime);

    print "Monitor::PerfLib::new( $host )\n" if( $BigSister::common::dl>2 );
    bless($self, $class);

    my %counternames;
    my %helptexts;
    Win32::PerfLib::GetCounterNames($host,\%counternames);
    Win32::PerfLib::GetCounterHelp($host,\%helptexts); 
    my %r_counternames = map { $counternames{$_} => $_ } keys %counternames;

    $self->{"counternames"} = \%counternames;
    $self->{"helptexts"} = \%helptexts;
    $self->{"r_counternames"} = \%r_counternames;
    $self->{"objects"} = [];
    $self->{"perflib"} = new Win32::PerfLib($host);
    $self->{"previous"} = {};
    $self->{"host"} = $host;
     
    unless( $self->{"perflib"} && %counternames ) {
	BigSister::common::log( "err", "cannot open perflib to host $host" );
    }

    return($self);
}


sub add_object {
    my( $self, $alias, $object ) = @_;
    my $r_counternames = $self->{"r_counternames"};

    $alias = $self->{"host"} unless( $alias );
    print "Monitor::PerfLib::add_object( $alias, $object )\n" if( $BigSister::common::dl>3 );
    return(0) if( grep( $_->[1] eq $object, @{$self->{"objects"}} ) );
    if( defined $r_counternames->{$object} ) {
	print "  adding object $object\n" if( $BigSister::common::dl>4 );
	push( @{$self->{"objects"}}, [ $alias, $object ] );
	return 1;
    }
    return 0;
}



sub check {
    my( $self, $checkobject ) = @_;
    my $counternames = $self->{"counternames"};
    my $r_counternames = $self->{"r_counternames"};
    my $objects = $self->{"objects"};
    my $object;
    my $host;
    my $check;

    foreach $check (@$objects) {
	($host, $object) = @$check;
	next if( $checkobject && ($checkobject ne $object) );
	my %vars;
	my $id = $r_counternames->{$object};
	my $ref = {};
	print "Monitor::PerfLib: getting object $object($id)\n" if( $BigSister::common::dl>4 );
	$self->{"perflib"}->GetObjectList($id, $ref);
	my $instance;
	$vars{"perflib.$object.Instances"} = [ (keys %{$ref->{"Objects"}->{$id}->{"Instances"}}) ];
	unless( @{$vars{"perflib.$object.Instances"}} ) {
	    $vars{"perflib.$object.Instances"} = [0];
	    $ref->{"Objects"}->{$id}->{"Instances"} = {
		0 => { 
		    "Counters" => $ref->{"Objects"}->{$id}->{"Counters"}
		}
	    };
	}
	foreach $instance (@{$vars{"perflib.$object.Instances"}}) {
	    print "  computing instance $instance\n" if( $BigSister::common::dl>5 );
	    $self->compute( $ref, $self->{"previous"}->{$object},
		$id, $instance, "perflib.$object", \%vars );
	}
#	$self->print_hash( $ref );
	$self->{"previous"}->{$object} = $ref;
	my( $key, $val );
	while( ($key,$val) = each %vars ) {
	    $self->set_variable( $host, $key, $val );
	}
    }
}



sub print_hash {
    my( $self, $ref, $indent ) = @_;
    my( $key, $val );
    my $counternames = $self->{"counternames"};
    my $helptexts = $self->{"helptexts"};

    ($indent = 0) unless( $indent );
    foreach $key (sort keys %$ref) {
	$val = $ref->{$key};
	print " " x $indent;
	print $key."\t";
	if( ref($val) eq "HASH" ) {
	    print "\n";
	    $self->print_hash( $val, $indent+2 );
	    print "\n";
	}
	else {
	    (($key eq "CounterNameTitleIndex") && (defined $counternames->{$val})) && ($val=$counternames->{$val});
	    (($key eq "CounterHelpTitleIndex") && (defined $helptexts->{$val})) && ($val=$helptexts->{$val});
	    if( ($key eq "CounterType") ) {
		my $portion;
		my $type = $val;

		$val = "";
		foreach $portion (keys %countertype_fields) {
		    next if( grep( $portion eq $_, "texttype", "display", "numberdisplay", "size" ) );
		    my $type = $type & $countertype_fields{$portion};
		    my $flag;

		    if( $flag = ($countertypes{$portion}->{$type}) ) {
			$val .= " $flag";
		    }
		    else {
			my $lis;

			foreach $lis (keys %{$countertypes{$portion}}) {
			    if( $type & $lis == $lis ) {
				$val .= " ".$countertypes{$portion}->{$lis};
			    }
			}
		    }
		}
	    }
	    print $val;
	}
	print "\n";
    }
}



sub compute {
    my( $self, $current, $previous, $object, $instance, $prefix, $variables ) = @_;
    my $counternames = $self->{"counternames"};
    my $timediff = 0;
    my $timediff100 = 0;
    my( $countertype, $prevcounters, $id );
    my $counters = $current->{"Objects"}->{$object}->{"Instances"}->{$instance}->{"Counters"};
    my $perffreq = $current->{"PerfFreq"};

    $prefix .= ".$instance";
    $variables->{$prefix.".InstanceName"} = $current->{"Objects"}->{$object}->{"Instances"}->{$instance}->{"Name"};
    if( $previous ) {
	print "current: $current, previous: $previous\n";
	my $timediff = $current->{"PerfTime"} - $previous->{"PerfTime"};
	my $timediff100 = $current->{"PerfTime100nSec"} - $previous->{"PerfTime100nSec"};
	$prevcounters = $previous->{"Objects"}->{$object}->{"Instances"}->{$instance}->{"Counters"};
    }
    foreach $id (keys %$counters) {
	my $counter = $counters->{$id};
	my $name = $counternames->{$counter->{"CounterNameTitleIndex"}};
	$name =~ tr/\./_/;
	my $varname = $prefix.".".$name;
#	$BigSister::common::dl = 10 if( $name eq "% Disk Time" );
	my $typecode = $counter->{"CounterType"};
	my $type = $countertypes{"countertype"}->{$typecode & $countertype_fields{"countertype"}};
	my $valtype = $countertypes{"type"}->{$typecode & $countertype_fields{"type"}};
	my $time = $countertypes{"timer_subtype"}->{$typecode & $countertype_fields{"timer_subtype"}};
	my $display = $countertypes{"display"}->{$typecode & $countertype_fields{"display"}};
	my $calculation = $typecode & $countertype_fields{"calculation"};
	my $val = $counter->{"Counter"};
	my $result;
	my $countersize = $counter->{"CounterSize"};
	if( ($countersize==4) && $val < 0 ) {
	    $val = $counter->{"Counter"} = 0xffffffff + $val + 1;
	}
	my( $timebase, $timebase0 );

	print "value type: $valtype, counter type: $type, display type: $display\n" if( $BigSister::common::dl>8 );

	if( $time eq "timer_tick" ) {
	    print "time base is PerfTime\n" if( $BigSister::common::dl>9 );
	    $timebase = $current->{"PerfTime"};
	    $timebase0 = $previous->{"PerfTime"} if( $previous );
	} elsif( $time eq "timer_100ns" ) {
	    print "time base is PerfTime100nSec\n" if( $BigSister::common::dl>9 );
	    $timebase = $current->{"PerfTime100nSec"};
	    $timebase0 = $previous->{"PerfTime100nSec"} if( $previous );
	} elsif( $time eq "timer_object" ) {
	    print "time base is PerfFreq\n" if( $BigSister::common::dl>9 );
	    $perffreq = $timebase = $current->{"Objects"}->{$object}->{"PerfFreq"};
	}

	if( $calculation & $countertypes{"calculation"}->{"delta_counter"} ) {
	    next unless $previous;
	    $val -= $prevcounters->{$id}->{"Counter"};
	    if( ($val < 0) && ($countersize==4) ) {
		$val += 0xffffffff + 1;
	    }
	    $timebase = $timebase-$timebase0 if( defined $timebase0 );
	    print "delta counter: delta is $val\n" if( $BigSister::common::dl>8 );
	}

	if( $valtype != "type_counter" ) {
	    $result = $val;
	}
	elsif( $type eq "counter_value" ) {
	    $result = $val;
	}
	elsif( $type eq "counter_rate" ) {
	    $result = $val / $timebase if( $timebase );
	}
	elsif( $type eq "counter_fraction" ) {
	    my $base = $counters->{$id+1}->{"Counter"};
	    if( $calculation & $countertypes{"calculation"}->{"delta_base"} ) {
		next unless( $previous );
		$base -= $previous->{"Objects"}->{$object}->{"Counters"}->{$id+1}->{"Counter"};
	    }
	    print "fraction val: $val, base: $base (id: ".($id+1).")\n" if( $BigSister::common::dl>9 );
	    $result = $val / $base if( $base );
	    $variables->{$varname.".fraction"} = $val;
	    $variables->{$varname.".base"} = $base;
	}
	elsif( $type eq "counter_elapsed" ) {
	    $result = $timebase-$val;
	}

	if( defined $result ) {
	    if( $calculation & $countertypes{"calculation"}->{"inverse_counter"} ) {
		$result = 1-$result;
	    }
	    if( $display eq "display_percent" ) {
		$result *= 100;
	    }
	    elsif( $display eq "display_per_sec" ) {
		($result *= $perffreq) if( $perffreq );
		print "perffreq: $perffreq\n" if( $BigSister::common::dl > 9 );
	    }
	    elsif( $display eq "display_seconds" ) {
		($result /= $perffreq) if( $perffreq );
	    }
	    $counter->{"DisplayCounter"} = $result;
	    $variables->{$prefix.".".$name} = $result;
	}
    }
}

	    

1;

