From 64357f04e785f61a86d993e486b0f9be54d44cb7 Mon Sep 17 00:00:00 2001 From: WAKAYAMA shirou Date: Mon, 20 Apr 2015 00:05:31 +0900 Subject: [PATCH] use StackExchange/wmi instead of invoking wmic process. Note: This may not work on some old Windows XP. --- common/common_windows.go | 34 ---------- cpu/cpu_windows.go | 72 ++++++++++---------- host/host_windows.go | 24 +++---- process/process_windows.go | 132 ++++++++++++++++++++++++------------- 4 files changed, 134 insertions(+), 128 deletions(-) diff --git a/common/common_windows.go b/common/common_windows.go index 4cc0ba9ea..d727378cb 100644 --- a/common/common_windows.go +++ b/common/common_windows.go @@ -3,9 +3,6 @@ package common import ( - "fmt" - "os/exec" - "strings" "syscall" "unsafe" ) @@ -75,37 +72,6 @@ func BytePtrToString(p *uint8) string { return string(a[:i]) } -// exec wmic and return lines splited by newline -func GetWmic(target string, query ...string) ([][]string, error) { - cmd := []string{target} - cmd = append(cmd, query...) - cmd = append(cmd, "/format:csv") - out, err := exec.Command("wmic", cmd...).Output() - if err != nil { - return [][]string{}, err - } - lines := strings.Split(string(out), "\r\r\n") - if len(lines) <= 2 { - return [][]string{}, fmt.Errorf("wmic result malformed: [%q]", lines) - } - var ret [][]string - for _, l := range lines[2:] { // skip first two lines - var lr []string - for _, r := range strings.Split(l, ",") { - if r == "" { - continue - } - lr = append(lr, strings.TrimSpace(r)) - } - if len(lr) != 0 { - ret = append(ret, lr) - } - } - - return ret, nil - -} - // CounterInfo // copied from https://github.com/mackerelio/mackerel-agent/ type CounterInfo struct { diff --git a/cpu/cpu_windows.go b/cpu/cpu_windows.go index c5f8c1d54..808bbb380 100644 --- a/cpu/cpu_windows.go +++ b/cpu/cpu_windows.go @@ -3,14 +3,28 @@ package cpu import ( - "strconv" + "fmt" "syscall" "time" "unsafe" + "github.com/StackExchange/wmi" + common "github.com/shirou/gopsutil/common" ) +type Win32_Processor struct { + LoadPercentage uint16 + L2CacheSize uint32 + Family uint16 + Manufacturer string + Name string + NumberOfLogicalProcessors uint32 + ProcessorId string + Stepping *string + MaxClockSpeed uint32 +} + // TODO: Get percpu func CPUTimes(percpu bool) ([]CPUTimesStat, error) { var ret []CPUTimesStat @@ -43,59 +57,41 @@ func CPUTimes(percpu bool) ([]CPUTimesStat, error) { func CPUInfo() ([]CPUInfoStat, error) { var ret []CPUInfoStat - lines, err := common.GetWmic("cpu", "get", "Family,L2CacheSize,Manufacturer,Name,NumberOfLogicalProcessors,ProcessorId,Stepping") + var dst []Win32_Processor + q := wmi.CreateQuery(&dst, "") + err := wmi.Query(q, &dst) if err != nil { return ret, err } - for i, t := range lines { - cache, err := strconv.Atoi(t[2]) - if err != nil { - cache = 0 - } - cores, err := strconv.Atoi(t[5]) - if err != nil { - cores = 0 - } - stepping := 0 - if len(t) > 7 { - stepping, err = strconv.Atoi(t[6]) - if err != nil { - stepping = 0 - } - } + for i, l := range dst { cpu := CPUInfoStat{ CPU: int32(i), - Family: t[1], - CacheSize: int32(cache), - VendorID: t[3], - ModelName: t[4], - Cores: int32(cores), - PhysicalID: t[6], - Stepping: int32(stepping), + Family: fmt.Sprintf("%d", l.Family), + CacheSize: int32(l.L2CacheSize), + VendorID: l.Manufacturer, + ModelName: l.Name, + Cores: int32(l.NumberOfLogicalProcessors), + PhysicalID: l.ProcessorId, + Mhz: float64(l.MaxClockSpeed), Flags: []string{}, } ret = append(ret, cpu) } + return ret, nil } func CPUPercent(interval time.Duration, percpu bool) ([]float64, error) { - ret := []float64{} - - lines, err := common.GetWmic("cpu", "get", "loadpercentage") + var ret []float64 + var dst []Win32_Processor + q := wmi.CreateQuery(&dst, "") + err := wmi.Query(q, &dst) if err != nil { return ret, err } - for _, l := range lines { - if len(l) < 2 { - continue - } - p, err := strconv.Atoi(l[1]) - if err != nil { - p = 0 - } - // but windows can only get one percent. - ret = append(ret, float64(p)/100.0) + for _, l := range dst { + // use range but windows can only get one percent. + ret = append(ret, float64(l.LoadPercentage)/100.0) } return ret, nil } diff --git a/host/host_windows.go b/host/host_windows.go index cfbdd69e5..052faf803 100644 --- a/host/host_windows.go +++ b/host/host_windows.go @@ -3,11 +3,11 @@ package host import ( - "fmt" "os" - "strings" "time" + "github.com/StackExchange/wmi" + common "github.com/shirou/gopsutil/common" process "github.com/shirou/gopsutil/process" ) @@ -16,6 +16,10 @@ var ( procGetSystemTimeAsFileTime = common.Modkernel32.NewProc("GetSystemTimeAsFileTime") ) +type Win32_OperatingSystem struct { + LastBootUpTime time.Time +} + func HostInfo() (*HostInfoStat, error) { ret := &HostInfoStat{} hostname, err := os.Hostname() @@ -40,19 +44,15 @@ func HostInfo() (*HostInfoStat, error) { } func BootTime() (uint64, error) { - lines, err := common.GetWmic("os", "get", "LastBootUpTime") - if err != nil { - return 0, err - } - if len(lines) == 0 || len(lines[0]) != 2 { - return 0, fmt.Errorf("could not get LastBootUpTime") - } - format := "20060102150405" - t, err := time.Parse(format, strings.Split(lines[0][1], ".")[0]) + now := time.Now() + + var dst []Win32_OperatingSystem + q := wmi.CreateQuery(&dst, "") + err := wmi.Query(q, &dst) if err != nil { return 0, err } - now := time.Now() + t := dst[0].LastBootUpTime.Local() return uint64(now.Sub(t).Seconds()), nil } diff --git a/process/process_windows.go b/process/process_windows.go index f31fe7795..1c02932ca 100644 --- a/process/process_windows.go +++ b/process/process_windows.go @@ -5,10 +5,11 @@ package process import ( "errors" "fmt" - "strconv" "syscall" + "time" "unsafe" + "github.com/StackExchange/wmi" "github.com/shirou/w32" common "github.com/shirou/gopsutil/common" @@ -43,6 +44,48 @@ type MemoryInfoExStat struct { type MemoryMapsStat struct { } +type Win32_Process struct { + Name string + ExecutablePath *string + CommandLine *string + Priority uint32 + CreationDate *time.Time + ProcessId uint32 + ThreadCount uint32 + + /* + CSCreationClassName string + CSName string + Caption *string + CreationClassName string + Description *string + ExecutionState *uint16 + HandleCount uint32 + KernelModeTime uint64 + MaximumWorkingSetSize *uint32 + MinimumWorkingSetSize *uint32 + OSCreationClassName string + OSName string + OtherOperationCount uint64 + OtherTransferCount uint64 + PageFaults uint32 + PageFileUsage uint32 + ParentProcessId uint32 + PeakPageFileUsage uint32 + PeakVirtualSize uint64 + PeakWorkingSetSize uint32 + PrivatePageCount uint64 + ReadOperationCount uint64 + ReadTransferCount uint64 + Status *string + TerminationDate *time.Time + UserModeTime uint64 + WorkingSetSize uint64 + WriteOperationCount uint64 + WriteTransferCount uint64 + */ +} + func Pids() ([]int32, error) { var ret []int32 @@ -66,38 +109,43 @@ func (p *Process) Ppid() (int32, error) { return ret, nil } func (p *Process) Name() (string, error) { - query := fmt.Sprintf("ProcessId = %d", p.Pid) - lines, err := common.GetWmic("process", "where", query, "get", "Name") + var dst []Win32_Process + query := fmt.Sprintf("WHERE ProcessId = %d", p.Pid) + q := wmi.CreateQuery(&dst, query) + err := wmi.Query(q, &dst) if err != nil { return "", err } - if len(lines) == 0 { + if len(dst) != 1 { return "", fmt.Errorf("could not get Name") } - return lines[0][1], nil + return dst[0].Name, nil } func (p *Process) Exe() (string, error) { - query := fmt.Sprintf("ProcessId = %d", p.Pid) - lines, err := common.GetWmic("process", "where", query, "get", "ExecutablePath") + var dst []Win32_Process + query := fmt.Sprintf("WHERE ProcessId = %d", p.Pid) + q := wmi.CreateQuery(&dst, query) + err := wmi.Query(q, &dst) if err != nil { return "", err } - if len(lines) == 0 { + if len(dst) != 1 { return "", fmt.Errorf("could not get ExecutablePath") } - return lines[0][1], nil + return *dst[0].ExecutablePath, nil } func (p *Process) Cmdline() (string, error) { - query := fmt.Sprintf("ProcessId = %d", p.Pid) - lines, err := common.GetWmic("process", "where", query, "get", "CommandLine") + var dst []Win32_Process + query := fmt.Sprintf("WHERE ProcessId = %d", p.Pid) + q := wmi.CreateQuery(&dst, query) + err := wmi.Query(q, &dst) if err != nil { return "", err } - if len(lines) == 0 { - return "", fmt.Errorf("could not get command line") + if len(dst) != 1 { + return "", fmt.Errorf("could not get CommandLine") } - - return lines[0][1], nil + return *dst[0].CommandLine, nil } func (p *Process) Cwd() (string, error) { return "", common.NotImplementedError @@ -126,20 +174,17 @@ func (p *Process) Terminal() (string, error) { // Nice returnes priority in Windows func (p *Process) Nice() (int32, error) { - query := fmt.Sprintf("ProcessId = %d", p.Pid) - lines, err := common.GetWmic("process", "where", query, "get", "Priority") + var dst []Win32_Process + query := fmt.Sprintf("WHERE ProcessId = %d", p.Pid) + q := wmi.CreateQuery(&dst, query) + err := wmi.Query(q, &dst) if err != nil { return 0, err } - if len(lines) == 0 { - return 0, fmt.Errorf("could not get command line") - } - priority, err := strconv.Atoi(lines[0][1]) - if err != nil { - return 0, err + if len(dst) != 1 { + return 0, fmt.Errorf("could not get Priority") } - - return int32(priority), nil + return int32(dst[0].Priority), nil } func (p *Process) IOnice() (int32, error) { return 0, common.NotImplementedError @@ -159,20 +204,17 @@ func (p *Process) NumFDs() (int32, error) { return 0, common.NotImplementedError } func (p *Process) NumThreads() (int32, error) { - query := fmt.Sprintf("ProcessId = %d", p.Pid) - lines, err := common.GetWmic("process", "where", query, "get", "ThreadCount") + var dst []Win32_Process + query := fmt.Sprintf("WHERE ProcessId = %d", p.Pid) + q := wmi.CreateQuery(&dst, query) + err := wmi.Query(q, &dst) if err != nil { return 0, err } - if len(lines) == 0 { - return 0, fmt.Errorf("could not get command line") - } - count, err := strconv.Atoi(lines[0][1]) - if err != nil { - return 0, err + if len(dst) != 1 { + return 0, fmt.Errorf("could not get ThreadCount") } - - return int32(count), nil + return int32(dst[0].ThreadCount), nil } func (p *Process) Threads() (map[string]string, error) { ret := make(map[string]string, 0) @@ -266,17 +308,19 @@ func (p *Process) getFromSnapProcess(pid int32) (int32, int32, string, error) { // Get processes func processes() ([]*Process, error) { - lines, err := common.GetWmic("process", "get", "processid") + + var dst []Win32_Process + q := wmi.CreateQuery(&dst, "") + err := wmi.Query(q, &dst) if err != nil { - return nil, err + return []*Process{}, err } - var results []*Process - for _, l := range lines { - pid, err := strconv.Atoi(l[1]) - if err != nil { - continue - } - p, err := NewProcess(int32(pid)) + if len(dst) == 0 { + return []*Process{}, fmt.Errorf("could not get Process") + } + results := make([]*Process, 0, len(dst)) + for _, proc := range dst { + p, err := NewProcess(int32(proc.ProcessId)) if err != nil { continue }