From 701a74be41d2830146f10c3cff1c93a4acff19a2 Mon Sep 17 00:00:00 2001 From: uubulb Date: Wed, 4 Sep 2024 22:19:05 +0800 Subject: [PATCH 01/24] feat(cpu, mem, sensors)(darwin): cgo-free implementations --- cpu/cpu_darwin.go | 92 ++++++++++++++++- cpu/cpu_darwin_cgo.go | 111 -------------------- cpu/cpu_darwin_nocgo.go | 14 --- go.mod | 1 + go.sum | 2 + internal/common/common_darwin.go | 217 +++++++++++++++++++++++++++++++++++++++ mem/mem_darwin.go | 58 +++++++++++ mem/mem_darwin_cgo.go | 58 ----------- mem/mem_darwin_nocgo.go | 89 ---------------- sensors/darwin_arm_sensors.h | 110 -------------------- sensors/sensors_darwin.go | 181 ++++++++++++++++++++++++++++++++ sensors/sensors_darwin_arm64.go | 186 +++++++++++++++++++++++++++++++++ sensors/sensors_darwin_cgo.go | 95 ----------------- sensors/sensors_darwin_nocgo.go | 14 --- sensors/smc_darwin.c | 170 ------------------------------ sensors/smc_darwin.h | 33 ------ 16 files changed, 735 insertions(+), 696 deletions(-) delete mode 100644 cpu/cpu_darwin_cgo.go delete mode 100644 cpu/cpu_darwin_nocgo.go delete mode 100644 mem/mem_darwin_cgo.go delete mode 100644 mem/mem_darwin_nocgo.go delete mode 100644 sensors/darwin_arm_sensors.h create mode 100644 sensors/sensors_darwin.go create mode 100644 sensors/sensors_darwin_arm64.go delete mode 100644 sensors/sensors_darwin_cgo.go delete mode 100644 sensors/sensors_darwin_nocgo.go delete mode 100644 sensors/smc_darwin.c delete mode 100644 sensors/smc_darwin.h diff --git a/cpu/cpu_darwin.go b/cpu/cpu_darwin.go index 79a458b..29d9a71 100644 --- a/cpu/cpu_darwin.go +++ b/cpu/cpu_darwin.go @@ -5,12 +5,16 @@ package cpu import ( "context" + "fmt" "strconv" "strings" + "unsafe" "github.com/shoenig/go-m1cpu" "github.com/tklauser/go-sysconf" "golang.org/x/sys/unix" + + "github.com/shirou/gopsutil/v4/internal/common" ) // sys/resource.h @@ -23,6 +27,24 @@ const ( cpUStates = 5 ) +// mach/machine.h +const ( + cpuStateUser = 0 + cpuStateSystem = 1 + cpuStateIdle = 2 + cpuStateNice = 3 + cpuStateMax = 4 +) + +// mach/processor_info.h +const ( + processorCpuLoadInfo = 2 +) + +type hostCpuLoadInfoData struct { + cpuTicks [cpuStateMax]uint32 +} + // default value. from time.h var ClocksPerSec = float64(128) @@ -39,11 +61,17 @@ func Times(percpu bool) ([]TimesStat, error) { } func TimesWithContext(ctx context.Context, percpu bool) ([]TimesStat, error) { + lib, err := common.NewLibrary(common.Kernel) + if err != nil { + return nil, err + } + defer lib.Close() + if percpu { - return perCPUTimes() + return perCPUTimes(lib) } - return allCPUTimes() + return allCPUTimes(lib) } // Returns only one CPUInfoStat on FreeBSD @@ -115,3 +143,63 @@ func CountsWithContext(ctx context.Context, logical bool) (int, error) { return int(count), nil } + +func perCPUTimes(machLib *common.Library) ([]TimesStat, error) { + machHostSelf := common.GetFunc[common.MachHostSelfFunc](machLib, common.MachHostSelfSym) + machTaskSelf := common.GetFunc[common.MachTaskSelfFunc](machLib, common.MachTaskSelfSym) + hostProcessorInfo := common.GetFunc[common.HostProcessorInfoFunc](machLib, common.HostProcessorInfoSym) + vmDeallocate := common.GetFunc[common.VMDeallocateFunc](machLib, common.VMDeallocateSym) + + var count, ncpu uint32 + var cpuload *hostCpuLoadInfoData + + status := hostProcessorInfo(machHostSelf(), processorCpuLoadInfo, &ncpu, uintptr(unsafe.Pointer(&cpuload)), &count) + + if status != common.KERN_SUCCESS { + return nil, fmt.Errorf("host_processor_info error=%d", status) + } + + defer vmDeallocate(machTaskSelf(), uintptr(unsafe.Pointer(cpuload)), uintptr(ncpu)) + + ret := []TimesStat{} + loads := unsafe.Slice(cpuload, ncpu) + + for i := 0; i < int(ncpu); i++ { + c := TimesStat{ + CPU: fmt.Sprintf("cpu%d", i), + User: float64(loads[i].cpuTicks[cpuStateUser]) / ClocksPerSec, + System: float64(loads[i].cpuTicks[cpuStateSystem]) / ClocksPerSec, + Nice: float64(loads[i].cpuTicks[cpuStateNice]) / ClocksPerSec, + Idle: float64(loads[i].cpuTicks[cpuStateIdle]) / ClocksPerSec, + } + + ret = append(ret, c) + } + + return ret, nil +} + +func allCPUTimes(machLib *common.Library) ([]TimesStat, error) { + machHostSelf := common.GetFunc[common.MachHostSelfFunc](machLib, common.MachHostSelfSym) + hostStatistics := common.GetFunc[common.HostStatisticsFunc](machLib, common.HostStatisticsSym) + + var cpuload hostCpuLoadInfoData + count := uint32(cpuStateMax) + + status := hostStatistics(machHostSelf(), common.HOST_CPU_LOAD_INFO, + uintptr(unsafe.Pointer(&cpuload)), &count) + + if status != common.KERN_SUCCESS { + return nil, fmt.Errorf("host_statistics error=%d", status) + } + + c := TimesStat{ + CPU: "cpu-total", + User: float64(cpuload.cpuTicks[cpuStateUser]) / ClocksPerSec, + System: float64(cpuload.cpuTicks[cpuStateSystem]) / ClocksPerSec, + Nice: float64(cpuload.cpuTicks[cpuStateNice]) / ClocksPerSec, + Idle: float64(cpuload.cpuTicks[cpuStateIdle]) / ClocksPerSec, + } + + return []TimesStat{c}, nil +} diff --git a/cpu/cpu_darwin_cgo.go b/cpu/cpu_darwin_cgo.go deleted file mode 100644 index 3a02024..0000000 --- a/cpu/cpu_darwin_cgo.go +++ /dev/null @@ -1,111 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -//go:build darwin && cgo - -package cpu - -/* -#include -#include -#include -#include -#include -#include -#include -#if TARGET_OS_MAC -#include -#endif -#include -#include -*/ -import "C" - -import ( - "bytes" - "encoding/binary" - "fmt" - "unsafe" -) - -// these CPU times for darwin is borrowed from influxdb/telegraf. - -func perCPUTimes() ([]TimesStat, error) { - var ( - count C.mach_msg_type_number_t - cpuload *C.processor_cpu_load_info_data_t - ncpu C.natural_t - ) - - status := C.host_processor_info(C.host_t(C.mach_host_self()), - C.PROCESSOR_CPU_LOAD_INFO, - &ncpu, - (*C.processor_info_array_t)(unsafe.Pointer(&cpuload)), - &count) - - if status != C.KERN_SUCCESS { - return nil, fmt.Errorf("host_processor_info error=%d", status) - } - - // jump through some cgo casting hoops and ensure we properly free - // the memory that cpuload points to - target := C.vm_map_t(C.mach_task_self_) - address := C.vm_address_t(uintptr(unsafe.Pointer(cpuload))) - defer C.vm_deallocate(target, address, C.vm_size_t(ncpu)) - - // the body of struct processor_cpu_load_info - // aka processor_cpu_load_info_data_t - var cpu_ticks [C.CPU_STATE_MAX]uint32 - - // copy the cpuload array to a []byte buffer - // where we can binary.Read the data - size := int(ncpu) * binary.Size(cpu_ticks) - buf := (*[1 << 30]byte)(unsafe.Pointer(cpuload))[:size:size] - - bbuf := bytes.NewBuffer(buf) - - var ret []TimesStat - - for i := 0; i < int(ncpu); i++ { - err := binary.Read(bbuf, binary.LittleEndian, &cpu_ticks) - if err != nil { - return nil, err - } - - c := TimesStat{ - CPU: fmt.Sprintf("cpu%d", i), - User: float64(cpu_ticks[C.CPU_STATE_USER]) / ClocksPerSec, - System: float64(cpu_ticks[C.CPU_STATE_SYSTEM]) / ClocksPerSec, - Nice: float64(cpu_ticks[C.CPU_STATE_NICE]) / ClocksPerSec, - Idle: float64(cpu_ticks[C.CPU_STATE_IDLE]) / ClocksPerSec, - } - - ret = append(ret, c) - } - - return ret, nil -} - -func allCPUTimes() ([]TimesStat, error) { - var count C.mach_msg_type_number_t - var cpuload C.host_cpu_load_info_data_t - - count = C.HOST_CPU_LOAD_INFO_COUNT - - status := C.host_statistics(C.host_t(C.mach_host_self()), - C.HOST_CPU_LOAD_INFO, - C.host_info_t(unsafe.Pointer(&cpuload)), - &count) - - if status != C.KERN_SUCCESS { - return nil, fmt.Errorf("host_statistics error=%d", status) - } - - c := TimesStat{ - CPU: "cpu-total", - User: float64(cpuload.cpu_ticks[C.CPU_STATE_USER]) / ClocksPerSec, - System: float64(cpuload.cpu_ticks[C.CPU_STATE_SYSTEM]) / ClocksPerSec, - Nice: float64(cpuload.cpu_ticks[C.CPU_STATE_NICE]) / ClocksPerSec, - Idle: float64(cpuload.cpu_ticks[C.CPU_STATE_IDLE]) / ClocksPerSec, - } - - return []TimesStat{c}, nil -} diff --git a/cpu/cpu_darwin_nocgo.go b/cpu/cpu_darwin_nocgo.go deleted file mode 100644 index 1af8566..0000000 --- a/cpu/cpu_darwin_nocgo.go +++ /dev/null @@ -1,14 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -//go:build darwin && !cgo - -package cpu - -import "github.com/shirou/gopsutil/v4/internal/common" - -func perCPUTimes() ([]TimesStat, error) { - return []TimesStat{}, common.ErrNotImplementedError -} - -func allCPUTimes() ([]TimesStat, error) { - return []TimesStat{}, common.ErrNotImplementedError -} diff --git a/go.mod b/go.mod index 4ead244..79ecebb 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/shirou/gopsutil/v4 go 1.18 require ( + github.com/ebitengine/purego v0.7.1 github.com/google/go-cmp v0.6.0 github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c diff --git a/go.sum b/go.sum index 7f6b7fb..a2896db 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,7 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/ebitengine/purego v0.7.1 h1:6/55d26lG3o9VCZX8lping+bZcmShseiqlh2bnUDiPA= +github.com/ebitengine/purego v0.7.1/go.mod h1:ah1In8AOtksoNK6yk5z1HTJeUkC1Ez4Wk2idgGslMwQ= github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= diff --git a/internal/common/common_darwin.go b/internal/common/common_darwin.go index 53f9ae8..0a1da93 100644 --- a/internal/common/common_darwin.go +++ b/internal/common/common_darwin.go @@ -5,11 +5,13 @@ package common import ( "context" + "fmt" "os" "os/exec" "strings" "unsafe" + "github.com/ebitengine/purego" "golang.org/x/sys/unix" ) @@ -64,3 +66,218 @@ func CallSyscall(mib []int32) ([]byte, uint64, error) { return buf, length, nil } + +// Library represents a dynamic library loaded by purego. +type Library struct { + addr uintptr + path string + close func() +} + +// library paths +const ( + IOKit = "/System/Library/Frameworks/IOKit.framework/IOKit" + CoreFoundation = "/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation" + Kernel = "/usr/lib/system/libsystem_kernel.dylib" +) + +func NewLibrary(path string) (*Library, error) { + lib, err := purego.Dlopen(path, purego.RTLD_LAZY|purego.RTLD_GLOBAL) + if err != nil { + return nil, err + } + + closeFunc := func() { + purego.Dlclose(lib) + } + + return &Library{ + addr: lib, + path: path, + close: closeFunc, + }, nil +} + +func (lib *Library) Dlsym(symbol string) (uintptr, error) { + return purego.Dlsym(lib.addr, symbol) +} + +func GetFunc[T any](lib *Library, symbol string) T { + var fptr T + purego.RegisterLibFunc(&fptr, lib.addr, symbol) + return fptr +} + +func (lib *Library) Close() { + lib.close() +} + +// status codes +const ( + KERN_SUCCESS = 0 +) + +// IOKit functions and symbols. +type ( + IOServiceGetMatchingServiceFunc func(mainPort uint32, matching uintptr) uint32 + IOServiceMatchingFunc func(name string) unsafe.Pointer + IOServiceOpenFunc func(service, owningTask, connType uint32, connect *uint32) int + IOServiceCloseFunc func(connect uint32) int + IOObjectReleaseFunc func(object uint32) int + IOConnectCallStructMethodFunc func(connection, selector uint32, inputStruct, inputStructCnt, outputStruct uintptr, outputStructCnt *uintptr) int + + IOHIDEventSystemClientCreateFunc func(allocator uintptr) unsafe.Pointer + IOHIDEventSystemClientSetMatchingFunc func(client, match uintptr) int + IOHIDServiceClientCopyEventFunc func(service uintptr, eventType int64, + options int32, timeout int64) unsafe.Pointer + IOHIDServiceClientCopyPropertyFunc func(service, property uintptr) unsafe.Pointer + IOHIDEventGetFloatValueFunc func(event uintptr, field int32) float64 + IOHIDEventSystemClientCopyServicesFunc func(client uintptr) unsafe.Pointer +) + +const ( + IOServiceGetMatchingServiceSym = "IOServiceGetMatchingService" + IOServiceMatchingSym = "IOServiceMatching" + IOServiceOpenSym = "IOServiceOpen" + IOServiceCloseSym = "IOServiceClose" + IOObjectReleaseSym = "IOObjectRelease" + IOConnectCallStructMethodSym = "IOConnectCallStructMethod" + + IOHIDEventSystemClientCreateSym = "IOHIDEventSystemClientCreate" + IOHIDEventSystemClientSetMatchingSym = "IOHIDEventSystemClientSetMatching" + IOHIDServiceClientCopyEventSym = "IOHIDServiceClientCopyEvent" + IOHIDServiceClientCopyPropertySym = "IOHIDServiceClientCopyProperty" + IOHIDEventGetFloatValueSym = "IOHIDEventGetFloatValue" + IOHIDEventSystemClientCopyServicesSym = "IOHIDEventSystemClientCopyServices" +) + +const ( + KIOHIDEventTypeTemperature = 15 +) + +// CoreFoundation functions and symbols. +type ( + CFNumberCreateFunc func(allocator uintptr, theType int32, valuePtr uintptr) unsafe.Pointer + CFDictionaryCreateFunc func(allocator uintptr, keys, values *unsafe.Pointer, numValues int32, + keyCallBacks, valueCallBacks uintptr) unsafe.Pointer + CFArrayGetCountFunc func(theArray uintptr) int32 + CFArrayGetValueAtIndexFunc func(theArray uintptr, index int32) unsafe.Pointer + CFStringCreateMutableFunc func(alloc uintptr, maxLength int32) unsafe.Pointer + CFStringGetLengthFunc func(theString uintptr) int32 + CFStringGetCStringFunc func(theString uintptr, buffer *byte, bufferSize int32, encoding uint32) + CFStringCreateWithCStringFunc func(alloc uintptr, cStr string, encoding uint32) unsafe.Pointer + CFReleaseFunc func(cf uintptr) +) + +const ( + CFNumberCreateSym = "CFNumberCreate" + CFDictionaryCreateSym = "CFDictionaryCreate" + CFArrayGetCountSym = "CFArrayGetCount" + CFArrayGetValueAtIndexSym = "CFArrayGetValueAtIndex" + CFStringCreateMutableSym = "CFStringCreateMutable" + CFStringGetLengthSym = "CFStringGetLength" + CFStringGetCStringSym = "CFStringGetCString" + CFStringCreateWithCStringSym = "CFStringCreateWithCString" + CFReleaseSym = "CFRelease" +) + +const ( + KCFStringEncodingUTF8 = 0x08000100 + KCFNumberIntType = 9 + KCFAllocatorDefault = 0 +) + +// Kernel functions and symbols. +type ( + HostProcessorInfoFunc func(host uint32, flavor int, outProcessorCount *uint32, outProcessorInfo uintptr, + outProcessorInfoCnt *uint32) int + HostStatisticsFunc func(host uint32, flavor int, hostInfoOut uintptr, hostInfoOutCnt *uint32) int + MachHostSelfFunc func() uint32 + MachTaskSelfFunc func() uint32 + VMDeallocateFunc func(targetTask uint32, vmAddress, vmSize uintptr) int +) + +const ( + HostProcessorInfoSym = "host_processor_info" + HostStatisticsSym = "host_statistics" + MachHostSelfSym = "mach_host_self" + MachTaskSelfSym = "mach_task_self" + VMDeallocateSym = "vm_deallocate" +) + +const ( + HOST_VM_INFO = 2 + HOST_CPU_LOAD_INFO = 3 + + HOST_VM_INFO_COUNT = 0xf +) + +// SMC represents a SMC instance. +type SMC struct { + lib *Library + conn uint32 + callStruct IOConnectCallStructMethodFunc +} + +const ioServiceSMC = "AppleSMC" + +const ( + KSMCUserClientOpen = 0 + KSMCUserClientClose = 1 + KSMCHandleYPCEvent = 2 + KSMCReadKey = 5 + KSMCWriteKey = 6 + KSMCGetKeyCount = 7 + KSMCGetKeyFromIndex = 8 + KSMCGetKeyInfo = 9 +) + +const ( + KSMCSuccess = 0 + KSMCError = 1 + KSMCKeyNotFound = 132 +) + +func NewSMC(ioKit *Library) (*SMC, error) { + if ioKit.path != IOKit { + return nil, fmt.Errorf("library is not IOKit") + } + + ioServiceGetMatchingService := GetFunc[IOServiceGetMatchingServiceFunc](ioKit, IOServiceGetMatchingServiceSym) + ioServiceMatching := GetFunc[IOServiceMatchingFunc](ioKit, IOServiceMatchingSym) + ioServiceOpen := GetFunc[IOServiceOpenFunc](ioKit, IOServiceOpenSym) + ioObjectRelease := GetFunc[IOObjectReleaseFunc](ioKit, IOObjectReleaseSym) + machTaskSelf := GetFunc[MachTaskSelfFunc](ioKit, MachTaskSelfSym) + + ioConnectCallStructMethod := GetFunc[IOConnectCallStructMethodFunc](ioKit, IOConnectCallStructMethodSym) + + service := ioServiceGetMatchingService(0, uintptr(ioServiceMatching(ioServiceSMC))) + if service == 0 { + return nil, fmt.Errorf("ERROR: %s NOT FOUND", ioServiceSMC) + } + + var conn uint32 + if result := ioServiceOpen(service, machTaskSelf(), 0, &conn); result != 0 { + return nil, fmt.Errorf("ERROR: IOServiceOpen failed") + } + + ioObjectRelease(service) + return &SMC{ + lib: ioKit, + conn: conn, + callStruct: ioConnectCallStructMethod, + }, nil +} + +func (s *SMC) CallStruct(selector uint32, inputStruct, inputStructCnt, outputStruct uintptr, outputStructCnt *uintptr) int { + return s.callStruct(s.conn, selector, inputStruct, inputStructCnt, outputStruct, outputStructCnt) +} + +func (s *SMC) Close() error { + ioServiceClose := GetFunc[IOServiceCloseFunc](s.lib, IOServiceCloseSym) + + if result := ioServiceClose(s.conn); result != 0 { + return fmt.Errorf("ERROR: IOServiceClose failed") + } + return nil +} diff --git a/mem/mem_darwin.go b/mem/mem_darwin.go index a33c5f1..4442cbc 100644 --- a/mem/mem_darwin.go +++ b/mem/mem_darwin.go @@ -70,3 +70,61 @@ func SwapDevices() ([]*SwapDevice, error) { func SwapDevicesWithContext(ctx 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(ctx context.Context) (*VirtualMemoryStat, error) { + machLib, err := common.NewLibrary(common.Kernel) + if err != nil { + return nil, err + } + defer machLib.Close() + + hostStatistics := common.GetFunc[common.HostStatisticsFunc](machLib, common.HostStatisticsSym) + machHostSelf := common.GetFunc[common.MachHostSelfFunc](machLib, common.MachHostSelfSym) + + count := uint32(common.HOST_VM_INFO_COUNT) + var vmstat vmStatisticsData + + status := hostStatistics(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, _ := machLib.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 +} diff --git a/mem/mem_darwin_cgo.go b/mem/mem_darwin_cgo.go deleted file mode 100644 index cc6657d..0000000 --- a/mem/mem_darwin_cgo.go +++ /dev/null @@ -1,58 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -//go:build darwin && cgo - -package mem - -/* -#include -#include -*/ -import "C" - -import ( - "context" - "fmt" - "unsafe" -) - -// VirtualMemory returns VirtualmemoryStat. -func VirtualMemory() (*VirtualMemoryStat, error) { - return VirtualMemoryWithContext(context.Background()) -} - -func VirtualMemoryWithContext(ctx context.Context) (*VirtualMemoryStat, error) { - count := C.mach_msg_type_number_t(C.HOST_VM_INFO_COUNT) - var vmstat C.vm_statistics_data_t - - status := C.host_statistics(C.host_t(C.mach_host_self()), - C.HOST_VM_INFO, - C.host_info_t(unsafe.Pointer(&vmstat)), - &count) - - if status != C.KERN_SUCCESS { - return nil, fmt.Errorf("host_statistics error=%d", status) - } - - pageSize := uint64(C.vm_kernel_page_size) - total, err := getHwMemsize() - if err != nil { - return nil, err - } - totalCount := C.natural_t(total / pageSize) - - availableCount := vmstat.inactive_count + vmstat.free_count - 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.free_count), - Active: pageSize * uint64(vmstat.active_count), - Inactive: pageSize * uint64(vmstat.inactive_count), - Wired: pageSize * uint64(vmstat.wire_count), - }, nil -} diff --git a/mem/mem_darwin_nocgo.go b/mem/mem_darwin_nocgo.go deleted file mode 100644 index 097a93e..0000000 --- a/mem/mem_darwin_nocgo.go +++ /dev/null @@ -1,89 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -//go:build darwin && !cgo - -package mem - -import ( - "context" - "strconv" - "strings" - - "golang.org/x/sys/unix" -) - -// Runs vm_stat and returns Free and inactive pages -func getVMStat(vms *VirtualMemoryStat) error { - out, err := invoke.Command("vm_stat") - if err != nil { - return err - } - return parseVMStat(string(out), vms) -} - -func parseVMStat(out string, vms *VirtualMemoryStat) error { - var err error - - lines := strings.Split(out, "\n") - pagesize := uint64(unix.Getpagesize()) - for _, line := range lines { - fields := strings.Split(line, ":") - if len(fields) < 2 { - continue - } - key := strings.TrimSpace(fields[0]) - value := strings.Trim(fields[1], " .") - switch key { - case "Pages free": - free, e := strconv.ParseUint(value, 10, 64) - if e != nil { - err = e - } - vms.Free = free * pagesize - case "Pages inactive": - inactive, e := strconv.ParseUint(value, 10, 64) - if e != nil { - err = e - } - vms.Inactive = inactive * pagesize - case "Pages active": - active, e := strconv.ParseUint(value, 10, 64) - if e != nil { - err = e - } - vms.Active = active * pagesize - case "Pages wired down": - wired, e := strconv.ParseUint(value, 10, 64) - if e != nil { - err = e - } - vms.Wired = wired * pagesize - } - } - return err -} - -// VirtualMemory returns VirtualmemoryStat. -func VirtualMemory() (*VirtualMemoryStat, error) { - return VirtualMemoryWithContext(context.Background()) -} - -func VirtualMemoryWithContext(ctx context.Context) (*VirtualMemoryStat, error) { - ret := &VirtualMemoryStat{} - - total, err := getHwMemsize() - if err != nil { - return nil, err - } - err = getVMStat(ret) - if err != nil { - return nil, err - } - - ret.Available = ret.Free + ret.Inactive - ret.Total = total - - ret.Used = ret.Total - ret.Available - ret.UsedPercent = 100 * float64(ret.Used) / float64(ret.Total) - - return ret, nil -} diff --git a/sensors/darwin_arm_sensors.h b/sensors/darwin_arm_sensors.h deleted file mode 100644 index b1d2ebb..0000000 --- a/sensors/darwin_arm_sensors.h +++ /dev/null @@ -1,110 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2016-2018, "freedom" Koan-Sin Tan -// SPDX-License-Identifier: BSD-3-Clause -// https://github.com/freedomtan/sensors/blob/master/sensors/sensors.m -#import -#import -#include - -typedef struct __IOHIDEvent *IOHIDEventRef; -typedef struct __IOHIDServiceClient *IOHIDServiceClientRef; -typedef double IOHIDFloat; - -IOHIDEventSystemClientRef IOHIDEventSystemClientCreate(CFAllocatorRef allocator); - -int IOHIDEventSystemClientSetMatching(IOHIDEventSystemClientRef client, CFDictionaryRef match); - -IOHIDEventRef IOHIDServiceClientCopyEvent(IOHIDServiceClientRef, int64_t, int32_t, int64_t); - -CFStringRef IOHIDServiceClientCopyProperty(IOHIDServiceClientRef service, CFStringRef property); - -IOHIDFloat IOHIDEventGetFloatValue(IOHIDEventRef event, int32_t field); - -NSDictionary *matching(int page, int usage) { - NSDictionary *dict = @{ - @"PrimaryUsagePage" : [NSNumber numberWithInt:page], - @"PrimaryUsage" : [NSNumber numberWithInt:usage], - }; - - return dict; -} - -NSArray *getProductNames(NSDictionary *sensors) { - IOHIDEventSystemClientRef system = IOHIDEventSystemClientCreate(kCFAllocatorDefault); - - IOHIDEventSystemClientSetMatching(system, (__bridge CFDictionaryRef)sensors); - NSArray *matchingsrvs = (__bridge NSArray *)IOHIDEventSystemClientCopyServices(system); - - long count = [matchingsrvs count]; - NSMutableArray *array = [[NSMutableArray alloc] init]; - - for (int i = 0; i < count; i++) { - IOHIDServiceClientRef sc = (IOHIDServiceClientRef)matchingsrvs[i]; - NSString *name = (NSString *)IOHIDServiceClientCopyProperty(sc, (__bridge CFStringRef)@"Product"); - - if (name) { - [array addObject:name]; - } else { - [array addObject:@"noname"]; - } - } - - return array; -} - -#define IOHIDEventFieldBase(type) (type << 16) -#define kIOHIDEventTypeTemperature 15 -#define kIOHIDEventTypePower 25 - -NSArray *getThermalValues(NSDictionary *sensors) { - IOHIDEventSystemClientRef system = IOHIDEventSystemClientCreate(kCFAllocatorDefault); - - IOHIDEventSystemClientSetMatching(system, (__bridge CFDictionaryRef)sensors); - NSArray *matchingsrvs = (__bridge NSArray *)IOHIDEventSystemClientCopyServices(system); - - long count = [matchingsrvs count]; - NSMutableArray *array = [[NSMutableArray alloc] init]; - - for (int i = 0; i < count; i++) { - IOHIDServiceClientRef sc = (IOHIDServiceClientRef)matchingsrvs[i]; - IOHIDEventRef event = IOHIDServiceClientCopyEvent(sc, kIOHIDEventTypeTemperature, 0, 0); - - NSNumber *value; - double temp = 0.0; - - if (event != 0) { - temp = IOHIDEventGetFloatValue(event, IOHIDEventFieldBase(kIOHIDEventTypeTemperature)); - } - - value = [NSNumber numberWithDouble:temp]; - [array addObject:value]; - } - - return array; -} - -NSString *dumpNamesValues(NSArray *kvsN, NSArray *kvsV) { - NSMutableString *valueString = [[NSMutableString alloc] init]; - int count = [kvsN count]; - - for (int i = 0; i < count; i++) { - NSString *output = [NSString stringWithFormat:@"%s:%lf\n", [kvsN[i] UTF8String], [kvsV[i] doubleValue]]; - [valueString appendString:output]; - } - - return valueString; -} - -char *getThermals() { - NSDictionary *thermalSensors = matching(0xff00, 5); - NSArray *thermalNames = getProductNames(thermalSensors); - NSArray *thermalValues = getThermalValues(thermalSensors); - NSString *result = dumpNamesValues(thermalNames, thermalValues); - char *finalStr = strdup([result UTF8String]); - - CFRelease(thermalSensors); - CFRelease(thermalNames); - CFRelease(thermalValues); - CFRelease(result); - - return finalStr; -} diff --git a/sensors/sensors_darwin.go b/sensors/sensors_darwin.go new file mode 100644 index 0000000..b58bf96 --- /dev/null +++ b/sensors/sensors_darwin.go @@ -0,0 +1,181 @@ +// SPDX-License-Identifier: BSD-3-Clause +//go:build darwin && !arm64 + +package sensors + +import ( + "context" + "fmt" + "unsafe" + + "github.com/shirou/gopsutil/v4/internal/common" +) + +func TemperaturesWithContext(ctx context.Context) ([]TemperatureStat, error) { + ioKit, err := common.NewLibrary(common.IOKit) + if err != nil { + return nil, err + } + defer ioKit.Close() + + smc, err := common.NewSMC(ioKit) + if err != nil { + return nil, err + } + defer smc.Close() + + var temperatures []TemperatureStat + for _, key := range temperatureKeys { + temperatures = append(temperatures, TemperatureStat{ + SensorKey: key, + Temperature: getTemperature(smc, key), + }) + } + + return temperatures, nil +} + +var temperatureKeys = []string{ + "TA0P", // AMBIENT_AIR_0 + "TA1P", // AMBIENT_AIR_1 + "TC0D", // CPU_0_DIODE + "TC0H", // CPU_0_HEATSINK + "TC0P", // CPU_0_PROXIMITY + "TB0T", // ENCLOSURE_BASE_0 + "TB1T", // ENCLOSURE_BASE_1 + "TB2T", // ENCLOSURE_BASE_2 + "TB3T", // ENCLOSURE_BASE_3 + "TG0D", // GPU_0_DIODE + "TG0H", // GPU_0_HEATSINK + "TG0P", // GPU_0_PROXIMITY + "TH0P", // HARD_DRIVE_BAY + "TM0S", // MEMORY_SLOT_0 + "TM0P", // MEMORY_SLOTS_PROXIMITY + "TN0H", // NORTHBRIDGE + "TN0D", // NORTHBRIDGE_DIODE + "TN0P", // NORTHBRIDGE_PROXIMITY + "TI0P", // THUNDERBOLT_0 + "TI1P", // THUNDERBOLT_1 + "TW0P", // WIRELESS_MODULE +} + +type smcReturn struct { + data [32]uint8 + dataType uint32 + dataSize uint32 + kSMC uint8 +} + +type smcPLimitData struct { + version uint16 + length uint16 + cpuPLimit uint32 + gpuPLimit uint32 + memPLimit uint32 +} + +type smcKeyInfoData struct { + dataSize uint32 + dataType uint32 + dataAttributes uint8 +} + +type smcVersion struct { + major byte + minor byte + build byte + reserved byte + release uint16 +} + +type smcParamStruct struct { + key uint32 + vers smcVersion + plimitData smcPLimitData + keyInfo smcKeyInfoData + result uint8 + status uint8 + data8 uint8 + data32 uint32 + bytes [32]byte +} + +const ( + smcKeySize = 4 + dataTypeSp78 = "sp78" +) + +func getTemperature(smc *common.SMC, key string) float64 { + result, err := readSMC(smc, key) + if err != nil { + return 0.0 + } + + if result.dataSize == 2 && result.dataType == toUint32(dataTypeSp78) { + return 0.0 + } + + return float64(result.data[0]) +} + +func readSMC(smc *common.SMC, key string) (*smcReturn, error) { + input := new(smcParamStruct) + resultSmc := new(smcReturn) + + input.key = toUint32(key) + input.data8 = common.KSMCGetKeyInfo + + result, err := callSMC(smc, input) + resultSmc.kSMC = result.result + + if err != nil || result.result != common.KSMCSuccess { + return resultSmc, fmt.Errorf("ERROR: IOConnectCallStructMethod failed") + } + + resultSmc.dataSize = uint32(result.keyInfo.dataSize) + resultSmc.dataType = uint32(result.keyInfo.dataSize) + + input.keyInfo.dataSize = result.keyInfo.dataSize + input.data8 = common.KSMCReadKey + + result, err = callSMC(smc, input) + resultSmc.kSMC = result.result + + if err != nil || result.result != common.KSMCSuccess { + return resultSmc, err + } + + resultSmc.data = result.bytes + return resultSmc, nil +} + +func callSMC(smc *common.SMC, input *smcParamStruct) (*smcParamStruct, error) { + output := new(smcParamStruct) + inputCnt := unsafe.Sizeof(*input) + outputCnt := unsafe.Sizeof(*output) + + result := smc.CallStruct(common.KSMCHandleYPCEvent, + uintptr(unsafe.Pointer(input)), inputCnt, uintptr(unsafe.Pointer(output)), &outputCnt) + + if result != 0 { + return output, fmt.Errorf("ERROR: IOConnectCallStructMethod failed") + } + + return output, nil +} + +func toUint32(key string) uint32 { + if len(key) != smcKeySize { + return 0 + } + + var ans uint32 = 0 + var shift uint32 = 24 + + for i := 0; i < smcKeySize; i++ { + ans += uint32(key[i]) << shift + shift -= 8 + } + + return ans +} diff --git a/sensors/sensors_darwin_arm64.go b/sensors/sensors_darwin_arm64.go new file mode 100644 index 0000000..2cf5b48 --- /dev/null +++ b/sensors/sensors_darwin_arm64.go @@ -0,0 +1,186 @@ +// SPDX-License-Identifier: BSD-3-Clause +//go:build darwin && arm64 + +package sensors + +import ( + "context" + "unsafe" + + "github.com/shirou/gopsutil/v4/internal/common" +) + +func ReadTemperaturesArm() []TemperatureStat { + temperatures, _ := TemperaturesWithContext(context.Background()) + return temperatures +} + +func TemperaturesWithContext(ctx context.Context) ([]TemperatureStat, error) { + ioKit, err := common.NewLibrary(common.IOKit) + if err != nil { + return nil, err + } + defer ioKit.Close() + + coreFoundation, err := common.NewLibrary(common.CoreFoundation) + if err != nil { + return nil, err + } + defer coreFoundation.Close() + + ta := &temperatureArm{ + ioKit: ioKit, + cf: coreFoundation, + cfRelease: common.GetFunc[common.CFReleaseFunc](coreFoundation, common.CFReleaseSym), + cfStringCreateWithCString: common.GetFunc[common.CFStringCreateWithCStringFunc](coreFoundation, common.CFStringCreateWithCStringSym), + cfArrayGetCount: common.GetFunc[common.CFArrayGetCountFunc](coreFoundation, common.CFArrayGetCountSym), + cfArrayGetValueAtIndex: common.GetFunc[common.CFArrayGetValueAtIndexFunc](coreFoundation, common.CFArrayGetValueAtIndexSym), + ioHIDEventSystemClientCreate: common.GetFunc[common.IOHIDEventSystemClientCreateFunc](ioKit, common.IOHIDEventSystemClientCreateSym), + ioHIDEventSystemClientSetMatching: common.GetFunc[common.IOHIDEventSystemClientSetMatchingFunc](ioKit, common.IOHIDEventSystemClientSetMatchingSym), + ioHIDEventSystemClientCopyServices: common.GetFunc[common.IOHIDEventSystemClientCopyServicesFunc](ioKit, common.IOHIDEventSystemClientCopyServicesSym), + } + + ta.matching(0xff00, 5) + thermalNames := ta.getProductNames() + thermalValues := ta.getThermalValues() + result := dumpNameValues(thermalNames, thermalValues) + + ta.cfRelease(uintptr(ta.sensors)) + return result, nil +} + +func dumpNameValues(kvsN []string, kvsV []float64) []TemperatureStat { + count := len(kvsN) + temperatureMap := make(map[string]TemperatureStat) + + for i := 0; i < count; i++ { + temperatureMap[kvsN[i]] = TemperatureStat{ + SensorKey: kvsN[i], + Temperature: kvsV[i], + } + } + + temperatures := make([]TemperatureStat, 0, len(temperatureMap)) + for _, stat := range temperatureMap { + temperatures = append(temperatures, stat) + } + + return temperatures +} + +type temperatureArm struct { + ioKit *common.Library + cf *common.Library + + cfRelease common.CFReleaseFunc + cfStringCreateWithCString common.CFStringCreateWithCStringFunc + cfArrayGetCount common.CFArrayGetCountFunc + cfArrayGetValueAtIndex common.CFArrayGetValueAtIndexFunc + + ioHIDEventSystemClientCreate common.IOHIDEventSystemClientCreateFunc + ioHIDEventSystemClientSetMatching common.IOHIDEventSystemClientSetMatchingFunc + ioHIDEventSystemClientCopyServices common.IOHIDEventSystemClientCopyServicesFunc + + sensors unsafe.Pointer +} + +func (ta *temperatureArm) getProductNames() []string { + ioHIDServiceClientCopyProperty := common.GetFunc[common.IOHIDServiceClientCopyPropertyFunc](ta.ioKit, common.IOHIDServiceClientCopyPropertySym) + + cfStringGetLength := common.GetFunc[common.CFStringGetLengthFunc](ta.cf, common.CFStringGetLengthSym) + cfStringGetCString := common.GetFunc[common.CFStringGetCStringFunc](ta.cf, common.CFStringGetCStringSym) + + var names []string + system := ta.ioHIDEventSystemClientCreate(common.KCFAllocatorDefault) + + ta.ioHIDEventSystemClientSetMatching(uintptr(system), uintptr(ta.sensors)) + matchingsrvs := ta.ioHIDEventSystemClientCopyServices(uintptr(system)) + + count := ta.cfArrayGetCount(uintptr(matchingsrvs)) + + var i int32 + str := ta.cfStr("Product") + for i = 0; i < count; i++ { + sc := ta.cfArrayGetValueAtIndex(uintptr(matchingsrvs), i) + name := ioHIDServiceClientCopyProperty(uintptr(sc), uintptr(str)) + + if name != nil { + length := cfStringGetLength(uintptr(name)) + 1 // null terminator + buf := make([]byte, length-1) + cfStringGetCString(uintptr(name), &buf[0], length, common.KCFStringEncodingUTF8) + + names = append(names, string(buf)) + ta.cfRelease(uintptr(name)) + } else { + names = append(names, "noname") + } + } + + ta.cfRelease(uintptr(matchingsrvs)) + ta.cfRelease(uintptr(str)) + return names +} + +func (ta *temperatureArm) getThermalValues() []float64 { + ioHIDServiceClientCopyEvent := common.GetFunc[common.IOHIDServiceClientCopyEventFunc](ta.ioKit, common.IOHIDServiceClientCopyEventSym) + ioHIDEventGetFloatValue := common.GetFunc[common.IOHIDEventGetFloatValueFunc](ta.ioKit, common.IOHIDEventGetFloatValueSym) + + system := ta.ioHIDEventSystemClientCreate(common.KCFAllocatorDefault) + + ta.ioHIDEventSystemClientSetMatching(uintptr(system), uintptr(ta.sensors)) + matchingsrvs := ta.ioHIDEventSystemClientCopyServices(uintptr(system)) + + count := ta.cfArrayGetCount(uintptr(matchingsrvs)) + + var values []float64 + var i int32 + for i = 0; i < count; i++ { + sc := ta.cfArrayGetValueAtIndex(uintptr(matchingsrvs), i) + event := ioHIDServiceClientCopyEvent(uintptr(sc), common.KIOHIDEventTypeTemperature, 0, 0) + temp := 0.0 + + if event != nil { + temp = ioHIDEventGetFloatValue(uintptr(event), ioHIDEventFieldBase(common.KIOHIDEventTypeTemperature)) + ta.cfRelease(uintptr(event)) + } + + values = append(values, temp) + } + + ta.cfRelease(uintptr(matchingsrvs)) + return values +} + +func (ta *temperatureArm) matching(page, usage int) { + cfNumberCreate := common.GetFunc[common.CFNumberCreateFunc](ta.cf, common.CFNumberCreateSym) + cfDictionaryCreate := common.GetFunc[common.CFDictionaryCreateFunc](ta.cf, common.CFDictionaryCreateSym) + + pageNum := cfNumberCreate(common.KCFAllocatorDefault, common.KCFNumberIntType, uintptr(unsafe.Pointer(&page))) + usageNum := cfNumberCreate(common.KCFAllocatorDefault, common.KCFNumberIntType, uintptr(unsafe.Pointer(&usage))) + + k1 := ta.cfStr("PrimaryUsagePage") + k2 := ta.cfStr("PrimaryUsage") + + keys := []unsafe.Pointer{k1, k2} + values := []unsafe.Pointer{pageNum, usageNum} + + kCFTypeDictionaryKeyCallBacks, _ := ta.cf.Dlsym("kCFTypeDictionaryKeyCallBacks") + kCFTypeDictionaryValueCallBacks, _ := ta.cf.Dlsym("kCFTypeDictionaryValueCallBacks") + + ta.sensors = cfDictionaryCreate(common.KCFAllocatorDefault, &keys[0], &values[0], 2, + kCFTypeDictionaryKeyCallBacks, + kCFTypeDictionaryValueCallBacks) + + ta.cfRelease(uintptr(pageNum)) + ta.cfRelease(uintptr(usageNum)) + ta.cfRelease(uintptr(k1)) + ta.cfRelease(uintptr(k2)) +} + +func (ta *temperatureArm) cfStr(str string) unsafe.Pointer { + return ta.cfStringCreateWithCString(common.KCFAllocatorDefault, str, common.KCFStringEncodingUTF8) +} + +func ioHIDEventFieldBase(i int32) int32 { + return i << 16 +} diff --git a/sensors/sensors_darwin_cgo.go b/sensors/sensors_darwin_cgo.go deleted file mode 100644 index aa3d291..0000000 --- a/sensors/sensors_darwin_cgo.go +++ /dev/null @@ -1,95 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -//go:build darwin && cgo - -package sensors - -// #cgo CFLAGS: -x objective-c -// #cgo LDFLAGS: -framework Foundation -framework IOKit -// #include "smc_darwin.h" -// #include "darwin_arm_sensors.h" -import "C" -import ( - "bufio" - "context" - "math" - "runtime" - "strconv" - "strings" - "unsafe" -) - -func ReadTemperaturesArm() []TemperatureStat { - cStr := C.getThermals() - defer C.free(unsafe.Pointer(cStr)) - - var stats []TemperatureStat - goStr := C.GoString(cStr) - scanner := bufio.NewScanner(strings.NewReader(goStr)) - for scanner.Scan() { - split := strings.Split(scanner.Text(), ":") - if len(split) != 2 { - continue - } - - val, err := strconv.ParseFloat(split[1], 32) - if err != nil { - continue - } - - sensorKey := strings.Split(split[0], " ")[0] - - val = math.Abs(val) - - stats = append(stats, TemperatureStat{ - SensorKey: sensorKey, - Temperature: float64(val), - }) - } - - return stats -} - -func TemperaturesWithContext(ctx context.Context) ([]TemperatureStat, error) { - if runtime.GOARCH == "arm64" { - return ReadTemperaturesArm(), nil - } - - temperatureKeys := []string{ - C.AMBIENT_AIR_0, - C.AMBIENT_AIR_1, - C.CPU_0_DIODE, - C.CPU_0_HEATSINK, - C.CPU_0_PROXIMITY, - C.ENCLOSURE_BASE_0, - C.ENCLOSURE_BASE_1, - C.ENCLOSURE_BASE_2, - C.ENCLOSURE_BASE_3, - C.GPU_0_DIODE, - C.GPU_0_HEATSINK, - C.GPU_0_PROXIMITY, - C.HARD_DRIVE_BAY, - C.MEMORY_SLOT_0, - C.MEMORY_SLOTS_PROXIMITY, - C.NORTHBRIDGE, - C.NORTHBRIDGE_DIODE, - C.NORTHBRIDGE_PROXIMITY, - C.THUNDERBOLT_0, - C.THUNDERBOLT_1, - C.WIRELESS_MODULE, - } - var temperatures []TemperatureStat - - C.gopsutil_v4_open_smc() - defer C.gopsutil_v4_close_smc() - - for _, key := range temperatureKeys { - ckey := C.CString(key) - defer C.free(unsafe.Pointer(ckey)) - temperatures = append(temperatures, TemperatureStat{ - SensorKey: key, - Temperature: float64(C.gopsutil_v4_get_temperature(ckey)), - }) - } - - return temperatures, nil -} diff --git a/sensors/sensors_darwin_nocgo.go b/sensors/sensors_darwin_nocgo.go deleted file mode 100644 index 45c8506..0000000 --- a/sensors/sensors_darwin_nocgo.go +++ /dev/null @@ -1,14 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -//go:build darwin && !cgo - -package sensors - -import ( - "context" - - "github.com/shirou/gopsutil/v4/internal/common" -) - -func TemperaturesWithContext(ctx context.Context) ([]TemperatureStat, error) { - return []TemperatureStat{}, common.ErrNotImplementedError -} diff --git a/sensors/smc_darwin.c b/sensors/smc_darwin.c deleted file mode 100644 index c91a90c..0000000 --- a/sensors/smc_darwin.c +++ /dev/null @@ -1,170 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -#include -#include -#include "smc_darwin.h" - -#define IOSERVICE_SMC "AppleSMC" -#define IOSERVICE_MODEL "IOPlatformExpertDevice" - -#define DATA_TYPE_SP78 "sp78" - -typedef enum { - kSMCUserClientOpen = 0, - kSMCUserClientClose = 1, - kSMCHandleYPCEvent = 2, - kSMCReadKey = 5, - kSMCWriteKey = 6, - kSMCGetKeyCount = 7, - kSMCGetKeyFromIndex = 8, - kSMCGetKeyInfo = 9, -} selector_t; - -typedef struct { - unsigned char major; - unsigned char minor; - unsigned char build; - unsigned char reserved; - unsigned short release; -} SMCVersion; - -typedef struct { - uint16_t version; - uint16_t length; - uint32_t cpuPLimit; - uint32_t gpuPLimit; - uint32_t memPLimit; -} SMCPLimitData; - -typedef struct { - IOByteCount data_size; - uint32_t data_type; - uint8_t data_attributes; -} SMCKeyInfoData; - -typedef struct { - uint32_t key; - SMCVersion vers; - SMCPLimitData p_limit_data; - SMCKeyInfoData key_info; - uint8_t result; - uint8_t status; - uint8_t data8; - uint32_t data32; - uint8_t bytes[32]; -} SMCParamStruct; - -typedef enum { - kSMCSuccess = 0, - kSMCError = 1, - kSMCKeyNotFound = 0x84, -} kSMC_t; - -typedef struct { - uint8_t data[32]; - uint32_t data_type; - uint32_t data_size; - kSMC_t kSMC; -} smc_return_t; - -static const int SMC_KEY_SIZE = 4; // number of characters in an SMC key. -static io_connect_t conn; // our connection to the SMC. - -kern_return_t gopsutil_v4_open_smc(void) { - kern_return_t result; - io_service_t service; - - service = IOServiceGetMatchingService(0, IOServiceMatching(IOSERVICE_SMC)); - if (service == 0) { - // Note: IOServiceMatching documents 0 on failure - printf("ERROR: %s NOT FOUND\n", IOSERVICE_SMC); - return kIOReturnError; - } - - result = IOServiceOpen(service, mach_task_self(), 0, &conn); - IOObjectRelease(service); - - return result; -} - -kern_return_t gopsutil_v4_close_smc(void) { return IOServiceClose(conn); } - -static uint32_t to_uint32(char *key) { - uint32_t ans = 0; - uint32_t shift = 24; - - if (strlen(key) != SMC_KEY_SIZE) { - return 0; - } - - for (int i = 0; i < SMC_KEY_SIZE; i++) { - ans += key[i] << shift; - shift -= 8; - } - - return ans; -} - -static kern_return_t call_smc(SMCParamStruct *input, SMCParamStruct *output) { - kern_return_t result; - size_t input_cnt = sizeof(SMCParamStruct); - size_t output_cnt = sizeof(SMCParamStruct); - - result = IOConnectCallStructMethod(conn, kSMCHandleYPCEvent, input, input_cnt, - output, &output_cnt); - - if (result != kIOReturnSuccess) { - result = err_get_code(result); - } - return result; -} - -static kern_return_t read_smc(char *key, smc_return_t *result_smc) { - kern_return_t result; - SMCParamStruct input; - SMCParamStruct output; - - memset(&input, 0, sizeof(SMCParamStruct)); - memset(&output, 0, sizeof(SMCParamStruct)); - memset(result_smc, 0, sizeof(smc_return_t)); - - input.key = to_uint32(key); - input.data8 = kSMCGetKeyInfo; - - result = call_smc(&input, &output); - result_smc->kSMC = output.result; - - if (result != kIOReturnSuccess || output.result != kSMCSuccess) { - return result; - } - - result_smc->data_size = output.key_info.data_size; - result_smc->data_type = output.key_info.data_type; - - input.key_info.data_size = output.key_info.data_size; - input.data8 = kSMCReadKey; - - result = call_smc(&input, &output); - result_smc->kSMC = output.result; - - if (result != kIOReturnSuccess || output.result != kSMCSuccess) { - return result; - } - - memcpy(result_smc->data, output.bytes, sizeof(output.bytes)); - - return result; -} - -double gopsutil_v4_get_temperature(char *key) { - kern_return_t result; - smc_return_t result_smc; - - result = read_smc(key, &result_smc); - - if (!(result == kIOReturnSuccess) && result_smc.data_size == 2 && - result_smc.data_type == to_uint32(DATA_TYPE_SP78)) { - return 0.0; - } - - return (double)result_smc.data[0]; -} diff --git a/sensors/smc_darwin.h b/sensors/smc_darwin.h deleted file mode 100644 index e49abb5..0000000 --- a/sensors/smc_darwin.h +++ /dev/null @@ -1,33 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -#ifndef __SMC_H__ -#define __SMC_H__ 1 - -#include - -#define AMBIENT_AIR_0 "TA0P" -#define AMBIENT_AIR_1 "TA1P" -#define CPU_0_DIODE "TC0D" -#define CPU_0_HEATSINK "TC0H" -#define CPU_0_PROXIMITY "TC0P" -#define ENCLOSURE_BASE_0 "TB0T" -#define ENCLOSURE_BASE_1 "TB1T" -#define ENCLOSURE_BASE_2 "TB2T" -#define ENCLOSURE_BASE_3 "TB3T" -#define GPU_0_DIODE "TG0D" -#define GPU_0_HEATSINK "TG0H" -#define GPU_0_PROXIMITY "TG0P" -#define HARD_DRIVE_BAY "TH0P" -#define MEMORY_SLOT_0 "TM0S" -#define MEMORY_SLOTS_PROXIMITY "TM0P" -#define NORTHBRIDGE "TN0H" -#define NORTHBRIDGE_DIODE "TN0D" -#define NORTHBRIDGE_PROXIMITY "TN0P" -#define THUNDERBOLT_0 "TI0P" -#define THUNDERBOLT_1 "TI1P" -#define WIRELESS_MODULE "TW0P" - -kern_return_t gopsutil_v4_open_smc(void); -kern_return_t gopsutil_v4_close_smc(void); -double gopsutil_v4_get_temperature(char *); - -#endif // __SMC_H__ From f824d50add586f6af7d761d2332c391bad5bbc66 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 5 Sep 2024 01:03:44 +0000 Subject: [PATCH 02/24] chore(deps): bump golang.org/x/sys from 0.24.0 to 0.25.0 Bumps [golang.org/x/sys](https://github.com/golang/sys) from 0.24.0 to 0.25.0. - [Commits](https://github.com/golang/sys/compare/v0.24.0...v0.25.0) --- updated-dependencies: - dependency-name: golang.org/x/sys dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 4ead244..ae634f4 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/stretchr/testify v1.9.0 github.com/tklauser/go-sysconf v0.3.12 github.com/yusufpapurcu/wmi v1.2.4 - golang.org/x/sys v0.24.0 + golang.org/x/sys v0.25.0 ) require ( diff --git a/go.sum b/go.sum index 7f6b7fb..f3c3158 100644 --- a/go.sum +++ b/go.sum @@ -26,8 +26,8 @@ golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= -golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= +golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= From 97b1aaee94a168074790ba39bf2f3bbff2d15a1f Mon Sep 17 00:00:00 2001 From: shirou Date: Sat, 7 Sep 2024 00:00:45 +0900 Subject: [PATCH 03/24] fix: remove coverall and godocs.io badge from README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4ce50b6..675a572 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # gopsutil: psutil for golang -[![Test](https://github.com/shirou/gopsutil/actions/workflows/test.yml/badge.svg)](https://github.com/shirou/gopsutil/actions/workflows/test.yml) [![Coverage Status](https://coveralls.io/repos/github/shirou/gopsutil/badge.svg?branch=master)](https://coveralls.io/github/shirou/gopsutil?branch=master) [![Go Reference](https://pkg.go.dev/badge/github.com/shirou/gopsutil/v4.svg)](https://pkg.go.dev/github.com/shirou/gopsutil/v4) [![Go Documentation](https://godocs.io/github.com/shirou/gopsutil/v4?status.svg)](https://godocs.io/github.com/shirou/gopsutil/v4) [![Calendar Versioning](https://img.shields.io/badge/calver-vMAJOR.YY.MM-22bfda.svg)](https://calver.org/) +[![Test](https://github.com/shirou/gopsutil/actions/workflows/test.yml/badge.svg)](https://github.com/shirou/gopsutil/actions/workflows/test.yml) [![Go Reference](https://pkg.go.dev/badge/github.com/shirou/gopsutil/v4.svg)](https://pkg.go.dev/github.com/shirou/gopsutil/v4) [![Calendar Versioning](https://img.shields.io/badge/calver-vMAJOR.YY.MM-22bfda.svg)](https://calver.org/) This is a port of psutil (https://github.com/giampaolo/psutil). The challenge is porting all psutil functions on some architectures. From 76ccf0d2203da9f4a1c2ba5ab0c3a53b6a3eb65a Mon Sep 17 00:00:00 2001 From: Lomanic <5020919+Lomanic@users.noreply.github.com> Date: Sat, 7 Sep 2024 03:10:57 +0200 Subject: [PATCH 04/24] [process][darwin][freebsd][linux][openbsd] Make process.Children not reliant on pgrep pgrep -P $PID exits with status of 1 (and nothing in stdout nor stderr) both if a process doesn't exist or it doesn't have child processes, so we don't use it anymore on these OSes. We sort PIDs as pgrep did. Also deprecate the ErrorNoChildren error when there are no child processes, this is erroneous (simply check for the length of the returned slice, plus this is not an error per se), this was only returned on linux anyway. Fixes #1698 --- internal/common/common_unix.go | 20 -------------------- process/process.go | 2 +- process/process_darwin.go | 18 +++++++++++------- process/process_freebsd.go | 18 +++++++++++------- process/process_linux.go | 32 +++++++++++++++++++++++--------- process/process_openbsd.go | 18 +++++++++++------- 6 files changed, 57 insertions(+), 51 deletions(-) diff --git a/internal/common/common_unix.go b/internal/common/common_unix.go index 2715b89..c9f91b1 100644 --- a/internal/common/common_unix.go +++ b/internal/common/common_unix.go @@ -40,23 +40,3 @@ func CallLsofWithContext(ctx context.Context, invoke Invoker, pid int32, args .. } return ret, nil } - -func CallPgrepWithContext(ctx context.Context, invoke Invoker, pid int32) ([]int32, error) { - out, err := invoke.CommandWithContext(ctx, "pgrep", "-P", strconv.Itoa(int(pid))) - if err != nil { - return []int32{}, err - } - lines := strings.Split(string(out), "\n") - ret := make([]int32, 0, len(lines)) - for _, l := range lines { - if len(l) == 0 { - continue - } - i, err := strconv.ParseInt(l, 10, 32) - if err != nil { - continue - } - ret = append(ret, int32(i)) - } - return ret, nil -} diff --git a/process/process.go b/process/process.go index d73f1f9..70411c6 100644 --- a/process/process.go +++ b/process/process.go @@ -18,7 +18,7 @@ import ( var ( invoke common.Invoker = common.Invoke{} - ErrorNoChildren = errors.New("process does not have children") + ErrorNoChildren = errors.New("process does not have children") // Deprecated: ErrorNoChildren is never returned by process.Children(), check its returned []*Process slice length instead ErrorProcessNotRunning = errors.New("process does not exist") ErrorNotPermitted = errors.New("operation not permitted") ) diff --git a/process/process_darwin.go b/process/process_darwin.go index 66b3684..119be2e 100644 --- a/process/process_darwin.go +++ b/process/process_darwin.go @@ -7,6 +7,7 @@ import ( "context" "fmt" "path/filepath" + "sort" "strconv" "strings" @@ -233,18 +234,21 @@ func convertCPUTimes(s string) (ret float64, err error) { } func (p *Process) ChildrenWithContext(ctx context.Context) ([]*Process, error) { - pids, err := common.CallPgrepWithContext(ctx, invoke, p.Pid) + procs, err := ProcessesWithContext(ctx) if err != nil { - return nil, err + return nil, nil } - ret := make([]*Process, 0, len(pids)) - for _, pid := range pids { - np, err := NewProcessWithContext(ctx, pid) + ret := make([]*Process, 0, len(procs)) + for _, proc := range procs { + ppid, err := proc.PpidWithContext(ctx) if err != nil { - return nil, err + continue + } + if ppid == p.Pid { + ret = append(ret, proc) } - ret = append(ret, np) } + sort.Slice(ret, func(i, j int) bool { return ret[i].Pid < ret[j].Pid }) return ret, nil } diff --git a/process/process_freebsd.go b/process/process_freebsd.go index 436dcf0..7637373 100644 --- a/process/process_freebsd.go +++ b/process/process_freebsd.go @@ -8,6 +8,7 @@ import ( "context" "errors" "path/filepath" + "sort" "strconv" "strings" @@ -269,18 +270,21 @@ func (p *Process) MemoryInfoWithContext(ctx context.Context) (*MemoryInfoStat, e } func (p *Process) ChildrenWithContext(ctx context.Context) ([]*Process, error) { - pids, err := common.CallPgrepWithContext(ctx, invoke, p.Pid) + procs, err := ProcessesWithContext(ctx) if err != nil { - return nil, err + return nil, nil } - ret := make([]*Process, 0, len(pids)) - for _, pid := range pids { - np, err := NewProcessWithContext(ctx, pid) + ret := make([]*Process, 0, len(procs)) + for _, proc := range procs { + ppid, err := proc.PpidWithContext(ctx) if err != nil { - return nil, err + continue + } + if ppid == p.Pid { + ret = append(ret, proc) } - ret = append(ret, np) } + sort.Slice(ret, func(i, j int) bool { return ret[i].Pid < ret[j].Pid }) return ret, nil } diff --git a/process/process_linux.go b/process/process_linux.go index 7aff044..4f16693 100644 --- a/process/process_linux.go +++ b/process/process_linux.go @@ -12,6 +12,7 @@ import ( "math" "os" "path/filepath" + "sort" "strconv" "strings" @@ -338,21 +339,34 @@ func (p *Process) PageFaultsWithContext(ctx context.Context) (*PageFaultsStat, e } func (p *Process) ChildrenWithContext(ctx context.Context) ([]*Process, error) { - pids, err := common.CallPgrepWithContext(ctx, invoke, p.Pid) + statFiles, err := filepath.Glob(common.HostProcWithContext(ctx, "[0-9]*/stat")) if err != nil { return nil, err } - if len(pids) == 0 { - return nil, ErrorNoChildren - } - ret := make([]*Process, 0, len(pids)) - for _, pid := range pids { - np, err := NewProcessWithContext(ctx, pid) + ret := make([]*Process, 0, len(statFiles)) + for _, statFile := range statFiles { + statContents, err := os.ReadFile(statFile) if err != nil { - return nil, err + continue + } + fields := splitProcStat(statContents) + pid, err := strconv.ParseInt(fields[1], 10, 32) + if err != nil { + continue + } + ppid, err := strconv.ParseInt(fields[4], 10, 32) + if err != nil { + continue + } + if int32(ppid) == p.Pid { + np, err := NewProcessWithContext(ctx, int32(pid)) + if err != nil { + continue + } + ret = append(ret, np) } - ret = append(ret, np) } + sort.Slice(ret, func(i, j int) bool { return ret[i].Pid < ret[j].Pid }) return ret, nil } diff --git a/process/process_openbsd.go b/process/process_openbsd.go index e2d0ab4..5e8a9e0 100644 --- a/process/process_openbsd.go +++ b/process/process_openbsd.go @@ -11,6 +11,7 @@ import ( "fmt" "io" "path/filepath" + "sort" "strconv" "strings" "unsafe" @@ -286,18 +287,21 @@ func (p *Process) MemoryInfoWithContext(ctx context.Context) (*MemoryInfoStat, e } func (p *Process) ChildrenWithContext(ctx context.Context) ([]*Process, error) { - pids, err := common.CallPgrepWithContext(ctx, invoke, p.Pid) + procs, err := ProcessesWithContext(ctx) if err != nil { - return nil, err + return nil, nil } - ret := make([]*Process, 0, len(pids)) - for _, pid := range pids { - np, err := NewProcessWithContext(ctx, pid) + ret := make([]*Process, 0, len(procs)) + for _, proc := range procs { + ppid, err := proc.PpidWithContext(ctx) if err != nil { - return nil, err + continue + } + if ppid == p.Pid { + ret = append(ret, proc) } - ret = append(ret, np) } + sort.Slice(ret, func(i, j int) bool { return ret[i].Pid < ret[j].Pid }) return ret, nil } From 811185d3fef2ecfbcc0e7d0d5a2034a6d4d751a1 Mon Sep 17 00:00:00 2001 From: Antoine Toulme Date: Tue, 10 Sep 2024 08:29:09 -0700 Subject: [PATCH 05/24] [common] add HOST_PROC_MOUNTINFO to EnvMap --- common/env.go | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/common/env.go b/common/env.go index 4acad1f..47e471c 100644 --- a/common/env.go +++ b/common/env.go @@ -12,13 +12,14 @@ type EnvKeyType string var EnvKey = EnvKeyType("env") const ( - HostProcEnvKey EnvKeyType = "HOST_PROC" - HostSysEnvKey EnvKeyType = "HOST_SYS" - HostEtcEnvKey EnvKeyType = "HOST_ETC" - HostVarEnvKey EnvKeyType = "HOST_VAR" - HostRunEnvKey EnvKeyType = "HOST_RUN" - HostDevEnvKey EnvKeyType = "HOST_DEV" - HostRootEnvKey EnvKeyType = "HOST_ROOT" + HostProcEnvKey EnvKeyType = "HOST_PROC" + HostSysEnvKey EnvKeyType = "HOST_SYS" + HostEtcEnvKey EnvKeyType = "HOST_ETC" + HostVarEnvKey EnvKeyType = "HOST_VAR" + HostRunEnvKey EnvKeyType = "HOST_RUN" + HostDevEnvKey EnvKeyType = "HOST_DEV" + HostRootEnvKey EnvKeyType = "HOST_ROOT" + HostProcMountinfo EnvKeyType = "HOST_PROC_MOUNTINFO" ) type EnvMap map[EnvKeyType]string From cbc32afb654d89fb1d3a39c18624ff37a775e86b Mon Sep 17 00:00:00 2001 From: huang <2971803929@qq.com> Date: Thu, 19 Sep 2024 23:59:11 +0800 Subject: [PATCH 06/24] implement NumFDs for Windows --- process/process_windows.go | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/process/process_windows.go b/process/process_windows.go index 52e1086..f8fa2f4 100644 --- a/process/process_windows.go +++ b/process/process_windows.go @@ -43,6 +43,7 @@ var ( procGetPriorityClass = common.Modkernel32.NewProc("GetPriorityClass") procGetProcessIoCounters = common.Modkernel32.NewProc("GetProcessIoCounters") procGetNativeSystemInfo = common.Modkernel32.NewProc("GetNativeSystemInfo") + procGetProcessHandleCount = common.Modkernel32.NewProc("GetProcessHandleCount") processorArchitecture uint ) @@ -549,7 +550,18 @@ func (p *Process) NumCtxSwitchesWithContext(ctx context.Context) (*NumCtxSwitche } func (p *Process) NumFDsWithContext(ctx context.Context) (int32, error) { - return 0, common.ErrNotImplementedError + handle, err := windows.OpenProcess(processQueryInformation, false, uint32(p.Pid)) + if err != nil { + return 0, err + } + defer windows.CloseHandle(handle) + + var handleCount uint32 + ret, _, err := procGetProcessHandleCount.Call(uintptr(handle), uintptr(unsafe.Pointer(&handleCount))) + if ret == 0 { + return 0, err + } + return int32(handleCount), nil } func (p *Process) NumThreadsWithContext(ctx context.Context) (int32, error) { From 9e6efdb991614ad075c2f8d7416c356ab6b7fe6b Mon Sep 17 00:00:00 2001 From: uubulb Date: Tue, 17 Sep 2024 17:04:23 +0800 Subject: [PATCH 07/24] update disk & cpu & process --- README.md | 2 +- common/env.go | 15 ++- cpu/cpu_darwin.go | 15 +-- cpu/cpu_darwin_arm64.go | 80 +++++++++++ cpu/cpu_darwin_fallback.go | 13 ++ cpu/cpu_darwin_test.go | 5 +- disk/disk_darwin.go | 200 +++++++++++++++++++++++++++ disk/disk_darwin_cgo.go | 45 ------- disk/disk_darwin_nocgo.go | 14 -- disk/iostat_darwin.c | 131 ------------------ disk/iostat_darwin.h | 34 ----- go.mod | 3 +- go.sum | 7 +- internal/common/common_darwin.go | 117 +++++++++++++--- internal/common/common_unix.go | 20 --- mem/mem_darwin.go | 2 +- process/process.go | 2 +- process/process_darwin.go | 283 ++++++++++++++++++++++++++++++--------- process/process_darwin_amd64.go | 21 +++ process/process_darwin_arm64.go | 21 +++ process/process_darwin_cgo.go | 222 ------------------------------ process/process_darwin_nocgo.go | 134 ------------------ process/process_freebsd.go | 18 ++- process/process_linux.go | 32 +++-- process/process_openbsd.go | 18 ++- process/types_darwin.go | 3 + 26 files changed, 721 insertions(+), 736 deletions(-) create mode 100644 cpu/cpu_darwin_arm64.go create mode 100644 cpu/cpu_darwin_fallback.go delete mode 100644 disk/disk_darwin_cgo.go delete mode 100644 disk/disk_darwin_nocgo.go delete mode 100644 disk/iostat_darwin.c delete mode 100644 disk/iostat_darwin.h delete mode 100644 process/process_darwin_cgo.go delete mode 100644 process/process_darwin_nocgo.go diff --git a/README.md b/README.md index 4ce50b6..675a572 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # gopsutil: psutil for golang -[![Test](https://github.com/shirou/gopsutil/actions/workflows/test.yml/badge.svg)](https://github.com/shirou/gopsutil/actions/workflows/test.yml) [![Coverage Status](https://coveralls.io/repos/github/shirou/gopsutil/badge.svg?branch=master)](https://coveralls.io/github/shirou/gopsutil?branch=master) [![Go Reference](https://pkg.go.dev/badge/github.com/shirou/gopsutil/v4.svg)](https://pkg.go.dev/github.com/shirou/gopsutil/v4) [![Go Documentation](https://godocs.io/github.com/shirou/gopsutil/v4?status.svg)](https://godocs.io/github.com/shirou/gopsutil/v4) [![Calendar Versioning](https://img.shields.io/badge/calver-vMAJOR.YY.MM-22bfda.svg)](https://calver.org/) +[![Test](https://github.com/shirou/gopsutil/actions/workflows/test.yml/badge.svg)](https://github.com/shirou/gopsutil/actions/workflows/test.yml) [![Go Reference](https://pkg.go.dev/badge/github.com/shirou/gopsutil/v4.svg)](https://pkg.go.dev/github.com/shirou/gopsutil/v4) [![Calendar Versioning](https://img.shields.io/badge/calver-vMAJOR.YY.MM-22bfda.svg)](https://calver.org/) This is a port of psutil (https://github.com/giampaolo/psutil). The challenge is porting all psutil functions on some architectures. diff --git a/common/env.go b/common/env.go index 4acad1f..47e471c 100644 --- a/common/env.go +++ b/common/env.go @@ -12,13 +12,14 @@ type EnvKeyType string var EnvKey = EnvKeyType("env") const ( - HostProcEnvKey EnvKeyType = "HOST_PROC" - HostSysEnvKey EnvKeyType = "HOST_SYS" - HostEtcEnvKey EnvKeyType = "HOST_ETC" - HostVarEnvKey EnvKeyType = "HOST_VAR" - HostRunEnvKey EnvKeyType = "HOST_RUN" - HostDevEnvKey EnvKeyType = "HOST_DEV" - HostRootEnvKey EnvKeyType = "HOST_ROOT" + HostProcEnvKey EnvKeyType = "HOST_PROC" + HostSysEnvKey EnvKeyType = "HOST_SYS" + HostEtcEnvKey EnvKeyType = "HOST_ETC" + HostVarEnvKey EnvKeyType = "HOST_VAR" + HostRunEnvKey EnvKeyType = "HOST_RUN" + HostDevEnvKey EnvKeyType = "HOST_DEV" + HostRootEnvKey EnvKeyType = "HOST_ROOT" + HostProcMountinfo EnvKeyType = "HOST_PROC_MOUNTINFO" ) type EnvMap map[EnvKeyType]string diff --git a/cpu/cpu_darwin.go b/cpu/cpu_darwin.go index 29d9a71..b3e3a66 100644 --- a/cpu/cpu_darwin.go +++ b/cpu/cpu_darwin.go @@ -10,7 +10,6 @@ import ( "strings" "unsafe" - "github.com/shoenig/go-m1cpu" "github.com/tklauser/go-sysconf" "golang.org/x/sys/unix" @@ -61,7 +60,7 @@ func Times(percpu bool) ([]TimesStat, error) { } func TimesWithContext(ctx context.Context, percpu bool) ([]TimesStat, error) { - lib, err := common.NewLibrary(common.Kernel) + lib, err := common.NewLibrary(common.System) if err != nil { return nil, err } @@ -114,15 +113,9 @@ func InfoWithContext(ctx context.Context) ([]InfoStat, error) { c.CacheSize = int32(cacheSize) c.VendorID, _ = unix.Sysctl("machdep.cpu.vendor") - if m1cpu.IsAppleSilicon() { - c.Mhz = float64(m1cpu.PCoreHz() / 1_000_000) - } else { - // Use the rated frequency of the CPU. This is a static value and does not - // account for low power or Turbo Boost modes. - cpuFrequency, err := unix.SysctlUint64("hw.cpufrequency") - if err == nil { - c.Mhz = float64(cpuFrequency) / 1000000.0 - } + v, err := getFrequency() + if err == nil { + c.Mhz = v } return append(ret, c), nil diff --git a/cpu/cpu_darwin_arm64.go b/cpu/cpu_darwin_arm64.go new file mode 100644 index 0000000..5031842 --- /dev/null +++ b/cpu/cpu_darwin_arm64.go @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: BSD-3-Clause +//go:build darwin && arm64 + +package cpu + +import ( + "encoding/binary" + "fmt" + "unsafe" + + "github.com/shirou/gopsutil/v4/internal/common" +) + +// https://github.com/shoenig/go-m1cpu/blob/v0.1.6/cpu.go +func getFrequency() (float64, error) { + ioKit, err := common.NewLibrary(common.IOKit) + if err != nil { + return 0, err + } + defer ioKit.Close() + + coreFoundation, err := common.NewLibrary(common.CoreFoundation) + if err != nil { + return 0, err + } + defer coreFoundation.Close() + + ioServiceMatching := common.GetFunc[common.IOServiceMatchingFunc](ioKit, common.IOServiceMatchingSym) + ioServiceGetMatchingServices := common.GetFunc[common.IOServiceGetMatchingServicesFunc](ioKit, common.IOServiceGetMatchingServicesSym) + ioIteratorNext := common.GetFunc[common.IOIteratorNextFunc](ioKit, common.IOIteratorNextSym) + ioRegistryEntryGetName := common.GetFunc[common.IORegistryEntryGetNameFunc](ioKit, common.IORegistryEntryGetNameSym) + ioRegistryEntryCreateCFProperty := common.GetFunc[common.IORegistryEntryCreateCFPropertyFunc](ioKit, common.IORegistryEntryCreateCFPropertySym) + ioObjectRelease := common.GetFunc[common.IOObjectReleaseFunc](ioKit, common.IOObjectReleaseSym) + + cfStringCreateWithCString := common.GetFunc[common.CFStringCreateWithCStringFunc](coreFoundation, common.CFStringCreateWithCStringSym) + cfDataGetLength := common.GetFunc[common.CFDataGetLengthFunc](coreFoundation, common.CFDataGetLengthSym) + cfDataGetBytePtr := common.GetFunc[common.CFDataGetBytePtrFunc](coreFoundation, common.CFDataGetBytePtrSym) + cfRelease := common.GetFunc[common.CFReleaseFunc](coreFoundation, common.CFReleaseSym) + + matching := ioServiceMatching("AppleARMIODevice") + + var iterator uint32 + if status := ioServiceGetMatchingServices(common.KIOMainPortDefault, uintptr(matching), &iterator); status != common.KERN_SUCCESS { + return 0.0, fmt.Errorf("IOServiceGetMatchingServices error=%d", status) + } + defer ioObjectRelease(iterator) + + pCorekey := cfStringCreateWithCString(common.KCFAllocatorDefault, "voltage-states5-sram", common.KCFStringEncodingUTF8) + defer cfRelease(uintptr(pCorekey)) + + var pCoreHz uint32 + for { + service := ioIteratorNext(iterator) + if !(service > 0) { + break + } + + buf := make([]byte, 512) + ioRegistryEntryGetName(service, &buf[0]) + + if common.GoString(&buf[0]) == "pmgr" { + pCoreRef := ioRegistryEntryCreateCFProperty(service, uintptr(pCorekey), common.KCFAllocatorDefault, common.KNilOptions) + length := cfDataGetLength(uintptr(pCoreRef)) + data := cfDataGetBytePtr(uintptr(pCoreRef)) + + // composite uint32 from the byte array + buf := unsafe.Slice((*byte)(data), length) + + // combine the bytes into a uint32 value + b := buf[length-8 : length-4] + pCoreHz = binary.LittleEndian.Uint32(b) + ioObjectRelease(service) + break + } + + ioObjectRelease(service) + } + + return float64(pCoreHz / 1_000_000), nil +} diff --git a/cpu/cpu_darwin_fallback.go b/cpu/cpu_darwin_fallback.go new file mode 100644 index 0000000..b9e52ab --- /dev/null +++ b/cpu/cpu_darwin_fallback.go @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: BSD-3-Clause +//go:build darwin && !arm64 + +package cpu + +import "golang.org/x/sys/unix" + +func getFrequency() (float64, error) { + // Use the rated frequency of the CPU. This is a static value and does not + // account for low power or Turbo Boost modes. + cpuFrequency, err := unix.SysctlUint64("hw.cpufrequency") + return float64(cpuFrequency) / 1000000.0, err +} diff --git a/cpu/cpu_darwin_test.go b/cpu/cpu_darwin_test.go index 5548cf9..20a8da6 100644 --- a/cpu/cpu_darwin_test.go +++ b/cpu/cpu_darwin_test.go @@ -5,13 +5,12 @@ package cpu import ( "os" + "runtime" "testing" - - "github.com/shoenig/go-m1cpu" ) func TestInfo_AppleSilicon(t *testing.T) { - if !m1cpu.IsAppleSilicon() { + if runtime.GOARCH != "arm64" { t.Skip("wrong cpu type") } diff --git a/disk/disk_darwin.go b/disk/disk_darwin.go index 6ed7400..52afb23 100644 --- a/disk/disk_darwin.go +++ b/disk/disk_darwin.go @@ -5,6 +5,8 @@ package disk import ( "context" + "fmt" + "unsafe" "golang.org/x/sys/unix" @@ -92,3 +94,201 @@ func SerialNumberWithContext(ctx context.Context, name string) (string, error) { func LabelWithContext(ctx context.Context, name string) (string, error) { return "", common.ErrNotImplementedError } + +func IOCountersWithContext(ctx context.Context, names ...string) (map[string]IOCountersStat, error) { + ioKit, err := common.NewLibrary(common.IOKit) + if err != nil { + return nil, err + } + defer ioKit.Close() + + coreFoundation, err := common.NewLibrary(common.CoreFoundation) + if err != nil { + return nil, err + } + defer coreFoundation.Close() + + ioServiceMatching := common.GetFunc[common.IOServiceMatchingFunc](ioKit, common.IOServiceMatchingSym) + ioServiceGetMatchingServices := common.GetFunc[common.IOServiceGetMatchingServicesFunc](ioKit, common.IOServiceGetMatchingServicesSym) + ioIteratorNext := common.GetFunc[common.IOIteratorNextFunc](ioKit, common.IOIteratorNextSym) + ioObjectRelease := common.GetFunc[common.IOObjectReleaseFunc](ioKit, common.IOObjectReleaseSym) + + cfDictionaryAddValue := common.GetFunc[common.CFDictionaryAddValueFunc](coreFoundation, common.CFDictionaryAddValueSym) + cfStringCreateWithCString := common.GetFunc[common.CFStringCreateWithCStringFunc](coreFoundation, common.CFStringCreateWithCStringSym) + cfRelease := common.GetFunc[common.CFReleaseFunc](coreFoundation, common.CFReleaseSym) + + kCFBooleanTruePtr, _ := coreFoundation.Dlsym("kCFBooleanTrue") + + match := ioServiceMatching("IOMedia") + + key := cfStringCreateWithCString(common.KCFAllocatorDefault, common.KIOMediaWholeKey, common.KCFStringEncodingUTF8) + defer cfRelease(uintptr(key)) + + var drives uint32 + kCFBooleanTrue := **(**uintptr)(unsafe.Pointer(&kCFBooleanTruePtr)) + cfDictionaryAddValue(uintptr(match), uintptr(key), kCFBooleanTrue) + if status := ioServiceGetMatchingServices(common.KIOMainPortDefault, uintptr(match), &drives); status != common.KERN_SUCCESS { + return nil, fmt.Errorf("IOServiceGetMatchingServices error=%d", status) + } + defer ioObjectRelease(drives) + + ic := &ioCounters{ + ioKit: ioKit, + coreFoundation: coreFoundation, + + ioRegistryEntryCreateCFProperties: common.GetFunc[common.IORegistryEntryCreateCFPropertiesFunc](ioKit, common.IORegistryEntryCreateCFPropertiesSym), + ioObjectRelease: ioObjectRelease, + + cfStringCreateWithCString: cfStringCreateWithCString, + cfDictionaryGetValue: common.GetFunc[common.CFDictionaryGetValueFunc](coreFoundation, common.CFDictionaryGetValueSym), + cfNumberGetValue: common.GetFunc[common.CFNumberGetValueFunc](coreFoundation, common.CFNumberGetValueSym), + cfRelease: cfRelease, + } + + stats := make([]IOCountersStat, 0, 16) + for { + d := ioIteratorNext(drives) + if !(d > 0) { + break + } + + stat, err := ic.getDriveStat(d) + if err != nil { + return nil, err + } + + if stat != nil { + stats = append(stats, *stat) + } + + ioObjectRelease(d) + } + + ret := make(map[string]IOCountersStat, 0) + for i := 0; i < len(stats); i++ { + if len(names) > 0 && !common.StringsHas(names, stats[i].Name) { + continue + } + + stats[i].ReadTime = stats[i].ReadTime / 1000 / 1000 // note: read/write time are in ns, but we want ms. + stats[i].WriteTime = stats[i].WriteTime / 1000 / 1000 + stats[i].IoTime = stats[i].ReadTime + stats[i].WriteTime + + ret[stats[i].Name] = stats[i] + } + + return ret, nil +} + +const ( + kIOBSDNameKey = "BSD Name" + kIOMediaSizeKey = "Size" + kIOMediaPreferredBlockSizeKey = "Preferred Block Size" + + kIOBlockStorageDriverStatisticsKey = "Statistics" + kIOBlockStorageDriverStatisticsBytesReadKey = "Bytes (Read)" + kIOBlockStorageDriverStatisticsBytesWrittenKey = "Bytes (Write)" + kIOBlockStorageDriverStatisticsReadsKey = "Operations (Read)" + kIOBlockStorageDriverStatisticsWritesKey = "Operations (Write)" + kIOBlockStorageDriverStatisticsTotalReadTimeKey = "Total Time (Read)" + kIOBlockStorageDriverStatisticsTotalWriteTimeKey = "Total Time (Write)" +) + +type ioCounters struct { + ioKit *common.Library + coreFoundation *common.Library + + ioRegistryEntryCreateCFProperties common.IORegistryEntryCreateCFPropertiesFunc + ioObjectRelease common.IOObjectReleaseFunc + + cfStringCreateWithCString common.CFStringCreateWithCStringFunc + cfDictionaryGetValue common.CFDictionaryGetValueFunc + cfNumberGetValue common.CFNumberGetValueFunc + cfRelease common.CFReleaseFunc +} + +func (i *ioCounters) getDriveStat(d uint32) (*IOCountersStat, error) { + ioRegistryEntryGetParentEntry := common.GetFunc[common.IORegistryEntryGetParentEntryFunc](i.ioKit, common.IORegistryEntryGetParentEntrySym) + ioObjectConformsTo := common.GetFunc[common.IOObjectConformsToFunc](i.ioKit, common.IOObjectConformsToSym) + + cfStringGetLength := common.GetFunc[common.CFStringGetLengthFunc](i.coreFoundation, common.CFStringGetLengthSym) + cfStringGetCString := common.GetFunc[common.CFStringGetCStringFunc](i.coreFoundation, common.CFStringGetCStringSym) + + var parent uint32 + if status := ioRegistryEntryGetParentEntry(d, common.KIOServicePlane, &parent); status != common.KERN_SUCCESS { + return nil, fmt.Errorf("IORegistryEntryGetParentEntry error=%d", status) + } + defer i.ioObjectRelease(parent) + + if !ioObjectConformsTo(parent, "IOBlockStorageDriver") { + //return nil, fmt.Errorf("ERROR: the object is not of the IOBlockStorageDriver class") + return nil, nil + } + + var props unsafe.Pointer + if status := i.ioRegistryEntryCreateCFProperties(d, unsafe.Pointer(&props), common.KCFAllocatorDefault, common.KNilOptions); status != common.KERN_SUCCESS { + return nil, fmt.Errorf("IORegistryEntryCreateCFProperties error=%d", status) + } + defer i.cfRelease(uintptr(props)) + + key := i.cfStr(kIOBSDNameKey) + defer i.cfRelease(uintptr(key)) + name := i.cfDictionaryGetValue(uintptr(props), uintptr(key)) + length := cfStringGetLength(uintptr(name)) + 1 + buf := make([]byte, length-1) + cfStringGetCString(uintptr(name), &buf[0], length, common.KCFStringEncodingUTF8) + + stat, err := i.fillStat(parent) + if err != nil { + return nil, err + } + + if stat != nil { + stat.Name = string(buf) + return stat, nil + } + return nil, nil +} + +func (i *ioCounters) fillStat(d uint32) (*IOCountersStat, error) { + var props unsafe.Pointer + status := i.ioRegistryEntryCreateCFProperties(d, unsafe.Pointer(&props), common.KCFAllocatorDefault, common.KNilOptions) + if status != common.KERN_SUCCESS { + return nil, fmt.Errorf("IORegistryEntryCreateCFProperties error=%d", status) + } + if props == nil { + return nil, nil + } + defer i.cfRelease(uintptr(props)) + + key := i.cfStr(kIOBlockStorageDriverStatisticsKey) + defer i.cfRelease(uintptr(key)) + v := i.cfDictionaryGetValue(uintptr(props), uintptr(key)) + if v == nil { + return nil, fmt.Errorf("CFDictionaryGetValue failed") + } + + var stat IOCountersStat + statstab := map[string]uintptr{ + kIOBlockStorageDriverStatisticsBytesReadKey: unsafe.Offsetof(stat.ReadBytes), + kIOBlockStorageDriverStatisticsBytesWrittenKey: unsafe.Offsetof(stat.WriteBytes), + kIOBlockStorageDriverStatisticsReadsKey: unsafe.Offsetof(stat.ReadCount), + kIOBlockStorageDriverStatisticsWritesKey: unsafe.Offsetof(stat.WriteCount), + kIOBlockStorageDriverStatisticsTotalReadTimeKey: unsafe.Offsetof(stat.ReadTime), + kIOBlockStorageDriverStatisticsTotalWriteTimeKey: unsafe.Offsetof(stat.WriteTime), + } + + for key, off := range statstab { + s := i.cfStr(key) + defer i.cfRelease(uintptr(s)) + if num := i.cfDictionaryGetValue(uintptr(v), uintptr(s)); num != nil { + i.cfNumberGetValue(uintptr(num), common.KCFNumberSInt64Type, uintptr(unsafe.Pointer(uintptr(unsafe.Pointer(&stat))+off))) + } + } + + return &stat, nil +} + +func (i *ioCounters) cfStr(str string) unsafe.Pointer { + return i.cfStringCreateWithCString(common.KCFAllocatorDefault, str, common.KCFStringEncodingUTF8) +} diff --git a/disk/disk_darwin_cgo.go b/disk/disk_darwin_cgo.go deleted file mode 100644 index 3a98b61..0000000 --- a/disk/disk_darwin_cgo.go +++ /dev/null @@ -1,45 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -//go:build darwin && cgo && !ios - -package disk - -/* -#cgo LDFLAGS: -framework CoreFoundation -framework IOKit -#include -#include -#include "iostat_darwin.h" -*/ -import "C" - -import ( - "context" - - "github.com/shirou/gopsutil/v4/internal/common" -) - -func IOCountersWithContext(ctx context.Context, names ...string) (map[string]IOCountersStat, error) { - var buf [C.NDRIVE]C.DriveStats - n, err := C.gopsutil_v4_readdrivestat(&buf[0], C.int(len(buf))) - if err != nil { - return nil, err - } - ret := make(map[string]IOCountersStat, 0) - for i := 0; i < int(n); i++ { - d := IOCountersStat{ - ReadBytes: uint64(buf[i].read), - WriteBytes: uint64(buf[i].written), - ReadCount: uint64(buf[i].nread), - WriteCount: uint64(buf[i].nwrite), - ReadTime: uint64(buf[i].readtime / 1000 / 1000), // note: read/write time are in ns, but we want ms. - WriteTime: uint64(buf[i].writetime / 1000 / 1000), - IoTime: uint64((buf[i].readtime + buf[i].writetime) / 1000 / 1000), - Name: C.GoString(&buf[i].name[0]), - } - if len(names) > 0 && !common.StringsHas(names, d.Name) { - continue - } - - ret[d.Name] = d - } - return ret, nil -} diff --git a/disk/disk_darwin_nocgo.go b/disk/disk_darwin_nocgo.go deleted file mode 100644 index 8d55ca3..0000000 --- a/disk/disk_darwin_nocgo.go +++ /dev/null @@ -1,14 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -//go:build (darwin && !cgo) || ios - -package disk - -import ( - "context" - - "github.com/shirou/gopsutil/v4/internal/common" -) - -func IOCountersWithContext(ctx context.Context, names ...string) (map[string]IOCountersStat, error) { - return nil, common.ErrNotImplementedError -} diff --git a/disk/iostat_darwin.c b/disk/iostat_darwin.c deleted file mode 100644 index ba1e4c5..0000000 --- a/disk/iostat_darwin.c +++ /dev/null @@ -1,131 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -// SPDX-FileCopyrightText: Copyright (c) 2017, kadota kyohei -// https://github.com/lufia/iostat/blob/9f7362b77ad333b26c01c99de52a11bdb650ded2/iostat_darwin.c -#include -#include -#include "iostat_darwin.h" - -#define IOKIT 1 /* to get io_name_t in device_types.h */ - -#include -#include -#include -#include - -#include - -static int getdrivestat(io_registry_entry_t d, DriveStats *stat); -static int fillstat(io_registry_entry_t d, DriveStats *stat); - -int -gopsutil_v4_readdrivestat(DriveStats a[], int n) -{ - CFMutableDictionaryRef match; - io_iterator_t drives; - io_registry_entry_t d; - kern_return_t status; - int na, rv; - - match = IOServiceMatching("IOMedia"); - CFDictionaryAddValue(match, CFSTR(kIOMediaWholeKey), kCFBooleanTrue); - status = IOServiceGetMatchingServices(0, match, &drives); - if(status != KERN_SUCCESS) - return -1; - - na = 0; - while(na < n && (d=IOIteratorNext(drives)) > 0){ - rv = getdrivestat(d, &a[na]); - if(rv < 0) - return -1; - if(rv > 0) - na++; - IOObjectRelease(d); - } - IOObjectRelease(drives); - return na; -} - -static int -getdrivestat(io_registry_entry_t d, DriveStats *stat) -{ - io_registry_entry_t parent; - kern_return_t status; - CFDictionaryRef props; - CFStringRef name; - CFNumberRef num; - int rv; - - memset(stat, 0, sizeof *stat); - status = IORegistryEntryGetParentEntry(d, kIOServicePlane, &parent); - if(status != KERN_SUCCESS) - return -1; - if(!IOObjectConformsTo(parent, "IOBlockStorageDriver")){ - IOObjectRelease(parent); - return 0; - } - - status = IORegistryEntryCreateCFProperties(d, (CFMutableDictionaryRef *)&props, kCFAllocatorDefault, kNilOptions); - if(status != KERN_SUCCESS){ - IOObjectRelease(parent); - return -1; - } - name = (CFStringRef)CFDictionaryGetValue(props, CFSTR(kIOBSDNameKey)); - CFStringGetCString(name, stat->name, NAMELEN, CFStringGetSystemEncoding()); - num = (CFNumberRef)CFDictionaryGetValue(props, CFSTR(kIOMediaSizeKey)); - CFNumberGetValue(num, kCFNumberSInt64Type, &stat->size); - num = (CFNumberRef)CFDictionaryGetValue(props, CFSTR(kIOMediaPreferredBlockSizeKey)); - CFNumberGetValue(num, kCFNumberSInt64Type, &stat->blocksize); - CFRelease(props); - - rv = fillstat(parent, stat); - IOObjectRelease(parent); - if(rv < 0) - return -1; - return 1; -} - -static struct { - char *key; - size_t off; -} statstab[] = { - {kIOBlockStorageDriverStatisticsBytesReadKey, offsetof(DriveStats, read)}, - {kIOBlockStorageDriverStatisticsBytesWrittenKey, offsetof(DriveStats, written)}, - {kIOBlockStorageDriverStatisticsReadsKey, offsetof(DriveStats, nread)}, - {kIOBlockStorageDriverStatisticsWritesKey, offsetof(DriveStats, nwrite)}, - {kIOBlockStorageDriverStatisticsTotalReadTimeKey, offsetof(DriveStats, readtime)}, - {kIOBlockStorageDriverStatisticsTotalWriteTimeKey, offsetof(DriveStats, writetime)}, - {kIOBlockStorageDriverStatisticsLatentReadTimeKey, offsetof(DriveStats, readlat)}, - {kIOBlockStorageDriverStatisticsLatentWriteTimeKey, offsetof(DriveStats, writelat)}, -}; - -static int -fillstat(io_registry_entry_t d, DriveStats *stat) -{ - CFDictionaryRef props, v; - CFNumberRef num; - kern_return_t status; - typeof(statstab[0]) *bp, *ep; - - status = IORegistryEntryCreateCFProperties(d, (CFMutableDictionaryRef *)&props, kCFAllocatorDefault, kNilOptions); - if(status != KERN_SUCCESS) - return -1; - v = (CFDictionaryRef)CFDictionaryGetValue(props, CFSTR(kIOBlockStorageDriverStatisticsKey)); - if(v == NULL){ - CFRelease(props); - return -1; - } - - ep = &statstab[sizeof(statstab)/sizeof(statstab[0])]; - for(bp = &statstab[0]; bp < ep; bp++){ - CFStringRef s; - - s = CFStringCreateWithCString(kCFAllocatorDefault, bp->key, CFStringGetSystemEncoding()); - num = (CFNumberRef)CFDictionaryGetValue(v, s); - if(num) - CFNumberGetValue(num, kCFNumberSInt64Type, ((char*)stat)+bp->off); - CFRelease(s); - } - - CFRelease(props); - return 0; -} diff --git a/disk/iostat_darwin.h b/disk/iostat_darwin.h deleted file mode 100644 index 7b702aa..0000000 --- a/disk/iostat_darwin.h +++ /dev/null @@ -1,34 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -// SPDX-FileCopyrightText: Copyright (c) 2017, kadota kyohei -// https://github.com/lufia/iostat/blob/9f7362b77ad333b26c01c99de52a11bdb650ded2/iostat_darwin.h -typedef struct DriveStats DriveStats; -typedef struct CPUStats CPUStats; - -enum { - NDRIVE = 16, - NAMELEN = 31 -}; - -struct DriveStats { - char name[NAMELEN+1]; - int64_t size; - int64_t blocksize; - - int64_t read; - int64_t written; - int64_t nread; - int64_t nwrite; - int64_t readtime; - int64_t writetime; - int64_t readlat; - int64_t writelat; -}; - -struct CPUStats { - natural_t user; - natural_t nice; - natural_t sys; - natural_t idle; -}; - -extern int gopsutil_v4_readdrivestat(DriveStats a[], int n); diff --git a/go.mod b/go.mod index 79ecebb..56f786f 100644 --- a/go.mod +++ b/go.mod @@ -7,11 +7,10 @@ require ( github.com/google/go-cmp v0.6.0 github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c - github.com/shoenig/go-m1cpu v0.1.6 github.com/stretchr/testify v1.9.0 github.com/tklauser/go-sysconf v0.3.12 github.com/yusufpapurcu/wmi v1.2.4 - golang.org/x/sys v0.24.0 + golang.org/x/sys v0.25.0 ) require ( diff --git a/go.sum b/go.sum index a2896db..10c491d 100644 --- a/go.sum +++ b/go.sum @@ -13,9 +13,6 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= -github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= -github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= -github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= @@ -28,8 +25,8 @@ golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= -golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= +golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/internal/common/common_darwin.go b/internal/common/common_darwin.go index 0a1da93..b473f88 100644 --- a/internal/common/common_darwin.go +++ b/internal/common/common_darwin.go @@ -78,7 +78,7 @@ type Library struct { const ( IOKit = "/System/Library/Frameworks/IOKit.framework/IOKit" CoreFoundation = "/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation" - Kernel = "/usr/lib/system/libsystem_kernel.dylib" + System = "/usr/lib/libSystem.B.dylib" ) func NewLibrary(path string) (*Library, error) { @@ -119,12 +119,19 @@ const ( // IOKit functions and symbols. type ( - IOServiceGetMatchingServiceFunc func(mainPort uint32, matching uintptr) uint32 - IOServiceMatchingFunc func(name string) unsafe.Pointer - IOServiceOpenFunc func(service, owningTask, connType uint32, connect *uint32) int - IOServiceCloseFunc func(connect uint32) int - IOObjectReleaseFunc func(object uint32) int - IOConnectCallStructMethodFunc func(connection, selector uint32, inputStruct, inputStructCnt, outputStruct uintptr, outputStructCnt *uintptr) int + IOServiceGetMatchingServiceFunc func(mainPort uint32, matching uintptr) uint32 + IOServiceGetMatchingServicesFunc func(mainPort uint32, matching uintptr, existing *uint32) int + IOServiceMatchingFunc func(name string) unsafe.Pointer + IOServiceOpenFunc func(service, owningTask, connType uint32, connect *uint32) int + IOServiceCloseFunc func(connect uint32) int + IOIteratorNextFunc func(iterator uint32) uint32 + IORegistryEntryGetNameFunc func(entry uint32, name *byte) int + IORegistryEntryGetParentEntryFunc func(entry uint32, plane string, parent *uint32) int + IORegistryEntryCreateCFPropertyFunc func(entry uint32, key, allocator uintptr, options uint32) unsafe.Pointer + IORegistryEntryCreateCFPropertiesFunc func(entry uint32, properties unsafe.Pointer, allocator uintptr, options uint32) int + IOObjectConformsToFunc func(object uint32, className string) bool + IOObjectReleaseFunc func(object uint32) int + IOConnectCallStructMethodFunc func(connection, selector uint32, inputStruct, inputStructCnt, outputStruct uintptr, outputStructCnt *uintptr) int IOHIDEventSystemClientCreateFunc func(allocator uintptr) unsafe.Pointer IOHIDEventSystemClientSetMatchingFunc func(client, match uintptr) int @@ -136,12 +143,19 @@ type ( ) const ( - IOServiceGetMatchingServiceSym = "IOServiceGetMatchingService" - IOServiceMatchingSym = "IOServiceMatching" - IOServiceOpenSym = "IOServiceOpen" - IOServiceCloseSym = "IOServiceClose" - IOObjectReleaseSym = "IOObjectRelease" - IOConnectCallStructMethodSym = "IOConnectCallStructMethod" + IOServiceGetMatchingServiceSym = "IOServiceGetMatchingService" + IOServiceGetMatchingServicesSym = "IOServiceGetMatchingServices" + IOServiceMatchingSym = "IOServiceMatching" + IOServiceOpenSym = "IOServiceOpen" + IOServiceCloseSym = "IOServiceClose" + IOIteratorNextSym = "IOIteratorNext" + IORegistryEntryGetNameSym = "IORegistryEntryGetName" + IORegistryEntryGetParentEntrySym = "IORegistryEntryGetParentEntry" + IORegistryEntryCreateCFPropertySym = "IORegistryEntryCreateCFProperty" + IORegistryEntryCreateCFPropertiesSym = "IORegistryEntryCreateCFProperties" + IOObjectConformsToSym = "IOObjectConformsTo" + IOObjectReleaseSym = "IOObjectRelease" + IOConnectCallStructMethodSym = "IOConnectCallStructMethod" IOHIDEventSystemClientCreateSym = "IOHIDEventSystemClientCreate" IOHIDEventSystemClientSetMatchingSym = "IOHIDEventSystemClientSetMatching" @@ -152,49 +166,77 @@ const ( ) const ( + KIOMainPortDefault = 0 + KIOHIDEventTypeTemperature = 15 + + KNilOptions = 0 +) + +const ( + KIOMediaWholeKey = "Media" + KIOServicePlane = "IOService" ) // CoreFoundation functions and symbols. type ( + CFGetTypeIDFunc func(cf uintptr) int32 CFNumberCreateFunc func(allocator uintptr, theType int32, valuePtr uintptr) unsafe.Pointer + CFNumberGetValueFunc func(num uintptr, theType int32, valuePtr uintptr) bool CFDictionaryCreateFunc func(allocator uintptr, keys, values *unsafe.Pointer, numValues int32, keyCallBacks, valueCallBacks uintptr) unsafe.Pointer + CFDictionaryAddValueFunc func(theDict, key, value uintptr) + CFDictionaryGetValueFunc func(theDict, key uintptr) unsafe.Pointer CFArrayGetCountFunc func(theArray uintptr) int32 CFArrayGetValueAtIndexFunc func(theArray uintptr, index int32) unsafe.Pointer CFStringCreateMutableFunc func(alloc uintptr, maxLength int32) unsafe.Pointer CFStringGetLengthFunc func(theString uintptr) int32 CFStringGetCStringFunc func(theString uintptr, buffer *byte, bufferSize int32, encoding uint32) CFStringCreateWithCStringFunc func(alloc uintptr, cStr string, encoding uint32) unsafe.Pointer + CFDataGetLengthFunc func(theData uintptr) int32 + CFDataGetBytePtrFunc func(theData uintptr) unsafe.Pointer CFReleaseFunc func(cf uintptr) ) const ( + CFGetTypeIDSym = "CFGetTypeID" CFNumberCreateSym = "CFNumberCreate" + CFNumberGetValueSym = "CFNumberGetValue" CFDictionaryCreateSym = "CFDictionaryCreate" + CFDictionaryAddValueSym = "CFDictionaryAddValue" + CFDictionaryGetValueSym = "CFDictionaryGetValue" CFArrayGetCountSym = "CFArrayGetCount" CFArrayGetValueAtIndexSym = "CFArrayGetValueAtIndex" CFStringCreateMutableSym = "CFStringCreateMutable" CFStringGetLengthSym = "CFStringGetLength" CFStringGetCStringSym = "CFStringGetCString" CFStringCreateWithCStringSym = "CFStringCreateWithCString" + CFDataGetLengthSym = "CFDataGetLength" + CFDataGetBytePtrSym = "CFDataGetBytePtr" CFReleaseSym = "CFRelease" ) const ( KCFStringEncodingUTF8 = 0x08000100 + KCFNumberSInt64Type = 4 KCFNumberIntType = 9 KCFAllocatorDefault = 0 ) // Kernel functions and symbols. +type MachTimeBaseInfo struct { + Numer uint32 + Denom uint32 +} + type ( - HostProcessorInfoFunc func(host uint32, flavor int, outProcessorCount *uint32, outProcessorInfo uintptr, + HostProcessorInfoFunc func(host uint32, flavor int32, outProcessorCount *uint32, outProcessorInfo uintptr, outProcessorInfoCnt *uint32) int - HostStatisticsFunc func(host uint32, flavor int, hostInfoOut uintptr, hostInfoOutCnt *uint32) int - MachHostSelfFunc func() uint32 - MachTaskSelfFunc func() uint32 - VMDeallocateFunc func(targetTask uint32, vmAddress, vmSize uintptr) int + HostStatisticsFunc func(host uint32, flavor int32, hostInfoOut uintptr, hostInfoOutCnt *uint32) int + MachHostSelfFunc func() uint32 + MachTaskSelfFunc func() uint32 + MachTimeBaseInfoFunc func(info uintptr) int + VMDeallocateFunc func(targetTask uint32, vmAddress, vmSize uintptr) int ) const ( @@ -202,16 +244,40 @@ const ( HostStatisticsSym = "host_statistics" MachHostSelfSym = "mach_host_self" MachTaskSelfSym = "mach_task_self" + MachTimeBaseInfoSym = "mach_timebase_info" VMDeallocateSym = "vm_deallocate" ) const ( + CTL_KERN = 1 + KERN_ARGMAX = 8 + KERN_PROCARGS2 = 49 + HOST_VM_INFO = 2 HOST_CPU_LOAD_INFO = 3 HOST_VM_INFO_COUNT = 0xf ) +// System functions and symbols. +type ( + ProcPidPathFunc func(pid int32, buffer uintptr, bufferSize uint32) int32 + ProcPidInfoFunc func(pid, flavor int32, arg uint64, buffer uintptr, bufferSize int32) int32 +) + +const ( + SysctlSym = "sysctl" + ProcPidPathSym = "proc_pidpath" + ProcPidInfoSym = "proc_pidinfo" +) + +const ( + MAXPATHLEN = 1024 + PROC_PIDPATHINFO_MAXSIZE = 4 * MAXPATHLEN + PROC_PIDTASKINFO = 4 + PROC_PIDVNODEPATHINFO = 9 +) + // SMC represents a SMC instance. type SMC struct { lib *Library @@ -281,3 +347,18 @@ func (s *SMC) Close() error { } return nil } + +// https://github.com/ebitengine/purego/blob/main/internal/strings/strings.go#L26 +func GoString(cStr *byte) string { + if cStr == nil { + return "" + } + var length int + for { + if *(*byte)(unsafe.Add(unsafe.Pointer(cStr), uintptr(length))) == '\x00' { + break + } + length++ + } + return string(unsafe.Slice(cStr, length)) +} diff --git a/internal/common/common_unix.go b/internal/common/common_unix.go index 2715b89..c9f91b1 100644 --- a/internal/common/common_unix.go +++ b/internal/common/common_unix.go @@ -40,23 +40,3 @@ func CallLsofWithContext(ctx context.Context, invoke Invoker, pid int32, args .. } return ret, nil } - -func CallPgrepWithContext(ctx context.Context, invoke Invoker, pid int32) ([]int32, error) { - out, err := invoke.CommandWithContext(ctx, "pgrep", "-P", strconv.Itoa(int(pid))) - if err != nil { - return []int32{}, err - } - lines := strings.Split(string(out), "\n") - ret := make([]int32, 0, len(lines)) - for _, l := range lines { - if len(l) == 0 { - continue - } - i, err := strconv.ParseInt(l, 10, 32) - if err != nil { - continue - } - ret = append(ret, int32(i)) - } - return ret, nil -} diff --git a/mem/mem_darwin.go b/mem/mem_darwin.go index 4442cbc..a4c15f6 100644 --- a/mem/mem_darwin.go +++ b/mem/mem_darwin.go @@ -85,7 +85,7 @@ func VirtualMemory() (*VirtualMemoryStat, error) { } func VirtualMemoryWithContext(ctx context.Context) (*VirtualMemoryStat, error) { - machLib, err := common.NewLibrary(common.Kernel) + machLib, err := common.NewLibrary(common.System) if err != nil { return nil, err } diff --git a/process/process.go b/process/process.go index d73f1f9..70411c6 100644 --- a/process/process.go +++ b/process/process.go @@ -18,7 +18,7 @@ import ( var ( invoke common.Invoker = common.Invoke{} - ErrorNoChildren = errors.New("process does not have children") + ErrorNoChildren = errors.New("process does not have children") // Deprecated: ErrorNoChildren is never returned by process.Children(), check its returned []*Process slice length instead ErrorProcessNotRunning = errors.New("process does not exist") ErrorNotPermitted = errors.New("operation not permitted") ) diff --git a/process/process_darwin.go b/process/process_darwin.go index 66b3684..05c7562 100644 --- a/process/process_darwin.go +++ b/process/process_darwin.go @@ -4,15 +4,20 @@ package process import ( + "bytes" "context" + "encoding/binary" "fmt" "path/filepath" + "runtime" + "sort" "strconv" "strings" + "unsafe" - "github.com/tklauser/go-sysconf" "golang.org/x/sys/unix" + "github.com/shirou/gopsutil/v4/cpu" "github.com/shirou/gopsutil/v4/internal/common" "github.com/shirou/gopsutil/v4/net" ) @@ -27,16 +32,6 @@ const ( KernProcPathname = 12 // path to executable ) -var clockTicks = 100 // default value - -func init() { - clkTck, err := sysconf.Sysconf(sysconf.SC_CLK_TCK) - // ignore errors - if err == nil { - clockTicks = int(clkTck) - } -} - type _Ctype_struct___0 struct { Pad uint64 } @@ -186,65 +181,22 @@ func (p *Process) IOCountersWithContext(ctx context.Context) (*IOCountersStat, e return nil, common.ErrNotImplementedError } -func convertCPUTimes(s string) (ret float64, err error) { - var t int - var _tmp string - if strings.Contains(s, ":") { - _t := strings.Split(s, ":") - switch len(_t) { - case 3: - hour, err := strconv.ParseInt(_t[0], 10, 32) - if err != nil { - return ret, err - } - t += int(hour) * 60 * 60 * clockTicks - - mins, err := strconv.ParseInt(_t[1], 10, 32) - if err != nil { - return ret, err - } - t += int(mins) * 60 * clockTicks - _tmp = _t[2] - case 2: - mins, err := strconv.ParseInt(_t[0], 10, 32) - if err != nil { - return ret, err - } - t += int(mins) * 60 * clockTicks - _tmp = _t[1] - case 1, 0: - _tmp = s - default: - return ret, fmt.Errorf("wrong cpu time string") - } - } else { - _tmp = s - } - - _t := strings.Split(_tmp, ".") - if err != nil { - return ret, err - } - h, err := strconv.ParseInt(_t[0], 10, 32) - t += int(h) * clockTicks - h, err = strconv.ParseInt(_t[1], 10, 32) - t += int(h) - return float64(t) / float64(clockTicks), nil -} - func (p *Process) ChildrenWithContext(ctx context.Context) ([]*Process, error) { - pids, err := common.CallPgrepWithContext(ctx, invoke, p.Pid) + procs, err := ProcessesWithContext(ctx) if err != nil { - return nil, err + return nil, nil } - ret := make([]*Process, 0, len(pids)) - for _, pid := range pids { - np, err := NewProcessWithContext(ctx, pid) + ret := make([]*Process, 0, len(procs)) + for _, proc := range procs { + ppid, err := proc.PpidWithContext(ctx) if err != nil { - return nil, err + continue + } + if ppid == p.Pid { + ret = append(ret, proc) } - ret = append(ret, np) } + sort.Slice(ret, func(i, j int) bool { return ret[i].Pid < ret[j].Pid }) return ret, nil } @@ -323,3 +275,206 @@ func callPsWithContext(ctx context.Context, arg string, pid int32, threadOption return ret, nil } + +var ( + procPidPath common.ProcPidPathFunc + procPidInfo common.ProcPidInfoFunc + machTimeBaseInfo common.MachTimeBaseInfoFunc +) + +func registerFuncs() (*common.Library, error) { + lib, err := common.NewLibrary(common.System) + if err != nil { + return nil, err + } + + procPidPath = common.GetFunc[common.ProcPidPathFunc](lib, common.ProcPidPathSym) + procPidInfo = common.GetFunc[common.ProcPidInfoFunc](lib, common.ProcPidInfoSym) + machTimeBaseInfo = common.GetFunc[common.MachTimeBaseInfoFunc](lib, common.MachTimeBaseInfoSym) + + return lib, nil +} + +func getTimeScaleToNanoSeconds() float64 { + var timeBaseInfo common.MachTimeBaseInfo + + machTimeBaseInfo(uintptr(unsafe.Pointer(&timeBaseInfo))) + + return float64(timeBaseInfo.Numer) / float64(timeBaseInfo.Denom) +} + +func (p *Process) ExeWithContext(ctx context.Context) (string, error) { + lib, err := registerFuncs() + if err != nil { + return "", err + } + defer lib.Close() + + buf := make([]byte, common.PROC_PIDPATHINFO_MAXSIZE) + ret := procPidPath(p.Pid, uintptr(unsafe.Pointer(&buf[0])), common.PROC_PIDPATHINFO_MAXSIZE) + + if ret <= 0 { + return "", fmt.Errorf("unknown error: proc_pidpath returned %d", ret) + } + + return common.GoString(&buf[0]), nil +} + +// sys/proc_info.h +type vnodePathInfo struct { + _ [152]byte + vipPath [common.MAXPATHLEN]byte + _ [1176]byte +} + +// CwdWithContext retrieves the Current Working Directory for the given process. +// It uses the proc_pidinfo from libproc and will only work for processes the +// EUID can access. Otherwise "operation not permitted" will be returned as the +// error. +// Note: This might also work for other *BSD OSs. +func (p *Process) CwdWithContext(ctx context.Context) (string, error) { + lib, err := registerFuncs() + if err != nil { + return "", err + } + defer lib.Close() + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + var vpi vnodePathInfo + const vpiSize = int32(unsafe.Sizeof(vpi)) + ret := procPidInfo(p.Pid, common.PROC_PIDVNODEPATHINFO, 0, uintptr(unsafe.Pointer(&vpi)), vpiSize) + errno, _ := lib.Dlsym("errno") + err = *(**unix.Errno)(unsafe.Pointer(&errno)) + if err == unix.EPERM { + return "", ErrorNotPermitted + } + + if ret <= 0 { + return "", fmt.Errorf("unknown error: proc_pidinfo returned %d", ret) + } + + if ret != vpiSize { + return "", fmt.Errorf("too few bytes; expected %d, got %d", vpiSize, ret) + } + return common.GoString(&vpi.vipPath[0]), nil +} + +func procArgs(pid int32) ([]byte, int, error) { + procargs, _, err := common.CallSyscall([]int32{common.CTL_KERN, common.KERN_PROCARGS2, pid}) + if err != nil { + return nil, 0, err + } + nargs := procargs[:4] + return procargs, int(binary.LittleEndian.Uint32(nargs)), nil +} + +func (p *Process) CmdlineSliceWithContext(ctx context.Context) ([]string, error) { + return p.cmdlineSliceWithContext(ctx, true) +} + +func (p *Process) cmdlineSliceWithContext(ctx context.Context, fallback bool) ([]string, error) { + pargs, nargs, err := procArgs(p.Pid) + if err != nil { + return nil, err + } + // The first bytes hold the nargs int, skip it. + args := bytes.Split((pargs)[unsafe.Sizeof(int(0)):], []byte{0}) + var argStr string + // The first element is the actual binary/command path. + // command := args[0] + var argSlice []string + // var envSlice []string + // All other, non-zero elements are arguments. The first "nargs" elements + // are the arguments. Everything else in the slice is then the environment + // of the process. + for _, arg := range args[1:] { + argStr = string(arg[:]) + if len(argStr) > 0 { + if nargs > 0 { + argSlice = append(argSlice, argStr) + nargs-- + continue + } + break + // envSlice = append(envSlice, argStr) + } + } + return argSlice, err +} + +// cmdNameWithContext returns the command name (including spaces) without any arguments +func (p *Process) cmdNameWithContext(ctx context.Context) (string, error) { + r, err := p.cmdlineSliceWithContext(ctx, false) + if err != nil { + return "", err + } + + if len(r) == 0 { + return "", nil + } + + return r[0], err +} + +func (p *Process) CmdlineWithContext(ctx context.Context) (string, error) { + r, err := p.CmdlineSliceWithContext(ctx) + if err != nil { + return "", err + } + return strings.Join(r, " "), err +} + +func (p *Process) NumThreadsWithContext(ctx context.Context) (int32, error) { + lib, err := registerFuncs() + if err != nil { + return 0, err + } + defer lib.Close() + + var ti ProcTaskInfo + const tiSize = int32(unsafe.Sizeof(ti)) + procPidInfo(p.Pid, common.PROC_PIDTASKINFO, 0, uintptr(unsafe.Pointer(&ti)), tiSize) + + return int32(ti.Threadnum), nil +} + +func (p *Process) TimesWithContext(ctx context.Context) (*cpu.TimesStat, error) { + lib, err := registerFuncs() + if err != nil { + return nil, err + } + defer lib.Close() + + var ti ProcTaskInfo + const tiSize = int32(unsafe.Sizeof(ti)) + procPidInfo(p.Pid, common.PROC_PIDTASKINFO, 0, uintptr(unsafe.Pointer(&ti)), tiSize) + + timescaleToNanoSeconds := getTimeScaleToNanoSeconds() + ret := &cpu.TimesStat{ + CPU: "cpu", + User: float64(ti.Total_user) * timescaleToNanoSeconds / 1e9, + System: float64(ti.Total_system) * timescaleToNanoSeconds / 1e9, + } + return ret, nil +} + +func (p *Process) MemoryInfoWithContext(ctx context.Context) (*MemoryInfoStat, error) { + lib, err := registerFuncs() + if err != nil { + return nil, err + } + defer lib.Close() + + var ti ProcTaskInfo + const tiSize = int32(unsafe.Sizeof(ti)) + procPidInfo(p.Pid, common.PROC_PIDTASKINFO, 0, uintptr(unsafe.Pointer(&ti)), tiSize) + + ret := &MemoryInfoStat{ + RSS: uint64(ti.Resident_size), + VMS: uint64(ti.Virtual_size), + Swap: uint64(ti.Pageins), + } + return ret, nil +} diff --git a/process/process_darwin_amd64.go b/process/process_darwin_amd64.go index a135224..890a5d5 100644 --- a/process/process_darwin_amd64.go +++ b/process/process_darwin_amd64.go @@ -212,6 +212,27 @@ type Posix_cred struct { type Label struct{} +type ProcTaskInfo struct { + Virtual_size uint64 + Resident_size uint64 + Total_user uint64 + Total_system uint64 + Threads_user uint64 + Threads_system uint64 + Policy int32 + Faults int32 + Pageins int32 + Cow_faults int32 + Messages_sent int32 + Messages_received int32 + Syscalls_mach int32 + Syscalls_unix int32 + Csw int32 + Threadnum int32 + Numrunning int32 + Priority int32 +} + type AuditinfoAddr struct { Auid uint32 Mask AuMask diff --git a/process/process_darwin_arm64.go b/process/process_darwin_arm64.go index f1f3df3..8075cf2 100644 --- a/process/process_darwin_arm64.go +++ b/process/process_darwin_arm64.go @@ -190,6 +190,27 @@ type Posix_cred struct{} type Label struct{} +type ProcTaskInfo struct { + Virtual_size uint64 + Resident_size uint64 + Total_user uint64 + Total_system uint64 + Threads_user uint64 + Threads_system uint64 + Policy int32 + Faults int32 + Pageins int32 + Cow_faults int32 + Messages_sent int32 + Messages_received int32 + Syscalls_mach int32 + Syscalls_unix int32 + Csw int32 + Threadnum int32 + Numrunning int32 + Priority int32 +} + type AuditinfoAddr struct { Auid uint32 Mask AuMask diff --git a/process/process_darwin_cgo.go b/process/process_darwin_cgo.go deleted file mode 100644 index bbdfc96..0000000 --- a/process/process_darwin_cgo.go +++ /dev/null @@ -1,222 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -//go:build darwin && cgo - -package process - -// #include -// #include -// #include -// #include -// #include -// #include -// #include -import "C" - -import ( - "bytes" - "context" - "fmt" - "strings" - "syscall" - "unsafe" - - "github.com/shirou/gopsutil/v4/cpu" -) - -var ( - argMax int - timescaleToNanoSeconds float64 -) - -func init() { - argMax = getArgMax() - timescaleToNanoSeconds = getTimeScaleToNanoSeconds() -} - -func getArgMax() int { - var ( - mib = [...]C.int{C.CTL_KERN, C.KERN_ARGMAX} - argmax C.int - size C.size_t = C.ulong(unsafe.Sizeof(argmax)) - ) - retval := C.sysctl(&mib[0], 2, unsafe.Pointer(&argmax), &size, C.NULL, 0) - if retval == 0 { - return int(argmax) - } - return 0 -} - -func getTimeScaleToNanoSeconds() float64 { - var timeBaseInfo C.struct_mach_timebase_info - - C.mach_timebase_info(&timeBaseInfo) - - return float64(timeBaseInfo.numer) / float64(timeBaseInfo.denom) -} - -func (p *Process) ExeWithContext(ctx context.Context) (string, error) { - var c C.char // need a var for unsafe.Sizeof need a var - const bufsize = C.PROC_PIDPATHINFO_MAXSIZE * unsafe.Sizeof(c) - buffer := (*C.char)(C.malloc(C.size_t(bufsize))) - defer C.free(unsafe.Pointer(buffer)) - - ret, err := C.proc_pidpath(C.int(p.Pid), unsafe.Pointer(buffer), C.uint32_t(bufsize)) - if err != nil { - return "", err - } - if ret <= 0 { - return "", fmt.Errorf("unknown error: proc_pidpath returned %d", ret) - } - - return C.GoString(buffer), nil -} - -// CwdWithContext retrieves the Current Working Directory for the given process. -// It uses the proc_pidinfo from libproc and will only work for processes the -// EUID can access. Otherwise "operation not permitted" will be returned as the -// error. -// Note: This might also work for other *BSD OSs. -func (p *Process) CwdWithContext(ctx context.Context) (string, error) { - const vpiSize = C.sizeof_struct_proc_vnodepathinfo - vpi := (*C.struct_proc_vnodepathinfo)(C.malloc(vpiSize)) - defer C.free(unsafe.Pointer(vpi)) - ret, err := C.proc_pidinfo(C.int(p.Pid), C.PROC_PIDVNODEPATHINFO, 0, unsafe.Pointer(vpi), vpiSize) - if err != nil { - // fmt.Printf("ret: %d %T\n", ret, err) - if err == syscall.EPERM { - return "", ErrorNotPermitted - } - return "", err - } - if ret <= 0 { - return "", fmt.Errorf("unknown error: proc_pidinfo returned %d", ret) - } - if ret != C.sizeof_struct_proc_vnodepathinfo { - return "", fmt.Errorf("too few bytes; expected %d, got %d", vpiSize, ret) - } - return C.GoString(&vpi.pvi_cdir.vip_path[0]), err -} - -func procArgs(pid int32) ([]byte, int, error) { - var ( - mib = [...]C.int{C.CTL_KERN, C.KERN_PROCARGS2, C.int(pid)} - size C.size_t = C.ulong(argMax) - nargs C.int - result []byte - ) - procargs := (*C.char)(C.malloc(C.ulong(argMax))) - defer C.free(unsafe.Pointer(procargs)) - retval, err := C.sysctl(&mib[0], 3, unsafe.Pointer(procargs), &size, C.NULL, 0) - if retval == 0 { - C.memcpy(unsafe.Pointer(&nargs), unsafe.Pointer(procargs), C.sizeof_int) - result = C.GoBytes(unsafe.Pointer(procargs), C.int(size)) - // fmt.Printf("size: %d %d\n%s\n", size, nargs, hex.Dump(result)) - return result, int(nargs), nil - } - return nil, 0, err -} - -func (p *Process) CmdlineSliceWithContext(ctx context.Context) ([]string, error) { - return p.cmdlineSliceWithContext(ctx, true) -} - -func (p *Process) cmdlineSliceWithContext(ctx context.Context, fallback bool) ([]string, error) { - pargs, nargs, err := procArgs(p.Pid) - if err != nil { - return nil, err - } - // The first bytes hold the nargs int, skip it. - args := bytes.Split((pargs)[C.sizeof_int:], []byte{0}) - var argStr string - // The first element is the actual binary/command path. - // command := args[0] - var argSlice []string - // var envSlice []string - // All other, non-zero elements are arguments. The first "nargs" elements - // are the arguments. Everything else in the slice is then the environment - // of the process. - for _, arg := range args[1:] { - argStr = string(arg[:]) - if len(argStr) > 0 { - if nargs > 0 { - argSlice = append(argSlice, argStr) - nargs-- - continue - } - break - // envSlice = append(envSlice, argStr) - } - } - return argSlice, err -} - -// cmdNameWithContext returns the command name (including spaces) without any arguments -func (p *Process) cmdNameWithContext(ctx context.Context) (string, error) { - r, err := p.cmdlineSliceWithContext(ctx, false) - if err != nil { - return "", err - } - - if len(r) == 0 { - return "", nil - } - - return r[0], err -} - -func (p *Process) CmdlineWithContext(ctx context.Context) (string, error) { - r, err := p.CmdlineSliceWithContext(ctx) - if err != nil { - return "", err - } - return strings.Join(r, " "), err -} - -func (p *Process) NumThreadsWithContext(ctx context.Context) (int32, error) { - const tiSize = C.sizeof_struct_proc_taskinfo - ti := (*C.struct_proc_taskinfo)(C.malloc(tiSize)) - defer C.free(unsafe.Pointer(ti)) - - _, err := C.proc_pidinfo(C.int(p.Pid), C.PROC_PIDTASKINFO, 0, unsafe.Pointer(ti), tiSize) - if err != nil { - return 0, err - } - - return int32(ti.pti_threadnum), nil -} - -func (p *Process) TimesWithContext(ctx context.Context) (*cpu.TimesStat, error) { - const tiSize = C.sizeof_struct_proc_taskinfo - ti := (*C.struct_proc_taskinfo)(C.malloc(tiSize)) - defer C.free(unsafe.Pointer(ti)) - - _, err := C.proc_pidinfo(C.int(p.Pid), C.PROC_PIDTASKINFO, 0, unsafe.Pointer(ti), tiSize) - if err != nil { - return nil, err - } - - ret := &cpu.TimesStat{ - CPU: "cpu", - User: float64(ti.pti_total_user) * timescaleToNanoSeconds / 1e9, - System: float64(ti.pti_total_system) * timescaleToNanoSeconds / 1e9, - } - return ret, nil -} - -func (p *Process) MemoryInfoWithContext(ctx context.Context) (*MemoryInfoStat, error) { - const tiSize = C.sizeof_struct_proc_taskinfo - ti := (*C.struct_proc_taskinfo)(C.malloc(tiSize)) - defer C.free(unsafe.Pointer(ti)) - - _, err := C.proc_pidinfo(C.int(p.Pid), C.PROC_PIDTASKINFO, 0, unsafe.Pointer(ti), tiSize) - if err != nil { - return nil, err - } - - ret := &MemoryInfoStat{ - RSS: uint64(ti.pti_resident_size), - VMS: uint64(ti.pti_virtual_size), - Swap: uint64(ti.pti_pageins), - } - return ret, nil -} diff --git a/process/process_darwin_nocgo.go b/process/process_darwin_nocgo.go deleted file mode 100644 index d498c93..0000000 --- a/process/process_darwin_nocgo.go +++ /dev/null @@ -1,134 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -//go:build darwin && !cgo - -package process - -import ( - "context" - "fmt" - "strconv" - "strings" - - "github.com/shirou/gopsutil/v4/cpu" - "github.com/shirou/gopsutil/v4/internal/common" -) - -func (p *Process) CwdWithContext(ctx context.Context) (string, error) { - return "", common.ErrNotImplementedError -} - -func (p *Process) ExeWithContext(ctx context.Context) (string, error) { - out, err := invoke.CommandWithContext(ctx, "lsof", "-p", strconv.Itoa(int(p.Pid)), "-Fpfn") - if err != nil { - return "", fmt.Errorf("bad call to lsof: %w", err) - } - txtFound := 0 - lines := strings.Split(string(out), "\n") - fallback := "" - for i := 1; i < len(lines); i++ { - if lines[i] == "ftxt" { - txtFound++ - if txtFound == 1 { - fallback = lines[i-1][1:] - } - if txtFound == 2 { - return lines[i-1][1:], nil - } - } - } - if fallback != "" { - return fallback, nil - } - return "", fmt.Errorf("missing txt data returned by lsof") -} - -func (p *Process) CmdlineWithContext(ctx context.Context) (string, error) { - r, err := callPsWithContext(ctx, "command", p.Pid, false, false) - if err != nil { - return "", err - } - return strings.Join(r[0], " "), err -} - -func (p *Process) cmdNameWithContext(ctx context.Context) (string, error) { - r, err := callPsWithContext(ctx, "command", p.Pid, false, true) - if err != nil { - return "", err - } - if len(r) > 0 && len(r[0]) > 0 { - return r[0][0], err - } - - return "", err -} - -// CmdlineSliceWithContext returns the command line arguments of the process as a slice with each -// element being an argument. Because of current deficiencies in the way that the command -// line arguments are found, single arguments that have spaces in the will actually be -// reported as two separate items. In order to do something better CGO would be needed -// to use the native darwin functions. -func (p *Process) CmdlineSliceWithContext(ctx context.Context) ([]string, error) { - r, err := callPsWithContext(ctx, "command", p.Pid, false, false) - if err != nil { - return nil, err - } - return r[0], err -} - -func (p *Process) NumThreadsWithContext(ctx context.Context) (int32, error) { - r, err := callPsWithContext(ctx, "utime,stime", p.Pid, true, false) - if err != nil { - return 0, err - } - return int32(len(r)), nil -} - -func (p *Process) TimesWithContext(ctx context.Context) (*cpu.TimesStat, error) { - r, err := callPsWithContext(ctx, "utime,stime", p.Pid, false, false) - if err != nil { - return nil, err - } - - utime, err := convertCPUTimes(r[0][0]) - if err != nil { - return nil, err - } - stime, err := convertCPUTimes(r[0][1]) - if err != nil { - return nil, err - } - - ret := &cpu.TimesStat{ - CPU: "cpu", - User: utime, - System: stime, - } - return ret, nil -} - -func (p *Process) MemoryInfoWithContext(ctx context.Context) (*MemoryInfoStat, error) { - r, err := callPsWithContext(ctx, "rss,vsize,pagein", p.Pid, false, false) - if err != nil { - return nil, err - } - rss, err := strconv.ParseInt(r[0][0], 10, 64) - if err != nil { - return nil, err - } - vms, err := strconv.ParseInt(r[0][1], 10, 64) - if err != nil { - return nil, err - } - pagein, err := strconv.ParseInt(r[0][2], 10, 64) - if err != nil { - return nil, err - } - - ret := &MemoryInfoStat{ - RSS: uint64(rss) * 1024, - VMS: uint64(vms) * 1024, - Swap: uint64(pagein), - } - - return ret, nil -} diff --git a/process/process_freebsd.go b/process/process_freebsd.go index 436dcf0..7637373 100644 --- a/process/process_freebsd.go +++ b/process/process_freebsd.go @@ -8,6 +8,7 @@ import ( "context" "errors" "path/filepath" + "sort" "strconv" "strings" @@ -269,18 +270,21 @@ func (p *Process) MemoryInfoWithContext(ctx context.Context) (*MemoryInfoStat, e } func (p *Process) ChildrenWithContext(ctx context.Context) ([]*Process, error) { - pids, err := common.CallPgrepWithContext(ctx, invoke, p.Pid) + procs, err := ProcessesWithContext(ctx) if err != nil { - return nil, err + return nil, nil } - ret := make([]*Process, 0, len(pids)) - for _, pid := range pids { - np, err := NewProcessWithContext(ctx, pid) + ret := make([]*Process, 0, len(procs)) + for _, proc := range procs { + ppid, err := proc.PpidWithContext(ctx) if err != nil { - return nil, err + continue + } + if ppid == p.Pid { + ret = append(ret, proc) } - ret = append(ret, np) } + sort.Slice(ret, func(i, j int) bool { return ret[i].Pid < ret[j].Pid }) return ret, nil } diff --git a/process/process_linux.go b/process/process_linux.go index 7aff044..4f16693 100644 --- a/process/process_linux.go +++ b/process/process_linux.go @@ -12,6 +12,7 @@ import ( "math" "os" "path/filepath" + "sort" "strconv" "strings" @@ -338,21 +339,34 @@ func (p *Process) PageFaultsWithContext(ctx context.Context) (*PageFaultsStat, e } func (p *Process) ChildrenWithContext(ctx context.Context) ([]*Process, error) { - pids, err := common.CallPgrepWithContext(ctx, invoke, p.Pid) + statFiles, err := filepath.Glob(common.HostProcWithContext(ctx, "[0-9]*/stat")) if err != nil { return nil, err } - if len(pids) == 0 { - return nil, ErrorNoChildren - } - ret := make([]*Process, 0, len(pids)) - for _, pid := range pids { - np, err := NewProcessWithContext(ctx, pid) + ret := make([]*Process, 0, len(statFiles)) + for _, statFile := range statFiles { + statContents, err := os.ReadFile(statFile) if err != nil { - return nil, err + continue + } + fields := splitProcStat(statContents) + pid, err := strconv.ParseInt(fields[1], 10, 32) + if err != nil { + continue + } + ppid, err := strconv.ParseInt(fields[4], 10, 32) + if err != nil { + continue + } + if int32(ppid) == p.Pid { + np, err := NewProcessWithContext(ctx, int32(pid)) + if err != nil { + continue + } + ret = append(ret, np) } - ret = append(ret, np) } + sort.Slice(ret, func(i, j int) bool { return ret[i].Pid < ret[j].Pid }) return ret, nil } diff --git a/process/process_openbsd.go b/process/process_openbsd.go index e2d0ab4..5e8a9e0 100644 --- a/process/process_openbsd.go +++ b/process/process_openbsd.go @@ -11,6 +11,7 @@ import ( "fmt" "io" "path/filepath" + "sort" "strconv" "strings" "unsafe" @@ -286,18 +287,21 @@ func (p *Process) MemoryInfoWithContext(ctx context.Context) (*MemoryInfoStat, e } func (p *Process) ChildrenWithContext(ctx context.Context) ([]*Process, error) { - pids, err := common.CallPgrepWithContext(ctx, invoke, p.Pid) + procs, err := ProcessesWithContext(ctx) if err != nil { - return nil, err + return nil, nil } - ret := make([]*Process, 0, len(pids)) - for _, pid := range pids { - np, err := NewProcessWithContext(ctx, pid) + ret := make([]*Process, 0, len(procs)) + for _, proc := range procs { + ppid, err := proc.PpidWithContext(ctx) if err != nil { - return nil, err + continue + } + if ppid == p.Pid { + ret = append(ret, proc) } - ret = append(ret, np) } + sort.Slice(ret, func(i, j int) bool { return ret[i].Pid < ret[j].Pid }) return ret, nil } diff --git a/process/types_darwin.go b/process/types_darwin.go index 7dc9351..a38cba0 100644 --- a/process/types_darwin.go +++ b/process/types_darwin.go @@ -53,6 +53,7 @@ package process #include #include #include +#include #include #include #include @@ -154,6 +155,8 @@ type Posix_cred C.struct_posix_cred type Label C.struct_label +type ProcTaskInfo C.struct_proc_taskinfo + type ( AuditinfoAddr C.struct_auditinfo_addr AuMask C.struct_au_mask From 462736cb8b6c3a0bce352908303593e649491993 Mon Sep 17 00:00:00 2001 From: huang <2971803929@qq.com> Date: Mon, 23 Sep 2024 22:05:49 +0800 Subject: [PATCH 08/24] add comment for NumFDsWithContext --- process/process_windows.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/process/process_windows.go b/process/process_windows.go index f8fa2f4..b00c671 100644 --- a/process/process_windows.go +++ b/process/process_windows.go @@ -549,6 +549,8 @@ func (p *Process) NumCtxSwitchesWithContext(ctx context.Context) (*NumCtxSwitche return nil, common.ErrNotImplementedError } +// NumFDsWithContext returns the number of handles for a process on Windows, +// not the number of file descriptors (FDs). func (p *Process) NumFDsWithContext(ctx context.Context) (int32, error) { handle, err := windows.OpenProcess(processQueryInformation, false, uint32(p.Pid)) if err != nil { From f56b53a15517a3a006f10197072f3fe651763df6 Mon Sep 17 00:00:00 2001 From: uubulb Date: Tue, 24 Sep 2024 23:11:59 +0800 Subject: [PATCH 09/24] update purego --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 56f786f..0dca255 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/shirou/gopsutil/v4 go 1.18 require ( - github.com/ebitengine/purego v0.7.1 + github.com/ebitengine/purego v0.8.0 github.com/google/go-cmp v0.6.0 github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c diff --git a/go.sum b/go.sum index 10c491d..8863f6f 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,7 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/ebitengine/purego v0.7.1 h1:6/55d26lG3o9VCZX8lping+bZcmShseiqlh2bnUDiPA= -github.com/ebitengine/purego v0.7.1/go.mod h1:ah1In8AOtksoNK6yk5z1HTJeUkC1Ez4Wk2idgGslMwQ= +github.com/ebitengine/purego v0.8.0 h1:JbqvnEzRvPpxhCJzJJ2y0RbiZ8nyjccVUrSM3q+GvvE= +github.com/ebitengine/purego v0.8.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= From 3dc12249d36575518e868bc39976d63ed4254516 Mon Sep 17 00:00:00 2001 From: Johannes Edmeier Date: Mon, 23 Sep 2024 15:43:36 +0200 Subject: [PATCH 10/24] Allow subsecont precision for process create time --- process/process_linux.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/process/process_linux.go b/process/process_linux.go index 4f16693..68a8c88 100644 --- a/process/process_linux.go +++ b/process/process_linux.go @@ -1096,8 +1096,7 @@ func (p *Process) fillFromTIDStatWithContext(ctx context.Context, tid int32) (ui if err != nil { return 0, 0, nil, 0, 0, 0, nil, err } - ctime := (t / uint64(clockTicks)) + uint64(bootTime) - createTime := int64(ctime * 1000) + createTime := int64((t * 1000 / uint64(clockTicks)) + uint64(bootTime*1000)) rtpriority, err := strconv.ParseInt(fields[18], 10, 32) if err != nil { From 112f4c00b7100e342bf1d11273f88120373d1fac Mon Sep 17 00:00:00 2001 From: Henry Dollman Date: Thu, 26 Sep 2024 14:13:46 -0400 Subject: [PATCH 11/24] Fix error message typo in sensors_linux --- sensors/sensors_linux.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sensors/sensors_linux.go b/sensors/sensors_linux.go index 6899d2d..03eb515 100644 --- a/sensors/sensors_linux.go +++ b/sensors/sensors_linux.go @@ -24,7 +24,7 @@ func TemperaturesWithContext(ctx context.Context) ([]TemperatureStat, error) { files, err := getTemperatureFiles(ctx) if err != nil { - return nil, fmt.Errorf("failed to get tempreteure files, %w", err) + return nil, fmt.Errorf("failed to get temperature files, %w", err) } if len(files) == 0 { // handle distributions without hwmon, like raspbian #391, parse legacy thermal_zone files From 3f241a0b08d1466d4683030bc18b89c3f80b9c1b Mon Sep 17 00:00:00 2001 From: Vyacheslav Artemiev Date: Fri, 4 Oct 2024 01:57:36 +0400 Subject: [PATCH 12/24] feat(mem): Add windows commit stats Closes Expose MemCommit info for Windows #1719 --- mem/ex_windows.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/mem/ex_windows.go b/mem/ex_windows.go index 4f1573b..5c49a47 100644 --- a/mem/ex_windows.go +++ b/mem/ex_windows.go @@ -11,7 +11,10 @@ import ( // 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"` } @@ -30,7 +33,16 @@ func (e *ExWindows) VirtualMemory() (*ExVirtualMemory, error) { return nil, windows.GetLastError() } + var perfInfo performanceInformation + perfInfo.cb = uint32(unsafe.Sizeof(perfInfo)) + perf, _, _ := procGetPerformanceInfo.Call(uintptr(unsafe.Pointer(&perfInfo)), uintptr(perfInfo.cb)) + if perf == 0 { + return nil, windows.GetLastError() + } + ret := &ExVirtualMemory{ + CommitLimit: perfInfo.commitLimit * perfInfo.pageSize, + CommitTotal: perfInfo.commitTotal * perfInfo.pageSize, VirtualTotal: memInfo.ullTotalVirtual, VirtualAvail: memInfo.ullAvailVirtual, } From b048ca575d031f2ea6caddcc5883f039e271b257 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 4 Oct 2024 01:04:29 +0000 Subject: [PATCH 13/24] chore(deps): bump golangci/golangci-lint-action from 6.1.0 to 6.1.1 Bumps [golangci/golangci-lint-action](https://github.com/golangci/golangci-lint-action) from 6.1.0 to 6.1.1. - [Release notes](https://github.com/golangci/golangci-lint-action/releases) - [Commits](https://github.com/golangci/golangci-lint-action/compare/aaa42aa0628b4ae2578232a66b541047968fac86...971e284b6050e8a5849b72094c50ab08da042db8) --- updated-dependencies: - dependency-name: golangci/golangci-lint-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/lint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 14a5b72..3d63ccd 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -23,7 +23,7 @@ jobs: - name: Checkout repository uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: Setup golangci-lint - uses: golangci/golangci-lint-action@aaa42aa0628b4ae2578232a66b541047968fac86 # v6.1.0 + uses: golangci/golangci-lint-action@971e284b6050e8a5849b72094c50ab08da042db8 # v6.1.1 with: args: --verbose version: latest From 3c43ac0060de5b70dab0b5fdcbfc2cfa7c1010cc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 4 Oct 2024 01:12:46 +0000 Subject: [PATCH 14/24] chore(deps): bump actions/checkout from 4.1.7 to 4.2.0 Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.7 to 4.2.0. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/692973e3d937129bcbf40652eb9f2f61becf3332...d632683dd7b4114ad314bca15554477dd762a938) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/build_test.yml | 2 +- .github/workflows/lint.yml | 2 +- .github/workflows/release.yml | 2 +- .github/workflows/sbom_generator.yml | 2 +- .github/workflows/shellcheck.yml | 2 +- .github/workflows/test.yml | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build_test.yml b/.github/workflows/build_test.yml index 7ec5267..f4143ee 100644 --- a/.github/workflows/build_test.yml +++ b/.github/workflows/build_test.yml @@ -26,7 +26,7 @@ jobs: with: go-version: ${{ matrix.go-version }} - name: Checkout code - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 - id: cache-paths run: | echo "::set-output name=cache::$(go env GOCACHE)" diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 3d63ccd..8a61404 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -21,7 +21,7 @@ jobs: go-version: 1.17 cache: false - name: Checkout repository - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 - name: Setup golangci-lint uses: golangci/golangci-lint-action@971e284b6050e8a5849b72094c50ab08da042db8 # v6.1.1 with: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 91780cb..09394e1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -10,6 +10,6 @@ jobs: release: runs-on: ubuntu-latest steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 - name: Release run: make release diff --git a/.github/workflows/sbom_generator.yml b/.github/workflows/sbom_generator.yml index f6af6c4..db8700e 100644 --- a/.github/workflows/sbom_generator.yml +++ b/.github/workflows/sbom_generator.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 - uses: advanced-security/sbom-generator-action@375dee8e6144d9fd0ec1f5667b4f6fb4faacefed # v0.0.1 id: sbom diff --git a/.github/workflows/shellcheck.yml b/.github/workflows/shellcheck.yml index 73cdc89..692e39c 100644 --- a/.github/workflows/shellcheck.yml +++ b/.github/workflows/shellcheck.yml @@ -8,6 +8,6 @@ jobs: name: Shellcheck runs-on: ubuntu-latest steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 - name: Run ShellCheck uses: ludeeus/action-shellcheck@00cae500b08a931fb5698e11e79bfbd38e612a38 # v2.0.0 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7bfa152..a2f36fb 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -27,7 +27,7 @@ jobs: with: go-version: ${{ matrix.go-version }} - name: Checkout code - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 - id: go-env run: | echo "::set-output name=cache::$(go env GOCACHE)" From 3773f6fe433cc09a4f9ec57297ea1cd074d05262 Mon Sep 17 00:00:00 2001 From: uubulb Date: Sat, 5 Oct 2024 15:29:36 +0800 Subject: [PATCH 15/24] fix(mem): possible memory leak on Windows --- mem/mem_windows.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mem/mem_windows.go b/mem/mem_windows.go index 522cfd1..a94b61f 100644 --- a/mem/mem_windows.go +++ b/mem/mem_windows.go @@ -82,6 +82,8 @@ func SwapMemoryWithContext(ctx context.Context) (*SwapMemoryStat, error) { if err != nil { return nil, err } + defer common.PdhCloseQuery.Call(uintptr(counter.Query)) + usedPercent, err := counter.GetValue() if err != nil { return nil, err From 4cb0abd1d23695415d1be5f0679d7b2559710d1b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Oct 2024 01:19:22 +0000 Subject: [PATCH 16/24] chore(deps): bump golang.org/x/sys from 0.25.0 to 0.26.0 Bumps [golang.org/x/sys](https://github.com/golang/sys) from 0.25.0 to 0.26.0. - [Commits](https://github.com/golang/sys/compare/v0.25.0...v0.26.0) --- updated-dependencies: - dependency-name: golang.org/x/sys dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 0dca255..67e2c1c 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/stretchr/testify v1.9.0 github.com/tklauser/go-sysconf v0.3.12 github.com/yusufpapurcu/wmi v1.2.4 - golang.org/x/sys v0.25.0 + golang.org/x/sys v0.26.0 ) require ( diff --git a/go.sum b/go.sum index 8863f6f..073210a 100644 --- a/go.sum +++ b/go.sum @@ -25,8 +25,8 @@ golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= -golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= +golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= From 10d9c04c2e6ba871b3f32f1dab3889fa0016960c Mon Sep 17 00:00:00 2001 From: uubulb Date: Tue, 8 Oct 2024 00:31:59 +0800 Subject: [PATCH 17/24] sensors: avoid passing nil pointer to CFArrayGetCount --- sensors/sensors_darwin_arm64.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/sensors/sensors_darwin_arm64.go b/sensors/sensors_darwin_arm64.go index 2cf5b48..509fadd 100644 --- a/sensors/sensors_darwin_arm64.go +++ b/sensors/sensors_darwin_arm64.go @@ -96,6 +96,10 @@ func (ta *temperatureArm) getProductNames() []string { ta.ioHIDEventSystemClientSetMatching(uintptr(system), uintptr(ta.sensors)) matchingsrvs := ta.ioHIDEventSystemClientCopyServices(uintptr(system)) + if matchingsrvs == nil { + return nil + } + count := ta.cfArrayGetCount(uintptr(matchingsrvs)) var i int32 @@ -130,6 +134,10 @@ func (ta *temperatureArm) getThermalValues() []float64 { ta.ioHIDEventSystemClientSetMatching(uintptr(system), uintptr(ta.sensors)) matchingsrvs := ta.ioHIDEventSystemClientCopyServices(uintptr(system)) + if matchingsrvs == nil { + return nil + } + count := ta.cfArrayGetCount(uintptr(matchingsrvs)) var values []float64 From 97325463827e3a8b7de833206518ab9a6fa3eda4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 8 Oct 2024 01:50:59 +0000 Subject: [PATCH 18/24] chore(deps): bump actions/checkout from 4.2.0 to 4.2.1 Bumps [actions/checkout](https://github.com/actions/checkout) from 4.2.0 to 4.2.1. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/d632683dd7b4114ad314bca15554477dd762a938...eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/build_test.yml | 2 +- .github/workflows/lint.yml | 2 +- .github/workflows/release.yml | 2 +- .github/workflows/sbom_generator.yml | 2 +- .github/workflows/shellcheck.yml | 2 +- .github/workflows/test.yml | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build_test.yml b/.github/workflows/build_test.yml index f4143ee..bf7ff56 100644 --- a/.github/workflows/build_test.yml +++ b/.github/workflows/build_test.yml @@ -26,7 +26,7 @@ jobs: with: go-version: ${{ matrix.go-version }} - name: Checkout code - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 + uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 - id: cache-paths run: | echo "::set-output name=cache::$(go env GOCACHE)" diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 8a61404..ab198e8 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -21,7 +21,7 @@ jobs: go-version: 1.17 cache: false - name: Checkout repository - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 + uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 - name: Setup golangci-lint uses: golangci/golangci-lint-action@971e284b6050e8a5849b72094c50ab08da042db8 # v6.1.1 with: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 09394e1..c86e636 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -10,6 +10,6 @@ jobs: release: runs-on: ubuntu-latest steps: - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 + - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 - name: Release run: make release diff --git a/.github/workflows/sbom_generator.yml b/.github/workflows/sbom_generator.yml index db8700e..64f372a 100644 --- a/.github/workflows/sbom_generator.yml +++ b/.github/workflows/sbom_generator.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 + - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 - uses: advanced-security/sbom-generator-action@375dee8e6144d9fd0ec1f5667b4f6fb4faacefed # v0.0.1 id: sbom diff --git a/.github/workflows/shellcheck.yml b/.github/workflows/shellcheck.yml index 692e39c..3faeac4 100644 --- a/.github/workflows/shellcheck.yml +++ b/.github/workflows/shellcheck.yml @@ -8,6 +8,6 @@ jobs: name: Shellcheck runs-on: ubuntu-latest steps: - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 + - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 - name: Run ShellCheck uses: ludeeus/action-shellcheck@00cae500b08a931fb5698e11e79bfbd38e612a38 # v2.0.0 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a2f36fb..8a28eb0 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -27,7 +27,7 @@ jobs: with: go-version: ${{ matrix.go-version }} - name: Checkout code - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 + uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 - id: go-env run: | echo "::set-output name=cache::$(go env GOCACHE)" From c37a5eba8c5a8a4c40a5e5d41590fb0566d6708d Mon Sep 17 00:00:00 2001 From: Lomanic <5020919+Lomanic@users.noreply.github.com> Date: Wed, 9 Oct 2024 00:14:19 +0200 Subject: [PATCH 19/24] [cpu][netbsd] Fix "undefined: cpuTimes" error at compile time on arm Fixes #1645 --- cpu/cpu_netbsd_arm.go | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 cpu/cpu_netbsd_arm.go diff --git a/cpu/cpu_netbsd_arm.go b/cpu/cpu_netbsd_arm.go new file mode 100644 index 0000000..e4799bc --- /dev/null +++ b/cpu/cpu_netbsd_arm.go @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: BSD-3-Clause +package cpu + +type cpuTimes struct { + User uint32 + Nice uint32 + Sys uint32 + Intr uint32 + Idle uint32 +} From ec973203e984456890ac13a570e8e303884bbdcd Mon Sep 17 00:00:00 2001 From: Lomanic <5020919+Lomanic@users.noreply.github.com> Date: Wed, 9 Oct 2024 00:17:20 +0200 Subject: [PATCH 20/24] [disk][netbsd] Generate cpu_netbsd_arm.go via mktypes.sh --- disk/disk_netbsd_arm.go | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 disk/disk_netbsd_arm.go diff --git a/disk/disk_netbsd_arm.go b/disk/disk_netbsd_arm.go new file mode 100644 index 0000000..96ae407 --- /dev/null +++ b/disk/disk_netbsd_arm.go @@ -0,0 +1,46 @@ +//go:build netbsd && arm +// +build netbsd,arm + +// Code generated by cmd/cgo -godefs; DO NOT EDIT. +// cgo -godefs disk/types_netbsd.go + +package disk + +const ( + sizeOfStatvfs = 0xcc8 +) + +type ( + Statvfs struct { + Flag uint32 + Bsize uint32 + Frsize uint32 + Iosize uint32 + Blocks uint64 + Bfree uint64 + Bavail uint64 + Bresvd uint64 + Files uint64 + Ffree uint64 + Favail uint64 + Fresvd uint64 + Syncreads uint64 + Syncwrites uint64 + Asyncreads uint64 + Asyncwrites uint64 + Fsidx _Ctype_struct___0 + Fsid uint32 + Namemax uint32 + Owner uint32 + Pad_cgo_0 [4]byte + Spare [4]uint64 + Fstypename [32]uint8 + Mntonname [1024]uint8 + Mntfromname [1024]uint8 + Mntfromlabel [1024]uint8 + } +) + +type _Ctype_struct___0 struct { + FsidVal [2]int32 +} From 9fc4f649135ee08c2f7b474a14cb5548d2b6395c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 9 Oct 2024 01:04:20 +0000 Subject: [PATCH 21/24] chore(deps): bump actions/cache from 4.0.2 to 4.1.1 Bumps [actions/cache](https://github.com/actions/cache) from 4.0.2 to 4.1.1. - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](https://github.com/actions/cache/compare/0c45773b623bea8c8e75f6c82b208c3cf94ea4f9...3624ceb22c1c5a301c8db4169662070a689d9ea8) --- updated-dependencies: - dependency-name: actions/cache dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/build_test.yml | 2 +- .github/workflows/test.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build_test.yml b/.github/workflows/build_test.yml index bf7ff56..e7e2b6d 100644 --- a/.github/workflows/build_test.yml +++ b/.github/workflows/build_test.yml @@ -32,7 +32,7 @@ jobs: echo "::set-output name=cache::$(go env GOCACHE)" echo "::set-output name=mod-cache::$(go env GOMODCACHE)" - name: Cache go modules - uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2 + uses: actions/cache@3624ceb22c1c5a301c8db4169662070a689d9ea8 # v4.1.1 with: path: | ${{ steps.cache-paths.outputs.cache }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 8a28eb0..db61cd2 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -33,7 +33,7 @@ jobs: echo "::set-output name=cache::$(go env GOCACHE)" echo "::set-output name=mod-cache::$(go env GOMODCACHE)" - name: Cache go modules - uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2 + uses: actions/cache@3624ceb22c1c5a301c8db4169662070a689d9ea8 # v4.1.1 with: path: | ${{ steps.go-env.outputs.cache }} From c30c83bf4ad599b7a5eb827fb179fca213e8464c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 10 Oct 2024 01:05:56 +0000 Subject: [PATCH 22/24] chore(deps): bump actions/upload-artifact from 4.4.0 to 4.4.3 Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.4.0 to 4.4.3. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/50769540e7f4bd5e21e526ee35c689e35e0d6874...b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/sbom_generator.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/sbom_generator.yml b/.github/workflows/sbom_generator.yml index 64f372a..bae2383 100644 --- a/.github/workflows/sbom_generator.yml +++ b/.github/workflows/sbom_generator.yml @@ -19,7 +19,7 @@ jobs: id: sbom env: GITHUB_TOKEN: ${{ github.token }} - - uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0 + - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 with: path: ${{steps.sbom.outputs.fileName }} name: "SBOM" From 9ae3f38658e489809c1e20e993d862e274bcb203 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 22 Oct 2024 01:29:59 +0000 Subject: [PATCH 23/24] chore(deps): bump github.com/ebitengine/purego from 0.8.0 to 0.8.1 Bumps [github.com/ebitengine/purego](https://github.com/ebitengine/purego) from 0.8.0 to 0.8.1. - [Release notes](https://github.com/ebitengine/purego/releases) - [Commits](https://github.com/ebitengine/purego/compare/v0.8.0...v0.8.1) --- updated-dependencies: - dependency-name: github.com/ebitengine/purego dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 0dca255..7c3b829 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/shirou/gopsutil/v4 go 1.18 require ( - github.com/ebitengine/purego v0.8.0 + github.com/ebitengine/purego v0.8.1 github.com/google/go-cmp v0.6.0 github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c diff --git a/go.sum b/go.sum index 8863f6f..dcb466d 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,7 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/ebitengine/purego v0.8.0 h1:JbqvnEzRvPpxhCJzJJ2y0RbiZ8nyjccVUrSM3q+GvvE= -github.com/ebitengine/purego v0.8.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= +github.com/ebitengine/purego v0.8.1 h1:sdRKd6plj7KYW33EH5As6YKfe8m9zbN9JMrOjNVF/BE= +github.com/ebitengine/purego v0.8.1/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= From 137fd2acac96eba29ffb3a3965f62f4657a284fb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 24 Oct 2024 01:26:12 +0000 Subject: [PATCH 24/24] chore(deps): bump actions/checkout from 4.2.1 to 4.2.2 Bumps [actions/checkout](https://github.com/actions/checkout) from 4.2.1 to 4.2.2. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871...11bd71901bbe5b1630ceea73d27597364c9af683) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/build_test.yml | 2 +- .github/workflows/lint.yml | 2 +- .github/workflows/release.yml | 2 +- .github/workflows/sbom_generator.yml | 2 +- .github/workflows/shellcheck.yml | 2 +- .github/workflows/test.yml | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build_test.yml b/.github/workflows/build_test.yml index e7e2b6d..445a8da 100644 --- a/.github/workflows/build_test.yml +++ b/.github/workflows/build_test.yml @@ -26,7 +26,7 @@ jobs: with: go-version: ${{ matrix.go-version }} - name: Checkout code - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - id: cache-paths run: | echo "::set-output name=cache::$(go env GOCACHE)" diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index ab198e8..c22d5a6 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -21,7 +21,7 @@ jobs: go-version: 1.17 cache: false - name: Checkout repository - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Setup golangci-lint uses: golangci/golangci-lint-action@971e284b6050e8a5849b72094c50ab08da042db8 # v6.1.1 with: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c86e636..28c22d7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -10,6 +10,6 @@ jobs: release: runs-on: ubuntu-latest steps: - - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Release run: make release diff --git a/.github/workflows/sbom_generator.yml b/.github/workflows/sbom_generator.yml index bae2383..746c377 100644 --- a/.github/workflows/sbom_generator.yml +++ b/.github/workflows/sbom_generator.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - uses: advanced-security/sbom-generator-action@375dee8e6144d9fd0ec1f5667b4f6fb4faacefed # v0.0.1 id: sbom diff --git a/.github/workflows/shellcheck.yml b/.github/workflows/shellcheck.yml index 3faeac4..f6bca03 100644 --- a/.github/workflows/shellcheck.yml +++ b/.github/workflows/shellcheck.yml @@ -8,6 +8,6 @@ jobs: name: Shellcheck runs-on: ubuntu-latest steps: - - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Run ShellCheck uses: ludeeus/action-shellcheck@00cae500b08a931fb5698e11e79bfbd38e612a38 # v2.0.0 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index db61cd2..f1acd24 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -27,7 +27,7 @@ jobs: with: go-version: ${{ matrix.go-version }} - name: Checkout code - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - id: go-env run: | echo "::set-output name=cache::$(go env GOCACHE)"