use strict;
use warnings;
use POSIX;
use Win32::PerfLib;

my $pql_counter = create_ql_counter();
my $dql_counter = create_ql_counter();
sub create_ql_counter {
	my @array; push @array, 0 for (1..900);
	my ($sum60, $sum300, $sum900) = (0, 0, 0);
	my $count = 0;
	my $result;
	return sub {
		if (defined $_[0]) {
			$sum60  += $_[0] - $array[900 -  60];
			$sum300 += $_[0] - $array[900 - 300];
			$sum900 += $_[0] - $array[900 - 900];
			shift @array;
			push @array, $_[0];
			$count++ if $count < 900;
			$result->{a60}  = $sum60  / ($count >  60 ?  60 : $count);
			$result->{a300} = $sum300 / ($count > 300 ? 300 : $count);
			$result->{a900} = $sum900 / ($count > 900 ? 900 : $count);
		}
		return $result;
	};
}

my %counter;
Win32::PerfLib::GetCounterNames(undef, \%counter);
my %r_counter = map { $counter{$_} => $_ } keys %counter;

my $process_obj = $r_counter{Process};
my $processor_obj = $r_counter{Processor};
my $ptime_id = $r_counter{'% Processor Time'} or die;
my $system_obj = $r_counter{System};
my $pql_id = $r_counter{'Processor Queue Length'} or die;
my $disk_obj = $r_counter{PhysicalDisk};
my $dql_id = $r_counter{'Current Disk Queue Length'} or die;

my $perflib = new Win32::PerfLib or die;

my $lastptime;
my $lastnsec;
my $lastprinttime = -1;

print "Time,                 CPU, PQL,  Avg1,  Avg5,  Avg15, DQL,  Avg1,  Avg5,  Avg15\n";

while (1) {
	my $proc_ref = {};
	$perflib->GetObjectList($process_obj, $proc_ref);
	my $nsec = $proc_ref->{PerfTime100nSec};

	my $processor_ref = {};
	$perflib->GetObjectList($processor_obj, $processor_ref);
	my $instance_ref = $processor_ref->{Objects}->{$processor_obj}->{Instances};
	my $ptime;
	foreach (values %{$instance_ref}) {
		if ($_->{Name} eq '_Total') {
			foreach (values %{$_->{Counters}}) {
				if ($_->{CounterNameTitleIndex} == $ptime_id) {
					$ptime = $_->{Counter};
					last;
				}
			}
			last;
		}
	}

	my $system_ref = {};
	$perflib->GetObjectList($system_obj, $system_ref);
	$instance_ref = $system_ref->{Objects}->{$system_obj}->{Counters};
	my $pql;
	foreach (values %{$instance_ref}) {
		if ($_->{CounterNameTitleIndex} == $pql_id) {
			$pql = $_->{Counter};
			last;
		}
	}
	$pql_counter->($pql);

	my $disk_ref = {};
	$perflib->GetObjectList($disk_obj, $disk_ref);
	$instance_ref = $disk_ref->{Objects}->{$disk_obj}->{Instances};
	my $dql;
	foreach (values %{$instance_ref}) {
		if ($_->{Name} eq '_Total') {
			foreach (values %{$_->{Counters}}) {
				if ($_->{CounterNameTitleIndex} == $dql_id) {
					$dql = $_->{Counter};
					last;
				}
			}
			last;
		}
	}
	$dql_counter->($dql);

	my $t = strftime('%H%M', localtime);
	if ($t != $lastprinttime) {
		my $cpu = $lastptime ? int(100 - ($ptime - $lastptime) / ($nsec - $lastnsec) * 100 + 0.5) : 0;
		print strftime('%Y-%m-%d %H:%M:%S', localtime), ": ";
		printf '% 3d%%, ', $cpu;
		my $la = $pql_counter->();
		printf '% 3d, % 2.2f, % 2.2f, % 2.2f,  ', $pql, $la->{a60}, $la->{a300}, $la->{a900};
		$la = $dql_counter->();
		printf '% 3d, % 2.2f, % 2.2f, % 2.2f', $dql, $la->{a60}, $la->{a300}, $la->{a900};
		print "\n";
		$lastnsec = $nsec;
		$lastptime = $ptime;
		$lastprinttime = $t;
	}

	sleep 1;
}
