Skip to content

Commit 46180c6

Browse files
committed
process metrics on darwin
Adds support for metrics: - process_start_time_seconds - process_cpu_seconds_total - process_virtual_memory_max_bytes - process_open_fds - process_max_fds Inspired by https://github.com/prometheus/client_golang/blob/main/prometheus/process_collector_darwin.go
1 parent afb141d commit 46180c6

File tree

315 files changed

+189154
-1
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

315 files changed

+189154
-1
lines changed

process_metrics_darwin.go

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
//go:build darwin && !ios
2+
3+
package metrics
4+
5+
import (
6+
"io"
7+
"log"
8+
"os"
9+
"syscall"
10+
"time"
11+
12+
"golang.org/x/sys/unix"
13+
)
14+
15+
func writeProcessMetrics(w io.Writer) {
16+
if procs, err := unix.SysctlKinfoProcSlice("kern.proc.pid", os.Getpid()); err == nil {
17+
if len(procs) == 1 {
18+
startTime := float64(procs[0].Proc.P_starttime.Nano() / 1e9)
19+
WriteGaugeUint64(w, "process_start_time_seconds", uint64(startTime))
20+
} else {
21+
log.Printf("ERROR: metrics: sysctl() returned %d proc structs (expected 1)", len(procs))
22+
}
23+
} else {
24+
log.Printf("ERROR: metrics: %s", err)
25+
}
26+
27+
// The proc structure returned by kern.proc.pid above has an Rusage member,
28+
// but it is not filled in, so it needs to be fetched by getrusage(2). For
29+
// that call, the UTime, STime, and Maxrss members are filled out, but not
30+
// Ixrss, Idrss, or Isrss for the memory usage. Memory stats will require
31+
// access to the C API to call task_info(TASK_BASIC_INFO).
32+
rusage := unix.Rusage{}
33+
34+
if err := unix.Getrusage(syscall.RUSAGE_SELF, &rusage); err == nil {
35+
cpuTime := time.Duration(rusage.Stime.Nano() + rusage.Utime.Nano()).Seconds()
36+
WriteGaugeFloat64(w, "process_cpu_seconds_total", cpuTime)
37+
} else {
38+
log.Printf("ERROR: metrics: %s", err)
39+
}
40+
41+
if addressSpace, err := getSoftLimit(syscall.RLIMIT_AS); err == nil {
42+
WriteGaugeFloat64(w, "process_virtual_memory_max_bytes", float64(addressSpace))
43+
} else {
44+
log.Printf("ERROR: metrics: %s", err)
45+
}
46+
}
47+
48+
func writeFDMetrics(w io.Writer) {
49+
if fds, err := getOpenFileCount(); err == nil {
50+
WriteGaugeFloat64(w, "process_open_fds", fds)
51+
} else {
52+
log.Printf("ERROR: metrics: %s", err)
53+
}
54+
55+
if openFiles, err := getSoftLimit(syscall.RLIMIT_NOFILE); err == nil {
56+
WriteGaugeFloat64(w, "process_max_fds", float64(openFiles))
57+
} else {
58+
log.Printf("ERROR: metrics: %s", err)
59+
}
60+
}
61+
62+
func getOpenFileCount() (float64, error) {
63+
// Alternately, the undocumented proc_pidinfo(PROC_PIDLISTFDS) can be used to
64+
// return a list of open fds, but that requires a way to call C APIs. The
65+
// benefits, however, include fewer system calls and not failing when at the
66+
// open file soft limit.
67+
68+
if dir, err := os.Open("/dev/fd"); err != nil {
69+
return 0.0, err
70+
} else {
71+
defer dir.Close()
72+
73+
// Avoid ReadDir(), as it calls stat(2) on each descriptor. Not only is
74+
// that info not used, but KQUEUE descriptors fail stat(2), which causes
75+
// the whole method to fail.
76+
if names, err := dir.Readdirnames(0); err != nil {
77+
return 0.0, err
78+
} else {
79+
// Subtract 1 to ignore the open /dev/fd descriptor above.
80+
return float64(len(names) - 1), nil
81+
}
82+
}
83+
}
84+
85+
func getSoftLimit(which int) (uint64, error) {
86+
rlimit := syscall.Rlimit{}
87+
88+
if err := syscall.Getrlimit(which, &rlimit); err != nil {
89+
return 0, err
90+
}
91+
92+
return rlimit.Cur, nil
93+
}

process_metrics_other.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//go:build !linux && !windows && !solaris
1+
//go:build !linux && !windows && !solaris && !darwin
22

33
package metrics
44

vendor/golang.org/x/sys/unix/.gitignore

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

vendor/golang.org/x/sys/unix/README.md

Lines changed: 184 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

vendor/golang.org/x/sys/unix/affinity_linux.go

Lines changed: 86 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

vendor/golang.org/x/sys/unix/aliases.go

Lines changed: 13 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)