Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,20 @@ go-windows is a library for Go (golang) that provides wrappers to various
Windows APIs that are not covered by the stdlib or by
[golang.org/x/sys/windows](https://godoc.org/golang.org/x/sys/windows).

Goals / Features
## Goals / Features

- Does not use cgo.
- Provide abstractions to make using the APIs easier.

## Adding new Syscalls

Adding syscalls to zsyscall_windows.go is done by adding a comment under Syscalls in kernel32.go, psapi.go, etc describing the syscall.

Example:

```
// Syscalls
//sys _GetPerformanceInfo(pi *PerformanceInformation, cb uint32) (err error) = psapi.GetPerformanceInfo
```

And then calling `go generate` as described in [doc.go](./doc.go)
33 changes: 33 additions & 0 deletions psapi.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
//sys _GetProcessMemoryInfo(handle syscall.Handle, psmemCounters *ProcessMemoryCountersEx, cb uint32) (err error) = psapi.GetProcessMemoryInfo
//sys _GetProcessImageFileNameA(handle syscall.Handle, imageFileName *byte, nSize uint32) (len uint32, err error) = psapi.GetProcessImageFileNameA
//sys _EnumProcesses(lpidProcess *uint32, cb uint32, lpcbNeeded *uint32) (err error) = psapi.EnumProcesses
//sys _GetPerformanceInfo(pi *PerformanceInformation, cb uint32) (err error) = psapi.GetPerformanceInfo

var (
sizeofProcessMemoryCountersEx = uint32(unsafe.Sizeof(ProcessMemoryCountersEx{}))
Expand Down Expand Up @@ -98,3 +99,35 @@
}
}
}

// PerformanceInformation represents the [PERFORMANCE_INFORMATION] structure
//
// [PERFORMANCE_INFORMATION]: https://learn.microsoft.com/en-us/windows/win32/api/psapi/ns-psapi-performance_information
type PerformanceInformation struct {
cb uint32
CommitTotal uintptr // The number of pages currently committed by the system. Note that committing pages (using VirtualAlloc with MEM_COMMIT) changes this value immediately; however, the physical memory is not charged until the pages are accessed.
CommitLimit uintptr // The current maximum number of pages that can be committed by the system without extending the paging file(s). This number can change if memory is added or deleted, or if pagefiles have grown, shrunk, or been added. If the paging file can be extended, this is a soft limit.
CommitPeak uintptr // The maximum number of pages that were simultaneously in the committed state since the last system reboot.
PhysicalTotal uintptr // The amount of actual physical memory, in pages.
PhysicalAvailable uintptr // The amount of physical memory currently available, in pages. This is the amount of physical memory that can be immediately reused without having to write its contents to disk first. It is the sum of the size of the standby, free, and zero lists.
SystemCache uintptr // The amount of system cache memory, in pages. This is the size of the standby list plus the system working set.
KernelTotal uintptr // The sum of the memory currently in the paged and nonpaged kernel pools, in pages.
KernelPaged uintptr // The memory currently in the paged kernel pool, in pages.
KernelNonpaged uintptr // The memory currently in the nonpaged kernel pool, in pages.
PageSize uintptr // The size of a page, in bytes.
HandleCount uint32 // The current number of open handles.
ProcessCount uint32 // The current number of processes.
ThreadCount uint32 // The current number of threads.
}

// GetPerformanceInfo retrieves performance information for the system.
// https://learn.microsoft.com/en-us/windows/win32/api/psapi/nf-psapi-getperformanceinfo
func GetPerformanceInfo() (PerformanceInformation, error) {
var pi PerformanceInformation
pi.CB = uint32(unsafe.Sizeof(pi))

Check failure on line 127 in psapi.go

View workflow job for this annotation

GitHub Actions / test (1.22.x, windows-latest)

pi.CB undefined (type PerformanceInformation has no field or method CB)

Check failure on line 127 in psapi.go

View workflow job for this annotation

GitHub Actions / test (1.22.x, windows-latest)

pi.CB undefined (type PerformanceInformation has no field or method CB)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CB usages need updated to cb.


if err := _GetPerformanceInfo(&pi, pi.CB); err != nil {

Check failure on line 129 in psapi.go

View workflow job for this annotation

GitHub Actions / test (1.22.x, windows-latest)

pi.CB undefined (type PerformanceInformation has no field or method CB)

Check failure on line 129 in psapi.go

View workflow job for this annotation

GitHub Actions / test (1.22.x, windows-latest)

pi.CB undefined (type PerformanceInformation has no field or method CB) (compile)
return PerformanceInformation{}, fmt.Errorf("GetPerformanceInfo failed: %w", err)
}
return pi, nil
}
126 changes: 126 additions & 0 deletions psapi_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
// Licensed to Elasticsearch B.V. under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. Elasticsearch B.V. licenses this file to you under
// the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.

//go:build windows
// +build windows

package windows

import (
"slices"
"syscall"
"testing"
"unsafe"

"github.com/stretchr/testify/assert"
)

func TestGetPerformanceInfo(t *testing.T) {
pi, err := GetPerformanceInfo()
if err != nil {
t.Fatal(err)
}

// Verify the structure was populated correctly
assert.Equal(t, uint32(unsafe.Sizeof(pi)), pi.CB, "CB field should contain the size of the structure")

Check failure on line 39 in psapi_test.go

View workflow job for this annotation

GitHub Actions / test (1.22.x, windows-latest)

pi.CB undefined (type PerformanceInformation has no field or method CB) (compile)

// Verify that physical memory values are reasonable
assert.True(t, pi.PhysicalTotal > 0, "PhysicalTotal should be greater than 0")
assert.True(t, pi.PhysicalAvailable <= pi.PhysicalTotal, "PhysicalAvailable should not exceed PhysicalTotal")

// Verify that commit values are reasonable
assert.True(t, pi.CommitTotal > 0, "CommitTotal should be greater than 0")
assert.True(t, pi.CommitLimit > 0, "CommitLimit should be greater than 0")
assert.True(t, pi.CommitTotal <= pi.CommitLimit, "CommitTotal should not exceed CommitLimit")

// Verify that page size is reasonable (typically at least 4KB on x86/x64)
assert.True(t, pi.PageSize >= 4096, "PageSize should be at least 4KB")

// Verify that process and thread counts are reasonable
assert.True(t, pi.ProcessCount > 0, "ProcessCount should be greater than 0")
assert.True(t, pi.ThreadCount > 0, "ThreadCount should be greater than 0")
assert.True(t, pi.ThreadCount >= pi.ProcessCount, "ThreadCount should be at least equal to ProcessCount")

// Verify that handle count is reasonable
assert.True(t, pi.HandleCount > 0, "HandleCount should be greater than 0")

// Verify that kernel memory values are reasonable
assert.True(t, pi.KernelTotal > 0, "KernelTotal should be greater than 0")
assert.True(t, pi.KernelPaged <= pi.KernelTotal, "KernelPaged should not exceed KernelTotal")
assert.True(t, pi.KernelNonpaged <= pi.KernelTotal, "KernelNonpaged should not exceed KernelTotal")

// Verify that system cache is reasonable
assert.True(t, pi.SystemCache > 0, "SystemCache should be greater than 0")
}

func TestGetProcessMemoryInfo(t *testing.T) {
h, err := syscall.GetCurrentProcess()
if err != nil {
t.Fatal(err)
}

info, err := GetProcessMemoryInfo(h)
if err != nil {
t.Fatal(err)
}

// Verify the structure was populated correctly
assert.Equal(t, uint32(unsafe.Sizeof(info)), info.cb, "cb field should contain the size of the structure")

// Verify that memory values are reasonable
assert.True(t, info.WorkingSetSize > 0, "WorkingSetSize should be greater than 0")
assert.True(t, info.PeakWorkingSetSize >= info.WorkingSetSize, "PeakWorkingSetSize should be at least equal to WorkingSetSize")
assert.True(t, info.PrivateUsage > 0, "PrivateUsage should be greater than 0")

// Verify that page fault count is reasonable
assert.True(t, info.PageFaultCount > 0, "PageFaultCount should be greater than 0")
}

func TestGetProcessImageFileName(t *testing.T) {
h, err := syscall.GetCurrentProcess()
if err != nil {
t.Fatal(err)
}

filename, err := GetProcessImageFileName(h)
if err != nil {
t.Fatal(err)
}

// Verify that we got a valid device path
assert.True(t, len(filename) > 0, "Filename should not be empty")
assert.Contains(t, filename, "\\Device\\", "Filename should be a device path starting with \\Device\\")

// The path should contain the executable name
assert.Contains(t, filename, ".exe", "Filename should contain .exe extension")
}

func TestEnumProcesses(t *testing.T) {
pids, err := EnumProcesses()
if err != nil {
t.Fatal(err)
}

// Verify that we got a reasonable number of processes
assert.True(t, len(pids) > 0, "Should return at least one process")
assert.True(t, len(pids) < 10000, "Should not return an unreasonable number of processes")

// Verify that the current process is in the list
currentPID := uint32(syscall.Getpid())
found := slices.Contains(pids, currentPID)
assert.True(t, found, "Current process PID should be in the returned list")
}
9 changes: 9 additions & 0 deletions zsyscall_windows.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading