Merge pull request #1768 from uubulb/darwin

refactor(darwin): optimize string allocation
tags/v4.24.12 v4.24.12
shirou 2 months ago committed by GitHub
commit 252dcbf8a2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -55,10 +55,10 @@ func getFrequency() (float64, error) {
break break
} }
buf := make([]byte, 512) buf := common.NewCStr(512)
ioRegistryEntryGetName(service, &buf[0]) ioRegistryEntryGetName(service, buf)
if common.GoString(&buf[0]) == "pmgr" { if buf.GoString() == "pmgr" {
pCoreRef := ioRegistryEntryCreateCFProperty(service, uintptr(pCorekey), common.KCFAllocatorDefault, common.KNilOptions) pCoreRef := ioRegistryEntryCreateCFProperty(service, uintptr(pCorekey), common.KCFAllocatorDefault, common.KNilOptions)
length := cfDataGetLength(uintptr(pCoreRef)) length := cfDataGetLength(uintptr(pCoreRef))
data := cfDataGetBytePtr(uintptr(pCoreRef)) data := cfDataGetBytePtr(uintptr(pCoreRef))

@ -5,6 +5,7 @@ package disk
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"unsafe" "unsafe"
@ -234,9 +235,9 @@ func (i *ioCounters) getDriveStat(d uint32) (*IOCountersStat, error) {
key := i.cfStr(kIOBSDNameKey) key := i.cfStr(kIOBSDNameKey)
defer i.cfRelease(uintptr(key)) defer i.cfRelease(uintptr(key))
name := i.cfDictionaryGetValue(uintptr(props), uintptr(key)) name := i.cfDictionaryGetValue(uintptr(props), uintptr(key))
length := cfStringGetLength(uintptr(name)) + 1
buf := make([]byte, length-1) buf := common.NewCStr(cfStringGetLength(uintptr(name)))
cfStringGetCString(uintptr(name), &buf[0], length, common.KCFStringEncodingUTF8) cfStringGetCString(uintptr(name), buf, buf.Length(), common.KCFStringEncodingUTF8)
stat, err := i.fillStat(parent) stat, err := i.fillStat(parent)
if err != nil { if err != nil {
@ -244,7 +245,7 @@ func (i *ioCounters) getDriveStat(d uint32) (*IOCountersStat, error) {
} }
if stat != nil { if stat != nil {
stat.Name = string(buf) stat.Name = buf.GoString()
return stat, nil return stat, nil
} }
return nil, nil return nil, nil
@ -263,9 +264,10 @@ func (i *ioCounters) fillStat(d uint32) (*IOCountersStat, error) {
key := i.cfStr(kIOBlockStorageDriverStatisticsKey) key := i.cfStr(kIOBlockStorageDriverStatisticsKey)
defer i.cfRelease(uintptr(key)) defer i.cfRelease(uintptr(key))
v := i.cfDictionaryGetValue(uintptr(props), uintptr(key)) v := i.cfDictionaryGetValue(uintptr(props), uintptr(key))
if v == nil { if v == nil {
return nil, fmt.Errorf("CFDictionaryGetValue failed") return nil, errors.New("CFDictionaryGetValue failed")
} }
var stat IOCountersStat var stat IOCountersStat
@ -280,10 +282,10 @@ func (i *ioCounters) fillStat(d uint32) (*IOCountersStat, error) {
for key, off := range statstab { for key, off := range statstab {
s := i.cfStr(key) s := i.cfStr(key)
defer i.cfRelease(uintptr(s))
if num := i.cfDictionaryGetValue(uintptr(v), uintptr(s)); num != nil { if num := i.cfDictionaryGetValue(uintptr(v), uintptr(s)); num != nil {
i.cfNumberGetValue(uintptr(num), common.KCFNumberSInt64Type, uintptr(unsafe.Pointer(uintptr(unsafe.Pointer(&stat))+off))) i.cfNumberGetValue(uintptr(num), common.KCFNumberSInt64Type, uintptr(unsafe.Add(unsafe.Pointer(&stat), off)))
} }
i.cfRelease(uintptr(s))
} }
return &stat, nil return &stat, nil

@ -125,7 +125,7 @@ type (
IOServiceOpenFunc func(service, owningTask, connType uint32, connect *uint32) int IOServiceOpenFunc func(service, owningTask, connType uint32, connect *uint32) int
IOServiceCloseFunc func(connect uint32) int IOServiceCloseFunc func(connect uint32) int
IOIteratorNextFunc func(iterator uint32) uint32 IOIteratorNextFunc func(iterator uint32) uint32
IORegistryEntryGetNameFunc func(entry uint32, name *byte) int IORegistryEntryGetNameFunc func(entry uint32, name CStr) int
IORegistryEntryGetParentEntryFunc func(entry uint32, plane string, parent *uint32) int IORegistryEntryGetParentEntryFunc func(entry uint32, plane string, parent *uint32) int
IORegistryEntryCreateCFPropertyFunc func(entry uint32, key, allocator uintptr, options uint32) unsafe.Pointer IORegistryEntryCreateCFPropertyFunc func(entry uint32, key, allocator uintptr, options uint32) unsafe.Pointer
IORegistryEntryCreateCFPropertiesFunc func(entry uint32, properties unsafe.Pointer, allocator uintptr, options uint32) int IORegistryEntryCreateCFPropertiesFunc func(entry uint32, properties unsafe.Pointer, allocator uintptr, options uint32) int
@ -191,7 +191,7 @@ type (
CFArrayGetValueAtIndexFunc func(theArray uintptr, index int32) unsafe.Pointer CFArrayGetValueAtIndexFunc func(theArray uintptr, index int32) unsafe.Pointer
CFStringCreateMutableFunc func(alloc uintptr, maxLength int32) unsafe.Pointer CFStringCreateMutableFunc func(alloc uintptr, maxLength int32) unsafe.Pointer
CFStringGetLengthFunc func(theString uintptr) int32 CFStringGetLengthFunc func(theString uintptr) int32
CFStringGetCStringFunc func(theString uintptr, buffer *byte, bufferSize int32, encoding uint32) CFStringGetCStringFunc func(theString uintptr, buffer CStr, bufferSize int32, encoding uint32)
CFStringCreateWithCStringFunc func(alloc uintptr, cStr string, encoding uint32) unsafe.Pointer CFStringCreateWithCStringFunc func(alloc uintptr, cStr string, encoding uint32) unsafe.Pointer
CFDataGetLengthFunc func(theData uintptr) int32 CFDataGetLengthFunc func(theData uintptr) int32
CFDataGetBytePtrFunc func(theData uintptr) unsafe.Pointer CFDataGetBytePtrFunc func(theData uintptr) unsafe.Pointer
@ -348,6 +348,44 @@ func (s *SMC) Close() error {
return nil return nil
} }
type CStr []byte
func NewCStr(length int32) CStr {
return make(CStr, length)
}
func (s CStr) Length() int32 {
// Include null terminator to make CFStringGetCString properly functions
return int32(len(s)) + 1
}
func (s CStr) Ptr() *byte {
if len(s) < 1 {
return nil
}
return &s[0]
}
func (c CStr) Addr() uintptr {
return uintptr(unsafe.Pointer(c.Ptr()))
}
func (s CStr) GoString() string {
if s == nil {
return ""
}
var length int
for _, char := range s {
if char == '\x00' {
break
}
length++
}
return string(s[:length])
}
// https://github.com/ebitengine/purego/blob/main/internal/strings/strings.go#L26 // https://github.com/ebitengine/purego/blob/main/internal/strings/strings.go#L26
func GoString(cStr *byte) string { func GoString(cStr *byte) string {
if cStr == nil { if cStr == nil {

@ -310,14 +310,14 @@ func (p *Process) ExeWithContext(ctx context.Context) (string, error) {
} }
defer lib.Close() defer lib.Close()
buf := make([]byte, common.PROC_PIDPATHINFO_MAXSIZE) buf := common.NewCStr(common.PROC_PIDPATHINFO_MAXSIZE)
ret := procPidPath(p.Pid, uintptr(unsafe.Pointer(&buf[0])), common.PROC_PIDPATHINFO_MAXSIZE) ret := procPidPath(p.Pid, buf.Addr(), common.PROC_PIDPATHINFO_MAXSIZE)
if ret <= 0 { if ret <= 0 {
return "", fmt.Errorf("unknown error: proc_pidpath returned %d", ret) return "", fmt.Errorf("unknown error: proc_pidpath returned %d", ret)
} }
return common.GoString(&buf[0]), nil return buf.GoString(), nil
} }
// sys/proc_info.h // sys/proc_info.h
@ -339,6 +339,7 @@ func (p *Process) CwdWithContext(ctx context.Context) (string, error) {
} }
defer lib.Close() defer lib.Close()
// Lock OS thread to ensure the errno does not change
runtime.LockOSThread() runtime.LockOSThread()
defer runtime.UnlockOSThread() defer runtime.UnlockOSThread()
@ -366,6 +367,8 @@ func procArgs(pid int32) ([]byte, int, error) {
if err != nil { if err != nil {
return nil, 0, err return nil, 0, err
} }
// The first 4 bytes indicate the number of arguments.
nargs := procargs[:4] nargs := procargs[:4]
return procargs, int(binary.LittleEndian.Uint32(nargs)), nil return procargs, int(binary.LittleEndian.Uint32(nargs)), nil
} }
@ -434,8 +437,7 @@ func (p *Process) NumThreadsWithContext(ctx context.Context) (int32, error) {
defer lib.Close() defer lib.Close()
var ti ProcTaskInfo var ti ProcTaskInfo
const tiSize = int32(unsafe.Sizeof(ti)) procPidInfo(p.Pid, common.PROC_PIDTASKINFO, 0, uintptr(unsafe.Pointer(&ti)), int32(unsafe.Sizeof(ti)))
procPidInfo(p.Pid, common.PROC_PIDTASKINFO, 0, uintptr(unsafe.Pointer(&ti)), tiSize)
return int32(ti.Threadnum), nil return int32(ti.Threadnum), nil
} }
@ -448,8 +450,7 @@ func (p *Process) TimesWithContext(ctx context.Context) (*cpu.TimesStat, error)
defer lib.Close() defer lib.Close()
var ti ProcTaskInfo var ti ProcTaskInfo
const tiSize = int32(unsafe.Sizeof(ti)) procPidInfo(p.Pid, common.PROC_PIDTASKINFO, 0, uintptr(unsafe.Pointer(&ti)), int32(unsafe.Sizeof(ti)))
procPidInfo(p.Pid, common.PROC_PIDTASKINFO, 0, uintptr(unsafe.Pointer(&ti)), tiSize)
timescaleToNanoSeconds := getTimeScaleToNanoSeconds() timescaleToNanoSeconds := getTimeScaleToNanoSeconds()
ret := &cpu.TimesStat{ ret := &cpu.TimesStat{
@ -468,8 +469,7 @@ func (p *Process) MemoryInfoWithContext(ctx context.Context) (*MemoryInfoStat, e
defer lib.Close() defer lib.Close()
var ti ProcTaskInfo var ti ProcTaskInfo
const tiSize = int32(unsafe.Sizeof(ti)) procPidInfo(p.Pid, common.PROC_PIDTASKINFO, 0, uintptr(unsafe.Pointer(&ti)), int32(unsafe.Sizeof(ti)))
procPidInfo(p.Pid, common.PROC_PIDTASKINFO, 0, uintptr(unsafe.Pointer(&ti)), tiSize)
ret := &MemoryInfoStat{ ret := &MemoryInfoStat{
RSS: uint64(ti.Resident_size), RSS: uint64(ti.Resident_size),

@ -24,7 +24,7 @@ func TemperaturesWithContext(ctx context.Context) ([]TemperatureStat, error) {
} }
defer smc.Close() defer smc.Close()
var temperatures []TemperatureStat temperatures := make([]TemperatureStat, 0, len(temperatureKeys))
for _, key := range temperatureKeys { for _, key := range temperatureKeys {
temperatures = append(temperatures, TemperatureStat{ temperatures = append(temperatures, TemperatureStat{
SensorKey: key, SensorKey: key,

@ -94,8 +94,6 @@ func (ta *temperatureArm) getProductNames(system unsafe.Pointer) []string {
cfStringGetLength := common.GetFunc[common.CFStringGetLengthFunc](ta.cf, common.CFStringGetLengthSym) cfStringGetLength := common.GetFunc[common.CFStringGetLengthFunc](ta.cf, common.CFStringGetLengthSym)
cfStringGetCString := common.GetFunc[common.CFStringGetCStringFunc](ta.cf, common.CFStringGetCStringSym) cfStringGetCString := common.GetFunc[common.CFStringGetCStringFunc](ta.cf, common.CFStringGetCStringSym)
var names []string
ta.ioHIDEventSystemClientSetMatching(uintptr(system), uintptr(ta.sensors)) ta.ioHIDEventSystemClientSetMatching(uintptr(system), uintptr(ta.sensors))
matchingsrvs := ta.ioHIDEventSystemClientCopyServices(uintptr(system)) matchingsrvs := ta.ioHIDEventSystemClientCopyServices(uintptr(system))
@ -110,18 +108,19 @@ func (ta *temperatureArm) getProductNames(system unsafe.Pointer) []string {
str := ta.cfStr("Product") str := ta.cfStr("Product")
defer ta.cfRelease(uintptr(str)) defer ta.cfRelease(uintptr(str))
names := make([]string, 0, count)
for i = 0; i < count; i++ { for i = 0; i < count; i++ {
sc := ta.cfArrayGetValueAtIndex(uintptr(matchingsrvs), i) sc := ta.cfArrayGetValueAtIndex(uintptr(matchingsrvs), i)
name := ioHIDServiceClientCopyProperty(uintptr(sc), uintptr(str)) name := ioHIDServiceClientCopyProperty(uintptr(sc), uintptr(str))
if name != nil { if name != nil {
length := cfStringGetLength(uintptr(name)) + 1 // include null terminator buf := common.NewCStr(cfStringGetLength(uintptr(name)))
buf := make([]byte, length) // allocate buffer with full length cfStringGetCString(uintptr(name), buf, buf.Length(), common.KCFStringEncodingUTF8)
cfStringGetCString(uintptr(name), &buf[0], length, common.KCFStringEncodingUTF8)
names = append(names, string(buf[:length-1])) // remove null terminator names = append(names, buf.GoString())
ta.cfRelease(uintptr(name)) ta.cfRelease(uintptr(name))
} else { } else {
// make sure the number of names and values are consistent
names = append(names, "noname") names = append(names, "noname")
} }
} }
@ -166,11 +165,17 @@ func (ta *temperatureArm) matching(page, usage int) {
cfDictionaryCreate := common.GetFunc[common.CFDictionaryCreateFunc](ta.cf, common.CFDictionaryCreateSym) cfDictionaryCreate := common.GetFunc[common.CFDictionaryCreateFunc](ta.cf, common.CFDictionaryCreateSym)
pageNum := cfNumberCreate(common.KCFAllocatorDefault, common.KCFNumberIntType, uintptr(unsafe.Pointer(&page))) pageNum := cfNumberCreate(common.KCFAllocatorDefault, common.KCFNumberIntType, uintptr(unsafe.Pointer(&page)))
defer ta.cfRelease(uintptr(pageNum))
usageNum := cfNumberCreate(common.KCFAllocatorDefault, common.KCFNumberIntType, uintptr(unsafe.Pointer(&usage))) usageNum := cfNumberCreate(common.KCFAllocatorDefault, common.KCFNumberIntType, uintptr(unsafe.Pointer(&usage)))
defer ta.cfRelease(uintptr(usageNum))
k1 := ta.cfStr("PrimaryUsagePage") k1 := ta.cfStr("PrimaryUsagePage")
k2 := ta.cfStr("PrimaryUsage") k2 := ta.cfStr("PrimaryUsage")
defer ta.cfRelease(uintptr(k1))
defer ta.cfRelease(uintptr(k2))
keys := []unsafe.Pointer{k1, k2} keys := []unsafe.Pointer{k1, k2}
values := []unsafe.Pointer{pageNum, usageNum} values := []unsafe.Pointer{pageNum, usageNum}
@ -180,11 +185,6 @@ func (ta *temperatureArm) matching(page, usage int) {
ta.sensors = cfDictionaryCreate(common.KCFAllocatorDefault, &keys[0], &values[0], 2, ta.sensors = cfDictionaryCreate(common.KCFAllocatorDefault, &keys[0], &values[0], 2,
kCFTypeDictionaryKeyCallBacks, kCFTypeDictionaryKeyCallBacks,
kCFTypeDictionaryValueCallBacks) 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 { func (ta *temperatureArm) cfStr(str string) unsafe.Pointer {

@ -3,8 +3,12 @@
package sensors package sensors
import ( import (
"errors"
"fmt" "fmt"
"os"
"testing" "testing"
"github.com/shirou/gopsutil/v4/internal/common"
) )
func TestTemperatureStat_String(t *testing.T) { func TestTemperatureStat_String(t *testing.T) {
@ -19,3 +23,24 @@ func TestTemperatureStat_String(t *testing.T) {
t.Errorf("TemperatureStat string is invalid, %v", fmt.Sprintf("%v", v)) t.Errorf("TemperatureStat string is invalid, %v", fmt.Sprintf("%v", v))
} }
} }
func skipIfNotImplementedErr(t *testing.T, err error) {
if errors.Is(err, common.ErrNotImplementedError) {
t.Skip("not implemented")
}
}
func TestTemperatures(t *testing.T) {
if os.Getenv("CI") != "" {
t.Skip("Skip CI")
}
v, err := SensorsTemperatures()
skipIfNotImplementedErr(t, err)
if err != nil {
t.Errorf("error %v", err)
}
if len(v) == 0 {
t.Errorf("Could not get temperature %v", v)
}
t.Log(v)
}

Loading…
Cancel
Save