2008年8月24日

AとBの邂逅

ここまでは、各マシン毎の分類だった。ここからはマシンごとの特徴を見た上での分類が必要になる。要点としては図1のように分類する事になる。

 A only HIA only BORDERA only LOW
Aマシンのカウンター
B only HIBマシンのカウンター(A,B)=(HI,HI)(A,B)=(BORDER,HI)(A,B)=(LOW,HI)
B only BORDER(A,B)=(HI,BORDER)(A,B)=(BORDER,BORDER)(A,B)=(LOW,BORDER)
B only LOW(A,B)=(HI,LOW)(A,B)=(BORDER,LOW)(A,B)=(LOW,LOW)
図1. AとBの分類


まず最も判りやすいところから説明しよう。中央の紫色の3x3=9枡目は、AとBの両方で存在した(NaNによる除去を免れた)カウンターだ。A, B それぞれにおいて、どう分類されていたのか、に応じて3x3通りが存在する。

薄い黄色で塗られた3つの枡目、「A only xxxx」とあるのは『Aには存在したがBには存在しなかった』カウンターを現す。同様に「B only xxxx」とある水色の3つの升目は『Bには存在したが、Aには存在しなかった』カウンターを現す。

生き残ったカウンターは以上15個の升目のどこかに必ず入る。まずこの分類を行う。

さて、これらの升目の内、重要視するのは図2で示した6枡である。


 A only HIA only BORDERA only LOW
Aマシンのカウンター
B only HIBマシンのカウンター(A,B)=(HI,HI)(A,B)=(BORDER,HI)(A,B)=(LOW,HI)
B only BORDER(A,B)=(HI,BORDER)(A,B)=(BORDER,BORDER)(A,B)=(LOW,BORDER)
B only LOW(A,B)=(HI,LOW)(A,B)=(BORDER,LOW)(A,B)=(LOW,LOW)
図2. 重要視するポイント


症状はAでは発生しているが、Bでは発生していない。そして問題が生じているときにはプロセッサが忙しく働いている。ならば、「AのときはIdleと強い相関があり、Bの時にはIdleとの相関が弱い」所に探すべきネタがある。

Aにしか存在しないカウンターの内、相関がLOWのもの以外の2つは、当然検討対象にしなくてはいけない。これは判ると思う。逆にBにしか存在しないカウンターは無視して構わない。

A,Bともに存在したカウンターの内、Aにおいて相関がLOWだったものは、この場合検討対象から外してよい。

Bにおいて相関がHIだったものも、この場合検討から外してよいはずだ。AもHI,BもHIならそれは「いつでも相関が高いもの」…例えば、プロセッサ消費量の内の User の割合など…であることが考えられる。また、AがHIで、BがHIではないのでは明らかに今回探している対象から外れるのは自明だ。

残った6枡の内、A,Bともに BORDERなものは特別扱いが必要になる。BORDERにあるのは計測すると相関が高かったり高く無かったりしたものだ。つまり「AもBも、相関が高いんだか低いんだかよく判らん」ものだったりする。この場合、各カウンターの内容を調べて、個別に判断する必要が出る。しかし、逆に言うとここは手間がかかるところなので、それ以外の部分で何か見つかった場合は無視して構わない、ともいえる。

と言うわけで、残り5枡を最優先で調べるべし、と言うことになる。

2008年8月16日

sort.pl の出力

先に進む前に sort.pl の出力についてもう少し説明する。

sort.pl が入力として受け付ける(前提としている)のは、calcr2.pl の出力を複数 cat でつないだもの。今回は sort もかけることで同じカウンターが近接するようにしてあるが、本質的には sort の必要は無い。結果として、このような内容が延々続くファイルになる:


"\LogicalDisk(C:)\% Disk Read Time" "-0.04062578806352645403" "0.00165045465578256851" "-0.12770872885354865610" "90.45804378385737658023"
"\LogicalDisk(C:)\% Disk Read Time" "-0.04406606083496136481" "0.00194181771751051590" "-0.17957809895894711362" "90.36202077972433047033"
"\LogicalDisk(C:)\% Disk Read Time" "-0.10162240553099880448" "0.01032711330590677641" "-0.25960115664153218127" "91.91528872471089858070"
"\LogicalDisk(C:)\% Disk Read Time" "-0.10606592840069039460" "0.01124998116750038125" "-0.50117255489344281550" "89.78700245921672272973"
"\LogicalDisk(C:)\% Disk Read Time" "-0.63225077753384162361" "0.39974104569214729193" "-0.98409431769226710436" "99.62442055641675669955"
"\LogicalDisk(C:)\% Disk Time" "-0.04336533750022928025" "0.00188055249650879188" "-0.11305679326692022027" "90.53746144831214471477"
:


r2の値を使ってこれらをソートしていくわけだが、全ての計測結果においてr2が同じ性質を持つわけではない。実はここでは4種類に分類している。出力しているファイルは3種類だが、実際には分類は4種類あるのだ。

そこで、まず個々のr2の値はどのような値をとりえるのか、考えて見よう。

r2のとる値


すでに述べたが、r2は0.0から1.0までの間の値を取る。正確には計算できる場合は、それらの間の値をとる。そう。計算できない場合、と言うのがあるのだ。

指定したカウンターに値が全く記録されていない場合がありえる。この場合、データが全くないので、回帰直線も引けない。つまり y=ax+b という形のaとbも求められない。rの値は a と b がないと計算できないのでrも求められないし、その二乗値も求められない。
この場合、r, r2, a, b は全て Not a Number (NaN) となる。

rやr2が NaN になるケースがもう一つある。a が 0.0 になった場合だ。厳密には一定の誤差の範囲内であれば NaN になってしまう。rの計算式には a による除算が含まれているのだが、ここで 0割り(0を分母とする分数の値を計算しようとした時に生じるエラー)が起こるのだ。この場合、r, r2は NaN になるが、a は 0.0 になるし、bはどんな値になるかはデータ依存だ。

以上の2つのケース以外の場合、rの値は 0.0 から 1.0 の間の値を取る。厳密には、計算誤差のせいで 1.0 を超える数字が出たり、0.0を下回る数字が出たりすることもあるが、あくまでも誤差の範囲、と捉えてもらって大丈夫だ。

r2の値による分類


さて、今回、sort.pl でカウンターを分類するにあたって次のような規則を適用した。


1) 同じカウンターに属する、全てのr2の値が NaN ならば、そのカウンターは記録しない。

データが一切ないのであれば、そのカウンターは考慮に値しない。

データがあるが a が 0.0 になる、と言うことは Processor Idle の値に全く影響を受けてない/影響を与えていない、と言うことだ。今回は影響のあるものを探しているのだから、そのカウンターについて、以降考慮する必要は無い。
Process ID のように変わるはずの無いカウンターが該当する。

2) 同じカウンターの一部だけが NaN の場合、そのNaNは無視する。

取りあえず残りのデータだけで分析しよう、と言うことだ。

3) 全ての r2が境界値以上だったらHI

4) 全ての r2が境界値未満だったらLOW

5) r2が境界値以上、未満の両方存在したら、BORDER



なので、全てNaNの場合のカウンターがこの段階で消えている。カウンターの総数は分類前と分類後では、分類後の方が少ない可能性がある、ということだ。

2008年8月12日

相関係数によってカウンターを分類する

カウンターごとに相関が求まったので、それを元にカウンターを分類する。

その前に。カウンターにくっついているマシン名がうざったい。

"\\A\Processor(_Total)\% Idle Time" "0.99999999999999558689" ....


というこの「\\A\」がうざったいのだ。と言うのは、後で「マシン同士の」相関を取る必要があるからだ。カウンター名にマシン名が含まれていては「同じカウンター同士」を判別するのが面倒になる。だからこの場で外してしまおう。

そしてついでに、同じマシンに関して、別の日付のものをあわせて1つにしてしまおう。


次に。各TSVファイルを分類する。分類するスクリプトはこれ:

sort.pl
$r2_limitation = 0.25;

open( INFILE, "$ARGV[0]" ) or die "can't open file $ARGV[0] as read\n";
open( OHI, " > $ARGV[1]" ) or die "can't open file $ARGV[1] as write\n";
open( OBO, " > $ARGV[2]" ) or die "can't open file $ARGV[2] as write\n";
open( OLO, " > $ARGV[3]" ) or die "can't open file $ARGV[3] as write\n";

while () {
chomp;
my @cells = split /\t/;
my $i, $n;

for ( $i = 0; $i <= $#cells; $i++ ) {
$cells[$i] =~ s/"(.*)".*$/\1/g;
}

$n = $cells[0];
if ( $n eq "name" ) {
next;
}
if ( $counter{$n} <= 0 ) {
push @name, $n;
}

$i = $counter{$n};
$counter{$n}++;
$r{$n}[$i] = $cells[1];
$r2{$n}[$i] = $cells[2];
$a{$n}[$i] = $cells[3];
$b{$n}[$i] = $cells[4];
}

while ( $n = pop @name ) {
my $allbig = 1;
my $allsmall = 1;
my $findNaN = 0;

for ( $i = 0; $i < $counter{$n}; $i++ ) {
if ( $r2{$n}[$i] < $r2_limitation ) {
$allbig = 0;
}
if ( $r2{$n}[$i] >= $r2_limitation ) {
$allsmall = 0;
}
if ( $r2{$n}[$i] eq "NaN" ) {
$findNaN = 1;
}
}

if (( $allbig )&&( !$allsmall )) {
print OHI "\"$n\"\n";
for ( $i = 0; $i < $counter{$n}; $i++ ) {
print OHI "# \"$r{$n}[$i]\"\t\"$r2{$n}[$i]\"\t\"$a{$n}[$i]\"\t\"$b{$n}[$i]\"\n";
}
}

if (( !$allbig )&&( $allsmall )) {
print OLO "\"$n\"\n";
for ( $i = 0; $i < $counter{$n}; $i++ ) {
print OLO "# \"$r{$n}[$i]\"\t\"$r2{$n}[$i]\"\t\"$a{$n}[$i]\"\t\"$b{$n}[$i]\"\n";
}
}

if (( !$allbig )&&( !$allsmall )) {
print OBO "\"$n\"\n";
for ( $i = 0; $i < $counter{$n}; $i++ ) {
print OBO "# \"$r{$n}[$i]\"\t\"$r2{$n}[$i]\"\t\"$a{$n}[$i]\"\t\"$b{$n}[$i]\"\n";
}
}
if (( $allbig )&&( $allsmall )) {
if ( $findNaN ) {
print STDERR "\"$n\" seems to have NaN as it's r2 value\n";
} else {
die "\"$n\" did not have any element in $ARGV[0]?!\n";
}
}
}


引数は4つ。
1つ目が入力TSVファイル名。
2つ目は $r2_limitation 以上のr2値を持っているカウンターだけを集めたTSVファイルの名前。
4つ目は $r2_limitation 未満のr2値を持っているカウンターだけを集めたTSVファイルの名前。
3つ目は r2の値が $r2_limitation 以上だったり以下だったりふらつくものを集めたTSVファイルの名前。

とりあえず $r2_limitation はSTATISTICS HACKの推奨にあわせて「強い相関」の値0.25に設定してあります。

% cd $TOP
% cd $TOP/05sort
% cat filter.sh
#!/bin/sh

onebefore="../04Colleration";
for i in A B C D; do
cat $onebefore/$i.*.tsv | sed -e 's/^"\\\\[[:upper:][:digit:]]\+\\/"\\/g' | sort > $i.lst
done

for i in A B C D; do
./sort.pl $i.lst $i.HI $i.BORDER $i.LOW
done
% bash filter.sh


これで A.HI, A.BORDER, A.LOW や B.HI, B.BORDER, B.LOW ができた。

一番計算機パワーを食うところはこれでおしまい。ここから先は人間が悩むしかない…。

2008年8月11日

じゃぁ、相関とかを求めるかね。

全部のマシン、全部の日付、全部のエントリについて、"Processor(_Total)\% Idle Time" との相関 r ならびに r2, 回帰式 y=ax+b にマップしたときの a, b を求めよう。あ、x 側が"Processor(_Total)\% Idle Time"で、yが「相関を求めるべき他のエントリ」な。

Perlにはこの手の統計処理用のモジュールが結構ある。どれを使うか迷う所だが、今回は深く考えずにMath::NumberCruncherを使ってみよう。

もし、cygwinを使っているならば、次の命令を実行し、尋ねてくる質問に真摯に答えればインストールは完了する。
% perl -MCPAN -e 'install Math::NumberCruncher'

Active Perlのときはどうするのか?とかそういう質問の答は判らないのでよろしく。

http://search.cpan.org/~sifukurt/Math-NumberCruncher-5.00/NumberCruncher.pod
に一応説明らしきものがある事になっているが、はっきり言って説明になっていない。2引数を取る場合、どちらかがxでどちらかがy…という問題を解かなきゃいけないはずだが、それもない。強引に超能力をかけることにする。多分、こういうこったろう。

$correlation = Math::NumberCruncher::Correlation(\@yarray,\@xarray);

($slope,$y_intercept ) = Math::NumberCruncher::BestFit(\@yarray,\@xarray);


ここで、$correlation は「相関係数」つまり、今まで言っていた r の事だ。$slope (傾き)と $y_intercept (y切片) は、それぞれ a と b になる。r2を直接求める関数はないので、$r2=$r*$r; で求める事になる。




入力TSVファイルは全部次のようなフォーマットになっている。
"(PDH-TSV 4.0) (Tokyo Standard Time)(-540)" "\\A\Processor(_Total)\% Idle Time" "\\A\LogicalDisk(C:)\% Disk Read Time" ........
"07/15/2008 07:59:59.810" "0.079295186091091777" "0.00050253600663232288" .......
"07/15/2008 08:00:59.809" "98.125628004019234" "0.10616734613768194" .......
........


第一行がラベルで、各ラベルは ダブルクォート(") で囲まれている。さらに、それらはタブで分割されている。第2行以降は測定結果だ。

各行の最初は「測定時刻」だ。従って、相関を求めるべき一方の「Idle」は2つ目の要素になる。

ダブルクォートの中にはタブはないので、とりあえずタブで要素を分割すればいいだろう。その後、ダブルクォートを各要素から取り除けばよい。これを2次元配列にぶちこんだあと、2つ目の要素列と3つ目以降の各要素列との相関を求める。




各要素の出力は次のような形とする。
"name" "r" "r2" "slope" "y intercept"
"\\A\Processor(_Total)\% Idle Time" "0.99999999999999558689" "0.99999999999999117378" "0.99999999999995984122" "0.00000000000341678664"
"\\A\LogicalDisk(C:)\% Disk Read Time" "-0.08658618963563029212" "0.00749716823561733062" "-0.36956562874045379176" "85.25898260145136281611"
.......

第1行は各列のラベルだ。で、2行目以降は「相関を取った列のラベル」、相関係数 "r"、rの二乗値 "r2"、傾き "a"、y切片 "b"の順になる。1行毎に相関を取る相手が変わる。

ある TSVファイルから、この出力を求める perl スクリプトは次の通りだ。

calcr2.pl

use Math::NumberCruncher;
$ref = Math::NumberCruncher->new();

my $linenumber = 0;
my $maxcells = 0;

# read data.
while () {
chomp;

my @cells = split /\t/;

if ( $maxcells < $#cells ) {
$maxcells = $#cells;
}

if ( $linenumber == 0 ) {
for ( $i = 0; $i <= $#cells; $i++ ) {
$cells[$i] =~ /^\"(.+)\"/;

my $tmp = $1;
$countername[$i] = $tmp;
}
} elsif ( $linenumber == 1 ) {
# skip this one!
# first taken data is useless.
} else {
for ( $i = 0; $i <= $#cells; $i++ ) {
$cells[$i] =~ /^\"(.+)\"/;
my $tmp = $1;

# $linenumber == 2 got to be $index == 0.
my $index = $linenumber - 2;

$num[$i][$index] = $tmp;
}
}

$linenumber++;
}

# output the result
print "\"name\"\t\"r\"\t\"r2\"\t\"slope\"\t\"y intercept\"\n";

for ( $i = 1; $i <= $maxcells; $i++ ) {

$xname = $countername[1];
$yname = $countername[$i];
@xarray = @{$num[1]};
@yarray = @{$num[$i]};

$r = Math::NumberCruncher::Correlation( \@yarray, \@xarray );
($a, $b)
= Math::NumberCruncher::BestFit( \@yarray, \@xarray );
$r2 = $r * $r;

print "\"$yname\"\t\"$r\"\t\"$r2\"\t\"$a\"\t\"$b\"\n";
}


いくつかポイントがある。

入力は標準入力。出力は標準出力だ。

データ行になって最初の行はわざと捨てている。これは、この手の記録の多くが「前回取得したデータとの差分」から求められる事が多いからだ。1つ前のデータがない、最初の一手は往々にして間違ったデータである事が多い。故にそれは取り除く。

@xarray, @yarray をコピーしているが、これは単に「配列の配列」から「配列(2つ目の方)」を持ってくる方法を完全に失念していたためだ。うまく動かない…と七転八倒するためにコピーをとったのに過ぎない。多分、ここには無駄があり、この無駄を省けば結構速くなるんじゃないかな、と思う。

こいつを、すでに作ってあるTSVファイルに対して適用する。ただし、出力結果もTSV。ややこしい事おびただしいが、作ったときは深く考えてなかった。カっとなってやった。今では反省している。

% cd $TOP
% cd $TOP/04Colleration
% cat filter.sh
for i in ../03TSV/*.tsv; do
of=`echo $i | sed 's/\.\.\/03TSV\///g'`;
echo $of
cat $i | ./calcr2.pl > $of
done


私の環境では12時間ほどかかりました。もっとメモリとプロセッサパワーが欲しいです。

2008年8月4日

一覧の出現順序をいじったら…TSV化だっ

一覧から
\\A\Processor(_Total)\% Idle Time

とかを切り出して、一番最初に出てくるようにする。

% cd $TOP
% cd $TOP/02LST
% cat filter.sh
for i in A B C D; do
cat ../01PQRlst/$i* | fgrep 'Processor(_Total)\% Idle Time' | sort | uniq > ./$i.lst
cat ../01PQRlst/$i* | fgrep -v 'Processor(_Total)\% Idle Time' | sort | uniq >> ./$i.other
done
% bash filter.sh

大事なのは Sort を掛けてある事と、各マシンについて日付をまたいでリストをマージしている事。

Sortをかけているのは、リストのエントリが「大雑把に何\細かく言うと何」という形式に従っているから、と言うのが一つ。似た項目が集まるように。もう一つは uniq をかけるため。




さて、とりあえずここまできたら、blg ファイルの中身を TSVファイルに変換しよう。
先ほど作った A.lst, B.lst を relog.exe に適用すれば、そこで指定した順番でカウンターが出力される。

% cd $TOP
% cd $TOP/03TSV
% cat filter.sh
for i in A B C D; do
for j in $TOP/native_blg/$i*.blg; do
outfile=`echo $j | sed -e 's/^.*\//g' -e 's/\.blg$/.tsv/g'`
relog.exe $j -f TSV -cf $TOP/02LST/$i.lst -o $outfile;
done
done
% bash filter.sh


これで $TOP/03TSV/ 下に欲しい順序で、各 blg ファイルの中身が TSV の形式で格納されたわけだ。ただし、3500列 480行もあるファイルが4個 x サーバ4種類の合計16個もあり、このTSVファイルを Excel に食わせようとすると、Excel側がギブアップするってぐらいのおチャメさんなファイルになったわけだが…

2008年8月3日

まずは一覧

どこか適当なディレクトリを $TOP と定義する。

$TOP/native_blg/

ディレクトリ下に、blgファイルが全部置いてある、としよう。そして、cygwin が「全部インストール」してあると仮定しよう。bash も使い放題だ。Perl もCPANモジュールが適宜インストールできる、とする。

% ls $TOP/native_blg/
A_Jul15.blg
A_Jul16.blg
A_Jul17.blg
A_Jul18.blg
B_Jul15.blg
B_Jul16.blg
B_Jul17.blg
B_Jul18.blg
:

[マシン名]_日付.blg というファイル名フォーマットらしい。

まず、最初にするべきなのは、各 blg ファイルに含まれているエントリを全部引き出す事だ。Printer情報だのProcess情報だのと、結構それぞれ異なっているはずなので、一覧を取り出して、不要なものをある程度削らなくてはいけないし、Excelに食わせられるように200個ぐらいづつに切り分けるための一覧表が必要になる。

relog.exe というプログラムが .blg ファイルを CSV や TSV (Tab Separated Values)フォーマットに変換してくれる。 これには -q というオプションがあって、このカウンタの一覧を引っ張り出してくれる:

% relog A_Jul15.blg -q -o A_Jul15.lst
% cat A_Jul15.lst
\\A\LogicalDisk(C:)\Avg. Disk Bytes/Write
\\A\LogicalDisk(E:)\Avg. Disk Bytes/Write
\\A\LogicalDisk(F:)\Avg. Disk Bytes/Write
\\A\LogicalDisk(_Total)\Avg. Disk Bytes/Write
\\A\LogicalDisk(C:)\% Idle Time
\\A\LogicalDisk(E:)\% Idle Time
\\A\LogicalDisk(F:)\% Idle Time
\\A\LogicalDisk(_Total)\% Idle Time
\\A\LogicalDisk(C:)\Disk Reads/sec
:


すでに後悔の念バリバリな分量が出てくる。うわ、必要なんだかなんだか判らないものだらけ…。中にこれが見つかるはずだ。

\\A\Processor(_Total)\% Idle Time


これがCPUがIdleになっている割合を示すカウンターだと思われる。\\A\の部分までは「マシンを指定する」ためのものなので、こいつの出力と、その他の出力とのr2を求めればいいわけだ。

とりあえず、全部のファイルについて、このカウンターリストを作る。

% cd $TOP
% mkdir 00nativelist
% pushd 00nativelist
% cat filter.sh
for i in ../native_blg/*.blg; do
j=`echo $i | sed 's/..\/native_blg\///g' | sed 's/\.blg$/.lst/g'`;
relog $i -q -o $j
done
% bash filter.sh
% popd

これで $TOP/00nativelist/ の下に、各 blg ファイルのカウンター一覧ファイルが出来た。今回は Printer Queue は関係なさそうなので、削り落とそう。

% cd $TOP
% mkdir 01PQRlst
% pushd 01PQRlst
% cat filter.sh
for i in ../00nativelst/*.lst; do
j=`echo $i | sed 's/..\/00nativelst\///g'`;
egrep -v '^\\\\\w+\\Print Queue.*\\' $i > $j;
done
% bash filter.sh
% popd


これで、不要な要素をカウンターリストから削る事ができた。

performance monitor の出力に辟易している話

Windowsには Performance Monitor と言うものがある。

システムの評価、できてますか?

身近な定量評価その2 - Windowsのperfmon.exeを使ってみる


なんかを見ると起動の仕方とか、出てくるグラフとかが判ると思う。Unixでいうtopと *stat シリーズを足して貧相なGUIを付け加えたような代物だ。基本的にシステムの異常を監視するときにはよいが、何が悪いのか知ろうというときにはあまり役に立たない。

しかし、世界は広いというか物を知らない人は多いと言うか…
「システムの調子が悪いので見てください」
と言って、こいつが吐く .blg ファイルをゴッソリ送りつけてくるやからがいる。
「取りあえず何をとったらいいのか判らないので、全部とりましたから」
とか言いながら。そんな所にはヒントは無いと言うのに。

perfmonは「全部」取ったりすると 3000項目以上データを取得してくれる。1分に1回取得しても8時間分取得すると480回取得するわけだ。これだけのデータを perfmon の貧相なGUIで見て回るのははっきり言って不可能に近い。

このままではどうしようもないので、Excel にでも食わせて見るか…しかし MS-Office 2003 とかを使っていると、これもまた無理、と判る。Excel-2003は「1シート当たり、256列 65536行」しか受け付けないのだ。3000項目 × 480回のデータ取得ではどうあがいてもシートの中に納まりきらない。

そこで、だ。こういう場合にどうするか、と言うのが今回のお題。とは言っても、このままではノーヒント過ぎるのでヒントを1つもらう。

「調子が悪い、と言うのはどうやって判ったの?」
「あー、なんかCPUの利用率が天井に張り付いてるんですよ…」

なるほど。これで戦略は立った。CPUの利用率と相関が高い項目を見つけ出して、そいつらが何なのか調べればいい、と言うわけだ。

「あ、ちなみに。Aっていう場所では起こってるんですが、Bという場所では起こっていないんで…。両方とも perfmon の出力は取ってあります」

ふむ。ならば、AとBでそれぞれについてCPUの利用率との相関を取って、『Aでは相関が高いが、Bでは低いもの』を見つけ出せばいいって事になるな。



現実的には、『CPUの利用率』というのは「ユーザが利用している」場合と「kernelが利用している」場合の2通りがある。どちらが本命か判らない。こういう場合は CPUの非使用率 、つまり CPU Idle との相関を取ると良い。CPU Idle の値が低い場合に多く動くもの、あるいはリソースとして少なくなるものを見つければいい。

そういう時は R2 というものを取ればいい。あぁ、大丈夫。 R2というのは 0.0 から 1.0 の間の値を取り、『相関が高いほど値がでかくなる不思議な数字』とだけ覚えておけばいい。さらに言うと、注目するべきは 0.25 以上の値をとっているもので、なおかつ 1.0 ではないもの(1.0は直接的過ぎて、ちょっと怪しい)。

もっと詳しい事が知りたければこの本を見て欲しい。


R2 という値自体は、Excelの RSQ() という関数で求める事ができる。どうにかしてデータを Excel にぶち込み、各項目と CPU Idle 値との RSQ() 値を求め、その中から 0.25 以上の数字になっている項目を見つけ出せばいいのだ。

.....

そう。そうして問題が元に戻ってくる。perfmon の blg ファイルから、Excelに入る程度の小さなデータセットを作り出す方法、その中に必ず CPU Idle の値を入れておく方法、そしてできれば各項目との RSQ() 指定までは自動的にする方法…

なるべく苦労せず、ステップ バイ ステップで(つまりいつでも後戻りできる形で)実行するにはどうすればいいのか… その苦闘の歴史を綴ろう。

「tcpのいろは」は取りあえずあきらめますた

ちょっち出直しだ。

いや、描かなくちゃいけない図の多さに辟易しているうちに半年も経つとは…

2008年4月20日

tcpdumpのいろは(2)

ひえぇえええ、すごく間が空いてしまった。

さてさて、tcpdumpだがまずはデータを取得しなくてはいけない。そう、tcpdumpの解析は「まずダンプが取れて何ぼ」なのだ。

で、最初に一言重要な事を

tcpdumpが気楽に取れると思うな

一例を挙げよう。1Gbpsのネットワークカードが1枚あって、そいつが通信しているさまを取得したいとする。どうやるのかはともかく、どういう原理かはともかく、それができるとしよう。

すると、1Gbpsなので、全力全開でデータ通信されると、1Gbps…つまり8秒で1Gbyteのデータが取れる計算になる。もちろん、実際はネットワーク利用効率の問題とか、そもそもパケットをキャプチャするソフトがそんなに早く動作するのか、とかいろいろ要因はあってそこまでのデータ増量にはならないのだが、それでも 10-12秒で1Gbyteの量にはなるのだ。

当たり前だが、HDDもその速度に対応しなくてはいけない。PCのバス(PCI-Expressとか xPCIとか)は、NICの流量とHDDの流量の両方を流せるだけの速度が必要になる。同様にメモリだってかなりの容量がないと瞬間風速に耐えられないし、そのメモリはかなり速くなくてはいけない…。なので、普通はEthernet Switchの高いのを間に噛ませて、パケット duplication (パケットを別のポートにもべたコピーで送る機能がある、高いスイッチを使うということだ)した上で、パケットキャプチャ専用マシンを用意する。

ちゃんとしたプロは*データ取得用に Raid0 を組んだり、SSD(Solid State Disk)つまり RamDiskの化け物を用意したりする。そうしないと意味のあるデータが取れないのだ。HDDの速度に引っ張られてNICの送受信速度が変化してしまい、結果としてtcpdumpを取得しなかった場合には起こっていた現象が起こらなくなった…と言うのでは本末転倒だ。
*) 「ちゃんとしたプロは」と言うということは、「ちゃんとしていないプロ」が世の中に満ち溢れていて、全然役に立たないデータを取ってきて「さぁ、見てくれ」と言う馬鹿者が多い事をも意味する。
つまり、こういう特別な環境が無い場合、disk cache がどうにか対処できる時間程度しかパケットキャプチャはできない事を意味する。かなりリッチな環境でも5分取れたら良い方だろう。5分程度、負荷がかなり軽い環境であっても数Gbyteのデータにはなる。

高速なHDD (USB2接続なんか論外だ)を、きちんとデフラグした上で(できればファイルシステムを再フォーマットするのが望ましい)、キャプチャ専用に準備して欲しい。


今回は別件が忙しいので、とりあえずここまでっ!!

2008年2月29日

tcpdumpのいろは(1)

/.Jのokky の日記に書いたようにtcpdumpの基本についてお客様に説明するためにまとめた。結局その資料は使えなかったわけではあるが、まとめたおかげでイヤンなバグを発見する事ができた。

で、そのままだともったいないので、まとめたものについて再度まとめなおしてここに掲載してみる事にした。もしかしたら他の人の役に立つかもしれないし、最低限でも自分の役には立つから (^^;)。同じ資料を作り直す事自体はともかく、構成から考え直すのはしんどいので。それに運がよければ、ここへのポインタだけ張っておけばOKとかいう手抜きができる場合も…。

さて、この話は大きく3つに分ける事ができる。
  1. tcpdumpの物凄く大雑把な基本原理。どうやってパケットはキャプチャされているのか、どういう制限があるのか、等。ようするに基本データが持っている特性を理解するって事ですね。あと、どういう風に取るべきかとかも。
  2. tcpdumpの結果の解析方法。ただし、今は Wireshark というとても賢いソフトがあるので、そいつに押し付けられる部分は説明しません。
  3. multi sessionを利用するプロトコルを解析する上での注意点。まぁ、よく知られている例として FTP なんぞを。多分、読んでいる分には当たり前の話に聞こえると思いますが、たまに忘れるので。


もうすでに出てきたが、Wireshark程度は扱えるようになっていることを前提とする。どうもWindows版のWiresharkにはフィルターロジック周りに微妙なバグがあるんじゃないか…と思うことがあるが…同じロジックを Linux 上の Wireshark に与えたときと結果が違うし…その辺りはあまり深く突っ込むつもりは無い。細かい話は確か何冊か Wireshark の使い方に関する本が出ているので、そちらを参照する事。ただし、内容に関しては保障しない。まだ読んでいないので。

Wiresharkはものすさまじくメモリ(仮想アドレス)を消費する。Windowsの32bit版ではあまり大きなキャプチャファイルの解析はできないだろう。64bit版OS…Linuxとか…が動いている環境を用意し、その上で 64bit版バイナリの Wireshark を利用する事をお勧めする。editcapとかを使って、キャプチャファイルを一旦分割し、tshark を使ってそれぞれにフィルタをかけ、最後に mergecapを使って結合しなおす…というノウハウは説明しない。ただ、この場合も結局テキストコンソールの環境がリッチな Linux の方が便利なので(でなければ、cygwin とかね)、やはりそれ系の環境を整えておく事をお勧めはする。


Ethernetの動作原理だの、ルーティングアルゴリズムだの、そういうことは説明しない。実はパケットをキャプチャする上でこれらの知識は必須なのだが、対象があまりにも広大すぎる上に、殆どの人にとって使う知識はその中のごく一部だから。
ただし、殆どの人が使う共通の項目がある、という意味ではない。AさんとBさんはそれぞれが全体の 0.1%ぐらいしか必要としないが、重なる部分は殆ど無い、と言う意味だ。これは、必要な知識のうち大半が Cisco とその他、WAN と LAN の違い、などに起因するからで、そんな環境を個人で持っている人は、こんなドキュメントは読む必要すらないからだ。

最後に。「これはうちの話じゃないか?!」と思う人がいるかもしれないが、完全に気のせいです。ネットワーク障害と言うのは、はっきりいうとどこでも、ここでも、そこでも同じように起こるもんです。もう一つついでに言うと、機材が分散しているためでしょうか、複数の機器間で設定が破綻して通信がおかしくなる、というケアレスミス・人的ミスはその道のプロのかたがたの環境の方が頻繁に起こるぐらいです。パケット解析を必要とする状況というのはそんなものです。

では、行ってみることにしましょう。

2008年2月17日

キターーーー

きたきたきた、来ましたよ。

久しぶりの大ヒットの予感。NHKが来年受信料を払って欲しいと思うなら、今年の紅白歌合戦にJEROを出すのはもう必須でございましょう。

出せないなら、受信料を必要としなくなるレベルまで、放送を縮小していいから。つーか、教育テレビとニュースだけで十分だ、君らは。2チャンネルも占有する必要なし。ハイビジョンだの何だのと言った研究もする必要なし。結局君達が研究したアナログハイビジョンはゴミでしかない事が世界的に証明されたわけだし。税金ですでに半分以上支払ってるんだから、それだけで十分まかなえる規模になりなさい。

嫌ならこいつを紅白に出すのジャーーーーー。

2008年2月6日

桃を食べよう・辞書を引こう

そう言っていた人がいた。

明文術という本を読んだ。文章を判りやすくするという視点に立って、どう書くべきか、注意点はどこか、間違いやすいのはどういうパターンかをきちんと書いてある優れた本だ。はっきり言って、ここ3年ぐらいに読んだ「文章の書き方」本の中で No.1 だ。

この本が唯一指摘を忘れているのが辞書を引けという一点だ。

文章が下手な人にはいくつも問題点があるが、その中に
単語の意味を判っていない
と言うのがある。わかりやすく書こうと当人は努力しているつもりなのだろうが、そもそも使っている単語の意味を間違えているのでは、意味は伝わらない。それなのに「忙しい」とかそういう屁理屈をつけて、辞書を引かないのだ。お前が忙しいのは辞書を引かず、間違った意味の単語を間違って使っているせいで、お前がこちらの意図を捻じ曲げるからだと、100億年程 滝壺の底に押し込めて説教してやりたいぐらいだ。

最近こんな事例があった。

お客様の所で障害が起こった。お客様は弊社製品の性だと考えたが、tcpdump等の解析結果とswitch の設定は、お客様側のネットワーク設定の不備を示していた。そこで、条件を変えてテストをする事になった。

弊社側の技術者は
再現性のあるテストを行ってください
と繰り返し書いてきた。しかし、弊社の担当官がこれを
テストをして事象を再現させてください
と勝手に書き直していた。判る人は判るだろうが、前者と後者は言っている事が全然違う。

前者はテスト結果として障害が起こった条件と起こらなかった条件を見比べた場合に、何が主要な制御要因か判るようにしてくれ、と言う意味だ。条件の比較によって弊社製品に問題が無い事が示せるならばそれはそれでよい。逆に弊社製品に問題があると判った場合は再現テストを弊社側で行い、製品内部の何が原因なのか突き止めて障害が起こらないようにする方法を見つける事もできる。そのためにも、条件をきちんと記録し、かつその条件をなんどでもリプレイ可能なものにしてくれ、と言っているわけだ。

しかし、後者は「事象が起これば良い」だけだ。なにが原因なのか分析できる必要は無いし、条件を同時に複数変えてもかまわない。これでは、ネットワークの設定が悪かったのか、弊社製品が悪かったのかすら区別できない。このような条件で実験を繰り返しても、ただ時間を無駄に過ごすだけだし、そのための労力はすべてゴミにしかならない。参考情報が無いに等しいからだ。

「再現する」事と「再現性がある」事の違いを理解していないお客様担当が、後者を前者と勘違いして文章を書き換えた事によって、お客様がどれだけの被害をこうむったか…考えるだけでぞっとする。

意味が一意に決まるように文章を書けたとしても、それだけでは明文にはならない。オリジナルの文章があるならば、その文章が何を言っているのかをきちんと理解できなくてはいけないし、使われている単語についても自分勝手な解釈ではなく国語辞典に基づいた意味で解釈し、また記述しなくてはいけない。私文ならばともかく、お客様相手の、技術的な文章に関しては、用語が示す概念の共有を必要とするので
言葉は変化するものだ
などという寝言も却下だ。そういう寝言は正しい意味をきちんと説明できるようになってから言っていただきたい。

国語辞典では私は広辞苑をお勧めする。広辞苑は不正確だ、とかいろいろ言う人がいるが、この場合は全然構わない。そもそも国語辞典のお勧めを聞いている段階がナンセンスだからだ。

国語辞典について「これ」という意見を持っている人は、私に「お勧め」を尋ねたりはしない。お勧めを尋ねるのは、国語辞典をろくに引いた事がない人だ。そういう人は、まず広辞苑の厚みにビビッてもらう必要がある。ここで岩波国語辞典などのより薄くて軽い辞典に移動したら、その段階で論外。この人は内容ではなく「軽さ」と「薄さ」で辞典を選んだからだ。

国語辞典は「自分が知らない言葉」の意味を調べるためにある。あなたがどんな言葉を知らないのか、どうして国語辞典を引く前に判ると言うのか?? そう考えると「日常よく使う xxxx 語を厳選」など…余計なお世話である。その「厳選からもれた」単語が引きたかったらどうしろというのだ?? 2冊目、3冊目としてならばともかく、1冊目の国語辞典は量こそすべてである。この点において広辞苑は合格だ。無駄に分厚く、無駄に単語量が多い。これにビビッてはならないのだ。

あとは、入手しやすさが決定要因だ。広辞苑はほとんどすべての本屋で手に入れる事ができる。ある一軒で入手できなくても3軒以内で確実に手に入る。普段使わない単語の、枝葉末節な部分に間違いがあるなど、入手のしやすさに比べたら誤差だ。

一旦、辞典を手に入れたら、自分が知っていると思っている単語でも引いてみよう。意外と正しい意味を理解していなかった事に気がつくはずだ。その上で、この本に従って分祖hを書いてみよう。時間が掛かるかもしれないから、まずは短いものからで構わない。案外、簡単に書ける上に、その文書を使った仕事はそれまでのような突っかかりが無く、すいすいと進む事に気がつくはずだ。

それは、仕事がなかなか進まないのはあなたの文章が悪かった証拠、である。