スペースモラトリアムノカミサマ

日記+コメント付きブックマーク+他人にも役に立つかもしれない情報など。
(更新情報: RSS(ツッコミ付き) / RSS(ツッコミ抜き) / LIRS)

最近の TrackBack:
2004|01|02|03|04|05|06|07|08|09|10|11|12|
2005|01|02|03|04|05|06|07|08|09|10|11|12|
2006|01|02|03|04|05|06|07|08|09|10|11|12|
2007|01|02|03|04|05|06|07|08|09|10|11|12|
2008|01|02|03|04|05|06|07|08|09|10|11|12|
2009|01|02|03|04|05|06|07|08|09|10|11|12|
2010|01|02|03|04|06|07|08|09|10|
2011|01|02|03|07|10|11|
2012|02|03|04|07|08|09|
2013|01|06|07|
2014|02|08|09|11|
2015|09|
2016|01|05|
2017|07|
2018|05|07|
2019|07|
2020|08|09|10|12|
2021|05|
2022|03|

2009/05/16

_ [Perl][システム運用] Load Average っぽいものを Windows で

Linux の Load Average 的なものを Windows で取得できないのだろうか、ということで以前質問したところ、typeperf 使ってパフォーマンスカウンタ見ると良いよ、との回答がついた。

具体的には Processor Queue Length と Current Disk Queue Length を見るのが良さそうだ。

が、いずれもその瞬間の数値しか取得できないので整数値だし激しく変動するしでいまいち状態を掴みづらい。

ので、Win32::PerfLib 使って、Load Average と同様に1分平均、5分平均、15分平均を出すような Perl スクリプトを書いてみた。

win32la.pl

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;
}

こんな感じで出てくる。

Time,                 CPU, PQL,  Avg1,  Avg5,  Avg15, DQL,  Avg1,  Avg5,  Avg15
(中略)
2009-05-17 04:46:00:  26%,   0,  0.02,  0.10,  0.12,    0,  0.65,  0.61,  0.40
2009-05-17 04:47:00:  30%,   0,  0.18,  0.11,  0.13,    0,  0.97,  0.68,  0.44
2009-05-17 04:48:00:   9%,   0,  0.00,  0.10,  0.12,    0,  0.67,  0.74,  0.47
2009-05-17 04:49:00:   9%,   0,  0.03,  0.07,  0.11,    0,  0.05,  0.62,  0.43
2009-05-17 04:50:00:   6%,   0,  0.02,  0.05,  0.10,    0,  0.00,  0.46,  0.39

単純に1秒 sleep しているだけなので、正確には1分、5分、15分ではないのだがご愛敬。

これで Load Average と似た情報を取れるようになったが、スクリプトを走らせっぱなしにしなければいけないのが難点。

標準のパフォーマンスカウンタでもこういう数値を常時提供してくれりゃいいんですけどね…

関連: