Skip to content

Commit 43e50e1

Browse files
committed
Reduce call to ps for process package on darwin
1 parent 60eae48 commit 43e50e1

File tree

3 files changed

+158
-76
lines changed

3 files changed

+158
-76
lines changed

process/process_darwin.go

+2-70
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import (
1111
"strconv"
1212
"strings"
1313

14-
"github.com/shirou/gopsutil/v3/cpu"
1514
"github.com/shirou/gopsutil/v3/internal/common"
1615
"github.com/shirou/gopsutil/v3/net"
1716
"github.com/tklauser/go-sysconf"
@@ -80,27 +79,18 @@ func (p *Process) NameWithContext(ctx context.Context) (string, error) {
8079
return "", err
8180
}
8281
if len(cmdName) > 0 {
83-
extendedName := filepath.Base(cmdName[0])
82+
extendedName := filepath.Base(cmdName)
8483
if strings.HasPrefix(extendedName, p.name) {
8584
name = extendedName
8685
} else {
87-
name = cmdName[0]
86+
name = cmdName
8887
}
8988
}
9089
}
9190

9291
return name, nil
9392
}
9493

95-
// cmdNameWithContext returns the command name (including spaces) without any arguments
96-
func (p *Process) cmdNameWithContext(ctx context.Context) ([]string, error) {
97-
r, err := callPsWithContext(ctx, "command", p.Pid, false, true)
98-
if err != nil {
99-
return nil, err
100-
}
101-
return r[0], err
102-
}
103-
10494
func (p *Process) createTimeWithContext(ctx context.Context) (int64, error) {
10595
k, err := p.getKProc()
10696
if err != nil {
@@ -211,14 +201,6 @@ func (p *Process) IOCountersWithContext(ctx context.Context) (*IOCountersStat, e
211201
return nil, common.ErrNotImplementedError
212202
}
213203

214-
func (p *Process) NumThreadsWithContext(ctx context.Context) (int32, error) {
215-
r, err := callPsWithContext(ctx, "utime,stime", p.Pid, true, false)
216-
if err != nil {
217-
return 0, err
218-
}
219-
return int32(len(r)), nil
220-
}
221-
222204
func convertCPUTimes(s string) (ret float64, err error) {
223205
var t int
224206
var _tmp string
@@ -265,56 +247,6 @@ func convertCPUTimes(s string) (ret float64, err error) {
265247
return float64(t) / float64(clockTicks), nil
266248
}
267249

268-
func (p *Process) TimesWithContext(ctx context.Context) (*cpu.TimesStat, error) {
269-
r, err := callPsWithContext(ctx, "utime,stime", p.Pid, false, false)
270-
if err != nil {
271-
return nil, err
272-
}
273-
274-
utime, err := convertCPUTimes(r[0][0])
275-
if err != nil {
276-
return nil, err
277-
}
278-
stime, err := convertCPUTimes(r[0][1])
279-
if err != nil {
280-
return nil, err
281-
}
282-
283-
ret := &cpu.TimesStat{
284-
CPU: "cpu",
285-
User: utime,
286-
System: stime,
287-
}
288-
return ret, nil
289-
}
290-
291-
func (p *Process) MemoryInfoWithContext(ctx context.Context) (*MemoryInfoStat, error) {
292-
r, err := callPsWithContext(ctx, "rss,vsize,pagein", p.Pid, false, false)
293-
if err != nil {
294-
return nil, err
295-
}
296-
rss, err := strconv.Atoi(r[0][0])
297-
if err != nil {
298-
return nil, err
299-
}
300-
vms, err := strconv.Atoi(r[0][1])
301-
if err != nil {
302-
return nil, err
303-
}
304-
pagein, err := strconv.Atoi(r[0][2])
305-
if err != nil {
306-
return nil, err
307-
}
308-
309-
ret := &MemoryInfoStat{
310-
RSS: uint64(rss) * 1024,
311-
VMS: uint64(vms) * 1024,
312-
Swap: uint64(pagein),
313-
}
314-
315-
return ret, nil
316-
}
317-
318250
func (p *Process) ChildrenWithContext(ctx context.Context) ([]*Process, error) {
319251
pids, err := common.CallPgrepWithContext(ctx, invoke, p.Pid)
320252
if err != nil {

process/process_darwin_cgo.go

+85-6
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ package process
99
// #include <sys/errno.h>
1010
// #include <sys/proc_info.h>
1111
// #include <sys/sysctl.h>
12+
// #include <mach/mach_time.h>
1213
import "C"
1314

1415
import (
@@ -18,12 +19,18 @@ import (
1819
"strings"
1920
"syscall"
2021
"unsafe"
22+
23+
"github.com/shirou/gopsutil/v3/cpu"
2124
)
2225

23-
var argMax int
26+
var (
27+
argMax int
28+
timescaleToNanoSeconds float64
29+
)
2430

2531
func init() {
2632
argMax = getArgMax()
33+
timescaleToNanoSeconds = getTimeScaleToNanoSeconds()
2734
}
2835

2936
func getArgMax() int {
@@ -39,6 +46,14 @@ func getArgMax() int {
3946
return 0
4047
}
4148

49+
func getTimeScaleToNanoSeconds() float64 {
50+
var timeBaseInfo C.struct_mach_timebase_info
51+
52+
C.mach_timebase_info(&timeBaseInfo)
53+
54+
return float64(timeBaseInfo.numer) / float64(timeBaseInfo.denom)
55+
}
56+
4257
func (p *Process) ExeWithContext(ctx context.Context) (string, error) {
4358
var c C.char // need a var for unsafe.Sizeof need a var
4459
const bufsize = C.PROC_PIDPATHINFO_MAXSIZE * unsafe.Sizeof(c)
@@ -82,7 +97,7 @@ func (p *Process) CwdWithContext(ctx context.Context) (string, error) {
8297
return C.GoString(&vpi.pvi_cdir.vip_path[0]), err
8398
}
8499

85-
func procArgs(pid int32) (*[]byte, int, error) {
100+
func procArgs(pid int32) ([]byte, int, error) {
86101
var (
87102
mib = [...]C.int{C.CTL_KERN, C.KERN_PROCARGS2, C.int(pid)}
88103
size C.size_t = C.ulong(argMax)
@@ -91,23 +106,27 @@ func procArgs(pid int32) (*[]byte, int, error) {
91106
)
92107
procargs := (*C.char)(C.malloc(C.ulong(argMax)))
93108
defer C.free(unsafe.Pointer(procargs))
94-
retval := C.sysctl(&mib[0], 3, unsafe.Pointer(procargs), &size, C.NULL, 0)
109+
retval, err := C.sysctl(&mib[0], 3, unsafe.Pointer(procargs), &size, C.NULL, 0)
95110
if retval == 0 {
96111
C.memcpy(unsafe.Pointer(&nargs), unsafe.Pointer(procargs), C.sizeof_int)
97112
result = C.GoBytes(unsafe.Pointer(procargs), C.int(size))
98113
// fmt.Printf("size: %d %d\n%s\n", size, nargs, hex.Dump(result))
99-
return &result, int(nargs), nil
114+
return result, int(nargs), nil
100115
}
101-
return nil, 0, fmt.Errorf("error: %d", retval)
116+
return nil, 0, err
102117
}
103118

104119
func (p *Process) CmdlineSliceWithContext(ctx context.Context) ([]string, error) {
120+
return p.cmdlineSliceWithContext(ctx, true)
121+
}
122+
123+
func (p *Process) cmdlineSliceWithContext(ctx context.Context, fallback bool) ([]string, error) {
105124
pargs, nargs, err := procArgs(p.Pid)
106125
if err != nil {
107126
return nil, err
108127
}
109128
// The first bytes hold the nargs int, skip it.
110-
args := bytes.Split((*pargs)[C.sizeof_int:], []byte{0})
129+
args := bytes.Split((pargs)[C.sizeof_int:], []byte{0})
111130
var argStr string
112131
// The first element is the actual binary/command path.
113132
// command := args[0]
@@ -131,10 +150,70 @@ func (p *Process) CmdlineSliceWithContext(ctx context.Context) ([]string, error)
131150
return argSlice, err
132151
}
133152

153+
// cmdNameWithContext returns the command name (including spaces) without any arguments
154+
func (p *Process) cmdNameWithContext(ctx context.Context) (string, error) {
155+
r, err := p.cmdlineSliceWithContext(ctx, false)
156+
if err != nil {
157+
return "", err
158+
}
159+
160+
if len(r) == 0 {
161+
return "", nil
162+
}
163+
164+
return r[0], err
165+
}
166+
134167
func (p *Process) CmdlineWithContext(ctx context.Context) (string, error) {
135168
r, err := p.CmdlineSliceWithContext(ctx)
136169
if err != nil {
137170
return "", err
138171
}
139172
return strings.Join(r, " "), err
140173
}
174+
175+
func (p *Process) NumThreadsWithContext(ctx context.Context) (int32, error) {
176+
const tiSize = C.sizeof_struct_proc_taskinfo
177+
ti := (*C.struct_proc_taskinfo)(C.malloc(tiSize))
178+
179+
_, err := C.proc_pidinfo(C.int(p.Pid), C.PROC_PIDTASKINFO, 0, unsafe.Pointer(ti), tiSize)
180+
if err != nil {
181+
return 0, err
182+
}
183+
184+
return int32(ti.pti_threadnum), nil
185+
}
186+
187+
func (p *Process) TimesWithContext(ctx context.Context) (*cpu.TimesStat, error) {
188+
const tiSize = C.sizeof_struct_proc_taskinfo
189+
ti := (*C.struct_proc_taskinfo)(C.malloc(tiSize))
190+
191+
_, err := C.proc_pidinfo(C.int(p.Pid), C.PROC_PIDTASKINFO, 0, unsafe.Pointer(ti), tiSize)
192+
if err != nil {
193+
return nil, err
194+
}
195+
196+
ret := &cpu.TimesStat{
197+
CPU: "cpu",
198+
User: float64(ti.pti_total_user) * timescaleToNanoSeconds / 1e9,
199+
System: float64(ti.pti_total_system) * timescaleToNanoSeconds / 1e9,
200+
}
201+
return ret, nil
202+
}
203+
204+
func (p *Process) MemoryInfoWithContext(ctx context.Context) (*MemoryInfoStat, error) {
205+
const tiSize = C.sizeof_struct_proc_taskinfo
206+
ti := (*C.struct_proc_taskinfo)(C.malloc(tiSize))
207+
208+
_, err := C.proc_pidinfo(C.int(p.Pid), C.PROC_PIDTASKINFO, 0, unsafe.Pointer(ti), tiSize)
209+
if err != nil {
210+
return nil, err
211+
}
212+
213+
ret := &MemoryInfoStat{
214+
RSS: uint64(ti.pti_resident_size),
215+
VMS: uint64(ti.pti_virtual_size),
216+
Swap: uint64(ti.pti_pageins),
217+
}
218+
return ret, nil
219+
}

process/process_darwin_nocgo.go

+71
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"strconv"
1111
"strings"
1212

13+
"github.com/shirou/gopsutil/v3/cpu"
1314
"github.com/shirou/gopsutil/v3/internal/common"
1415
)
1516

@@ -47,6 +48,18 @@ func (p *Process) CmdlineWithContext(ctx context.Context) (string, error) {
4748
return strings.Join(r[0], " "), err
4849
}
4950

51+
func (p *Process) cmdNameWithContext(ctx context.Context) (string, error) {
52+
r, err := callPsWithContext(ctx, "command", p.Pid, false, true)
53+
if err != nil {
54+
return "", err
55+
}
56+
if len(r) > 0 && len(r[0]) > 0 {
57+
return r[0][0], err
58+
}
59+
60+
return "", err
61+
}
62+
5063
// CmdlineSliceWithContext returns the command line arguments of the process as a slice with each
5164
// element being an argument. Because of current deficiencies in the way that the command
5265
// line arguments are found, single arguments that have spaces in the will actually be
@@ -59,3 +72,61 @@ func (p *Process) CmdlineSliceWithContext(ctx context.Context) ([]string, error)
5972
}
6073
return r[0], err
6174
}
75+
76+
func (p *Process) NumThreadsWithContext(ctx context.Context) (int32, error) {
77+
r, err := callPsWithContext(ctx, "utime,stime", p.Pid, true, false)
78+
if err != nil {
79+
return 0, err
80+
}
81+
return int32(len(r)), nil
82+
}
83+
84+
func (p *Process) TimesWithContext(ctx context.Context) (*cpu.TimesStat, error) {
85+
r, err := callPsWithContext(ctx, "utime,stime", p.Pid, false, false)
86+
if err != nil {
87+
return nil, err
88+
}
89+
90+
utime, err := convertCPUTimes(r[0][0])
91+
if err != nil {
92+
return nil, err
93+
}
94+
stime, err := convertCPUTimes(r[0][1])
95+
if err != nil {
96+
return nil, err
97+
}
98+
99+
ret := &cpu.TimesStat{
100+
CPU: "cpu",
101+
User: utime,
102+
System: stime,
103+
}
104+
return ret, nil
105+
}
106+
107+
func (p *Process) MemoryInfoWithContext(ctx context.Context) (*MemoryInfoStat, error) {
108+
r, err := callPsWithContext(ctx, "rss,vsize,pagein", p.Pid, false, false)
109+
if err != nil {
110+
return nil, err
111+
}
112+
rss, err := strconv.Atoi(r[0][0])
113+
if err != nil {
114+
return nil, err
115+
}
116+
vms, err := strconv.Atoi(r[0][1])
117+
if err != nil {
118+
return nil, err
119+
}
120+
pagein, err := strconv.Atoi(r[0][2])
121+
if err != nil {
122+
return nil, err
123+
}
124+
125+
ret := &MemoryInfoStat{
126+
RSS: uint64(rss) * 1024,
127+
VMS: uint64(vms) * 1024,
128+
Swap: uint64(pagein),
129+
}
130+
131+
return ret, nil
132+
}

0 commit comments

Comments
 (0)