shirou / gopsutil

psutil for golang
Other
10.66k stars 1.59k forks source link

Using purego on darwin #1700

Closed uubulb closed 1 month ago

uubulb commented 2 months ago

Is your feature request related to a problem? Please describe. cgo implementions on darwin https://github.com/shirou/gopsutil/issues/66

Describe the solution you'd like purego has relatively good support for Darwin, perhaps gopsutil can use it to replace current cgo implementations. I tried to port perCPUTimes with purego and it seems working:

package main

import (
    "fmt"
    "unsafe"

    "github.com/ebitengine/purego"
)

const (
    KERN_SUCCESS            = 0
    PROCESSOR_CPU_LOAD_INFO = 2

    CPU_STATE_USER          = 0
    CPU_STATE_SYSTEM        = 1
    CPU_STATE_IDLE          = 2
    CPU_STATE_NICE          = 3
    CPU_STATE_MAX           = 4
)

var (
    hostProcessorInfo func(host uint32, flavor int, outProcessorCount *uint32, outProcessorInfo uintptr,
        outProcessorInfoCnt *uint32) int
    machHostSelf func() uint32
    machTaskSelf func() uint32
    vmDeallocate func(targetTask uint32, vmAddress, vmSize uintptr) int
)

var ClocksPerSec = float64(128)

type processorCpuLoadInfo struct {
    cpuTicks [CPU_STATE_MAX]uint32
}

type cpuStat struct {
    CPU    string
    User   float64
    System float64
    Nice   float64
    Idle   float64
}

func main() {
    mach, err := purego.Dlopen("/usr/lib/system/libsystem_kernel.dylib", purego.RTLD_LAZY|purego.RTLD_GLOBAL)
    if err != nil {
        fmt.Println(err)
        return
    }
    defer purego.Dlclose(mach)

    purego.RegisterLibFunc(&hostProcessorInfo, mach, "host_processor_info")
    purego.RegisterLibFunc(&machHostSelf, mach, "mach_host_self")
    purego.RegisterLibFunc(&machTaskSelf, mach, "mach_task_self")
    purego.RegisterLibFunc(&vmDeallocate, mach, "vm_deallocate")

    var count, ncpu uint32
    var cpuload *processorCpuLoadInfo

    status := hostProcessorInfo(machHostSelf(), PROCESSOR_CPU_LOAD_INFO, &ncpu,
        uintptr(unsafe.Pointer(&cpuload)), &count)

    if status != KERN_SUCCESS {
        return
    }

    defer vmDeallocate(machTaskSelf(), uintptr(unsafe.Pointer(cpuload)), uintptr(ncpu))

    ret := []cpuStat{}
    loads := unsafe.Slice(cpuload, ncpu)

    for i := 0; i < int(ncpu); i++ {
        c := cpuStat{
            CPU:    fmt.Sprintf("cpu%d", i),
            User:   float64(loads[i].cpuTicks[CPU_STATE_USER]) / ClocksPerSec,
            System: float64(loads[i].cpuTicks[CPU_STATE_SYSTEM]) / ClocksPerSec,
            Nice:   float64(loads[i].cpuTicks[CPU_STATE_NICE]) / ClocksPerSec,
            Idle:   float64(loads[i].cpuTicks[CPU_STATE_IDLE]) / ClocksPerSec,
        }

        ret = append(ret, c)
    }

    fmt.Println(ret)
}

The output values should resemble those of the cgo counterpart.

Describe alternatives you've considered N/A

Additional context N/A

shirou commented 2 months ago

Thank you for your suggestion. Yes, I completely and totally agree. I've always thought that purego is a great library for gopsutil. However, there is one problem—I don't own a mac, so I can't develop on it. I look forward to contributions from everyone!