111 lines
3.5 KiB
Go
111 lines
3.5 KiB
Go
// Copyright 2024 The Prometheus Authors
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package prometheus
|
|
|
|
import (
|
|
"fmt"
|
|
"golang.org/x/sys/unix"
|
|
"os"
|
|
"syscall"
|
|
"time"
|
|
)
|
|
|
|
func canCollectProcess() bool {
|
|
return true
|
|
}
|
|
|
|
func getSoftLimit(which int) (uint64, error) {
|
|
rlimit := syscall.Rlimit{}
|
|
|
|
if err := syscall.Getrlimit(which, &rlimit); err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
return rlimit.Cur, nil
|
|
}
|
|
|
|
func getOpenFileCount() (float64, error) {
|
|
// Alternately, the undocumented proc_pidinfo(PROC_PIDLISTFDS) can be used to
|
|
// return a list of open fds, but that requires a way to call C APIs. The
|
|
// benefits, however, include fewer system calls and not failing when at the
|
|
// open file soft limit.
|
|
|
|
if dir, err := os.Open("/dev/fd"); err != nil {
|
|
return 0.0, err
|
|
} else {
|
|
defer dir.Close()
|
|
|
|
// Avoid ReadDir(), as it calls stat(2) on each descriptor. Not only is
|
|
// that info not used, but KQUEUE descriptors fail stat(2), which causes
|
|
// the whole method to fail.
|
|
if names, err := dir.Readdirnames(0); err != nil {
|
|
return 0.0, err
|
|
} else {
|
|
// Subtract 1 to ignore the open /dev/fd descriptor above.
|
|
return float64(len(names) - 1), nil
|
|
}
|
|
}
|
|
}
|
|
|
|
func (c *processCollector) processCollect(ch chan<- Metric) {
|
|
if procs, err := unix.SysctlKinfoProcSlice("kern.proc.pid", os.Getpid()); err == nil {
|
|
if len(procs) == 1 {
|
|
startTime := float64(procs[0].Proc.P_starttime.Nano() / 1e9)
|
|
ch <- MustNewConstMetric(c.startTime, GaugeValue, startTime)
|
|
} else {
|
|
err = fmt.Errorf("sysctl() returned %d proc structs (expected 1)", len(procs))
|
|
c.reportError(ch, c.startTime, err)
|
|
}
|
|
} else {
|
|
c.reportError(ch, c.startTime, err)
|
|
}
|
|
|
|
// The proc structure returned by kern.proc.pid above has an Rusage member,
|
|
// but it is not filled in, so it needs to be fetched by getrusage(2). For
|
|
// that call, the UTime, STime, and Maxrss members are filled out, but not
|
|
// Ixrss, Idrss, or Isrss for the memory usage. Memory stats will require
|
|
// access to the C API to call task_info(TASK_BASIC_INFO).
|
|
rusage := unix.Rusage{}
|
|
|
|
if err := unix.Getrusage(syscall.RUSAGE_SELF, &rusage); err == nil {
|
|
cpuTime := time.Duration(rusage.Stime.Nano() + rusage.Utime.Nano()).Seconds()
|
|
ch <- MustNewConstMetric(c.cpuTotal, CounterValue, cpuTime)
|
|
} else {
|
|
c.reportError(ch, c.cpuTotal, err)
|
|
}
|
|
|
|
// TODO: publish c.vsize and c.rss values
|
|
|
|
if fds, err := getOpenFileCount(); err == nil {
|
|
ch <- MustNewConstMetric(c.openFDs, GaugeValue, fds)
|
|
} else {
|
|
c.reportError(ch, c.openFDs, err)
|
|
}
|
|
|
|
if openFiles, err := getSoftLimit(syscall.RLIMIT_NOFILE); err == nil {
|
|
ch <- MustNewConstMetric(c.maxFDs, GaugeValue, float64(openFiles))
|
|
} else {
|
|
c.reportError(ch, c.maxFDs, err)
|
|
}
|
|
|
|
if addressSpace, err := getSoftLimit(syscall.RLIMIT_AS); err == nil {
|
|
ch <- MustNewConstMetric(c.maxVsize, GaugeValue, float64(addressSpace))
|
|
} else {
|
|
c.reportError(ch, c.maxVsize, err)
|
|
}
|
|
|
|
// TODO: socket(PF_SYSTEM) to fetch "com.apple.network.statistics" might
|
|
// be able to get the per-process network send/receive counts.
|
|
}
|