Initial QSfera import
This commit is contained in:
+42
@@ -0,0 +1,42 @@
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
//go:build linux
|
||||
|
||||
package mem
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
type ExVirtualMemory struct {
|
||||
ActiveFile uint64 `json:"activefile"`
|
||||
InactiveFile uint64 `json:"inactivefile"`
|
||||
ActiveAnon uint64 `json:"activeanon"`
|
||||
InactiveAnon uint64 `json:"inactiveanon"`
|
||||
Unevictable uint64 `json:"unevictable"`
|
||||
Percpu uint64 `json:"percpu"`
|
||||
KernelStack uint64 `json:"kernelstack"`
|
||||
}
|
||||
|
||||
func (v ExVirtualMemory) String() string {
|
||||
s, _ := json.Marshal(v)
|
||||
return string(s)
|
||||
}
|
||||
|
||||
type ExLinux struct{}
|
||||
|
||||
func NewExLinux() *ExLinux {
|
||||
return &ExLinux{}
|
||||
}
|
||||
|
||||
func (ex *ExLinux) VirtualMemory() (*ExVirtualMemory, error) {
|
||||
return ex.VirtualMemoryWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (*ExLinux) VirtualMemoryWithContext(ctx context.Context) (*ExVirtualMemory, error) {
|
||||
_, vmEx, err := fillFromMeminfoWithContext(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return vmEx, nil
|
||||
}
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
//go:build windows
|
||||
|
||||
package mem
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// ExVirtualMemory represents Windows specific information
|
||||
// https://learn.microsoft.com/en-us/windows/win32/api/sysinfoapi/ns-sysinfoapi-memorystatusex
|
||||
// https://learn.microsoft.com/en-us/windows/win32/api/psapi/ns-psapi-performance_information
|
||||
type ExVirtualMemory struct {
|
||||
CommitLimit uint64 `json:"commitLimit"`
|
||||
CommitTotal uint64 `json:"commitTotal"`
|
||||
VirtualTotal uint64 `json:"virtualTotal"`
|
||||
VirtualAvail uint64 `json:"virtualAvail"`
|
||||
PhysTotal uint64 `json:"physTotal"`
|
||||
PhysAvail uint64 `json:"physAvail"`
|
||||
PageFileTotal uint64 `json:"pageFileTotal"`
|
||||
PageFileAvail uint64 `json:"pageFileAvail"`
|
||||
}
|
||||
|
||||
type ExWindows struct{}
|
||||
|
||||
func NewExWindows() *ExWindows {
|
||||
return &ExWindows{}
|
||||
}
|
||||
|
||||
func (*ExWindows) VirtualMemory() (*ExVirtualMemory, error) {
|
||||
var memInfo memoryStatusEx
|
||||
memInfo.cbSize = uint32(unsafe.Sizeof(memInfo))
|
||||
// If mem == 0 since this is an error according to GlobalMemoryStatusEx documentation
|
||||
// In that case, use err which is constructed from GetLastError(),
|
||||
// see https://pkg.go.dev/golang.org/x/sys/windows#LazyProc.Call
|
||||
mem, _, err := procGlobalMemoryStatusEx.Call(uintptr(unsafe.Pointer(&memInfo)))
|
||||
if mem == 0 {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var perfInfo performanceInformation
|
||||
perfInfo.cb = uint32(unsafe.Sizeof(perfInfo))
|
||||
// Analogous to above: perf == 0 is an error according to the GetPerformanceInfo documentation,
|
||||
// use err in that case
|
||||
perf, _, err := procGetPerformanceInfo.Call(uintptr(unsafe.Pointer(&perfInfo)), uintptr(perfInfo.cb))
|
||||
if perf == 0 {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret := &ExVirtualMemory{
|
||||
CommitLimit: perfInfo.commitLimit * perfInfo.pageSize,
|
||||
CommitTotal: perfInfo.commitTotal * perfInfo.pageSize,
|
||||
VirtualTotal: memInfo.ullTotalVirtual,
|
||||
VirtualAvail: memInfo.ullAvailVirtual,
|
||||
PhysTotal: memInfo.ullTotalPhys,
|
||||
PhysAvail: memInfo.ullAvailPhys,
|
||||
PageFileTotal: memInfo.ullTotalPageFile,
|
||||
PageFileAvail: memInfo.ullAvailPageFile,
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
+122
@@ -0,0 +1,122 @@
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
package mem
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/shirou/gopsutil/v4/internal/common"
|
||||
)
|
||||
|
||||
var invoke common.Invoker = common.Invoke{}
|
||||
|
||||
// Memory usage statistics. Total, Available and Used contain numbers of bytes
|
||||
// for human consumption.
|
||||
//
|
||||
// The other fields in this struct contain kernel specific values.
|
||||
type VirtualMemoryStat struct {
|
||||
// Total amount of RAM on this system
|
||||
Total uint64 `json:"total"`
|
||||
|
||||
// RAM available for programs to allocate
|
||||
//
|
||||
// This value is computed from the kernel specific values.
|
||||
Available uint64 `json:"available"`
|
||||
|
||||
// RAM used by programs
|
||||
//
|
||||
// This value is computed from the kernel specific values.
|
||||
Used uint64 `json:"used"`
|
||||
|
||||
// Percentage of RAM used by programs
|
||||
//
|
||||
// This value is computed from the kernel specific values.
|
||||
UsedPercent float64 `json:"usedPercent"`
|
||||
|
||||
// This is the kernel's notion of free memory; RAM chips whose bits nobody
|
||||
// cares about the value of right now. For a human consumable number,
|
||||
// Available is what you really want.
|
||||
Free uint64 `json:"free"`
|
||||
|
||||
// OS X / BSD specific numbers:
|
||||
// http://www.macyourself.com/2010/02/17/what-is-free-wired-active-and-inactive-system-memory-ram/
|
||||
Active uint64 `json:"active"`
|
||||
Inactive uint64 `json:"inactive"`
|
||||
Wired uint64 `json:"wired"`
|
||||
|
||||
// FreeBSD specific numbers:
|
||||
// https://reviews.freebsd.org/D8467
|
||||
Laundry uint64 `json:"laundry"`
|
||||
|
||||
// Linux specific numbers
|
||||
// https://blogs.oracle.com/linux/understanding-linux-kernel-memory-statistics
|
||||
// https://www.kernel.org/doc/Documentation/filesystems/proc.txt
|
||||
// https://www.kernel.org/doc/Documentation/vm/overcommit-accounting
|
||||
// https://www.kernel.org/doc/Documentation/vm/transhuge.txt
|
||||
//
|
||||
Buffers uint64 `json:"buffers"`
|
||||
Cached uint64 `json:"cached"`
|
||||
WriteBack uint64 `json:"writeBack"`
|
||||
Dirty uint64 `json:"dirty"`
|
||||
WriteBackTmp uint64 `json:"writeBackTmp"`
|
||||
Shared uint64 `json:"shared"`
|
||||
Slab uint64 `json:"slab"`
|
||||
Sreclaimable uint64 `json:"sreclaimable"`
|
||||
Sunreclaim uint64 `json:"sunreclaim"`
|
||||
PageTables uint64 `json:"pageTables"`
|
||||
SwapCached uint64 `json:"swapCached"`
|
||||
CommitLimit uint64 `json:"commitLimit"`
|
||||
CommittedAS uint64 `json:"committedAS"`
|
||||
HighTotal uint64 `json:"highTotal"`
|
||||
HighFree uint64 `json:"highFree"`
|
||||
LowTotal uint64 `json:"lowTotal"`
|
||||
LowFree uint64 `json:"lowFree"`
|
||||
SwapTotal uint64 `json:"swapTotal"`
|
||||
SwapFree uint64 `json:"swapFree"`
|
||||
Mapped uint64 `json:"mapped"`
|
||||
VmallocTotal uint64 `json:"vmallocTotal"`
|
||||
VmallocUsed uint64 `json:"vmallocUsed"`
|
||||
VmallocChunk uint64 `json:"vmallocChunk"`
|
||||
HugePagesTotal uint64 `json:"hugePagesTotal"`
|
||||
HugePagesFree uint64 `json:"hugePagesFree"`
|
||||
HugePagesRsvd uint64 `json:"hugePagesRsvd"`
|
||||
HugePagesSurp uint64 `json:"hugePagesSurp"`
|
||||
HugePageSize uint64 `json:"hugePageSize"`
|
||||
AnonHugePages uint64 `json:"anonHugePages"`
|
||||
}
|
||||
|
||||
type SwapMemoryStat struct {
|
||||
Total uint64 `json:"total"`
|
||||
Used uint64 `json:"used"`
|
||||
Free uint64 `json:"free"`
|
||||
UsedPercent float64 `json:"usedPercent"`
|
||||
Sin uint64 `json:"sin"`
|
||||
Sout uint64 `json:"sout"`
|
||||
PgIn uint64 `json:"pgIn"`
|
||||
PgOut uint64 `json:"pgOut"`
|
||||
PgFault uint64 `json:"pgFault"`
|
||||
|
||||
// Linux specific numbers
|
||||
// https://www.kernel.org/doc/Documentation/cgroup-v2.txt
|
||||
PgMajFault uint64 `json:"pgMajFault"`
|
||||
}
|
||||
|
||||
func (m VirtualMemoryStat) String() string {
|
||||
s, _ := json.Marshal(m)
|
||||
return string(s)
|
||||
}
|
||||
|
||||
func (m SwapMemoryStat) String() string {
|
||||
s, _ := json.Marshal(m)
|
||||
return string(s)
|
||||
}
|
||||
|
||||
type SwapDevice struct {
|
||||
Name string `json:"name"`
|
||||
UsedBytes uint64 `json:"usedBytes"`
|
||||
FreeBytes uint64 `json:"freeBytes"`
|
||||
}
|
||||
|
||||
func (m SwapDevice) String() string {
|
||||
s, _ := json.Marshal(m)
|
||||
return string(s)
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
//go:build aix
|
||||
|
||||
package mem
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/shirou/gopsutil/v4/internal/common"
|
||||
)
|
||||
|
||||
func VirtualMemory() (*VirtualMemoryStat, error) {
|
||||
return VirtualMemoryWithContext(context.Background())
|
||||
}
|
||||
|
||||
func SwapMemory() (*SwapMemoryStat, error) {
|
||||
return SwapMemoryWithContext(context.Background())
|
||||
}
|
||||
|
||||
func SwapDevices() ([]*SwapDevice, error) {
|
||||
return nil, common.ErrNotImplementedError
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
//go:build aix && cgo
|
||||
|
||||
package mem
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/power-devops/perfstat"
|
||||
)
|
||||
|
||||
func VirtualMemoryWithContext(ctx context.Context) (*VirtualMemoryStat, error) {
|
||||
m, err := perfstat.MemoryTotalStat()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pagesize := uint64(4096)
|
||||
ret := VirtualMemoryStat{
|
||||
Total: uint64(m.RealTotal) * pagesize,
|
||||
Available: uint64(m.RealAvailable) * pagesize,
|
||||
Free: uint64(m.RealFree) * pagesize,
|
||||
Used: uint64(m.RealInUse) * pagesize,
|
||||
UsedPercent: 100 * float64(m.RealInUse) / float64(m.RealTotal),
|
||||
Active: uint64(m.VirtualActive) * pagesize,
|
||||
SwapTotal: uint64(m.PgSpTotal) * pagesize,
|
||||
SwapFree: uint64(m.PgSpFree) * pagesize,
|
||||
}
|
||||
return &ret, nil
|
||||
}
|
||||
|
||||
func SwapMemoryWithContext(ctx context.Context) (*SwapMemoryStat, error) {
|
||||
m, err := perfstat.MemoryTotalStat()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pagesize := uint64(4096)
|
||||
swapUsed := uint64(m.PgSpTotal-m.PgSpFree-m.PgSpRsvd) * pagesize
|
||||
swapTotal := uint64(m.PgSpTotal) * pagesize
|
||||
ret := SwapMemoryStat{
|
||||
Total: swapTotal,
|
||||
Free: uint64(m.PgSpFree) * pagesize,
|
||||
Used: swapUsed,
|
||||
UsedPercent: float64(100*swapUsed) / float64(swapTotal),
|
||||
Sin: uint64(m.PgSpIn),
|
||||
Sout: uint64(m.PgSpOut),
|
||||
PgIn: uint64(m.PageIn),
|
||||
PgOut: uint64(m.PageOut),
|
||||
PgFault: uint64(m.PageFaults),
|
||||
}
|
||||
return &ret, nil
|
||||
}
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
//go:build aix && !cgo
|
||||
|
||||
package mem
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/shirou/gopsutil/v4/internal/common"
|
||||
)
|
||||
|
||||
func VirtualMemoryWithContext(ctx context.Context) (*VirtualMemoryStat, error) {
|
||||
vmem, swap, err := callSVMon(ctx, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if vmem.Total == 0 {
|
||||
return nil, common.ErrNotImplementedError
|
||||
}
|
||||
vmem.SwapTotal = swap.Total
|
||||
vmem.SwapFree = swap.Free
|
||||
return vmem, nil
|
||||
}
|
||||
|
||||
func SwapMemoryWithContext(ctx context.Context) (*SwapMemoryStat, error) {
|
||||
_, swap, err := callSVMon(ctx, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if swap.Total == 0 {
|
||||
return nil, common.ErrNotImplementedError
|
||||
}
|
||||
return swap, nil
|
||||
}
|
||||
|
||||
func callSVMon(ctx context.Context, virt bool) (*VirtualMemoryStat, *SwapMemoryStat, error) {
|
||||
out, err := invoke.CommandWithContext(ctx, "svmon", "-G")
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
pagesize := uint64(4096)
|
||||
vmem := &VirtualMemoryStat{}
|
||||
swap := &SwapMemoryStat{}
|
||||
for _, line := range strings.Split(string(out), "\n") {
|
||||
if virt && strings.HasPrefix(line, "memory") {
|
||||
p := strings.Fields(line)
|
||||
if len(p) > 2 {
|
||||
if t, err := strconv.ParseUint(p[1], 10, 64); err == nil {
|
||||
vmem.Total = t * pagesize
|
||||
}
|
||||
if t, err := strconv.ParseUint(p[2], 10, 64); err == nil {
|
||||
vmem.Used = t * pagesize
|
||||
if vmem.Total > 0 {
|
||||
vmem.UsedPercent = 100 * float64(vmem.Used) / float64(vmem.Total)
|
||||
}
|
||||
}
|
||||
if t, err := strconv.ParseUint(p[3], 10, 64); err == nil {
|
||||
vmem.Free = t * pagesize
|
||||
}
|
||||
}
|
||||
} else if strings.HasPrefix(line, "pg space") {
|
||||
p := strings.Fields(line)
|
||||
if len(p) > 3 {
|
||||
if t, err := strconv.ParseUint(p[2], 10, 64); err == nil {
|
||||
swap.Total = t * pagesize
|
||||
}
|
||||
if t, err := strconv.ParseUint(p[3], 10, 64); err == nil {
|
||||
swap.Free = swap.Total - t*pagesize
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
return vmem, swap, nil
|
||||
}
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
//go:build freebsd || openbsd || netbsd
|
||||
|
||||
package mem
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const swapCommand = "swapctl"
|
||||
|
||||
// swapctl column indexes
|
||||
const (
|
||||
nameCol = 0
|
||||
totalKiBCol = 1
|
||||
usedKiBCol = 2
|
||||
)
|
||||
|
||||
func SwapDevices() ([]*SwapDevice, error) {
|
||||
return SwapDevicesWithContext(context.Background())
|
||||
}
|
||||
|
||||
func SwapDevicesWithContext(ctx context.Context) ([]*SwapDevice, error) {
|
||||
output, err := invoke.CommandWithContext(ctx, swapCommand, "-lk")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not execute %q: %w", swapCommand, err)
|
||||
}
|
||||
|
||||
return parseSwapctlOutput(string(output))
|
||||
}
|
||||
|
||||
func parseSwapctlOutput(output string) ([]*SwapDevice, error) {
|
||||
lines := strings.Split(output, "\n")
|
||||
if len(lines) == 0 {
|
||||
return nil, fmt.Errorf("could not parse output of %q: no lines in %q", swapCommand, output)
|
||||
}
|
||||
|
||||
// Check header headerFields are as expected.
|
||||
header := lines[0]
|
||||
header = strings.ToLower(header)
|
||||
header = strings.ReplaceAll(header, ":", "")
|
||||
headerFields := strings.Fields(header)
|
||||
if len(headerFields) < usedKiBCol {
|
||||
return nil, fmt.Errorf("couldn't parse %q: too few fields in header %q", swapCommand, header)
|
||||
}
|
||||
if headerFields[nameCol] != "device" {
|
||||
return nil, fmt.Errorf("couldn't parse %q: expected %q to be %q", swapCommand, headerFields[nameCol], "device")
|
||||
}
|
||||
if headerFields[totalKiBCol] != "1kb-blocks" && headerFields[totalKiBCol] != "1k-blocks" {
|
||||
return nil, fmt.Errorf("couldn't parse %q: expected %q to be %q", swapCommand, headerFields[totalKiBCol], "1kb-blocks")
|
||||
}
|
||||
if headerFields[usedKiBCol] != "used" {
|
||||
return nil, fmt.Errorf("couldn't parse %q: expected %q to be %q", swapCommand, headerFields[usedKiBCol], "used")
|
||||
}
|
||||
|
||||
var swapDevices []*SwapDevice
|
||||
for _, line := range lines[1:] {
|
||||
if line == "" {
|
||||
continue // the terminal line is typically empty
|
||||
}
|
||||
fields := strings.Fields(line)
|
||||
if len(fields) < usedKiBCol {
|
||||
return nil, fmt.Errorf("couldn't parse %q: too few fields", swapCommand)
|
||||
}
|
||||
|
||||
totalKiB, err := strconv.ParseUint(fields[totalKiBCol], 10, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("couldn't parse 'Size' column in %q: %w", swapCommand, err)
|
||||
}
|
||||
|
||||
usedKiB, err := strconv.ParseUint(fields[usedKiBCol], 10, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("couldn't parse 'Used' column in %q: %w", swapCommand, err)
|
||||
}
|
||||
|
||||
swapDevices = append(swapDevices, &SwapDevice{
|
||||
Name: fields[nameCol],
|
||||
UsedBytes: usedKiB * 1024,
|
||||
FreeBytes: (totalKiB - usedKiB) * 1024,
|
||||
})
|
||||
}
|
||||
|
||||
return swapDevices, nil
|
||||
}
|
||||
+127
@@ -0,0 +1,127 @@
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
//go:build darwin
|
||||
|
||||
package mem
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"github.com/shirou/gopsutil/v4/internal/common"
|
||||
)
|
||||
|
||||
func getHwMemsize() (uint64, error) {
|
||||
total, err := unix.SysctlUint64("hw.memsize")
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return total, nil
|
||||
}
|
||||
|
||||
// xsw_usage in sys/sysctl.h
|
||||
type swapUsage struct {
|
||||
Total uint64
|
||||
Avail uint64
|
||||
Used uint64
|
||||
Pagesize int32
|
||||
Encrypted bool
|
||||
}
|
||||
|
||||
// SwapMemory returns swapinfo.
|
||||
func SwapMemory() (*SwapMemoryStat, error) {
|
||||
return SwapMemoryWithContext(context.Background())
|
||||
}
|
||||
|
||||
func SwapMemoryWithContext(_ context.Context) (*SwapMemoryStat, error) {
|
||||
// https://github.com/yanllearnn/go-osstat/blob/ae8a279d26f52ec946a03698c7f50a26cfb427e3/memory/memory_darwin.go
|
||||
var ret *SwapMemoryStat
|
||||
|
||||
value, err := unix.SysctlRaw("vm.swapusage")
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
if len(value) != 32 {
|
||||
return ret, fmt.Errorf("unexpected output of sysctl vm.swapusage: %v (len: %d)", value, len(value))
|
||||
}
|
||||
swap := (*swapUsage)(unsafe.Pointer(&value[0]))
|
||||
|
||||
u := float64(0)
|
||||
if swap.Total != 0 {
|
||||
u = ((float64(swap.Total) - float64(swap.Avail)) / float64(swap.Total)) * 100.0
|
||||
}
|
||||
|
||||
ret = &SwapMemoryStat{
|
||||
Total: swap.Total,
|
||||
Used: swap.Used,
|
||||
Free: swap.Avail,
|
||||
UsedPercent: u,
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func SwapDevices() ([]*SwapDevice, error) {
|
||||
return SwapDevicesWithContext(context.Background())
|
||||
}
|
||||
|
||||
func SwapDevicesWithContext(_ context.Context) ([]*SwapDevice, error) {
|
||||
return nil, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
type vmStatisticsData struct {
|
||||
freeCount uint32
|
||||
activeCount uint32
|
||||
inactiveCount uint32
|
||||
wireCount uint32
|
||||
_ [44]byte // Not used here
|
||||
}
|
||||
|
||||
// VirtualMemory returns VirtualmemoryStat.
|
||||
func VirtualMemory() (*VirtualMemoryStat, error) {
|
||||
return VirtualMemoryWithContext(context.Background())
|
||||
}
|
||||
|
||||
func VirtualMemoryWithContext(_ context.Context) (*VirtualMemoryStat, error) {
|
||||
sys, err := common.NewSystemLib()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer sys.Close()
|
||||
|
||||
count := uint32(common.HOST_VM_INFO_COUNT)
|
||||
var vmstat vmStatisticsData
|
||||
|
||||
status := sys.HostStatistics(sys.MachHostSelf(), common.HOST_VM_INFO,
|
||||
uintptr(unsafe.Pointer(&vmstat)), &count)
|
||||
|
||||
if status != common.KERN_SUCCESS {
|
||||
return nil, fmt.Errorf("host_statistics error=%d", status)
|
||||
}
|
||||
|
||||
pageSizeAddr, _ := sys.Dlsym("vm_kernel_page_size")
|
||||
pageSize := **(**uint64)(unsafe.Pointer(&pageSizeAddr))
|
||||
total, err := getHwMemsize()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
totalCount := uint32(total / pageSize)
|
||||
|
||||
availableCount := vmstat.inactiveCount + vmstat.freeCount
|
||||
usedPercent := 100 * float64(totalCount-availableCount) / float64(totalCount)
|
||||
|
||||
usedCount := totalCount - availableCount
|
||||
|
||||
return &VirtualMemoryStat{
|
||||
Total: total,
|
||||
Available: pageSize * uint64(availableCount),
|
||||
Used: pageSize * uint64(usedCount),
|
||||
UsedPercent: usedPercent,
|
||||
Free: pageSize * uint64(vmstat.freeCount),
|
||||
Active: pageSize * uint64(vmstat.activeCount),
|
||||
Inactive: pageSize * uint64(vmstat.inactiveCount),
|
||||
Wired: pageSize * uint64(vmstat.wireCount),
|
||||
}, nil
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
//go:build !darwin && !linux && !freebsd && !openbsd && !solaris && !windows && !plan9 && !aix && !netbsd
|
||||
|
||||
package mem
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/shirou/gopsutil/v4/internal/common"
|
||||
)
|
||||
|
||||
func VirtualMemory() (*VirtualMemoryStat, error) {
|
||||
return VirtualMemoryWithContext(context.Background())
|
||||
}
|
||||
|
||||
func VirtualMemoryWithContext(_ context.Context) (*VirtualMemoryStat, error) {
|
||||
return nil, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func SwapMemory() (*SwapMemoryStat, error) {
|
||||
return SwapMemoryWithContext(context.Background())
|
||||
}
|
||||
|
||||
func SwapMemoryWithContext(_ context.Context) (*SwapMemoryStat, error) {
|
||||
return nil, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func SwapDevices() ([]*SwapDevice, error) {
|
||||
return SwapDevicesWithContext(context.Background())
|
||||
}
|
||||
|
||||
func SwapDevicesWithContext(_ context.Context) ([]*SwapDevice, error) {
|
||||
return nil, common.ErrNotImplementedError
|
||||
}
|
||||
+168
@@ -0,0 +1,168 @@
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
//go:build freebsd
|
||||
|
||||
package mem
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"github.com/shirou/gopsutil/v4/internal/common"
|
||||
)
|
||||
|
||||
func VirtualMemory() (*VirtualMemoryStat, error) {
|
||||
return VirtualMemoryWithContext(context.Background())
|
||||
}
|
||||
|
||||
func VirtualMemoryWithContext(_ context.Context) (*VirtualMemoryStat, error) {
|
||||
pageSize, err := common.SysctlUint("vm.stats.vm.v_page_size")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
physmem, err := common.SysctlUint("hw.physmem")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
free, err := common.SysctlUint("vm.stats.vm.v_free_count")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
active, err := common.SysctlUint("vm.stats.vm.v_active_count")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
inactive, err := common.SysctlUint("vm.stats.vm.v_inactive_count")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
buffers, err := common.SysctlUint("vfs.bufspace")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
wired, err := common.SysctlUint("vm.stats.vm.v_wire_count")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var cached, laundry uint64
|
||||
osreldate, _ := common.SysctlUint("kern.osreldate")
|
||||
if osreldate < 1102000 {
|
||||
cached, err = common.SysctlUint("vm.stats.vm.v_cache_count")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
laundry, err = common.SysctlUint("vm.stats.vm.v_laundry_count")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
p := pageSize
|
||||
ret := &VirtualMemoryStat{
|
||||
Total: physmem,
|
||||
Free: free * p,
|
||||
Active: active * p,
|
||||
Inactive: inactive * p,
|
||||
Cached: cached * p,
|
||||
Buffers: buffers,
|
||||
Wired: wired * p,
|
||||
Laundry: laundry * p,
|
||||
}
|
||||
|
||||
ret.Available = ret.Inactive + ret.Cached + ret.Free + ret.Laundry
|
||||
ret.Used = ret.Total - ret.Available
|
||||
ret.UsedPercent = float64(ret.Used) / float64(ret.Total) * 100.0
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// Return swapinfo
|
||||
func SwapMemory() (*SwapMemoryStat, error) {
|
||||
return SwapMemoryWithContext(context.Background())
|
||||
}
|
||||
|
||||
// Constants from vm/vm_param.h
|
||||
const (
|
||||
XSWDEV_VERSION11 = 1
|
||||
XSWDEV_VERSION = 2
|
||||
)
|
||||
|
||||
// Types from vm/vm_param.h
|
||||
type xswdev struct {
|
||||
Version uint32 // Version is the version
|
||||
Dev uint64 // Dev is the device identifier
|
||||
Flags int32 // Flags is the swap flags applied to the device
|
||||
NBlks int32 // NBlks is the total number of blocks
|
||||
Used int32 // Used is the number of blocks used
|
||||
}
|
||||
|
||||
// xswdev11 is a compatibility for under FreeBSD 11
|
||||
// sys/vm/swap_pager.c
|
||||
type xswdev11 struct {
|
||||
Version uint32 // Version is the version
|
||||
Dev uint32 // Dev is the device identifier
|
||||
Flags int32 // Flags is the swap flags applied to the device
|
||||
NBlks int32 // NBlks is the total number of blocks
|
||||
Used int32 // Used is the number of blocks used
|
||||
}
|
||||
|
||||
func SwapMemoryWithContext(_ context.Context) (*SwapMemoryStat, error) {
|
||||
// FreeBSD can have multiple swap devices so we total them up
|
||||
i, err := common.SysctlUint("vm.nswapdev")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if i == 0 {
|
||||
return nil, errors.New("no swap devices found")
|
||||
}
|
||||
|
||||
c := int(i)
|
||||
|
||||
i, err = common.SysctlUint("vm.stats.vm.v_page_size")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pageSize := i
|
||||
|
||||
var buf []byte
|
||||
s := &SwapMemoryStat{}
|
||||
for n := 0; n < c; n++ {
|
||||
buf, err = unix.SysctlRaw("vm.swap_info", n)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// first, try to parse with version 2
|
||||
xsw := (*xswdev)(unsafe.Pointer(&buf[0]))
|
||||
switch {
|
||||
case xsw.Version == XSWDEV_VERSION11:
|
||||
// this is version 1, so try to parse again
|
||||
xsw := (*xswdev11)(unsafe.Pointer(&buf[0]))
|
||||
if xsw.Version != XSWDEV_VERSION11 {
|
||||
return nil, errors.New("xswdev version mismatch(11)")
|
||||
}
|
||||
s.Total += uint64(xsw.NBlks)
|
||||
s.Used += uint64(xsw.Used)
|
||||
case xsw.Version != XSWDEV_VERSION:
|
||||
return nil, errors.New("xswdev version mismatch")
|
||||
default:
|
||||
s.Total += uint64(xsw.NBlks)
|
||||
s.Used += uint64(xsw.Used)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if s.Total != 0 {
|
||||
s.UsedPercent = float64(s.Used) / float64(s.Total) * 100
|
||||
}
|
||||
s.Total *= pageSize
|
||||
s.Used *= pageSize
|
||||
s.Free = s.Total - s.Used
|
||||
|
||||
return s, nil
|
||||
}
|
||||
+524
@@ -0,0 +1,524 @@
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
//go:build linux
|
||||
|
||||
package mem
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"github.com/shirou/gopsutil/v4/internal/common"
|
||||
)
|
||||
|
||||
func VirtualMemory() (*VirtualMemoryStat, error) {
|
||||
return VirtualMemoryWithContext(context.Background())
|
||||
}
|
||||
|
||||
func VirtualMemoryWithContext(ctx context.Context) (*VirtualMemoryStat, error) {
|
||||
vm, _, err := fillFromMeminfoWithContext(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return vm, nil
|
||||
}
|
||||
|
||||
func fillFromMeminfoWithContext(ctx context.Context) (*VirtualMemoryStat, *ExVirtualMemory, error) {
|
||||
filename := common.HostProcWithContext(ctx, "meminfo")
|
||||
lines, err := common.ReadLines(filename)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("couldn't read %s: %w", filename, err)
|
||||
}
|
||||
|
||||
// flag if MemAvailable is in /proc/meminfo (kernel 3.14+)
|
||||
memavail := false
|
||||
activeFile := false // "Active(file)" not available: 2.6.28 / Dec 2008
|
||||
inactiveFile := false // "Inactive(file)" not available: 2.6.28 / Dec 2008
|
||||
sReclaimable := false // "Sreclaimable:" not available: 2.6.19 / Nov 2006
|
||||
|
||||
ret := &VirtualMemoryStat{}
|
||||
retEx := &ExVirtualMemory{}
|
||||
|
||||
for _, line := range lines {
|
||||
fields := strings.Split(line, ":")
|
||||
if len(fields) != 2 {
|
||||
continue
|
||||
}
|
||||
key := strings.TrimSpace(fields[0])
|
||||
value := strings.TrimSpace(fields[1])
|
||||
value = strings.ReplaceAll(value, " kB", "")
|
||||
|
||||
switch key {
|
||||
case "MemTotal":
|
||||
t, err := strconv.ParseUint(value, 10, 64)
|
||||
if err != nil {
|
||||
return ret, retEx, err
|
||||
}
|
||||
ret.Total = t * 1024
|
||||
case "MemFree":
|
||||
t, err := strconv.ParseUint(value, 10, 64)
|
||||
if err != nil {
|
||||
return ret, retEx, err
|
||||
}
|
||||
ret.Free = t * 1024
|
||||
case "MemAvailable":
|
||||
t, err := strconv.ParseUint(value, 10, 64)
|
||||
if err != nil {
|
||||
return ret, retEx, err
|
||||
}
|
||||
memavail = true
|
||||
ret.Available = t * 1024
|
||||
case "Buffers":
|
||||
t, err := strconv.ParseUint(value, 10, 64)
|
||||
if err != nil {
|
||||
return ret, retEx, err
|
||||
}
|
||||
ret.Buffers = t * 1024
|
||||
case "Cached":
|
||||
t, err := strconv.ParseUint(value, 10, 64)
|
||||
if err != nil {
|
||||
return ret, retEx, err
|
||||
}
|
||||
ret.Cached = t * 1024
|
||||
case "Active":
|
||||
t, err := strconv.ParseUint(value, 10, 64)
|
||||
if err != nil {
|
||||
return ret, retEx, err
|
||||
}
|
||||
ret.Active = t * 1024
|
||||
case "Inactive":
|
||||
t, err := strconv.ParseUint(value, 10, 64)
|
||||
if err != nil {
|
||||
return ret, retEx, err
|
||||
}
|
||||
ret.Inactive = t * 1024
|
||||
case "Active(anon)":
|
||||
t, err := strconv.ParseUint(value, 10, 64)
|
||||
if err != nil {
|
||||
return ret, retEx, err
|
||||
}
|
||||
retEx.ActiveAnon = t * 1024
|
||||
case "Inactive(anon)":
|
||||
t, err := strconv.ParseUint(value, 10, 64)
|
||||
if err != nil {
|
||||
return ret, retEx, err
|
||||
}
|
||||
retEx.InactiveAnon = t * 1024
|
||||
case "Active(file)":
|
||||
t, err := strconv.ParseUint(value, 10, 64)
|
||||
if err != nil {
|
||||
return ret, retEx, err
|
||||
}
|
||||
activeFile = true
|
||||
retEx.ActiveFile = t * 1024
|
||||
case "Inactive(file)":
|
||||
t, err := strconv.ParseUint(value, 10, 64)
|
||||
if err != nil {
|
||||
return ret, retEx, err
|
||||
}
|
||||
inactiveFile = true
|
||||
retEx.InactiveFile = t * 1024
|
||||
case "Unevictable":
|
||||
t, err := strconv.ParseUint(value, 10, 64)
|
||||
if err != nil {
|
||||
return ret, retEx, err
|
||||
}
|
||||
retEx.Unevictable = t * 1024
|
||||
case "Percpu":
|
||||
t, err := strconv.ParseUint(value, 10, 64)
|
||||
if err != nil {
|
||||
return ret, retEx, err
|
||||
}
|
||||
retEx.Percpu = t * 1024
|
||||
case "Writeback":
|
||||
t, err := strconv.ParseUint(value, 10, 64)
|
||||
if err != nil {
|
||||
return ret, retEx, err
|
||||
}
|
||||
ret.WriteBack = t * 1024
|
||||
case "WritebackTmp":
|
||||
t, err := strconv.ParseUint(value, 10, 64)
|
||||
if err != nil {
|
||||
return ret, retEx, err
|
||||
}
|
||||
ret.WriteBackTmp = t * 1024
|
||||
case "Dirty":
|
||||
t, err := strconv.ParseUint(value, 10, 64)
|
||||
if err != nil {
|
||||
return ret, retEx, err
|
||||
}
|
||||
ret.Dirty = t * 1024
|
||||
case "Shmem":
|
||||
t, err := strconv.ParseUint(value, 10, 64)
|
||||
if err != nil {
|
||||
return ret, retEx, err
|
||||
}
|
||||
ret.Shared = t * 1024
|
||||
case "Slab":
|
||||
t, err := strconv.ParseUint(value, 10, 64)
|
||||
if err != nil {
|
||||
return ret, retEx, err
|
||||
}
|
||||
ret.Slab = t * 1024
|
||||
case "SReclaimable":
|
||||
t, err := strconv.ParseUint(value, 10, 64)
|
||||
if err != nil {
|
||||
return ret, retEx, err
|
||||
}
|
||||
sReclaimable = true
|
||||
ret.Sreclaimable = t * 1024
|
||||
case "SUnreclaim":
|
||||
t, err := strconv.ParseUint(value, 10, 64)
|
||||
if err != nil {
|
||||
return ret, retEx, err
|
||||
}
|
||||
ret.Sunreclaim = t * 1024
|
||||
case "KernelStack":
|
||||
t, err := strconv.ParseUint(value, 10, 64)
|
||||
if err != nil {
|
||||
return ret, retEx, err
|
||||
}
|
||||
retEx.KernelStack = t * 1024
|
||||
case "PageTables":
|
||||
t, err := strconv.ParseUint(value, 10, 64)
|
||||
if err != nil {
|
||||
return ret, retEx, err
|
||||
}
|
||||
ret.PageTables = t * 1024
|
||||
case "SwapCached":
|
||||
t, err := strconv.ParseUint(value, 10, 64)
|
||||
if err != nil {
|
||||
return ret, retEx, err
|
||||
}
|
||||
ret.SwapCached = t * 1024
|
||||
case "CommitLimit":
|
||||
t, err := strconv.ParseUint(value, 10, 64)
|
||||
if err != nil {
|
||||
return ret, retEx, err
|
||||
}
|
||||
ret.CommitLimit = t * 1024
|
||||
case "Committed_AS":
|
||||
t, err := strconv.ParseUint(value, 10, 64)
|
||||
if err != nil {
|
||||
return ret, retEx, err
|
||||
}
|
||||
ret.CommittedAS = t * 1024
|
||||
case "HighTotal":
|
||||
t, err := strconv.ParseUint(value, 10, 64)
|
||||
if err != nil {
|
||||
return ret, retEx, err
|
||||
}
|
||||
ret.HighTotal = t * 1024
|
||||
case "HighFree":
|
||||
t, err := strconv.ParseUint(value, 10, 64)
|
||||
if err != nil {
|
||||
return ret, retEx, err
|
||||
}
|
||||
ret.HighFree = t * 1024
|
||||
case "LowTotal":
|
||||
t, err := strconv.ParseUint(value, 10, 64)
|
||||
if err != nil {
|
||||
return ret, retEx, err
|
||||
}
|
||||
ret.LowTotal = t * 1024
|
||||
case "LowFree":
|
||||
t, err := strconv.ParseUint(value, 10, 64)
|
||||
if err != nil {
|
||||
return ret, retEx, err
|
||||
}
|
||||
ret.LowFree = t * 1024
|
||||
case "SwapTotal":
|
||||
t, err := strconv.ParseUint(value, 10, 64)
|
||||
if err != nil {
|
||||
return ret, retEx, err
|
||||
}
|
||||
ret.SwapTotal = t * 1024
|
||||
case "SwapFree":
|
||||
t, err := strconv.ParseUint(value, 10, 64)
|
||||
if err != nil {
|
||||
return ret, retEx, err
|
||||
}
|
||||
ret.SwapFree = t * 1024
|
||||
case "Mapped":
|
||||
t, err := strconv.ParseUint(value, 10, 64)
|
||||
if err != nil {
|
||||
return ret, retEx, err
|
||||
}
|
||||
ret.Mapped = t * 1024
|
||||
case "VmallocTotal":
|
||||
t, err := strconv.ParseUint(value, 10, 64)
|
||||
if err != nil {
|
||||
return ret, retEx, err
|
||||
}
|
||||
ret.VmallocTotal = t * 1024
|
||||
case "VmallocUsed":
|
||||
t, err := strconv.ParseUint(value, 10, 64)
|
||||
if err != nil {
|
||||
return ret, retEx, err
|
||||
}
|
||||
ret.VmallocUsed = t * 1024
|
||||
case "VmallocChunk":
|
||||
t, err := strconv.ParseUint(value, 10, 64)
|
||||
if err != nil {
|
||||
return ret, retEx, err
|
||||
}
|
||||
ret.VmallocChunk = t * 1024
|
||||
case "HugePages_Total":
|
||||
t, err := strconv.ParseUint(value, 10, 64)
|
||||
if err != nil {
|
||||
return ret, retEx, err
|
||||
}
|
||||
ret.HugePagesTotal = t
|
||||
case "HugePages_Free":
|
||||
t, err := strconv.ParseUint(value, 10, 64)
|
||||
if err != nil {
|
||||
return ret, retEx, err
|
||||
}
|
||||
ret.HugePagesFree = t
|
||||
case "HugePages_Rsvd":
|
||||
t, err := strconv.ParseUint(value, 10, 64)
|
||||
if err != nil {
|
||||
return ret, retEx, err
|
||||
}
|
||||
ret.HugePagesRsvd = t
|
||||
case "HugePages_Surp":
|
||||
t, err := strconv.ParseUint(value, 10, 64)
|
||||
if err != nil {
|
||||
return ret, retEx, err
|
||||
}
|
||||
ret.HugePagesSurp = t
|
||||
case "Hugepagesize":
|
||||
t, err := strconv.ParseUint(value, 10, 64)
|
||||
if err != nil {
|
||||
return ret, retEx, err
|
||||
}
|
||||
ret.HugePageSize = t * 1024
|
||||
case "AnonHugePages":
|
||||
t, err := strconv.ParseUint(value, 10, 64)
|
||||
if err != nil {
|
||||
return ret, retEx, err
|
||||
}
|
||||
ret.AnonHugePages = t * 1024
|
||||
}
|
||||
}
|
||||
|
||||
ret.Cached += ret.Sreclaimable
|
||||
|
||||
if !memavail {
|
||||
if activeFile && inactiveFile && sReclaimable {
|
||||
ret.Available = calculateAvailVmem(ctx, ret, retEx)
|
||||
} else {
|
||||
ret.Available = ret.Cached + ret.Free
|
||||
}
|
||||
}
|
||||
ret.Used = ret.Total - ret.Available
|
||||
|
||||
ret.UsedPercent = float64(ret.Used) / float64(ret.Total) * 100.0
|
||||
|
||||
return ret, retEx, nil
|
||||
}
|
||||
|
||||
func SwapMemory() (*SwapMemoryStat, error) {
|
||||
return SwapMemoryWithContext(context.Background())
|
||||
}
|
||||
|
||||
func SwapMemoryWithContext(ctx context.Context) (*SwapMemoryStat, error) {
|
||||
sysinfo := &unix.Sysinfo_t{}
|
||||
|
||||
if err := unix.Sysinfo(sysinfo); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret := &SwapMemoryStat{
|
||||
Total: uint64(sysinfo.Totalswap) * uint64(sysinfo.Unit),
|
||||
Free: uint64(sysinfo.Freeswap) * uint64(sysinfo.Unit),
|
||||
}
|
||||
ret.Used = ret.Total - ret.Free
|
||||
// check Infinity
|
||||
if ret.Total != 0 {
|
||||
ret.UsedPercent = float64(ret.Total-ret.Free) / float64(ret.Total) * 100.0
|
||||
} else {
|
||||
ret.UsedPercent = 0
|
||||
}
|
||||
filename := common.HostProcWithContext(ctx, "vmstat")
|
||||
lines, err := common.ReadLines(filename)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("couldn't read %s: %w", filename, err)
|
||||
}
|
||||
for _, l := range lines {
|
||||
fields := strings.Fields(l)
|
||||
if len(fields) < 2 {
|
||||
continue
|
||||
}
|
||||
switch fields[0] {
|
||||
case "pswpin":
|
||||
value, err := strconv.ParseUint(fields[1], 10, 64)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
ret.Sin = value * 4 * 1024
|
||||
case "pswpout":
|
||||
value, err := strconv.ParseUint(fields[1], 10, 64)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
ret.Sout = value * 4 * 1024
|
||||
case "pgpgin":
|
||||
value, err := strconv.ParseUint(fields[1], 10, 64)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
ret.PgIn = value * 4 * 1024
|
||||
case "pgpgout":
|
||||
value, err := strconv.ParseUint(fields[1], 10, 64)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
ret.PgOut = value * 4 * 1024
|
||||
case "pgfault":
|
||||
value, err := strconv.ParseUint(fields[1], 10, 64)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
ret.PgFault = value * 4 * 1024
|
||||
case "pgmajfault":
|
||||
value, err := strconv.ParseUint(fields[1], 10, 64)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
ret.PgMajFault = value * 4 * 1024
|
||||
}
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// calculateAvailVmem is a fallback under kernel 3.14 where /proc/meminfo does not provide
|
||||
// "MemAvailable:" column. It reimplements an algorithm from the link below
|
||||
// https://github.com/giampaolo/psutil/pull/890
|
||||
func calculateAvailVmem(ctx context.Context, ret *VirtualMemoryStat, retEx *ExVirtualMemory) uint64 {
|
||||
var watermarkLow uint64
|
||||
|
||||
fn := common.HostProcWithContext(ctx, "zoneinfo")
|
||||
lines, err := common.ReadLines(fn)
|
||||
if err != nil {
|
||||
return ret.Free + ret.Cached // fallback under kernel 2.6.13
|
||||
}
|
||||
|
||||
pagesize := uint64(os.Getpagesize())
|
||||
watermarkLow = 0
|
||||
|
||||
for _, line := range lines {
|
||||
fields := strings.Fields(line)
|
||||
|
||||
if strings.HasPrefix(fields[0], "low") {
|
||||
lowValue, err := strconv.ParseUint(fields[1], 10, 64)
|
||||
if err != nil {
|
||||
lowValue = 0
|
||||
}
|
||||
watermarkLow += lowValue
|
||||
}
|
||||
}
|
||||
|
||||
watermarkLow *= pagesize
|
||||
|
||||
availMemory := ret.Free - watermarkLow
|
||||
pageCache := retEx.ActiveFile + retEx.InactiveFile
|
||||
pageCache -= uint64(math.Min(float64(pageCache/2), float64(watermarkLow)))
|
||||
availMemory += pageCache
|
||||
availMemory += ret.Sreclaimable - uint64(math.Min(float64(ret.Sreclaimable/2.0), float64(watermarkLow)))
|
||||
|
||||
if availMemory < 0 {
|
||||
availMemory = 0
|
||||
}
|
||||
|
||||
return availMemory
|
||||
}
|
||||
|
||||
const swapsFilename = "swaps"
|
||||
|
||||
// swaps file column indexes
|
||||
const (
|
||||
nameCol = 0
|
||||
// typeCol = 1
|
||||
totalCol = 2
|
||||
usedCol = 3
|
||||
// priorityCol = 4
|
||||
)
|
||||
|
||||
func SwapDevices() ([]*SwapDevice, error) {
|
||||
return SwapDevicesWithContext(context.Background())
|
||||
}
|
||||
|
||||
func SwapDevicesWithContext(ctx context.Context) ([]*SwapDevice, error) {
|
||||
swapsFilePath := common.HostProcWithContext(ctx, swapsFilename)
|
||||
f, err := os.Open(swapsFilePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
return parseSwapsFile(ctx, f)
|
||||
}
|
||||
|
||||
func parseSwapsFile(ctx context.Context, r io.Reader) ([]*SwapDevice, error) {
|
||||
swapsFilePath := common.HostProcWithContext(ctx, swapsFilename)
|
||||
scanner := bufio.NewScanner(r)
|
||||
if !scanner.Scan() {
|
||||
if err := scanner.Err(); err != nil {
|
||||
return nil, fmt.Errorf("couldn't read file %q: %w", swapsFilePath, err)
|
||||
}
|
||||
return nil, fmt.Errorf("unexpected end-of-file in %q", swapsFilePath)
|
||||
|
||||
}
|
||||
|
||||
// Check header headerFields are as expected
|
||||
headerFields := strings.Fields(scanner.Text())
|
||||
if len(headerFields) < usedCol {
|
||||
return nil, fmt.Errorf("couldn't parse %q: too few fields in header", swapsFilePath)
|
||||
}
|
||||
if headerFields[nameCol] != "Filename" {
|
||||
return nil, fmt.Errorf("couldn't parse %q: expected %q to be %q", swapsFilePath, headerFields[nameCol], "Filename")
|
||||
}
|
||||
if headerFields[totalCol] != "Size" {
|
||||
return nil, fmt.Errorf("couldn't parse %q: expected %q to be %q", swapsFilePath, headerFields[totalCol], "Size")
|
||||
}
|
||||
if headerFields[usedCol] != "Used" {
|
||||
return nil, fmt.Errorf("couldn't parse %q: expected %q to be %q", swapsFilePath, headerFields[usedCol], "Used")
|
||||
}
|
||||
|
||||
var swapDevices []*SwapDevice
|
||||
for scanner.Scan() {
|
||||
fields := strings.Fields(scanner.Text())
|
||||
if len(fields) < usedCol {
|
||||
return nil, fmt.Errorf("couldn't parse %q: too few fields", swapsFilePath)
|
||||
}
|
||||
|
||||
totalKiB, err := strconv.ParseUint(fields[totalCol], 10, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("couldn't parse 'Size' column in %q: %w", swapsFilePath, err)
|
||||
}
|
||||
|
||||
usedKiB, err := strconv.ParseUint(fields[usedCol], 10, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("couldn't parse 'Used' column in %q: %w", swapsFilePath, err)
|
||||
}
|
||||
|
||||
swapDevices = append(swapDevices, &SwapDevice{
|
||||
Name: fields[nameCol],
|
||||
UsedBytes: usedKiB * 1024,
|
||||
FreeBytes: (totalKiB - usedKiB) * 1024,
|
||||
})
|
||||
}
|
||||
|
||||
if err := scanner.Err(); err != nil {
|
||||
return nil, fmt.Errorf("couldn't read file %q: %w", swapsFilePath, err)
|
||||
}
|
||||
|
||||
return swapDevices, nil
|
||||
}
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
//go:build netbsd
|
||||
|
||||
package mem
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func GetPageSize() (uint64, error) {
|
||||
return GetPageSizeWithContext(context.Background())
|
||||
}
|
||||
|
||||
func GetPageSizeWithContext(_ context.Context) (uint64, error) {
|
||||
uvmexp, err := unix.SysctlUvmexp("vm.uvmexp2")
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return uint64(uvmexp.Pagesize), nil
|
||||
}
|
||||
|
||||
func VirtualMemory() (*VirtualMemoryStat, error) {
|
||||
return VirtualMemoryWithContext(context.Background())
|
||||
}
|
||||
|
||||
func VirtualMemoryWithContext(_ context.Context) (*VirtualMemoryStat, error) {
|
||||
uvmexp, err := unix.SysctlUvmexp("vm.uvmexp2")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p := uint64(uvmexp.Pagesize)
|
||||
|
||||
ret := &VirtualMemoryStat{
|
||||
Total: uint64(uvmexp.Npages) * p,
|
||||
Free: uint64(uvmexp.Free) * p,
|
||||
Active: uint64(uvmexp.Active) * p,
|
||||
Inactive: uint64(uvmexp.Inactive) * p,
|
||||
Cached: 0, // not available
|
||||
Wired: uint64(uvmexp.Wired) * p,
|
||||
}
|
||||
|
||||
ret.Available = ret.Inactive + ret.Cached + ret.Free
|
||||
ret.Used = ret.Total - ret.Available
|
||||
ret.UsedPercent = float64(ret.Used) / float64(ret.Total) * 100.0
|
||||
|
||||
// Get buffers from vm.bufmem sysctl
|
||||
ret.Buffers, err = unix.SysctlUint64("vm.bufmem")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// Return swapctl summary info
|
||||
func SwapMemory() (*SwapMemoryStat, error) {
|
||||
return SwapMemoryWithContext(context.Background())
|
||||
}
|
||||
|
||||
func SwapMemoryWithContext(ctx context.Context) (*SwapMemoryStat, error) {
|
||||
out, err := invoke.CommandWithContext(ctx, "swapctl", "-sk")
|
||||
if err != nil {
|
||||
return &SwapMemoryStat{}, nil
|
||||
}
|
||||
|
||||
line := string(out)
|
||||
var total, used, free uint64
|
||||
|
||||
_, err = fmt.Sscanf(line,
|
||||
"total: %d 1K-blocks allocated, %d used, %d available",
|
||||
&total, &used, &free)
|
||||
if err != nil {
|
||||
return nil, errors.New("failed to parse swapctl output")
|
||||
}
|
||||
|
||||
percent := float64(used) / float64(total) * 100
|
||||
return &SwapMemoryStat{
|
||||
Total: total * 1024,
|
||||
Used: used * 1024,
|
||||
Free: free * 1024,
|
||||
UsedPercent: percent,
|
||||
}, nil
|
||||
}
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
//go:build openbsd
|
||||
|
||||
package mem
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"github.com/shirou/gopsutil/v4/internal/common"
|
||||
)
|
||||
|
||||
func GetPageSize() (uint64, error) {
|
||||
return GetPageSizeWithContext(context.Background())
|
||||
}
|
||||
|
||||
func GetPageSizeWithContext(_ context.Context) (uint64, error) {
|
||||
uvmexp, err := unix.SysctlUvmexp("vm.uvmexp")
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return uint64(uvmexp.Pagesize), nil
|
||||
}
|
||||
|
||||
func VirtualMemory() (*VirtualMemoryStat, error) {
|
||||
return VirtualMemoryWithContext(context.Background())
|
||||
}
|
||||
|
||||
func VirtualMemoryWithContext(_ context.Context) (*VirtualMemoryStat, error) {
|
||||
uvmexp, err := unix.SysctlUvmexp("vm.uvmexp")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p := uint64(uvmexp.Pagesize)
|
||||
|
||||
mib := []int32{CTLVfs, VfsGeneric, VfsBcacheStat}
|
||||
buf, length, err := common.CallSyscall(mib)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if length < sizeOfBcachestats {
|
||||
return nil, fmt.Errorf("short syscall ret %d bytes", length)
|
||||
}
|
||||
var bcs Bcachestats
|
||||
br := bytes.NewReader(buf)
|
||||
if err := binary.Read(br, binary.LittleEndian, &bcs); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// On OpenBSD, the buffer cache is the closest equivalent to both
|
||||
// Linux's Buffers and Cached memory.
|
||||
bcache := uint64(bcs.Numbufpages) * p
|
||||
|
||||
ret := &VirtualMemoryStat{
|
||||
Total: uint64(uvmexp.Npages) * p,
|
||||
Free: uint64(uvmexp.Free) * p,
|
||||
Active: uint64(uvmexp.Active) * p,
|
||||
Inactive: uint64(uvmexp.Inactive) * p,
|
||||
Cached: bcache,
|
||||
Buffers: bcache,
|
||||
Wired: uint64(uvmexp.Wired) * p,
|
||||
}
|
||||
|
||||
ret.Available = ret.Inactive + ret.Cached + ret.Free
|
||||
ret.Used = ret.Total - ret.Available
|
||||
ret.UsedPercent = float64(ret.Used) / float64(ret.Total) * 100.0
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// Return swapctl summary info
|
||||
func SwapMemory() (*SwapMemoryStat, error) {
|
||||
return SwapMemoryWithContext(context.Background())
|
||||
}
|
||||
|
||||
func SwapMemoryWithContext(ctx context.Context) (*SwapMemoryStat, error) {
|
||||
out, err := invoke.CommandWithContext(ctx, "swapctl", "-sk")
|
||||
if err != nil {
|
||||
return &SwapMemoryStat{}, nil
|
||||
}
|
||||
|
||||
line := string(out)
|
||||
var total, used, free uint64
|
||||
|
||||
_, err = fmt.Sscanf(line,
|
||||
"total: %d 1K-blocks allocated, %d used, %d available",
|
||||
&total, &used, &free)
|
||||
if err != nil {
|
||||
return nil, errors.New("failed to parse swapctl output")
|
||||
}
|
||||
|
||||
percent := float64(used) / float64(total) * 100
|
||||
return &SwapMemoryStat{
|
||||
Total: total * 1024,
|
||||
Used: used * 1024,
|
||||
Free: free * 1024,
|
||||
UsedPercent: percent,
|
||||
}, nil
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
//go:build openbsd && 386
|
||||
|
||||
// Code generated by cmd/cgo -godefs; DO NOT EDIT.
|
||||
// cgo -godefs mem/types_openbsd.go
|
||||
|
||||
package mem
|
||||
|
||||
const (
|
||||
CTLVfs = 10
|
||||
VfsGeneric = 0
|
||||
VfsBcacheStat = 3
|
||||
)
|
||||
|
||||
const (
|
||||
sizeOfBcachestats = 0x90
|
||||
)
|
||||
|
||||
type Bcachestats struct {
|
||||
Numbufs int64
|
||||
Numbufpages int64
|
||||
Numdirtypages int64
|
||||
Numcleanpages int64
|
||||
Pendingwrites int64
|
||||
Pendingreads int64
|
||||
Numwrites int64
|
||||
Numreads int64
|
||||
Cachehits int64
|
||||
Busymapped int64
|
||||
Dmapages int64
|
||||
Highpages int64
|
||||
Delwribufs int64
|
||||
Kvaslots int64
|
||||
Avail int64
|
||||
Highflips int64
|
||||
Highflops int64
|
||||
Dmaflips int64
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
// Created by cgo -godefs - DO NOT EDIT
|
||||
// cgo -godefs types_openbsd.go
|
||||
|
||||
package mem
|
||||
|
||||
const (
|
||||
CTLVfs = 10
|
||||
VfsGeneric = 0
|
||||
VfsBcacheStat = 3
|
||||
)
|
||||
|
||||
const (
|
||||
sizeOfBcachestats = 0x78
|
||||
)
|
||||
|
||||
type Bcachestats struct {
|
||||
Numbufs int64
|
||||
Numbufpages int64
|
||||
Numdirtypages int64
|
||||
Numcleanpages int64
|
||||
Pendingwrites int64
|
||||
Pendingreads int64
|
||||
Numwrites int64
|
||||
Numreads int64
|
||||
Cachehits int64
|
||||
Busymapped int64
|
||||
Dmapages int64
|
||||
Highpages int64
|
||||
Delwribufs int64
|
||||
Kvaslots int64
|
||||
Avail int64
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
//go:build openbsd && arm
|
||||
|
||||
// Code generated by cmd/cgo -godefs; DO NOT EDIT.
|
||||
// cgo -godefs mem/types_openbsd.go
|
||||
|
||||
package mem
|
||||
|
||||
const (
|
||||
CTLVfs = 10
|
||||
VfsGeneric = 0
|
||||
VfsBcacheStat = 3
|
||||
)
|
||||
|
||||
const (
|
||||
sizeOfBcachestats = 0x90
|
||||
)
|
||||
|
||||
type Bcachestats struct {
|
||||
Numbufs int64
|
||||
Numbufpages int64
|
||||
Numdirtypages int64
|
||||
Numcleanpages int64
|
||||
Pendingwrites int64
|
||||
Pendingreads int64
|
||||
Numwrites int64
|
||||
Numreads int64
|
||||
Cachehits int64
|
||||
Busymapped int64
|
||||
Dmapages int64
|
||||
Highpages int64
|
||||
Delwribufs int64
|
||||
Kvaslots int64
|
||||
Avail int64
|
||||
Highflips int64
|
||||
Highflops int64
|
||||
Dmaflips int64
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
//go:build openbsd && arm64
|
||||
|
||||
// Code generated by cmd/cgo -godefs; DO NOT EDIT.
|
||||
// cgo -godefs mem/types_openbsd.go
|
||||
|
||||
package mem
|
||||
|
||||
const (
|
||||
CTLVfs = 10
|
||||
VfsGeneric = 0
|
||||
VfsBcacheStat = 3
|
||||
)
|
||||
|
||||
const (
|
||||
sizeOfBcachestats = 0x90
|
||||
)
|
||||
|
||||
type Bcachestats struct {
|
||||
Numbufs int64
|
||||
Numbufpages int64
|
||||
Numdirtypages int64
|
||||
Numcleanpages int64
|
||||
Pendingwrites int64
|
||||
Pendingreads int64
|
||||
Numwrites int64
|
||||
Numreads int64
|
||||
Cachehits int64
|
||||
Busymapped int64
|
||||
Dmapages int64
|
||||
Highpages int64
|
||||
Delwribufs int64
|
||||
Kvaslots int64
|
||||
Avail int64
|
||||
Highflips int64
|
||||
Highflops int64
|
||||
Dmaflips int64
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
//go:build openbsd && riscv64
|
||||
|
||||
// Code generated by cmd/cgo -godefs; DO NOT EDIT.
|
||||
// cgo -godefs mem/types_openbsd.go
|
||||
|
||||
package mem
|
||||
|
||||
const (
|
||||
CTLVfs = 10
|
||||
VfsGeneric = 0
|
||||
VfsBcacheStat = 3
|
||||
)
|
||||
|
||||
const (
|
||||
sizeOfBcachestats = 0x90
|
||||
)
|
||||
|
||||
type Bcachestats struct {
|
||||
Numbufs int64
|
||||
Numbufpages int64
|
||||
Numdirtypages int64
|
||||
Numcleanpages int64
|
||||
Pendingwrites int64
|
||||
Pendingreads int64
|
||||
Numwrites int64
|
||||
Numreads int64
|
||||
Cachehits int64
|
||||
Busymapped int64
|
||||
Dmapages int64
|
||||
Highpages int64
|
||||
Delwribufs int64
|
||||
Kvaslots int64
|
||||
Avail int64
|
||||
Highflips int64
|
||||
Highflops int64
|
||||
Dmaflips int64
|
||||
}
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
//go:build plan9
|
||||
|
||||
package mem
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
|
||||
stats "github.com/lufia/plan9stats"
|
||||
|
||||
"github.com/shirou/gopsutil/v4/internal/common"
|
||||
)
|
||||
|
||||
func SwapMemory() (*SwapMemoryStat, error) {
|
||||
return SwapMemoryWithContext(context.Background())
|
||||
}
|
||||
|
||||
func SwapMemoryWithContext(ctx context.Context) (*SwapMemoryStat, error) {
|
||||
root := os.Getenv("HOST_ROOT")
|
||||
m, err := stats.ReadMemStats(ctx, stats.WithRootDir(root))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
u := 0.0
|
||||
if m.SwapPages.Avail != 0 {
|
||||
u = float64(m.SwapPages.Used) / float64(m.SwapPages.Avail) * 100.0
|
||||
}
|
||||
return &SwapMemoryStat{
|
||||
Total: uint64(m.SwapPages.Avail * m.PageSize),
|
||||
Used: uint64(m.SwapPages.Used * m.PageSize),
|
||||
Free: uint64(m.SwapPages.Free() * m.PageSize),
|
||||
UsedPercent: u,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func VirtualMemory() (*VirtualMemoryStat, error) {
|
||||
return VirtualMemoryWithContext(context.Background())
|
||||
}
|
||||
|
||||
func VirtualMemoryWithContext(ctx context.Context) (*VirtualMemoryStat, error) {
|
||||
root := os.Getenv("HOST_ROOT")
|
||||
m, err := stats.ReadMemStats(ctx, stats.WithRootDir(root))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
u := 0.0
|
||||
if m.UserPages.Avail != 0 {
|
||||
u = float64(m.UserPages.Used) / float64(m.UserPages.Avail) * 100.0
|
||||
}
|
||||
return &VirtualMemoryStat{
|
||||
Total: uint64(m.Total),
|
||||
Available: uint64(m.UserPages.Free() * m.PageSize),
|
||||
Used: uint64(m.UserPages.Used * m.PageSize),
|
||||
UsedPercent: u,
|
||||
Free: uint64(m.UserPages.Free() * m.PageSize),
|
||||
|
||||
SwapTotal: uint64(m.SwapPages.Avail * m.PageSize),
|
||||
SwapFree: uint64(m.SwapPages.Free() * m.PageSize),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func SwapDevices() ([]*SwapDevice, error) {
|
||||
return SwapDevicesWithContext(context.Background())
|
||||
}
|
||||
|
||||
func SwapDevicesWithContext(_ context.Context) ([]*SwapDevice, error) {
|
||||
return nil, common.ErrNotImplementedError
|
||||
}
|
||||
+211
@@ -0,0 +1,211 @@
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
//go:build solaris
|
||||
|
||||
package mem
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/tklauser/go-sysconf"
|
||||
|
||||
"github.com/shirou/gopsutil/v4/internal/common"
|
||||
)
|
||||
|
||||
// VirtualMemory for Solaris is a minimal implementation which only returns
|
||||
// what Nomad needs. It does take into account global vs zone, however.
|
||||
func VirtualMemory() (*VirtualMemoryStat, error) {
|
||||
return VirtualMemoryWithContext(context.Background())
|
||||
}
|
||||
|
||||
func VirtualMemoryWithContext(ctx context.Context) (*VirtualMemoryStat, error) {
|
||||
result := &VirtualMemoryStat{}
|
||||
|
||||
zoneName, err := zoneName(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if zoneName == "global" {
|
||||
capacity, err := globalZoneMemoryCapacity(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result.Total = capacity
|
||||
freemem, err := globalZoneFreeMemory(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result.Available = freemem
|
||||
result.Free = freemem
|
||||
result.Used = result.Total - result.Free
|
||||
} else {
|
||||
capacity, err := nonGlobalZoneMemoryCapacity(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result.Total = capacity
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func SwapMemory() (*SwapMemoryStat, error) {
|
||||
return SwapMemoryWithContext(context.Background())
|
||||
}
|
||||
|
||||
func SwapMemoryWithContext(_ context.Context) (*SwapMemoryStat, error) {
|
||||
return nil, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func zoneName(ctx context.Context) (string, error) {
|
||||
out, err := invoke.CommandWithContext(ctx, "zonename")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return strings.TrimSpace(string(out)), nil
|
||||
}
|
||||
|
||||
var globalZoneMemoryCapacityMatch = regexp.MustCompile(`[Mm]emory size: (\d+) Megabytes`)
|
||||
|
||||
func globalZoneMemoryCapacity(ctx context.Context) (uint64, error) {
|
||||
out, err := invoke.CommandWithContext(ctx, "prtconf")
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
match := globalZoneMemoryCapacityMatch.FindAllStringSubmatch(string(out), -1)
|
||||
if len(match) != 1 {
|
||||
return 0, errors.New("memory size not contained in output of prtconf")
|
||||
}
|
||||
|
||||
totalMB, err := strconv.ParseUint(match[0][1], 10, 64)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return totalMB * 1024 * 1024, nil
|
||||
}
|
||||
|
||||
func globalZoneFreeMemory(ctx context.Context) (uint64, error) {
|
||||
output, err := invoke.CommandWithContext(ctx, "pagesize")
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
pagesize, err := strconv.ParseUint(strings.TrimSpace(string(output)), 10, 64)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
free, err := sysconf.Sysconf(sysconf.SC_AVPHYS_PAGES)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return uint64(free) * pagesize, nil
|
||||
}
|
||||
|
||||
var kstatMatch = regexp.MustCompile(`(\S+)\s+(\S*)`)
|
||||
|
||||
func nonGlobalZoneMemoryCapacity(ctx context.Context) (uint64, error) {
|
||||
out, err := invoke.CommandWithContext(ctx, "kstat", "-p", "-c", "zone_memory_cap", "memory_cap:*:*:physcap")
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
kstats := kstatMatch.FindAllStringSubmatch(string(out), -1)
|
||||
if len(kstats) != 1 {
|
||||
return 0, fmt.Errorf("expected 1 kstat, found %d", len(kstats))
|
||||
}
|
||||
|
||||
memSizeBytes, err := strconv.ParseUint(kstats[0][2], 10, 64)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return memSizeBytes, nil
|
||||
}
|
||||
|
||||
const swapCommand = "swap"
|
||||
|
||||
// The blockSize as reported by `swap -l`. See https://docs.oracle.com/cd/E23824_01/html/821-1459/fsswap-52195.html
|
||||
const blockSize = 512
|
||||
|
||||
// swapctl column indexes
|
||||
const (
|
||||
nameCol = 0
|
||||
// devCol = 1
|
||||
// swaploCol = 2
|
||||
totalBlocksCol = 3
|
||||
freeBlocksCol = 4
|
||||
)
|
||||
|
||||
func SwapDevices() ([]*SwapDevice, error) {
|
||||
return SwapDevicesWithContext(context.Background())
|
||||
}
|
||||
|
||||
func SwapDevicesWithContext(ctx context.Context) ([]*SwapDevice, error) {
|
||||
output, err := invoke.CommandWithContext(ctx, swapCommand, "-l")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not execute %q: %w", swapCommand, err)
|
||||
}
|
||||
|
||||
return parseSwapsCommandOutput(string(output))
|
||||
}
|
||||
|
||||
func parseSwapsCommandOutput(output string) ([]*SwapDevice, error) {
|
||||
lines := strings.Split(output, "\n")
|
||||
if len(lines) == 0 {
|
||||
return nil, fmt.Errorf("could not parse output of %q: no lines in %q", swapCommand, output)
|
||||
}
|
||||
|
||||
// Check header headerFields are as expected.
|
||||
headerFields := strings.Fields(lines[0])
|
||||
if len(headerFields) < freeBlocksCol {
|
||||
return nil, fmt.Errorf("couldn't parse %q: too few fields in header %q", swapCommand, lines[0])
|
||||
}
|
||||
if headerFields[nameCol] != "swapfile" {
|
||||
return nil, fmt.Errorf("couldn't parse %q: expected %q to be %q", swapCommand, headerFields[nameCol], "swapfile")
|
||||
}
|
||||
if headerFields[totalBlocksCol] != "blocks" {
|
||||
return nil, fmt.Errorf("couldn't parse %q: expected %q to be %q", swapCommand, headerFields[totalBlocksCol], "blocks")
|
||||
}
|
||||
if headerFields[freeBlocksCol] != "free" {
|
||||
return nil, fmt.Errorf("couldn't parse %q: expected %q to be %q", swapCommand, headerFields[freeBlocksCol], "free")
|
||||
}
|
||||
|
||||
var swapDevices []*SwapDevice
|
||||
for _, line := range lines[1:] {
|
||||
if line == "" {
|
||||
continue // the terminal line is typically empty
|
||||
}
|
||||
fields := strings.Fields(line)
|
||||
if len(fields) < freeBlocksCol {
|
||||
return nil, fmt.Errorf("couldn't parse %q: too few fields", swapCommand)
|
||||
}
|
||||
|
||||
totalBlocks, err := strconv.ParseUint(fields[totalBlocksCol], 10, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("couldn't parse 'Size' column in %q: %w", swapCommand, err)
|
||||
}
|
||||
|
||||
freeBlocks, err := strconv.ParseUint(fields[freeBlocksCol], 10, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("couldn't parse 'Used' column in %q: %w", swapCommand, err)
|
||||
}
|
||||
|
||||
swapDevices = append(swapDevices, &SwapDevice{
|
||||
Name: fields[nameCol],
|
||||
UsedBytes: (totalBlocks - freeBlocks) * blockSize,
|
||||
FreeBytes: freeBlocks * blockSize,
|
||||
})
|
||||
}
|
||||
|
||||
return swapDevices, nil
|
||||
}
|
||||
+189
@@ -0,0 +1,189 @@
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
//go:build windows
|
||||
|
||||
package mem
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
|
||||
"github.com/shirou/gopsutil/v4/internal/common"
|
||||
)
|
||||
|
||||
var (
|
||||
procEnumPageFilesW = common.ModPsapi.NewProc("EnumPageFilesW")
|
||||
procGetNativeSystemInfo = common.Modkernel32.NewProc("GetNativeSystemInfo")
|
||||
procGetPerformanceInfo = common.ModPsapi.NewProc("GetPerformanceInfo")
|
||||
procGlobalMemoryStatusEx = common.Modkernel32.NewProc("GlobalMemoryStatusEx")
|
||||
)
|
||||
|
||||
type memoryStatusEx struct {
|
||||
cbSize uint32
|
||||
dwMemoryLoad uint32
|
||||
ullTotalPhys uint64 // in bytes
|
||||
ullAvailPhys uint64
|
||||
ullTotalPageFile uint64
|
||||
ullAvailPageFile uint64
|
||||
ullTotalVirtual uint64
|
||||
ullAvailVirtual uint64
|
||||
ullAvailExtendedVirtual uint64
|
||||
}
|
||||
|
||||
func VirtualMemory() (*VirtualMemoryStat, error) {
|
||||
return VirtualMemoryWithContext(context.Background())
|
||||
}
|
||||
|
||||
func VirtualMemoryWithContext(_ context.Context) (*VirtualMemoryStat, error) {
|
||||
var memInfo memoryStatusEx
|
||||
memInfo.cbSize = uint32(unsafe.Sizeof(memInfo))
|
||||
// GlobalMemoryStatusEx returns 0 for error, in which case we check err,
|
||||
// see https://pkg.go.dev/golang.org/x/sys/windows#LazyProc.Call
|
||||
mem, _, err := procGlobalMemoryStatusEx.Call(uintptr(unsafe.Pointer(&memInfo)))
|
||||
if mem == 0 {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret := &VirtualMemoryStat{
|
||||
Total: memInfo.ullTotalPhys,
|
||||
Available: memInfo.ullAvailPhys,
|
||||
Free: memInfo.ullAvailPhys,
|
||||
UsedPercent: float64(memInfo.dwMemoryLoad),
|
||||
}
|
||||
|
||||
ret.Used = ret.Total - ret.Available
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
type performanceInformation struct {
|
||||
cb uint32
|
||||
commitTotal uint64
|
||||
commitLimit uint64
|
||||
commitPeak uint64
|
||||
physicalTotal uint64
|
||||
physicalAvailable uint64
|
||||
systemCache uint64
|
||||
kernelTotal uint64
|
||||
kernelPaged uint64
|
||||
kernelNonpaged uint64
|
||||
pageSize uint64
|
||||
handleCount uint32
|
||||
processCount uint32
|
||||
threadCount uint32
|
||||
}
|
||||
|
||||
func SwapMemory() (*SwapMemoryStat, error) {
|
||||
return SwapMemoryWithContext(context.Background())
|
||||
}
|
||||
|
||||
func SwapMemoryWithContext(_ context.Context) (*SwapMemoryStat, error) {
|
||||
// Use the performance counter to get the swap usage percentage
|
||||
counter, err := common.NewWin32PerformanceCounter("swap_percentage", `\Paging File(_Total)\% Usage`)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer common.PdhCloseQuery.Call(uintptr(counter.Query))
|
||||
|
||||
usedPercent, err := counter.GetValue()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get total memory from performance information
|
||||
var perfInfo performanceInformation
|
||||
perfInfo.cb = uint32(unsafe.Sizeof(perfInfo))
|
||||
// GetPerformanceInfo returns 0 for error, in which case we check err,
|
||||
// see https://pkg.go.dev/golang.org/x/sys/windows#LazyProc.Call
|
||||
mem, _, err := procGetPerformanceInfo.Call(uintptr(unsafe.Pointer(&perfInfo)), uintptr(perfInfo.cb))
|
||||
if mem == 0 {
|
||||
return nil, err
|
||||
}
|
||||
totalPhys := perfInfo.physicalTotal * perfInfo.pageSize
|
||||
totalSys := perfInfo.commitLimit * perfInfo.pageSize
|
||||
total := totalSys - totalPhys
|
||||
|
||||
var used uint64
|
||||
if total > 0 {
|
||||
used = uint64(0.01 * usedPercent * float64(total))
|
||||
} else {
|
||||
usedPercent = 0.0
|
||||
used = 0
|
||||
}
|
||||
|
||||
ret := &SwapMemoryStat{
|
||||
Total: total,
|
||||
Used: used,
|
||||
Free: total - used,
|
||||
UsedPercent: common.Round(usedPercent, 1),
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
var (
|
||||
pageSize uint64
|
||||
pageSizeOnce sync.Once
|
||||
)
|
||||
|
||||
type systemInfo struct {
|
||||
wProcessorArchitecture uint16
|
||||
wReserved uint16
|
||||
dwPageSize uint32
|
||||
lpMinimumApplicationAddress uintptr
|
||||
lpMaximumApplicationAddress uintptr
|
||||
dwActiveProcessorMask uintptr
|
||||
dwNumberOfProcessors uint32
|
||||
dwProcessorType uint32
|
||||
dwAllocationGranularity uint32
|
||||
wProcessorLevel uint16
|
||||
wProcessorRevision uint16
|
||||
}
|
||||
|
||||
// system type as defined in https://docs.microsoft.com/en-us/windows/win32/api/psapi/ns-psapi-enum_page_file_information
|
||||
type enumPageFileInformation struct {
|
||||
cb uint32
|
||||
reserved uint32
|
||||
totalSize uint64
|
||||
totalInUse uint64
|
||||
peakUsage uint64
|
||||
}
|
||||
|
||||
func SwapDevices() ([]*SwapDevice, error) {
|
||||
return SwapDevicesWithContext(context.Background())
|
||||
}
|
||||
|
||||
func SwapDevicesWithContext(_ context.Context) ([]*SwapDevice, error) {
|
||||
pageSizeOnce.Do(func() {
|
||||
var sysInfo systemInfo
|
||||
procGetNativeSystemInfo.Call(uintptr(unsafe.Pointer(&sysInfo)))
|
||||
pageSize = uint64(sysInfo.dwPageSize)
|
||||
})
|
||||
|
||||
// the following system call invokes the supplied callback function once for each page file before returning
|
||||
// see https://docs.microsoft.com/en-us/windows/win32/api/psapi/nf-psapi-enumpagefilesw
|
||||
var swapDevices []*SwapDevice
|
||||
// EnumPageFilesW returns 0 for error, in which case we check err,
|
||||
// see https://pkg.go.dev/golang.org/x/sys/windows#LazyProc.Call
|
||||
result, _, err := procEnumPageFilesW.Call(windows.NewCallback(pEnumPageFileCallbackW), uintptr(unsafe.Pointer(&swapDevices)))
|
||||
if result == 0 {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return swapDevices, nil
|
||||
}
|
||||
|
||||
// system callback as defined in https://docs.microsoft.com/en-us/windows/win32/api/psapi/nc-psapi-penum_page_file_callbackw
|
||||
func pEnumPageFileCallbackW(swapDevices *[]*SwapDevice, enumPageFileInfo *enumPageFileInformation, lpFilenamePtr *[syscall.MAX_LONG_PATH]uint16) *bool {
|
||||
*swapDevices = append(*swapDevices, &SwapDevice{
|
||||
Name: syscall.UTF16ToString((*lpFilenamePtr)[:]),
|
||||
UsedBytes: enumPageFileInfo.totalInUse * pageSize,
|
||||
FreeBytes: (enumPageFileInfo.totalSize - enumPageFileInfo.totalInUse) * pageSize,
|
||||
})
|
||||
|
||||
// return true to continue enumerating page files
|
||||
ret := true
|
||||
return &ret
|
||||
}
|
||||
Reference in New Issue
Block a user