1
0
mirror of https://github.com/shirou/gopsutil.git synced 2025-04-26 13:48:59 +08:00

[process][windows] Fix #846 use win32 API in disk.IOCounters() instead of slow WMI call returning wrong values

This commit is contained in:
Lomanic 2020-03-01 23:30:05 +01:00
parent a3b23c5ccf
commit f98100720e

View File

@ -5,6 +5,8 @@ package disk
import ( import (
"bytes" "bytes"
"context" "context"
"fmt"
"syscall"
"unsafe" "unsafe"
"github.com/shirou/gopsutil/internal/common" "github.com/shirou/gopsutil/internal/common"
@ -23,18 +25,24 @@ var (
FileReadOnlyVolume = int64(524288) // 0x00080000 FileReadOnlyVolume = int64(524288) // 0x00080000
) )
type Win32_PerfFormattedData struct { // diskPerformance is an equivalent representation of DISK_PERFORMANCE in the Windows API.
Name string // https://docs.microsoft.com/fr-fr/windows/win32/api/winioctl/ns-winioctl-disk_performance
AvgDiskBytesPerRead uint64 type diskPerformance struct {
AvgDiskBytesPerWrite uint64 BytesRead int64
AvgDiskReadQueueLength uint64 BytesWritten int64
AvgDiskWriteQueueLength uint64 ReadTime int64
AvgDisksecPerRead uint64 WriteTime int64
AvgDisksecPerWrite uint64 IdleTime int64
ReadCount uint32
WriteCount uint32
QueueDepth uint32
SplitCount uint32
QueryTime int64
StorageDeviceNumber uint32
StorageManagerName [8]uint16
alignmentPadding uint32 // necessary for 32bit support, see https://github.com/elastic/beats/pull/16553
} }
const WaitMSec = 500
func Usage(path string) (*UsageStat, error) { func Usage(path string) (*UsageStat, error) {
return UsageWithContext(context.Background(), path) return UsageWithContext(context.Background(), path)
} }
@ -136,31 +144,52 @@ func IOCounters(names ...string) (map[string]IOCountersStat, error) {
} }
func IOCountersWithContext(ctx context.Context, names ...string) (map[string]IOCountersStat, error) { func IOCountersWithContext(ctx context.Context, names ...string) (map[string]IOCountersStat, error) {
ret := make(map[string]IOCountersStat, 0) // https://github.com/giampaolo/psutil/blob/544e9daa4f66a9f80d7bf6c7886d693ee42f0a13/psutil/arch/windows/disk.c#L83
var dst []Win32_PerfFormattedData drivemap := make(map[string]IOCountersStat, 0)
var diskPerformance diskPerformance
err := common.WMIQueryWithContext(ctx, "SELECT * FROM Win32_PerfFormattedData_PerfDisk_LogicalDisk", &dst) lpBuffer := make([]uint16, 254)
lpBufferLen, err := windows.GetLogicalDriveStrings(uint32(len(lpBuffer)), &lpBuffer[0])
if err != nil { if err != nil {
return ret, err return drivemap, err
} }
for _, d := range dst { for _, v := range lpBuffer[:lpBufferLen] {
if len(d.Name) > 3 { // not get _Total or Harddrive if 'A' <= v && v <= 'Z' {
continue path := string(v) + ":"
} typepath, _ := windows.UTF16PtrFromString(path)
typeret := windows.GetDriveType(typepath)
if typeret == 0 {
return drivemap, windows.GetLastError()
}
if typeret != windows.DRIVE_FIXED {
continue
}
szDevice := fmt.Sprintf(`\\.\%s`, path)
const IOCTL_DISK_PERFORMANCE = 0x70020
h, err := windows.CreateFile(syscall.StringToUTF16Ptr(szDevice), 0, windows.FILE_SHARE_READ|windows.FILE_SHARE_WRITE, nil, windows.OPEN_EXISTING, 0, 0)
if err != nil {
if err == windows.ERROR_FILE_NOT_FOUND {
continue
}
return drivemap, err
}
defer windows.CloseHandle(h)
if len(names) > 0 && !common.StringsHas(names, d.Name) { var diskPerformanceSize uint32
continue err = windows.DeviceIoControl(h, IOCTL_DISK_PERFORMANCE, nil, 0, (*byte)(unsafe.Pointer(&diskPerformance)), uint32(unsafe.Sizeof(diskPerformance)), &diskPerformanceSize, nil)
} if err != nil {
return drivemap, err
ret[d.Name] = IOCountersStat{ }
Name: d.Name, drivemap[path] = IOCountersStat{
ReadCount: uint64(d.AvgDiskReadQueueLength), ReadBytes: uint64(diskPerformance.BytesRead),
WriteCount: d.AvgDiskWriteQueueLength, WriteBytes: uint64(diskPerformance.BytesWritten),
ReadBytes: uint64(d.AvgDiskBytesPerRead), ReadCount: uint64(diskPerformance.ReadCount),
WriteBytes: uint64(d.AvgDiskBytesPerWrite), WriteCount: uint64(diskPerformance.WriteCount),
ReadTime: d.AvgDisksecPerRead, ReadTime: uint64(diskPerformance.ReadTime / 10000 / 1000), // convert to ms: https://github.com/giampaolo/psutil/issues/1012
WriteTime: d.AvgDisksecPerWrite, WriteTime: uint64(diskPerformance.WriteTime / 10000 / 1000),
Name: path,
}
} }
} }
return ret, nil return drivemap, nil
} }