2009年3月1日

解答を書いておく -2- 部分解

まずは request3 から


request3 は比較的簡単だ。sed でも awk でもできる。

sed ならばこう:
sed 's/^/# /g' /proc/slabinfo


awk ならばこう:
awk '{ print "# " $0; }' /proc/slabinfo


awk の場合は正規表現を必要としない分早いかもしれない。でもそもそも sed の方がプログラムとして小さいのでこちらの方が早いかもしれない。どちらがいいのかはよく判らない。

つぎに request1


/proc/meminfo の各行から数字の部分を切り出すだけなら
awk '{ print $2; }' /proc/meminfo

だけで済む。しかしここには悪魔が潜んでいる。

難しいポイントは3つ。

  1. 時刻を出力する

  2. /proc/meminfo の中身の順序がカーネル依存である

  3. /proc/meminfo の Slab フィールドの値は、request2 でも使う



時刻を出力するには、最初に date 出力を getline で取得すればよい。
BEGIN {
"date +"%Y/%m/%d %H:%M:%S" | getline DATEVAL;
DATETAG="date";
}


/proc/meminfo の中身自体は適当なバージョンにあわせるとしよう。':' よりも左側の文字列を取ってくるには:
sed 's/^\(.*\):.*$/\1/g' /proc/meminfo

とやればよい。結果として
MemTotal
MemFree
Buffers
Cached
SwapCached
:
のような出力が得られる。これをベースに、<各エントリ名>TAG という変数と、<各エントリ名>VALという変数を作ろう。こんな感じだ:
for i in $(sed 's/^\(.*\):.*$/\1/g' /proc/meminfo ); do
echo "/^${i}:/ { ${i}VAL = \$2; ${i}TAG=\"${i}\"; }"
done

結果はこんな感じになる。
/^MemTotal:/ { MemTotalVAL = $2; MemTotalTAG="MemTotal"; }
/^MemFree:/ { MemFreeVAL = $2; MemFreeTAG="MemFree"; }
/^Buffers:/ { BuffersVAL = $2; BuffersTAG="Buffers"; }
/^Cached:/ { CachedVAL = $2; CachedTAG="Cached"; }
/^SwapCached:/ { SwapCachedVAL = $2; SwapCachedTAG="SwapCached"; }
:

各行が awk スクリプトにおける「対応する値をとってくるためのスクリプト」になっている。
同様にして、出力するコードを吐かせる。
for i in $(sed 's/^\(.*\):.*$/\1/g' meminfo ); do
echo " printf(\"\\\"\" ${i}VAL \"\\\"\\t\" );"
done
結果はこんな感じ。
 printf("\"" MemTotalVAL "\"\t" );
printf("\"" MemFreeVAL "\"\t" );
printf("\"" BuffersVAL "\"\t" );
printf("\"" CachedVAL "\"\t" );
printf("\"" SwapCachedVAL "\"\t" );
:
あと、「VAL」の部分を「TAG」に変えたものも用意する。

TAGをいつ出力するのか、その制御は bash 側からすることにしよう。PRINTTAGという変数を用意して、これが 0 だったら TAG を出さない。それ以外だったら出す。

printf の最後の要素だけは \t を \n に直す。また、date を出力するコードを加える。

以上を全部行うとこうなる:
BEGIN {
"date +'%Y/%m/%d %H:%M:%S'" | getline DATEVAL;
DATETAG="date";
}

/^MemTotal:/ { MemTotalVAL = $2; MemTotalTAG="MemTotal"; }
/^MemFree:/ { MemFreeVAL = $2; MemFreeTAG="MemFree"; }
/^Buffers:/ { BuffersVAL = $2; BuffersTAG="Buffers"; }
/^Cached:/ { CachedVAL = $2; CachedTAG="Cached"; }
/^SwapCached:/ { SwapCachedVAL = $2; SwapCachedTAG="SwapCached"; }
/^Active:/ { ActiveVAL = $2; ActiveTAG="Active"; }
/^Inactive:/ { InactiveVAL = $2; InactiveTAG="Inactive"; }
/^HighTotal:/ { HighTotalVAL = $2; HighTotalTAG="HighTotal"; }
/^HighFree:/ { HighFreeVAL = $2; HighFreeTAG="HighFree"; }
/^LowTotal:/ { LowTotalVAL = $2; LowTotalTAG="LowTotal"; }
/^LowFree:/ { LowFreeVAL = $2; LowFreeTAG="LowFree"; }
/^SwapTotal:/ { SwapTotalVAL = $2; SwapTotalTAG="SwapTotal"; }
/^SwapFree:/ { SwapFreeVAL = $2; SwapFreeTAG="SwapFree"; }
/^Dirty:/ { DirtyVAL = $2; DirtyTAG="Dirty"; }
/^Writeback:/ { WritebackVAL = $2; WritebackTAG="Writeback"; }
/^Mapped:/ { MappedVAL = $2; MappedTAG="Mapped"; }
/^Slab:/ { SlabVAL = $2; SlabTAG="Slab"; }
/^CommitLimit:/ { CommitLimitVAL = $2; CommitLimitTAG="CommitLimit"; }
/^Committed_AS:/ { Committed_ASVAL = $2; Committed_ASTAG="Committed_AS"; }
/^PageTables:/ { PageTablesVAL = $2; PageTablesTAG="PageTables"; }
/^VmallocTotal:/ { VmallocTotalVAL = $2; VmallocTotalTAG="VmallocTotal"; }
/^VmallocUsed:/ { VmallocUsedVAL = $2; VmallocUsedTAG="VmallocUsed"; }
/^VmallocChunk:/ { VmallocChunkVAL = $2; VmallocChunkTAG="VmallocChunk"; }
/^HugePages_Total:/ { HugePages_TotalVAL = $2; HugePages_TotalTAG="HugePages_Total"; }
/^HugePages_Free:/ { HugePages_FreeVAL = $2; HugePages_FreeTAG="HugePages_Free"; }
/^Hugepagesize:/ { HugepagesizeVAL = $2; HugepagesizeTAG="Hugepagesize"; }

END {
if ( PRINTTAG != 0 ) {
printf("\"" DATETAG "\"\t" );
printf("\"" MemTotalTAG "\"\t" );
printf("\"" MemFreeTAG "\"\t" );
printf("\"" BuffersTAG "\"\t" );
printf("\"" CachedTAG "\"\t" );
printf("\"" SwapCachedTAG "\"\t" );
printf("\"" ActiveTAG "\"\t" );
printf("\"" InactiveTAG "\"\t" );
printf("\"" HighTotalTAG "\"\t" );
printf("\"" HighFreeTAG "\"\t" );
printf("\"" LowTotalTAG "\"\t" );
printf("\"" LowFreeTAG "\"\t" );
printf("\"" SwapTotalTAG "\"\t" );
printf("\"" SwapFreeTAG "\"\t" );
printf("\"" DirtyTAG "\"\t" );
printf("\"" WritebackTAG "\"\t" );
printf("\"" MappedTAG "\"\t" );
printf("\"" SlabTAG "\"\t" );
printf("\"" CommitLimitTAG "\"\t" );
printf("\"" Committed_ASTAG "\"\t" );
printf("\"" PageTablesTAG "\"\t" );
printf("\"" VmallocTotalTAG "\"\t" );
printf("\"" VmallocUsedTAG "\"\t" );
printf("\"" VmallocChunkTAG "\"\t" );
printf("\"" HugePages_TotalTAG "\"\t" );
printf("\"" HugePages_FreeTAG "\"\t" );
printf("\"" HugepagesizeTAG "\"\n" );
}

printf("\"" DATEVAL "\"\t" );
printf("\"" MemTotalVAL "\"\t" );
printf("\"" MemFreeVAL "\"\t" );
printf("\"" BuffersVAL "\"\t" );
printf("\"" CachedVAL "\"\t" );
printf("\"" SwapCachedVAL "\"\t" );
printf("\"" ActiveVAL "\"\t" );
printf("\"" InactiveVAL "\"\t" );
printf("\"" HighTotalVAL "\"\t" );
printf("\"" HighFreeVAL "\"\t" );
printf("\"" LowTotalVAL "\"\t" );
printf("\"" LowFreeVAL "\"\t" );
printf("\"" SwapTotalVAL "\"\t" );
printf("\"" SwapFreeVAL "\"\t" );
printf("\"" DirtyVAL "\"\t" );
printf("\"" WritebackVAL "\"\t" );
printf("\"" MappedVAL "\"\t" );
printf("\"" SlabVAL "\"\t" );
printf("\"" CommitLimitVAL "\"\t" );
printf("\"" Committed_ASVAL "\"\t" );
printf("\"" PageTablesVAL "\"\t" );
printf("\"" VmallocTotalVAL "\"\t" );
printf("\"" VmallocUsedVAL "\"\t" );
printf("\"" VmallocChunkVAL "\"\t" );
printf("\"" HugePages_TotalVAL "\"\t" );
printf("\"" HugePages_FreeVAL "\"\t" );
printf("\"" HugepagesizeVAL "\"\n" );
}

このスクリプト全体を、meminfo1.awk として保存する。で、/proc/meminfo を食わせてみるとこんな感じになる。
$ gawk -v PRINTTAG=1 -f meminfo1.awk /proc/meminfo 
"date" "MemTotal" "MemFree" "Buffers" "Cached" "SwapCached" "Active" "Inactive" "HighTotal" "HighFree" "LowTotal" "LowFree" "SwapTotal" "SwapFree" "Dirty" "Writeback" "Mapped" "Slab" "CommitLimit" "Committed_AS" "PageTables" "VmallocTotal" "VmallocUsed" "VmallocChunk" "HugePages_Total" "HugePages_Free" "Hugepagesize"
"2009/03/01 23:00:00" "1001008" "200708" "43400" "395740" "0" "557556" "178624" "97216" "140" "903792" "200568" "2096472" "2096472" "224" "0" "356492" "47820" "2596976" "689048" "6192" "114680" "4560" "107264" "0" "0" "2048"
$ gawk -v PRINTTAG=0 -f meminfo1.awk /proc/meminfo
"2009/03/01 23:00:37" "1001008" "200708" "43400" "395740" "0" "557556" "178624" "97216" "140" "903792" "200568" "2096472" "2096472" "224" "0" "356492" "47820" "2596976" "689048" "6192" "114680" "4560" "107264" "0" "0" "2048"
$ gawk -f meminfo1.awk /proc/meminfo
"2009/03/01 23:00:37" "1001008" "200708" "43400" "395740" "0" "557556" "178624" "97216" "140" "903792" "200568" "2096472" "2096472" "224" "0" "356492" "47820" "2596976" "689048" "6192" "114680" "4560" "107264" "0" "0" "2048"

-v を使って PRINTTAG 変数をスクリプト外部から制御した。最後の -v を指定しない例は、ようするに「デフォルトの挙動」と言う奴になる。

やれこれで一安心…ではない。Slab の値だけ、別途別パスで外に出してやる必要がある。幸い、stderr という文明の利器があるのでこれを使う。
    print SlabVAL > "/dev/stderr";

これを END {} セクションの最後に加えるのだ。そうすると Slabの数字の部分だけが stderr で取り出せる。

request1に対する最終 meminfo.awk スクリプトはこうなる:
BEGIN {
"date +'%Y/%m/%d %H:%M:%S'" | getline DATEVAL;
DATETAG="date";
}

/^MemTotal:/ { MemTotalVAL = $2; MemTotalTAG="MemTotal"; }
/^MemFree:/ { MemFreeVAL = $2; MemFreeTAG="MemFree"; }
/^Buffers:/ { BuffersVAL = $2; BuffersTAG="Buffers"; }
/^Cached:/ { CachedVAL = $2; CachedTAG="Cached"; }
/^SwapCached:/ { SwapCachedVAL = $2; SwapCachedTAG="SwapCached"; }
/^Active:/ { ActiveVAL = $2; ActiveTAG="Active"; }
/^Inactive:/ { InactiveVAL = $2; InactiveTAG="Inactive"; }
/^HighTotal:/ { HighTotalVAL = $2; HighTotalTAG="HighTotal"; }
/^HighFree:/ { HighFreeVAL = $2; HighFreeTAG="HighFree"; }
/^LowTotal:/ { LowTotalVAL = $2; LowTotalTAG="LowTotal"; }
/^LowFree:/ { LowFreeVAL = $2; LowFreeTAG="LowFree"; }
/^SwapTotal:/ { SwapTotalVAL = $2; SwapTotalTAG="SwapTotal"; }
/^SwapFree:/ { SwapFreeVAL = $2; SwapFreeTAG="SwapFree"; }
/^Dirty:/ { DirtyVAL = $2; DirtyTAG="Dirty"; }
/^Writeback:/ { WritebackVAL = $2; WritebackTAG="Writeback"; }
/^Mapped:/ { MappedVAL = $2; MappedTAG="Mapped"; }
/^Slab:/ { SlabVAL = $2; SlabTAG="Slab"; }
/^CommitLimit:/ { CommitLimitVAL = $2; CommitLimitTAG="CommitLimit"; }
/^Committed_AS:/ { Committed_ASVAL = $2; Committed_ASTAG="Committed_AS"; }
/^PageTables:/ { PageTablesVAL = $2; PageTablesTAG="PageTables"; }
/^VmallocTotal:/ { VmallocTotalVAL = $2; VmallocTotalTAG="VmallocTotal"; }
/^VmallocUsed:/ { VmallocUsedVAL = $2; VmallocUsedTAG="VmallocUsed"; }
/^VmallocChunk:/ { VmallocChunkVAL = $2; VmallocChunkTAG="VmallocChunk"; }
/^HugePages_Total:/ { HugePages_TotalVAL = $2; HugePages_TotalTAG="HugePages_Total"; }
/^HugePages_Free:/ { HugePages_FreeVAL = $2; HugePages_FreeTAG="HugePages_Free"; }
/^Hugepagesize:/ { HugepagesizeVAL = $2; HugepagesizeTAG="Hugepagesize"; }

END {
if ( PRINTTAG != 0 ) {
printf("\"" DATETAG "\"\t" );
printf("\"" MemTotalTAG "\"\t" );
printf("\"" MemFreeTAG "\"\t" );
printf("\"" BuffersTAG "\"\t" );
printf("\"" CachedTAG "\"\t" );
printf("\"" SwapCachedTAG "\"\t" );
printf("\"" ActiveTAG "\"\t" );
printf("\"" InactiveTAG "\"\t" );
printf("\"" HighTotalTAG "\"\t" );
printf("\"" HighFreeTAG "\"\t" );
printf("\"" LowTotalTAG "\"\t" );
printf("\"" LowFreeTAG "\"\t" );
printf("\"" SwapTotalTAG "\"\t" );
printf("\"" SwapFreeTAG "\"\t" );
printf("\"" DirtyTAG "\"\t" );
printf("\"" WritebackTAG "\"\t" );
printf("\"" MappedTAG "\"\t" );
printf("\"" SlabTAG "\"\t" );
printf("\"" CommitLimitTAG "\"\t" );
printf("\"" Committed_ASTAG "\"\t" );
printf("\"" PageTablesTAG "\"\t" );
printf("\"" VmallocTotalTAG "\"\t" );
printf("\"" VmallocUsedTAG "\"\t" );
printf("\"" VmallocChunkTAG "\"\t" );
printf("\"" HugePages_TotalTAG "\"\t" );
printf("\"" HugePages_FreeTAG "\"\t" );
printf("\"" HugepagesizeTAG "\"\n" );
}

printf("\"" DATEVAL "\"\t" );
printf("\"" MemTotalVAL "\"\t" );
printf("\"" MemFreeVAL "\"\t" );
printf("\"" BuffersVAL "\"\t" );
printf("\"" CachedVAL "\"\t" );
printf("\"" SwapCachedVAL "\"\t" );
printf("\"" ActiveVAL "\"\t" );
printf("\"" InactiveVAL "\"\t" );
printf("\"" HighTotalVAL "\"\t" );
printf("\"" HighFreeVAL "\"\t" );
printf("\"" LowTotalVAL "\"\t" );
printf("\"" LowFreeVAL "\"\t" );
printf("\"" SwapTotalVAL "\"\t" );
printf("\"" SwapFreeVAL "\"\t" );
printf("\"" DirtyVAL "\"\t" );
printf("\"" WritebackVAL "\"\t" );
printf("\"" MappedVAL "\"\t" );
printf("\"" SlabVAL "\"\t" );
printf("\"" CommitLimitVAL "\"\t" );
printf("\"" Committed_ASVAL "\"\t" );
printf("\"" PageTablesVAL "\"\t" );
printf("\"" VmallocTotalVAL "\"\t" );
printf("\"" VmallocUsedVAL "\"\t" );
printf("\"" VmallocChunkVAL "\"\t" );
printf("\"" HugePages_TotalVAL "\"\t" );
printf("\"" HugePages_FreeVAL "\"\t" );
printf("\"" HugepagesizeVAL "\"\n" );

print SlabVAL > "/dev/stderr";
}

STDOUT はログファイルに出力させる。その後 STDERR を stdout にリダイレクトして、変数に食わせてやる。実行する側のシェルはこんな感じ:
SLAB=$(( awk -f meminfo.awk /proc/meminfo >> $LOGFILE ) 2>&1 )

これで SLAB 変数には Slab: の数字のフィールドが入り、かつ全部の出力が $LOGFILE に出力される。