use StackExchange/wmi instead of invoking wmic process.

Note: This may not work on some old Windows XP.
pull/47/head
WAKAYAMA shirou 10 years ago
parent b55d373cee
commit 64357f04e7

@ -3,9 +3,6 @@
package common package common
import ( import (
"fmt"
"os/exec"
"strings"
"syscall" "syscall"
"unsafe" "unsafe"
) )
@ -75,37 +72,6 @@ func BytePtrToString(p *uint8) string {
return string(a[:i]) 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 // CounterInfo
// copied from https://github.com/mackerelio/mackerel-agent/ // copied from https://github.com/mackerelio/mackerel-agent/
type CounterInfo struct { type CounterInfo struct {

@ -3,14 +3,28 @@
package cpu package cpu
import ( import (
"strconv" "fmt"
"syscall" "syscall"
"time" "time"
"unsafe" "unsafe"
"github.com/StackExchange/wmi"
common "github.com/shirou/gopsutil/common" 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 // TODO: Get percpu
func CPUTimes(percpu bool) ([]CPUTimesStat, error) { func CPUTimes(percpu bool) ([]CPUTimesStat, error) {
var ret []CPUTimesStat var ret []CPUTimesStat
@ -43,59 +57,41 @@ func CPUTimes(percpu bool) ([]CPUTimesStat, error) {
func CPUInfo() ([]CPUInfoStat, error) { func CPUInfo() ([]CPUInfoStat, error) {
var ret []CPUInfoStat 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 { if err != nil {
return ret, err return ret, err
} }
for i, t := range lines { for i, l := range dst {
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
}
}
cpu := CPUInfoStat{ cpu := CPUInfoStat{
CPU: int32(i), CPU: int32(i),
Family: t[1], Family: fmt.Sprintf("%d", l.Family),
CacheSize: int32(cache), CacheSize: int32(l.L2CacheSize),
VendorID: t[3], VendorID: l.Manufacturer,
ModelName: t[4], ModelName: l.Name,
Cores: int32(cores), Cores: int32(l.NumberOfLogicalProcessors),
PhysicalID: t[6], PhysicalID: l.ProcessorId,
Stepping: int32(stepping), Mhz: float64(l.MaxClockSpeed),
Flags: []string{}, Flags: []string{},
} }
ret = append(ret, cpu) ret = append(ret, cpu)
} }
return ret, nil return ret, nil
} }
func CPUPercent(interval time.Duration, percpu bool) ([]float64, error) { func CPUPercent(interval time.Duration, percpu bool) ([]float64, error) {
ret := []float64{} var ret []float64
var dst []Win32_Processor
lines, err := common.GetWmic("cpu", "get", "loadpercentage") q := wmi.CreateQuery(&dst, "")
err := wmi.Query(q, &dst)
if err != nil { if err != nil {
return ret, err return ret, err
} }
for _, l := range lines { for _, l := range dst {
if len(l) < 2 { // use range but windows can only get one percent.
continue ret = append(ret, float64(l.LoadPercentage)/100.0)
}
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)
} }
return ret, nil return ret, nil
} }

@ -3,11 +3,11 @@
package host package host
import ( import (
"fmt"
"os" "os"
"strings"
"time" "time"
"github.com/StackExchange/wmi"
common "github.com/shirou/gopsutil/common" common "github.com/shirou/gopsutil/common"
process "github.com/shirou/gopsutil/process" process "github.com/shirou/gopsutil/process"
) )
@ -16,6 +16,10 @@ var (
procGetSystemTimeAsFileTime = common.Modkernel32.NewProc("GetSystemTimeAsFileTime") procGetSystemTimeAsFileTime = common.Modkernel32.NewProc("GetSystemTimeAsFileTime")
) )
type Win32_OperatingSystem struct {
LastBootUpTime time.Time
}
func HostInfo() (*HostInfoStat, error) { func HostInfo() (*HostInfoStat, error) {
ret := &HostInfoStat{} ret := &HostInfoStat{}
hostname, err := os.Hostname() hostname, err := os.Hostname()
@ -40,19 +44,15 @@ func HostInfo() (*HostInfoStat, error) {
} }
func BootTime() (uint64, error) { func BootTime() (uint64, error) {
lines, err := common.GetWmic("os", "get", "LastBootUpTime") now := time.Now()
if err != nil {
return 0, err var dst []Win32_OperatingSystem
} q := wmi.CreateQuery(&dst, "")
if len(lines) == 0 || len(lines[0]) != 2 { err := wmi.Query(q, &dst)
return 0, fmt.Errorf("could not get LastBootUpTime")
}
format := "20060102150405"
t, err := time.Parse(format, strings.Split(lines[0][1], ".")[0])
if err != nil { if err != nil {
return 0, err return 0, err
} }
now := time.Now() t := dst[0].LastBootUpTime.Local()
return uint64(now.Sub(t).Seconds()), nil return uint64(now.Sub(t).Seconds()), nil
} }

@ -5,10 +5,11 @@ package process
import ( import (
"errors" "errors"
"fmt" "fmt"
"strconv"
"syscall" "syscall"
"time"
"unsafe" "unsafe"
"github.com/StackExchange/wmi"
"github.com/shirou/w32" "github.com/shirou/w32"
common "github.com/shirou/gopsutil/common" common "github.com/shirou/gopsutil/common"
@ -43,6 +44,48 @@ type MemoryInfoExStat struct {
type MemoryMapsStat 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) { func Pids() ([]int32, error) {
var ret []int32 var ret []int32
@ -66,38 +109,43 @@ func (p *Process) Ppid() (int32, error) {
return ret, nil return ret, nil
} }
func (p *Process) Name() (string, error) { func (p *Process) Name() (string, error) {
query := fmt.Sprintf("ProcessId = %d", p.Pid) var dst []Win32_Process
lines, err := common.GetWmic("process", "where", query, "get", "Name") query := fmt.Sprintf("WHERE ProcessId = %d", p.Pid)
q := wmi.CreateQuery(&dst, query)
err := wmi.Query(q, &dst)
if err != nil { if err != nil {
return "", err return "", err
} }
if len(lines) == 0 { if len(dst) != 1 {
return "", fmt.Errorf("could not get Name") return "", fmt.Errorf("could not get Name")
} }
return lines[0][1], nil return dst[0].Name, nil
} }
func (p *Process) Exe() (string, error) { func (p *Process) Exe() (string, error) {
query := fmt.Sprintf("ProcessId = %d", p.Pid) var dst []Win32_Process
lines, err := common.GetWmic("process", "where", query, "get", "ExecutablePath") query := fmt.Sprintf("WHERE ProcessId = %d", p.Pid)
q := wmi.CreateQuery(&dst, query)
err := wmi.Query(q, &dst)
if err != nil { if err != nil {
return "", err return "", err
} }
if len(lines) == 0 { if len(dst) != 1 {
return "", fmt.Errorf("could not get ExecutablePath") return "", fmt.Errorf("could not get ExecutablePath")
} }
return lines[0][1], nil return *dst[0].ExecutablePath, nil
} }
func (p *Process) Cmdline() (string, error) { func (p *Process) Cmdline() (string, error) {
query := fmt.Sprintf("ProcessId = %d", p.Pid) var dst []Win32_Process
lines, err := common.GetWmic("process", "where", query, "get", "CommandLine") query := fmt.Sprintf("WHERE ProcessId = %d", p.Pid)
q := wmi.CreateQuery(&dst, query)
err := wmi.Query(q, &dst)
if err != nil { if err != nil {
return "", err return "", err
} }
if len(lines) == 0 { if len(dst) != 1 {
return "", fmt.Errorf("could not get command line") return "", fmt.Errorf("could not get CommandLine")
} }
return *dst[0].CommandLine, nil
return lines[0][1], nil
} }
func (p *Process) Cwd() (string, error) { func (p *Process) Cwd() (string, error) {
return "", common.NotImplementedError return "", common.NotImplementedError
@ -126,20 +174,17 @@ func (p *Process) Terminal() (string, error) {
// Nice returnes priority in Windows // Nice returnes priority in Windows
func (p *Process) Nice() (int32, error) { func (p *Process) Nice() (int32, error) {
query := fmt.Sprintf("ProcessId = %d", p.Pid) var dst []Win32_Process
lines, err := common.GetWmic("process", "where", query, "get", "Priority") query := fmt.Sprintf("WHERE ProcessId = %d", p.Pid)
q := wmi.CreateQuery(&dst, query)
err := wmi.Query(q, &dst)
if err != nil { if err != nil {
return 0, err return 0, err
} }
if len(lines) == 0 { if len(dst) != 1 {
return 0, fmt.Errorf("could not get command line") return 0, fmt.Errorf("could not get Priority")
} }
priority, err := strconv.Atoi(lines[0][1]) return int32(dst[0].Priority), nil
if err != nil {
return 0, err
}
return int32(priority), nil
} }
func (p *Process) IOnice() (int32, error) { func (p *Process) IOnice() (int32, error) {
return 0, common.NotImplementedError return 0, common.NotImplementedError
@ -159,20 +204,17 @@ func (p *Process) NumFDs() (int32, error) {
return 0, common.NotImplementedError return 0, common.NotImplementedError
} }
func (p *Process) NumThreads() (int32, error) { func (p *Process) NumThreads() (int32, error) {
query := fmt.Sprintf("ProcessId = %d", p.Pid) var dst []Win32_Process
lines, err := common.GetWmic("process", "where", query, "get", "ThreadCount") query := fmt.Sprintf("WHERE ProcessId = %d", p.Pid)
q := wmi.CreateQuery(&dst, query)
err := wmi.Query(q, &dst)
if err != nil { if err != nil {
return 0, err return 0, err
} }
if len(lines) == 0 { if len(dst) != 1 {
return 0, fmt.Errorf("could not get command line") return 0, fmt.Errorf("could not get ThreadCount")
} }
count, err := strconv.Atoi(lines[0][1]) return int32(dst[0].ThreadCount), nil
if err != nil {
return 0, err
}
return int32(count), nil
} }
func (p *Process) Threads() (map[string]string, error) { func (p *Process) Threads() (map[string]string, error) {
ret := make(map[string]string, 0) ret := make(map[string]string, 0)
@ -266,17 +308,19 @@ func (p *Process) getFromSnapProcess(pid int32) (int32, int32, string, error) {
// Get processes // Get processes
func processes() ([]*Process, error) { 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 { if err != nil {
return nil, err return []*Process{}, err
} }
var results []*Process if len(dst) == 0 {
for _, l := range lines { return []*Process{}, fmt.Errorf("could not get Process")
pid, err := strconv.Atoi(l[1])
if err != nil {
continue
} }
p, err := NewProcess(int32(pid)) results := make([]*Process, 0, len(dst))
for _, proc := range dst {
p, err := NewProcess(int32(proc.ProcessId))
if err != nil { if err != nil {
continue continue
} }

Loading…
Cancel
Save