goでwindowsのWMI経由からディスクIOPSを取得してみた

WindowsのWMI経由からDisk IOPSをgoで取得してみました。

# 環境

| 項目    | 内容   |
|:--------|:-------|
| Windows | 2012R2 |
| Go      | 1.9.2  |

# 使ったパッケージ

以下のパッケージを使用しました。

https://github.com/StackExchange/wmi

## ドキュメント

https://godoc.org/github.com/StackExchange/wmi

# ソース

以下は、Cドライブの読み書きIOPSを１秒間隔で取得して表示します。

```go
package main

import (
	"fmt"
	"log"
	"time"

	"github.com/StackExchange/wmi"
)

// Windowsの物理ディスクパフォーマンスデータクラスの構造体
// https://msdn.microsoft.com/en-us/library/aa394308(v=vs.85).aspx
type Win32_PerfRawData_PerfDisk_PhysicalDisk struct {
	AvgDiskBytesPerRead          uint64 // 読み取り操作中にディスクから転送された平均バイト数
	AvgDiskBytesPerRead_Base     uint32 // AvgDiskBytesPerReadの基本値
	AvgDiskBytesPerTransfer      uint64 // 書き込みまたは読み取り操作中にディスクに転送された、またはディスクから転送された平均バイト数
	AvgDiskBytesPerTransfer_Base uint64 // AvgDiskBytesPerTransferの基本値。この値は、発生した操作の累積数を表します
	AvgDiskBytesPerWrite         uint64 // 書き込み操作中にディスクに転送された平均バイト数
	AvgDiskBytesPerWrite_Base    uint64 // AvgDiskBytesPerWriteの基本値。この値は、発生した操作の累積数を表します
	AvgDiskQueueLength           uint64 // サンプル間隔中に選択されたディスクに対してキューに入れられた読み取り要求と書き込み要求の平均数
	AvgDiskReadQueueLength       uint64 // サンプル間隔中に選択したディスクに対してキューに入れられた読み取り要求の平均数
	AvgDiskSecPerRead            uint32 // ディスクからのデータの読み取り操作の平均時間（秒単位）
	AvgDiskSecPerRead_Base       uint32 // AvgDiskSecPerReadの基本値。この値は、発生した操作の累積数を表します
	AvgDiskSecPerTransfer        uint32 // 平均ディスク転送時間（秒）
	AvgDiskSecPerTransfer_Base   uint32 // AvgDiskSecPerTransferの基本値。この値は、発生した操作の累積数を表します
	AvgDiskSecPerWrite           uint32 // ディスクへのデータの書き込み操作の平均時間（秒単位）
	AvgDiskSecPerWrite_Base      uint32 // AvgDiskSecPerWriteの基本値。この値は、発生した操作の累積数を表します
	AvgDiskWriteQueueLength      uint64 // サンプル間隔中に選択されたディスクに対してキューされた書き込み要求の平均数。時間軸は100ナノ秒です
	CurrentDiskQueueLength       uint32 // パフォーマンスデータが収集された時点でディスク上で未処理の要求の数
	DiskBytesPerSec              uint64 // 書き込みまたは読み取り操作中にディスクに/からディスクに転送される速度
	DiskReadBytesPerSec          uint64 // 読み取り操作中にディスクからバイトが転送される速度
	DiskReadsPerSec              uint32 // ディスク上の読み取り操作の割合
	DiskTransfersPerSec          uint32 // ディスク上の読み取りおよび書き込み操作の割合
	DiskWriteBytesPerSec         uint64 // 書き込み操作中にバイトがディスクに転送される速度
	DiskWritesPerSec             uint32 // ディスク上の書き込み操作の割合
	Frequency_Object             uint64 // Timestamp_Objectの頻度（秒単位）。このプロパティはプロバイダによって定義されます
	Frequency_PerfTime           uint64 // Timestamp_Perftimeの頻度（秒単位）。値は、Windows関数QueryPerformanceCounterを呼び出すことで取得できます
	Frequency_Sys100NS           uint64 // Timestamp_Sys100NS（10000000）の頻度（毎秒ティック）
	Name                         string // 統計またはメトリックが分かっているラベル
	PercentDiskReadTime          uint64 // 選択したディスクドライブが読み取り要求を処理している間に経過した時間の割合
	PercentDiskReadTime_Base     uint64 // PercentDiskReadTimeの基本値
	PercentDiskTime              uint64 // 選択したディスクドライブがビジー状態になっている時間のうち、読み取りまたは書き込み要求を処理している時間の割合
	PercentDiskTime_Base         uint64 // PercentDiskTimeの基本値
	PercentDiskWriteTime         uint64 // 選択したディスクドライブが書き込み要求を処理している間に経過した時間の割合
	PercentDiskWriteTime_Base    uint64 // PercentDiskWriteTimeの基本値
	PercentIdleTime              uint64 // ディスクがアイドル状態だったサンプル間隔中の時間の割合
	PercentIdleTime_Base         uint64 // PercentIdleTimeの基本値
	SplitIOPerSec                uint32 // ディスクへのI / Oが複数のI / Oに分割されたレート
	Timestamp_Object             uint64 // オブジェクト定義のタイムスタンプ
	Timestamp_PerfTime           uint64 // 高性能カウンタのタイムスタンプ。Windows関数QueryPerformanceCounterを呼び出すと、値を取得できます
	Timestamp_Sys100NS           uint64 // 100ナノ秒単位のタイムスタンプ値。このプロパティはWin32_Perfから継承されます
}

func getWmiObject(t string) []Win32_PerfRawData_PerfDisk_PhysicalDisk {
	var dst []Win32_PerfRawData_PerfDisk_PhysicalDisk
	q := wmi.CreateQuery(&dst, "Where Name = '"+t+"'")
	if err := wmi.Query(q, &dst); err != nil {
		log.Fatal(err)
	}
	return dst
}

func main() {
	var beforeTotalReadIo uint32
	var beforeTotalWriteIo uint32
	var afterTotalReadIo uint32
	var afterTotalWriteIo uint32

	// ラベル
	readIoLabel := "ReadIO:"
	writeIoLabel := "WriteIO:"

	// CドライブのRead/Write IOを表示する
	targetDrive := "0 C:"
	for {
		wmiResultObject := getWmiObject(targetDrive)
		if beforeTotalReadIo == 0 && beforeTotalWriteIo == 0 {
			for _, v := range wmiResultObject {
				if v.DiskReadsPerSec >= 0 {
					beforeTotalReadIo = v.DiskReadsPerSec
				}
				if v.DiskWritesPerSec >= 0 {
					beforeTotalWriteIo = v.DiskWritesPerSec
				}
			}
		} else {
			for _, v := range wmiResultObject {
				if v.DiskReadsPerSec >= 0 {
					afterTotalReadIo = v.DiskReadsPerSec
				}
				if v.DiskWritesPerSec >= 0 {
					afterTotalWriteIo = v.DiskWritesPerSec
				}
				readIo := afterTotalReadIo - beforeTotalReadIo
				writeIo := afterTotalWriteIo - beforeTotalWriteIo
				fmt.Printf("%s %d\n", readIoLabel, readIo)
				fmt.Printf("%s %d\n", writeIoLabel, writeIo)

				beforeTotalReadIo = afterTotalReadIo
				beforeTotalWriteIo = afterTotalWriteIo
				time.Sleep(1 * time.Second)
			}
		}
	}
}
```

WMIで取得できる値はトータルなので、データ取得前後で差分を出す必要があります。

# 使用例

```powershell
PS C:\Users\Administrator\Desktop\wmi> go build
PS C:\Users\Administrator\Desktop\wmi> .\wmi.exe
ReadIO: 0
WriteIO: 0
ReadIO: 0
WriteIO: 0
ReadIO: 0
WriteIO: 6
(snip)
```