mirror of
https://github.com/shirou/gopsutil.git
synced 2025-04-24 13:48:56 +08:00
update disk & cpu & process
This commit is contained in:
parent
701a74be41
commit
9e6efdb991
@ -1,6 +1,6 @@
|
|||||||
# gopsutil: psutil for golang
|
# gopsutil: psutil for golang
|
||||||
|
|
||||||
[](https://github.com/shirou/gopsutil/actions/workflows/test.yml) [](https://coveralls.io/github/shirou/gopsutil?branch=master) [](https://pkg.go.dev/github.com/shirou/gopsutil/v4) [](https://godocs.io/github.com/shirou/gopsutil/v4) [](https://calver.org/)
|
[](https://github.com/shirou/gopsutil/actions/workflows/test.yml) [](https://pkg.go.dev/github.com/shirou/gopsutil/v4) [](https://calver.org/)
|
||||||
|
|
||||||
This is a port of psutil (https://github.com/giampaolo/psutil). The
|
This is a port of psutil (https://github.com/giampaolo/psutil). The
|
||||||
challenge is porting all psutil functions on some architectures.
|
challenge is porting all psutil functions on some architectures.
|
||||||
|
@ -19,6 +19,7 @@ const (
|
|||||||
HostRunEnvKey EnvKeyType = "HOST_RUN"
|
HostRunEnvKey EnvKeyType = "HOST_RUN"
|
||||||
HostDevEnvKey EnvKeyType = "HOST_DEV"
|
HostDevEnvKey EnvKeyType = "HOST_DEV"
|
||||||
HostRootEnvKey EnvKeyType = "HOST_ROOT"
|
HostRootEnvKey EnvKeyType = "HOST_ROOT"
|
||||||
|
HostProcMountinfo EnvKeyType = "HOST_PROC_MOUNTINFO"
|
||||||
)
|
)
|
||||||
|
|
||||||
type EnvMap map[EnvKeyType]string
|
type EnvMap map[EnvKeyType]string
|
||||||
|
@ -10,7 +10,6 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
"github.com/shoenig/go-m1cpu"
|
|
||||||
"github.com/tklauser/go-sysconf"
|
"github.com/tklauser/go-sysconf"
|
||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
|
|
||||||
@ -61,7 +60,7 @@ func Times(percpu bool) ([]TimesStat, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TimesWithContext(ctx context.Context, 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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -114,15 +113,9 @@ func InfoWithContext(ctx context.Context) ([]InfoStat, error) {
|
|||||||
c.CacheSize = int32(cacheSize)
|
c.CacheSize = int32(cacheSize)
|
||||||
c.VendorID, _ = unix.Sysctl("machdep.cpu.vendor")
|
c.VendorID, _ = unix.Sysctl("machdep.cpu.vendor")
|
||||||
|
|
||||||
if m1cpu.IsAppleSilicon() {
|
v, err := getFrequency()
|
||||||
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 {
|
if err == nil {
|
||||||
c.Mhz = float64(cpuFrequency) / 1000000.0
|
c.Mhz = v
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return append(ret, c), nil
|
return append(ret, c), nil
|
||||||
|
80
cpu/cpu_darwin_arm64.go
Normal file
80
cpu/cpu_darwin_arm64.go
Normal file
@ -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
|
||||||
|
}
|
13
cpu/cpu_darwin_fallback.go
Normal file
13
cpu/cpu_darwin_fallback.go
Normal file
@ -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
|
||||||
|
}
|
@ -5,13 +5,12 @@ package cpu
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
|
"runtime"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/shoenig/go-m1cpu"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestInfo_AppleSilicon(t *testing.T) {
|
func TestInfo_AppleSilicon(t *testing.T) {
|
||||||
if !m1cpu.IsAppleSilicon() {
|
if runtime.GOARCH != "arm64" {
|
||||||
t.Skip("wrong cpu type")
|
t.Skip("wrong cpu type")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,8 @@ package disk
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
"golang.org/x/sys/unix"
|
"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) {
|
func LabelWithContext(ctx context.Context, name string) (string, error) {
|
||||||
return "", common.ErrNotImplementedError
|
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)
|
||||||
|
}
|
||||||
|
@ -1,45 +0,0 @@
|
|||||||
// SPDX-License-Identifier: BSD-3-Clause
|
|
||||||
//go:build darwin && cgo && !ios
|
|
||||||
|
|
||||||
package disk
|
|
||||||
|
|
||||||
/*
|
|
||||||
#cgo LDFLAGS: -framework CoreFoundation -framework IOKit
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <CoreFoundation/CoreFoundation.h>
|
|
||||||
#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
|
|
||||||
}
|
|
@ -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
|
|
||||||
}
|
|
@ -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 <stdint.h>
|
|
||||||
#include <CoreFoundation/CoreFoundation.h>
|
|
||||||
#include "iostat_darwin.h"
|
|
||||||
|
|
||||||
#define IOKIT 1 /* to get io_name_t in device_types.h */
|
|
||||||
|
|
||||||
#include <IOKit/IOKitLib.h>
|
|
||||||
#include <IOKit/storage/IOBlockStorageDriver.h>
|
|
||||||
#include <IOKit/storage/IOMedia.h>
|
|
||||||
#include <IOKit/IOBSD.h>
|
|
||||||
|
|
||||||
#include <mach/mach_host.h>
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
@ -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);
|
|
3
go.mod
3
go.mod
@ -7,11 +7,10 @@ require (
|
|||||||
github.com/google/go-cmp v0.6.0
|
github.com/google/go-cmp v0.6.0
|
||||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0
|
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0
|
||||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c
|
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/stretchr/testify v1.9.0
|
||||||
github.com/tklauser/go-sysconf v0.3.12
|
github.com/tklauser/go-sysconf v0.3.12
|
||||||
github.com/yusufpapurcu/wmi v1.2.4
|
github.com/yusufpapurcu/wmi v1.2.4
|
||||||
golang.org/x/sys v0.24.0
|
golang.org/x/sys v0.25.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
7
go.sum
7
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/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 h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
|
||||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
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 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
|
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.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.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.11.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.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
|
||||||
golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
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=
|
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 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
@ -78,7 +78,7 @@ type Library struct {
|
|||||||
const (
|
const (
|
||||||
IOKit = "/System/Library/Frameworks/IOKit.framework/IOKit"
|
IOKit = "/System/Library/Frameworks/IOKit.framework/IOKit"
|
||||||
CoreFoundation = "/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation"
|
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) {
|
func NewLibrary(path string) (*Library, error) {
|
||||||
@ -120,9 +120,16 @@ const (
|
|||||||
// IOKit functions and symbols.
|
// IOKit functions and symbols.
|
||||||
type (
|
type (
|
||||||
IOServiceGetMatchingServiceFunc func(mainPort uint32, matching uintptr) uint32
|
IOServiceGetMatchingServiceFunc func(mainPort uint32, matching uintptr) uint32
|
||||||
|
IOServiceGetMatchingServicesFunc func(mainPort uint32, matching uintptr, existing *uint32) int
|
||||||
IOServiceMatchingFunc func(name string) unsafe.Pointer
|
IOServiceMatchingFunc func(name string) unsafe.Pointer
|
||||||
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
|
||||||
|
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
|
IOObjectReleaseFunc func(object uint32) int
|
||||||
IOConnectCallStructMethodFunc func(connection, selector uint32, inputStruct, inputStructCnt, outputStruct uintptr, outputStructCnt *uintptr) int
|
IOConnectCallStructMethodFunc func(connection, selector uint32, inputStruct, inputStructCnt, outputStruct uintptr, outputStructCnt *uintptr) int
|
||||||
|
|
||||||
@ -137,9 +144,16 @@ type (
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
IOServiceGetMatchingServiceSym = "IOServiceGetMatchingService"
|
IOServiceGetMatchingServiceSym = "IOServiceGetMatchingService"
|
||||||
|
IOServiceGetMatchingServicesSym = "IOServiceGetMatchingServices"
|
||||||
IOServiceMatchingSym = "IOServiceMatching"
|
IOServiceMatchingSym = "IOServiceMatching"
|
||||||
IOServiceOpenSym = "IOServiceOpen"
|
IOServiceOpenSym = "IOServiceOpen"
|
||||||
IOServiceCloseSym = "IOServiceClose"
|
IOServiceCloseSym = "IOServiceClose"
|
||||||
|
IOIteratorNextSym = "IOIteratorNext"
|
||||||
|
IORegistryEntryGetNameSym = "IORegistryEntryGetName"
|
||||||
|
IORegistryEntryGetParentEntrySym = "IORegistryEntryGetParentEntry"
|
||||||
|
IORegistryEntryCreateCFPropertySym = "IORegistryEntryCreateCFProperty"
|
||||||
|
IORegistryEntryCreateCFPropertiesSym = "IORegistryEntryCreateCFProperties"
|
||||||
|
IOObjectConformsToSym = "IOObjectConformsTo"
|
||||||
IOObjectReleaseSym = "IOObjectRelease"
|
IOObjectReleaseSym = "IOObjectRelease"
|
||||||
IOConnectCallStructMethodSym = "IOConnectCallStructMethod"
|
IOConnectCallStructMethodSym = "IOConnectCallStructMethod"
|
||||||
|
|
||||||
@ -152,48 +166,76 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
KIOMainPortDefault = 0
|
||||||
|
|
||||||
KIOHIDEventTypeTemperature = 15
|
KIOHIDEventTypeTemperature = 15
|
||||||
|
|
||||||
|
KNilOptions = 0
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
KIOMediaWholeKey = "Media"
|
||||||
|
KIOServicePlane = "IOService"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CoreFoundation functions and symbols.
|
// CoreFoundation functions and symbols.
|
||||||
type (
|
type (
|
||||||
|
CFGetTypeIDFunc func(cf uintptr) int32
|
||||||
CFNumberCreateFunc func(allocator uintptr, theType int32, valuePtr uintptr) unsafe.Pointer
|
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,
|
CFDictionaryCreateFunc func(allocator uintptr, keys, values *unsafe.Pointer, numValues int32,
|
||||||
keyCallBacks, valueCallBacks uintptr) unsafe.Pointer
|
keyCallBacks, valueCallBacks uintptr) unsafe.Pointer
|
||||||
|
CFDictionaryAddValueFunc func(theDict, key, value uintptr)
|
||||||
|
CFDictionaryGetValueFunc func(theDict, key uintptr) unsafe.Pointer
|
||||||
CFArrayGetCountFunc func(theArray uintptr) int32
|
CFArrayGetCountFunc func(theArray uintptr) int32
|
||||||
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 *byte, 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
|
||||||
|
CFDataGetBytePtrFunc func(theData uintptr) unsafe.Pointer
|
||||||
CFReleaseFunc func(cf uintptr)
|
CFReleaseFunc func(cf uintptr)
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
CFGetTypeIDSym = "CFGetTypeID"
|
||||||
CFNumberCreateSym = "CFNumberCreate"
|
CFNumberCreateSym = "CFNumberCreate"
|
||||||
|
CFNumberGetValueSym = "CFNumberGetValue"
|
||||||
CFDictionaryCreateSym = "CFDictionaryCreate"
|
CFDictionaryCreateSym = "CFDictionaryCreate"
|
||||||
|
CFDictionaryAddValueSym = "CFDictionaryAddValue"
|
||||||
|
CFDictionaryGetValueSym = "CFDictionaryGetValue"
|
||||||
CFArrayGetCountSym = "CFArrayGetCount"
|
CFArrayGetCountSym = "CFArrayGetCount"
|
||||||
CFArrayGetValueAtIndexSym = "CFArrayGetValueAtIndex"
|
CFArrayGetValueAtIndexSym = "CFArrayGetValueAtIndex"
|
||||||
CFStringCreateMutableSym = "CFStringCreateMutable"
|
CFStringCreateMutableSym = "CFStringCreateMutable"
|
||||||
CFStringGetLengthSym = "CFStringGetLength"
|
CFStringGetLengthSym = "CFStringGetLength"
|
||||||
CFStringGetCStringSym = "CFStringGetCString"
|
CFStringGetCStringSym = "CFStringGetCString"
|
||||||
CFStringCreateWithCStringSym = "CFStringCreateWithCString"
|
CFStringCreateWithCStringSym = "CFStringCreateWithCString"
|
||||||
|
CFDataGetLengthSym = "CFDataGetLength"
|
||||||
|
CFDataGetBytePtrSym = "CFDataGetBytePtr"
|
||||||
CFReleaseSym = "CFRelease"
|
CFReleaseSym = "CFRelease"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
KCFStringEncodingUTF8 = 0x08000100
|
KCFStringEncodingUTF8 = 0x08000100
|
||||||
|
KCFNumberSInt64Type = 4
|
||||||
KCFNumberIntType = 9
|
KCFNumberIntType = 9
|
||||||
KCFAllocatorDefault = 0
|
KCFAllocatorDefault = 0
|
||||||
)
|
)
|
||||||
|
|
||||||
// Kernel functions and symbols.
|
// Kernel functions and symbols.
|
||||||
|
type MachTimeBaseInfo struct {
|
||||||
|
Numer uint32
|
||||||
|
Denom uint32
|
||||||
|
}
|
||||||
|
|
||||||
type (
|
type (
|
||||||
HostProcessorInfoFunc func(host uint32, flavor int, outProcessorCount *uint32, outProcessorInfo uintptr,
|
HostProcessorInfoFunc func(host uint32, flavor int32, outProcessorCount *uint32, outProcessorInfo uintptr,
|
||||||
outProcessorInfoCnt *uint32) int
|
outProcessorInfoCnt *uint32) int
|
||||||
HostStatisticsFunc func(host uint32, flavor int, hostInfoOut uintptr, hostInfoOutCnt *uint32) int
|
HostStatisticsFunc func(host uint32, flavor int32, hostInfoOut uintptr, hostInfoOutCnt *uint32) int
|
||||||
MachHostSelfFunc func() uint32
|
MachHostSelfFunc func() uint32
|
||||||
MachTaskSelfFunc func() uint32
|
MachTaskSelfFunc func() uint32
|
||||||
|
MachTimeBaseInfoFunc func(info uintptr) int
|
||||||
VMDeallocateFunc func(targetTask uint32, vmAddress, vmSize uintptr) int
|
VMDeallocateFunc func(targetTask uint32, vmAddress, vmSize uintptr) int
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -202,16 +244,40 @@ const (
|
|||||||
HostStatisticsSym = "host_statistics"
|
HostStatisticsSym = "host_statistics"
|
||||||
MachHostSelfSym = "mach_host_self"
|
MachHostSelfSym = "mach_host_self"
|
||||||
MachTaskSelfSym = "mach_task_self"
|
MachTaskSelfSym = "mach_task_self"
|
||||||
|
MachTimeBaseInfoSym = "mach_timebase_info"
|
||||||
VMDeallocateSym = "vm_deallocate"
|
VMDeallocateSym = "vm_deallocate"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
CTL_KERN = 1
|
||||||
|
KERN_ARGMAX = 8
|
||||||
|
KERN_PROCARGS2 = 49
|
||||||
|
|
||||||
HOST_VM_INFO = 2
|
HOST_VM_INFO = 2
|
||||||
HOST_CPU_LOAD_INFO = 3
|
HOST_CPU_LOAD_INFO = 3
|
||||||
|
|
||||||
HOST_VM_INFO_COUNT = 0xf
|
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.
|
// SMC represents a SMC instance.
|
||||||
type SMC struct {
|
type SMC struct {
|
||||||
lib *Library
|
lib *Library
|
||||||
@ -281,3 +347,18 @@ func (s *SMC) Close() error {
|
|||||||
}
|
}
|
||||||
return nil
|
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))
|
||||||
|
}
|
||||||
|
@ -40,23 +40,3 @@ func CallLsofWithContext(ctx context.Context, invoke Invoker, pid int32, args ..
|
|||||||
}
|
}
|
||||||
return ret, nil
|
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
|
|
||||||
}
|
|
||||||
|
@ -85,7 +85,7 @@ func VirtualMemory() (*VirtualMemoryStat, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func VirtualMemoryWithContext(ctx context.Context) (*VirtualMemoryStat, error) {
|
func VirtualMemoryWithContext(ctx context.Context) (*VirtualMemoryStat, error) {
|
||||||
machLib, err := common.NewLibrary(common.Kernel)
|
machLib, err := common.NewLibrary(common.System)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@ import (
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
invoke common.Invoker = common.Invoke{}
|
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")
|
ErrorProcessNotRunning = errors.New("process does not exist")
|
||||||
ErrorNotPermitted = errors.New("operation not permitted")
|
ErrorNotPermitted = errors.New("operation not permitted")
|
||||||
)
|
)
|
||||||
|
@ -4,15 +4,20 @@
|
|||||||
package process
|
package process
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/binary"
|
||||||
"fmt"
|
"fmt"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
"github.com/tklauser/go-sysconf"
|
|
||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
|
|
||||||
|
"github.com/shirou/gopsutil/v4/cpu"
|
||||||
"github.com/shirou/gopsutil/v4/internal/common"
|
"github.com/shirou/gopsutil/v4/internal/common"
|
||||||
"github.com/shirou/gopsutil/v4/net"
|
"github.com/shirou/gopsutil/v4/net"
|
||||||
)
|
)
|
||||||
@ -27,16 +32,6 @@ const (
|
|||||||
KernProcPathname = 12 // path to executable
|
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 {
|
type _Ctype_struct___0 struct {
|
||||||
Pad uint64
|
Pad uint64
|
||||||
}
|
}
|
||||||
@ -186,65 +181,22 @@ func (p *Process) IOCountersWithContext(ctx context.Context) (*IOCountersStat, e
|
|||||||
return nil, common.ErrNotImplementedError
|
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) {
|
func (p *Process) ChildrenWithContext(ctx context.Context) ([]*Process, error) {
|
||||||
pids, err := common.CallPgrepWithContext(ctx, invoke, p.Pid)
|
procs, err := ProcessesWithContext(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil
|
||||||
}
|
}
|
||||||
ret := make([]*Process, 0, len(pids))
|
ret := make([]*Process, 0, len(procs))
|
||||||
for _, pid := range pids {
|
for _, proc := range procs {
|
||||||
np, err := NewProcessWithContext(ctx, pid)
|
ppid, err := proc.PpidWithContext(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
continue
|
||||||
}
|
}
|
||||||
ret = append(ret, np)
|
if ppid == p.Pid {
|
||||||
|
ret = append(ret, proc)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
sort.Slice(ret, func(i, j int) bool { return ret[i].Pid < ret[j].Pid })
|
||||||
return ret, nil
|
return ret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -323,3 +275,206 @@ func callPsWithContext(ctx context.Context, arg string, pid int32, threadOption
|
|||||||
|
|
||||||
return ret, nil
|
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
|
||||||
|
}
|
||||||
|
@ -212,6 +212,27 @@ type Posix_cred struct {
|
|||||||
|
|
||||||
type Label 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 {
|
type AuditinfoAddr struct {
|
||||||
Auid uint32
|
Auid uint32
|
||||||
Mask AuMask
|
Mask AuMask
|
||||||
|
@ -190,6 +190,27 @@ type Posix_cred struct{}
|
|||||||
|
|
||||||
type Label 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 {
|
type AuditinfoAddr struct {
|
||||||
Auid uint32
|
Auid uint32
|
||||||
Mask AuMask
|
Mask AuMask
|
||||||
|
@ -1,222 +0,0 @@
|
|||||||
// SPDX-License-Identifier: BSD-3-Clause
|
|
||||||
//go:build darwin && cgo
|
|
||||||
|
|
||||||
package process
|
|
||||||
|
|
||||||
// #include <stdlib.h>
|
|
||||||
// #include <libproc.h>
|
|
||||||
// #include <string.h>
|
|
||||||
// #include <sys/errno.h>
|
|
||||||
// #include <sys/proc_info.h>
|
|
||||||
// #include <sys/sysctl.h>
|
|
||||||
// #include <mach/mach_time.h>
|
|
||||||
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
|
|
||||||
}
|
|
@ -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
|
|
||||||
}
|
|
@ -8,6 +8,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@ -269,18 +270,21 @@ func (p *Process) MemoryInfoWithContext(ctx context.Context) (*MemoryInfoStat, e
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *Process) ChildrenWithContext(ctx context.Context) ([]*Process, 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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil
|
||||||
}
|
}
|
||||||
ret := make([]*Process, 0, len(pids))
|
ret := make([]*Process, 0, len(procs))
|
||||||
for _, pid := range pids {
|
for _, proc := range procs {
|
||||||
np, err := NewProcessWithContext(ctx, pid)
|
ppid, err := proc.PpidWithContext(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
continue
|
||||||
}
|
}
|
||||||
ret = append(ret, np)
|
if ppid == p.Pid {
|
||||||
|
ret = append(ret, proc)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
sort.Slice(ret, func(i, j int) bool { return ret[i].Pid < ret[j].Pid })
|
||||||
return ret, nil
|
return ret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@ import (
|
|||||||
"math"
|
"math"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@ -338,21 +339,34 @@ func (p *Process) PageFaultsWithContext(ctx context.Context) (*PageFaultsStat, e
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *Process) ChildrenWithContext(ctx context.Context) ([]*Process, error) {
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if len(pids) == 0 {
|
ret := make([]*Process, 0, len(statFiles))
|
||||||
return nil, ErrorNoChildren
|
for _, statFile := range statFiles {
|
||||||
}
|
statContents, err := os.ReadFile(statFile)
|
||||||
ret := make([]*Process, 0, len(pids))
|
|
||||||
for _, pid := range pids {
|
|
||||||
np, err := NewProcessWithContext(ctx, pid)
|
|
||||||
if err != nil {
|
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
|
return ret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
@ -286,18 +287,21 @@ func (p *Process) MemoryInfoWithContext(ctx context.Context) (*MemoryInfoStat, e
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *Process) ChildrenWithContext(ctx context.Context) ([]*Process, 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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil
|
||||||
}
|
}
|
||||||
ret := make([]*Process, 0, len(pids))
|
ret := make([]*Process, 0, len(procs))
|
||||||
for _, pid := range pids {
|
for _, proc := range procs {
|
||||||
np, err := NewProcessWithContext(ctx, pid)
|
ppid, err := proc.PpidWithContext(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
continue
|
||||||
}
|
}
|
||||||
ret = append(ret, np)
|
if ppid == p.Pid {
|
||||||
|
ret = append(ret, proc)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
sort.Slice(ret, func(i, j int) bool { return ret[i].Pid < ret[j].Pid })
|
||||||
return ret, nil
|
return ret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,6 +53,7 @@ package process
|
|||||||
#include <sys/sysctl.h>
|
#include <sys/sysctl.h>
|
||||||
#include <sys/ucred.h>
|
#include <sys/ucred.h>
|
||||||
#include <sys/proc.h>
|
#include <sys/proc.h>
|
||||||
|
#include <sys/proc_info.h>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
#include <sys/_types/_timeval.h>
|
#include <sys/_types/_timeval.h>
|
||||||
#include <sys/appleapiopts.h>
|
#include <sys/appleapiopts.h>
|
||||||
@ -154,6 +155,8 @@ type Posix_cred C.struct_posix_cred
|
|||||||
|
|
||||||
type Label C.struct_label
|
type Label C.struct_label
|
||||||
|
|
||||||
|
type ProcTaskInfo C.struct_proc_taskinfo
|
||||||
|
|
||||||
type (
|
type (
|
||||||
AuditinfoAddr C.struct_auditinfo_addr
|
AuditinfoAddr C.struct_auditinfo_addr
|
||||||
AuMask C.struct_au_mask
|
AuMask C.struct_au_mask
|
||||||
|
Loading…
x
Reference in New Issue
Block a user