From 701a74be41d2830146f10c3cff1c93a4acff19a2 Mon Sep 17 00:00:00 2001 From: uubulb Date: Wed, 4 Sep 2024 22:19:05 +0800 Subject: [PATCH] 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 79a458b8e..29d9a71be 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 3a02024c5..000000000 --- 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 1af8566a6..000000000 --- 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 4ead2443f..79ecebbf8 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 7f6b7fb4c..a2896dba0 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 53f9ae8d9..0a1da931b 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 a33c5f125..4442cbc11 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 cc6657d04..000000000 --- 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 097a93e63..000000000 --- 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 b1d2ebb81..000000000 --- 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 000000000..b58bf96f8 --- /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 000000000..2cf5b4869 --- /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 aa3d29120..000000000 --- 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 45c85069e..000000000 --- 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 c91a90cd0..000000000 --- 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 e49abb512..000000000 --- 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__