use StackExchange/wmi instead of invoking wmic process.

Note: This may not work on some old Windows XP.
This commit is contained in:
WAKAYAMA shirou 2015-04-20 00:05:31 +09:00
parent b55d373cee
commit 64357f04e7
4 changed files with 136 additions and 130 deletions

View File

@ -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 {

View File

@ -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
}

View File

@ -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])
if err != nil {
return 0, err
}
now := time.Now()
var dst []Win32_OperatingSystem
q := wmi.CreateQuery(&dst, "")
err := wmi.Query(q, &dst)
if err != nil {
return 0, err
}
t := dst[0].LastBootUpTime.Local()
return uint64(now.Sub(t).Seconds()), nil
}

View File

@ -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")
if len(dst) != 1 {
return 0, fmt.Errorf("could not get Priority")
}
priority, err := strconv.Atoi(lines[0][1])
if err != nil {
return 0, err
}
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")
if len(dst) != 1 {
return 0, fmt.Errorf("could not get ThreadCount")
}
count, err := strconv.Atoi(lines[0][1])
if err != nil {
return 0, err
}
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
}