Skip to content

Commit

Permalink
Update gosigar windows code to use gosigar/sys/windows package
Browse files Browse the repository at this point in the history
- Fixes elastic#53 (ProcStatus.PPID value is wrong on Windows). The code was incorrectly using `CreateToolhelp32Snapshot` + `Process32First`. This could have been fixed, but it would have required iterating over each process until finding the PPID of process we were interested it. Instead the code has been changed to use `NtQueryInformationProcess` to get the PPID given a process token.
- Fixes elastic#6 (Get the cpu usage per core in Windows). I used `NtQuerySystemInformation` to collect the timing information on a per CPU basis.
- Adds OS version checks to the functions that make certain WMI calls. The `Win32_Process` data is only available on Vista and newer. On XP and Win2003, these methods will return `ErrNotImplemented`. This will help address elastic/beats#1704.
- Implements `Uptime.Get` for Windows.
- Implements `Swap.Get` for Windows based on page file metrics.
- Removes cgo usage for Windows.
- Adds support to `github.com/gosigar/sys/windows` for querying and enabling privileges in a process token. This will help in addressing elastic/beats#1897.
  • Loading branch information
andrewkroh committed Oct 27, 2016
1 parent a78543b commit 5218e01
Show file tree
Hide file tree
Showing 12 changed files with 855 additions and 444 deletions.
20 changes: 17 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,28 @@ This project adheres to [Semantic Versioning](http://semver.org/).
## [Unreleased]

### Added
- Added `CpuList` implementation for Windows that returns CPU timing information
on a per CPU basis. #55
- Added `Uptime` implementation for Windows. #55
- Added `Swap` implementation for Windows based on page file metrics. #55
- Added support to `github.com/gosigar/sys/windows` for querying and enabling
privileges in a process token.

### Changed
- Changed several `OpenProcess` calls on Windows to request the lowest possible access privilege. #50
- Changed several `OpenProcess` calls on Windows to request the lowest possible
access privileges. #50
- Removed cgo usage from Windows code.

- Added OS version checks to `ProcArgs.Get` on Windows because the
`Win32_Process` WMI query is not available prior to Windows vista. On XP and
Windows 2003, this method returns `ErrNotImplemented`. #55

### Deprecated

### Removed

### Fixed
- Fix value of `Mem.ActualFree` and `Mem.ActualUsed` on Windows. #49
- Fix `ProcTime.StartTime` on Windows to report value in milliseconds since Unix epoch. #51
- Fixed value of `Mem.ActualFree` and `Mem.ActualUsed` on Windows. #49
- Fixed `ProcTime.StartTime` on Windows to report value in milliseconds since
Unix epoch. #51
- Fixed #53 ProcStatus.PPID value is wrong on Windows. #55
6 changes: 2 additions & 4 deletions concrete_sigar_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ func TestConcreteCollectCpuStats(t *testing.T) {
func TestConcreteGetLoadAverage(t *testing.T) {
concreteSigar := &sigar.ConcreteSigar{}
avg, err := concreteSigar.GetLoadAverage()
skipNotImplemented(t, err, "windows")
if assert.NoError(t, err) {
assert.NotNil(t, avg.One)
assert.NotNil(t, avg.Five)
Expand Down Expand Up @@ -78,10 +79,7 @@ func TestConcreteFileSystemUsage(t *testing.T) {
func TestConcreteGetFDUsage(t *testing.T) {
concreteSigar := &sigar.ConcreteSigar{}
fdUsage, err := concreteSigar.GetFDUsage()
// if it's not implemented, don't test
if _, ok := err.(sigar.ErrNotImplemented); ok {
t.Skipf("Skipping *ConcreteSigar.GetFDUsage test because it is not implemented for " + runtime.GOOS)
}
skipNotImplemented(t, err, "windows")
if assert.NoError(t, err) {
assert.True(t, fdUsage.Open > 0)
assert.True(t, fdUsage.Open <= fdUsage.Max)
Expand Down
41 changes: 24 additions & 17 deletions sigar_interface_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,10 @@ func TestCpu(t *testing.T) {

func TestLoadAverage(t *testing.T) {
avg := LoadAverage{}
assert.NoError(t, avg.Get())
assert.NoError(t, skipNotImplemented(t, avg.Get(), "windows"))
}

func TestUptime(t *testing.T) {
skipWindows(t)
uptime := Uptime{}
if assert.NoError(t, uptime.Get()) {
assert.True(t, uptime.Length > 0, "Uptime (%f) must be positive", uptime.Length)
Expand All @@ -52,27 +51,26 @@ func TestSwap(t *testing.T) {
}

func TestCpuList(t *testing.T) {
skipWindows(t)
cpulist := CpuList{}
if assert.NoError(t, cpulist.Get()) {
numCore := len(cpulist.List)
cpuList := CpuList{}
if assert.NoError(t, cpuList.Get()) {
numCore := len(cpuList.List)
numCpu := runtime.NumCPU()
assert.True(t, numCore >= numCpu, "Number of cores (%d) >= number of logical CPUs (%d)",
numCore, numCpu)
}
}

func TestFileSystemList(t *testing.T) {
fslist := FileSystemList{}
if assert.NoError(t, fslist.Get()) {
assert.True(t, len(fslist.List) > 0)
fsList := FileSystemList{}
if assert.NoError(t, fsList.Get()) {
assert.True(t, len(fsList.List) > 0)
}
}

func TestFileSystemUsage(t *testing.T) {
root := "/"
if runtime.GOOS == "windows" {
root = "C:\\"
root = `C:\`
}
fsusage := FileSystemUsage{}
if assert.NoError(t, fsusage.Get(root)) {
Expand All @@ -99,6 +97,7 @@ func TestProcState(t *testing.T) {
assert.Contains(t, []RunState{RunStateRun, RunStateSleep}, state.State)
assert.Regexp(t, "go(.exe)?", state.Name)
assert.Equal(t, u.Username, state.Username)
assert.True(t, state.Ppid > 0, "ppid=%v is non-positive", state.Ppid)
}

assert.Error(t, state.Get(invalidPid))
Expand All @@ -123,23 +122,31 @@ func TestProcTime(t *testing.T) {
}

func TestProcArgs(t *testing.T) {
skipWindows(t)
args := ProcArgs{}
if assert.NoError(t, args.Get(os.Getppid())) {
assert.True(t, len(args.List) >= 2)
assert.NotEmpty(t, args.List)
}
}

func TestProcExe(t *testing.T) {
skipWindows(t)
exe := ProcExe{}
if assert.NoError(t, exe.Get(os.Getppid())) {
if assert.NoError(t, skipNotImplemented(t, exe.Get(os.Getppid()), "windows")) {
assert.Regexp(t, "go(.exe)?", filepath.Base(exe.Name))
}
}

func skipWindows(t testing.TB) {
if runtime.GOOS == "windows" {
t.Skipf("Skipping test on %s", runtime.GOOS)
func skipNotImplemented(t testing.TB, err error, goos ...string) error {
for _, os := range goos {
if runtime.GOOS == os {
if err == nil {
t.Fatal("expected ErrNotImplemented")
} else if IsNotImplemented(err) {
t.Skipf("Skipping test on %s", runtime.GOOS)
}

break
}
}

return err
}
Loading

0 comments on commit 5218e01

Please sign in to comment.