mirror of https://github.com/shirou/gopsutil
update disk & cpu & process
parent
701a74be41
commit
9e6efdb991
@ -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
|
||||
}
|
@ -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
|
||||
}
|
@ -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);
|
@ -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
|
||||
}
|
Loading…
Reference in New Issue