Windows がなんか重いときにコマンドで調べる（WMIC PROCESS）

Linux だと ps, top, vmstat, iostat 等々、有名かつ簡単なコマンドが、みんなに共有されていて、「Linuxだと、調べるのも簡単」のようなことがある。

Windows が好きだったり、利用を余儀なくされる方が、Linux の ps にある種の嫉妬を感じることがある。Linux ではあんなに簡単な事が、Windows では、やり方すらわからない。もちろん、Ctrl+Shift+ESC でタスクマネージャーを表示すれば、同じことはできるのだが、なんでもかんでも自動化が叫ばれる昨今において、タスクマネージャーを表示させるぐらいでは、どうにもならない。

この記事では、WMICコマンドでプロセスの調査(WMIC PROCESS, WMIC PATH Win32_PerfFormattedData_PerfProc_Process)、RubyでWMIC同等のことする方法を扱う。


## Tasklist コマンド

一番候補にあがるのは、tasklist コマンドかもしれない。tasklist コマンドを使った事がなければ、`tasklist /v /fo csv` とコマンドプロンプトから入力してみるとよい。
便利なコマンドだが、ps コマンドに慣れていると、出力内容が、物足りない。
もう少し、ディープな調査ができるコマンドを使いたい。

## PowerShell の Get-Process
PowerShell を使い慣れていれば、ps コマンドがある事を知っているかもしれない。このコマンドは、そこそこ、Linux の ps に似ていて、最低限の目的を達成できるかもしれない。

```
powershell -c ps
```

Linux の ps っぽいが、CPU時間は、パーセントではないし、PID は、プロセス名のすぐ左にあることに注意する。
PowerShell が使えれば、他にもいろいろできるので、そのまま PowerShell の世界に行くのもよいが、
今回は、コマンドプロンプトから、もっと、いろいろなデータを取得したい。


## WMIC コマンド（WMIC PROCESSS）
WMIC コマンドは、あまり使われないが、強力な Windows コマンドで、ほぼ全ての Windows の状態を取得できる。
今回、一番関心があるのは、 ps と同等のコマンドである。これを WMIC がサポートする。コマンドプロンプトから、以下の **WMIC PROCESS** コマンドを入力する。

```
WMIC PROCESS GET /FORMAT:LIST
```

マシンと起動しているプロセスによっては、数秒時間がかかるが、プロセス毎の、実行ファイルの名前やメモリー使用量などがリスト表示される。あまりに、たくさんの情報が表示されるので、これでは、何も見つけられない。
そこで、目的のプロセス（コマンド）がわかっている場合には、以下のようにできる。

```
WMIC PROCSSS WHERE "Name LIKE '%firefox%'" GET /FORMAT:LIST
```

WHERE の後につづく文字列は、SQLのWHEREライクの文字列を指定できる。
ここでは、**WHERE "Name LIKE '%firefox%'"** によって、FireFox のプロセスだけを表示させている。'%firefox%' は、SQLのそれと同様、前後になんらかの文字列で挟まれた firefox という文字列という意味である。
これで少しは、何が得られているか、わかるようになったはずだ。Windows のシステムに不慣れな方に説明しておくと、リスト表示されたもののうち、WorkingSetSize がプロセスが利用しているメモリ量だと考えてよい。

コマンドプロンプトを自分のユーザーで開いているときは、すべてのプロセスが表示されていない。すべてのプロセスを確認するには、管理者権限のプロンプトからコマンドを実行する必要があることに注意する。

さて、ここを眺めるのも興味深いのだが、今回は、重たい（=CPUがたくさん使われている）原因を調べたいのであった。UserModeTime と KernelModeTime が CPU を利用した時間（100ナノ秒単位) を表すので、これを使ってフィルタする。

```
WMIC PROCESS WHERE "UserModeTime > 10000000" GET /FORMAT:LIST
```

これで、CPUのユーザー時間を 1秒以上使われたプロセスがわかる。が、このやり方では、数値の単位も指定しにくいし、以前の値を覚えていないといけないし、KernelModeTime時間も別に使う必要があって、ちょっと使いにくい。

## WMIC で、パフォーマンスデータを得る

Linux の ps のように、プロセス毎の、CPUの利用率を知りたい。それをするには、以下のようにする。

```
WMIC PATH Win32_PerfFormattedData_PerfProc_Process GET /FORMAT:LIST
```

### CPUの利用率でフィルタ

再び、大量のデータが得られるので、ここでも、フィルタしたい。今回は、CPUの利用率でフィルタする。

```
WMIC PATH Win32_PerfFormattedData_PerfProc_Process WHERE "PercentUserTime > 10" GET Name,IDProcess,PercentUserTime /FORMAT:LIST
```

ここでは、**WHERE "PercentUserTime > 10"** によって、ユーザータイム（カーネルではない時間）が10%以上使われているプロセスの名前とPID、その利用率を調べている。
ユーザーとカーネルの時間を合計したCPU利用率として、PercentProcessorTime も利用できる。
カーネル（ドライバー）の時間の利用率から、フィルタしたい場合は、PercentUserTime の代わりに、PercentPrivilegedTime を利用する。

### メモリー利用量でフィルタ

CPUの利用率ではなく、メモリー利用量で調べたいなら、WorkingSet や PoolNonpagedBytes などいくつか興味深いパラメータがある。例えば、

```
WMIC PATH Win32_PerfFormattedData_PerfProc_Process WHERE "WorkingSet > 200000000 AND Name != '_Total'" GET /FORMAT:LIST
```

とすると、WorkingSet が 200MB以上あるプロセスが表示される。ここで、フィルタに `AND Name != '_Total'` を追加して、全プロセス総計を除外している。
また、

```
WMIC PATH Win32_PerfFormattedData_PerfProc_Process WHERE "Name = '_Total'" GET /FORMAT:LIST
```

とすると、プロセス全体の情報が得られるが、表示された結果の中で、WorkingSet は、使用中のメモリー量で、WorkingSetPeak は、利用されうるメモリー量とみなせるので、この値が、搭載RAMよりも大きいなら、メモリー増設を考えるのも、ひとつだろう。


### IO処理数でフィルタ

IOの処理度合いを基準にしたいなら、IOReadOperationsPersec や IOWriteOperationsPersec を WHERE の文字列に利用すればよいだろう。
例えば、

```
WMIC PATH Win32_PerfFormattedData_PerfProc_Process WHERE "IOWriteOperationsPersec > 10 AND Name != '_Total'" GET Name,IDProcess,IOWriteOperationsPersec /FORMAT:LIST
```

のようにすると、毎秒の書き込み数が 10 以上のプロセスが表示できる。

### スレッド毎の表示

もしスレッド毎の情報が必要なら、Win32_PerfFormattedData_PerfProc_Thread を利用する。ただ、スレッド毎の情報には、メモリー利用量やIO処理数などは含まれていないので、スレッドのCPUの利用率を調べるために使うだろう。



また、Win32_PerfFormattedData_PerfProc_Process で、表示されるプロセスの名前は、実行ファイル名ではないので、場合によっては、コマンド名を推測する必要がある。プロセス名としての `_Total` は、全体の値を知りたいのでなければ、実際のプロセスではないので、除外して考える必要がある。
先述のように WHERE のフィルタに `AND Name != '_Total'` を含ませておけば、事前に除外できる。

### コマンドラインからプロセスを特定

知りたいプロセスによっては、ここで困った事になる事がある。CPUの利用率が高いプロセスは、わかったが、他にも同じコマンド名のプロセスがあって、どのプロセスなのかわからないという場合である。IDProcess によって、PIDは、わかっているので、kill （= Windows では Taskkill コマンド) できるが、どういうプロセスかを知っておきたい。
そこで、もう一度、WIMIC PROCESS に戻る。

```
WMIC PROCESS WHERE "ProcessId = 1234" GET Name,ProcessId,CommandLine /FORMAT:LIST
```

WMIC PROCESS の WHERE で ProcessId (PID) に、さきほど表示された IDProcess の値を指定し、その PID の、名前とPID、そして、コマンドラインを取得する。コマンドラインには、指定された引数がずらずらと表示されるので、これをじっくり調べれば、例えば、Javaのプロセスが複数あっても、どのクラスを指定されて起動しているかがわかるので、どの Javaプロセスなのかがはっきりわかる。

コマンドラインの引数から、プロセスを調べたいなら、以下のようにもできる。

```
WMIC PROCESS WHERE "CommandLine LIKE '%hadoop%'" GET Name,ProcessId,CommandLine /FORMAT:LIST
```

ここでは、コマンドラインに、"hadoop" が含まれるプロセスをみつけて表示している。

### ドライブの利用状況を調査

プロセスから少し離れるが、ドライブ（ディスク）の利用状況を調べるには、以下のようにする。

```
WMIC PATH Win32_PerfFormattedData_PerfDisk_LogicalDisk WHERE "Name = 'C:'" GET /FORMAT:LIST
```

ここでは、`Name = 'C:'` でフィルタし、C:ドライブの状況を確認している。表示される項目の中には、PercentDiskTime や AvgDisksecPerTransfer 、AvgDiskQueueLength などがあり、ディスク利用時間率、平均ディスク転送量、平均ディスクキュー長のような値を調べる事ができる。

### メモリーの利用状況を調査

同じく、メモリーでの利用状況を調べるには以下のようにする。

```
WMIC PATH Win32_PerfFormattedData_PerfOS_Memory GET /FORMAT:LIST
```

表示される項目には、AvailableBytes、CommittedBytes、PoolNonpagedBytes などがあり、空きメモリー量、利用メモリー量、スワップできないメモリー量などの、メモリーに対する詳細なデータが得られる。

### PowerShell でパフォーマンスデータを取得する

PowerShell で、このようなパフォーマンスデータを処理したいときは、おおよそ以下のようにできるだろう。

```ps1
gwmi Win32_PerfFormattedData_PerfProc_Process | ? Name -LIKE "firefox" | ft Name,IDProcess,PercentProcessorTime,WorkingSet
```

`gwmi` は、Get-WmiObject の省略形のコマンドで、WMIC PATH で指定したパフォーマンスデータパスを指定できる。
フィルタ文字(`|`) につづく `? Name -LIKE "firefox"` は、WMIC での WHERE と同じ事をしている。CPU利用率でフィルタするなら、`? PercentProcessorTime -GE 10` などのようになるだろう。`ft` は、Format-Table の省略形のコマンドで、出力のうち、指定された項目だけのテーブルを作って表示する。


## WMICをテキスト処理する

ここまで、WMIC の出力を **`/FORMAT:LIST`** によって可読性がよい表示にしてきたが、バッチファイルなどにより処理するときには、やや不便である。その場合、**`/FORMAT:CSV`** を代わりに指定すれば、カンマ区切りで、行ごとに出力されるので、処理しやすくなる。
ただし、WMIC の出力は、Unicode でエンコードされているので、バッチファイルで処理できない事がある。その場合、WMIC の出力を more でフィルタしておく必要がある。つまり、

```
WMIC PROCESSS WHERE "Name LIKE '%firefox%'" GET Name,ProcessId,CommandLine /FORMAT:CSV | more > %TEMP%\wmic.out
```

のような形で、ファイルに出力しておき、あとは、得意な言語で、それを処理すればよい。Ruby や Python ならば簡単に処理できるだろう。


### FOR バッチコマンド

ここでは、バッチファイルで処理する事を考える。
例えば、先ほどの出力から、PIDとコマンドラインを表示するには、以下のようにする。

```
FOR /F "delims=, tokens=2,3,4" %i IN (%TEMP%\wmic.out) DO ECHO %k %i
```

シェルスクリプトには慣れていても、バッチファイルは、あまりに見慣れなくてわけがわからない方もいると思うが、バッチファイルはとても不自由なスクリプトで、ファイルの処理をするために FORコマンドを利用する。 `delims=,` というのは、カンマ区切り、`tokens=2,3,4` は、そのうち、2,3,4 カラム目を取り出すという指定。ここからが、さらに混乱するところだが、%i は、FOR コマンドに限定した変数で、先ほどの 2,3,4 番目のカラムを %i, %j, %k に割り当てるという指定である。WMIC の出力は、その GET の指定順と関係なく常に同じ順番でカラムが並び、この場合、%k に PID、%i にコマンドラインが割り当てされる。そして、DO 以降のコマンドによって、それを表示している。
実際のバッチファイルでの利用としては、DO 以降に `SET PID=%k&SET CMDLINE=%i` のような指定をして、結果を変数に保存するなどする。
以上のように、バッチファイルでできないこともないが、まじめに利用するなら、Ruby などで処理する方がよいだろう。

## Ruby で直接 Win32_PerfFormattedData_PerfProc_Process を処理する

少し調べると Ruby でも、簡単にパフォーマンスデータを取得できるようだ。

```rb
require 'win32ole'

wmi = WIN32OLE.connect 'winmgmts://./root/cimv2'
procs = wmi.InstancesOf 'Win32_PerfFormattedData_PerfProc_Process'
procs.each do |n|
  puts "#{n.Name} #{n.IDProcess} #{n.PercentProcessorTime}"
end
```

WMIC のようにフィルタする。

```rb
require 'win32ole'

wmi = WIN32OLE.connect 'winmgmts://./root/cimv2'
procs = wmi.ExecQuery 'SELECT * FROM Win32_PerfFormattedData_PerfProc_Process WHERE PercentProcessorTime > 10'
procs.each do |n|
  puts "#{n.Name} #{n.IDProcess} #{n.PercentProcessorTime}"
end
```



## まとめ
WMIC コマンドは、調べると、かなりいろいろなことができるので、Windows 上で、コマンドで調査をしたいと考えいてるときには、調べる価値のあるコマンドだろうと思う。
WMIC PROCESS は、その中でも、もっとも利用する機会が高いものかと思う。
パフォーマンスデータを調べるために WMIC PATH Win32_PerfFormattedData_PerfProc_Process を利用した。これは、パフォーマンスカウンターをコマンドで調べるのに役に立つ一例である。少々、コマンドラインが長くなるが、Windows のプロセスを追跡するなら知っておいてよいだろう。
自動化を考えるなら、Ruby でも簡単に処理できる。
ほかに、Typeperf コマンドというコマンドがあり、Linux の top のようなことを考えているときには、調べる価値があるだろう。