From 456bf5094a3234a1ad02e91c163967a2b281c65e Mon Sep 17 00:00:00 2001
From: Pavlo Kutishchev
Date: Tue, 5 Jun 2018 19:38:32 +0200
Subject: [PATCH] Add processes exporter (#950)
* Add processes exporter
Signed-off-by: Pavel Kutishchev
Signed-off-by: Ben Kochie
---
collector/fixtures/e2e-64k-page-output.txt | 16 +++
collector/fixtures/e2e-output.txt | 16 +++
collector/fixtures/proc/10/stat | 1 +
collector/fixtures/proc/sys/kernel/pid_max | 1 +
.../fixtures/proc/sys/kernel/threads-max | 1 +
collector/fixtures/proc/sys/pid_max | 1 +
collector/fixtures/proc/sys/threads-max | 1 +
collector/processes_linux.go | 109 ++++++++++++++++++
collector/processes_linux_test.go | 47 ++++++++
end-to-end-test.sh | 1 +
10 files changed, 194 insertions(+)
create mode 100644 collector/fixtures/proc/10/stat
create mode 100644 collector/fixtures/proc/sys/kernel/pid_max
create mode 100644 collector/fixtures/proc/sys/kernel/threads-max
create mode 100644 collector/fixtures/proc/sys/pid_max
create mode 100644 collector/fixtures/proc/sys/threads-max
create mode 100644 collector/processes_linux.go
create mode 100644 collector/processes_linux_test.go
diff --git a/collector/fixtures/e2e-64k-page-output.txt b/collector/fixtures/e2e-64k-page-output.txt
index 30152623..f2f660f8 100644
--- a/collector/fixtures/e2e-64k-page-output.txt
+++ b/collector/fixtures/e2e-64k-page-output.txt
@@ -2094,6 +2094,21 @@ node_nfsd_server_rpcs_total 18628
# HELP node_nfsd_server_threads Total number of NFSd kernel threads that are running.
# TYPE node_nfsd_server_threads gauge
node_nfsd_server_threads 8
+# HELP node_processes_max_processes Number of max PIDs limit
+# TYPE node_processes_max_processes gauge
+node_processes_max_processes 123
+# HELP node_processes_max_threads Limit of threads in the system
+# TYPE node_processes_max_threads gauge
+node_processes_max_threads 7801
+# HELP node_processes_pids Number of PIDs
+# TYPE node_processes_pids gauge
+node_processes_pids 1
+# HELP node_processes_state Number of processes in each state.
+# TYPE node_processes_state gauge
+node_processes_state{state="S"} 1
+# HELP node_processes_threads Allocated threads in system
+# TYPE node_processes_threads gauge
+node_processes_threads 1
# HELP node_procs_blocked Number of processes blocked waiting for I/O to complete.
# TYPE node_procs_blocked gauge
node_procs_blocked 0
@@ -2149,6 +2164,7 @@ node_scrape_collector_success{collector="netdev"} 1
node_scrape_collector_success{collector="netstat"} 1
node_scrape_collector_success{collector="nfs"} 1
node_scrape_collector_success{collector="nfsd"} 1
+node_scrape_collector_success{collector="processes"} 1
node_scrape_collector_success{collector="qdisc"} 1
node_scrape_collector_success{collector="sockstat"} 1
node_scrape_collector_success{collector="stat"} 1
diff --git a/collector/fixtures/e2e-output.txt b/collector/fixtures/e2e-output.txt
index 4eb1dd0e..218aaf56 100644
--- a/collector/fixtures/e2e-output.txt
+++ b/collector/fixtures/e2e-output.txt
@@ -2094,6 +2094,21 @@ node_nfsd_server_rpcs_total 18628
# HELP node_nfsd_server_threads Total number of NFSd kernel threads that are running.
# TYPE node_nfsd_server_threads gauge
node_nfsd_server_threads 8
+# HELP node_processes_max_processes Number of max PIDs limit
+# TYPE node_processes_max_processes gauge
+node_processes_max_processes 123
+# HELP node_processes_max_threads Limit of threads in the system
+# TYPE node_processes_max_threads gauge
+node_processes_max_threads 7801
+# HELP node_processes_pids Number of PIDs
+# TYPE node_processes_pids gauge
+node_processes_pids 1
+# HELP node_processes_state Number of processes in each state.
+# TYPE node_processes_state gauge
+node_processes_state{state="S"} 1
+# HELP node_processes_threads Allocated threads in system
+# TYPE node_processes_threads gauge
+node_processes_threads 1
# HELP node_procs_blocked Number of processes blocked waiting for I/O to complete.
# TYPE node_procs_blocked gauge
node_procs_blocked 0
@@ -2149,6 +2164,7 @@ node_scrape_collector_success{collector="netdev"} 1
node_scrape_collector_success{collector="netstat"} 1
node_scrape_collector_success{collector="nfs"} 1
node_scrape_collector_success{collector="nfsd"} 1
+node_scrape_collector_success{collector="processes"} 1
node_scrape_collector_success{collector="qdisc"} 1
node_scrape_collector_success{collector="sockstat"} 1
node_scrape_collector_success{collector="stat"} 1
diff --git a/collector/fixtures/proc/10/stat b/collector/fixtures/proc/10/stat
new file mode 100644
index 00000000..1451c8a4
--- /dev/null
+++ b/collector/fixtures/proc/10/stat
@@ -0,0 +1 @@
+17 (khungtaskd) S 2 0 0 0 -1 2129984 0 0 0 0 14 0 0 0 20 0 1 0 24 0 0 18446744073709551615 0 0 0 0 0 0 0 2147483647 0 0 0 0 17 0 0 0 0 0 0 0 0 0 0 0 0 0 0
\ No newline at end of file
diff --git a/collector/fixtures/proc/sys/kernel/pid_max b/collector/fixtures/proc/sys/kernel/pid_max
new file mode 100644
index 00000000..190a1803
--- /dev/null
+++ b/collector/fixtures/proc/sys/kernel/pid_max
@@ -0,0 +1 @@
+123
diff --git a/collector/fixtures/proc/sys/kernel/threads-max b/collector/fixtures/proc/sys/kernel/threads-max
new file mode 100644
index 00000000..0ccbf457
--- /dev/null
+++ b/collector/fixtures/proc/sys/kernel/threads-max
@@ -0,0 +1 @@
+7801
\ No newline at end of file
diff --git a/collector/fixtures/proc/sys/pid_max b/collector/fixtures/proc/sys/pid_max
new file mode 100644
index 00000000..190a1803
--- /dev/null
+++ b/collector/fixtures/proc/sys/pid_max
@@ -0,0 +1 @@
+123
diff --git a/collector/fixtures/proc/sys/threads-max b/collector/fixtures/proc/sys/threads-max
new file mode 100644
index 00000000..0ccbf457
--- /dev/null
+++ b/collector/fixtures/proc/sys/threads-max
@@ -0,0 +1 @@
+7801
\ No newline at end of file
diff --git a/collector/processes_linux.go b/collector/processes_linux.go
new file mode 100644
index 00000000..fd6ddb1f
--- /dev/null
+++ b/collector/processes_linux.go
@@ -0,0 +1,109 @@
+// Copyright 2018 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.
+
+// +build !noprocesses
+
+package collector
+
+import (
+ "fmt"
+ "github.com/prometheus/client_golang/prometheus"
+ "github.com/prometheus/procfs"
+)
+
+type processCollector struct {
+ threadAlloc *prometheus.Desc
+ threadLimit *prometheus.Desc
+ procsState *prometheus.Desc
+ pidUsed *prometheus.Desc
+ pidMax *prometheus.Desc
+}
+
+func init() {
+ registerCollector("processes", defaultDisabled, NewProcessStatCollector)
+}
+
+func NewProcessStatCollector() (Collector, error) {
+ subsystem := "processes"
+ return &processCollector{
+ threadAlloc: prometheus.NewDesc(
+ prometheus.BuildFQName(namespace, subsystem, "threads"),
+ "Allocated threads in system",
+ nil, nil,
+ ),
+ threadLimit: prometheus.NewDesc(
+ prometheus.BuildFQName(namespace, subsystem, "max_threads"),
+ "Limit of threads in the system",
+ nil, nil,
+ ),
+ procsState: prometheus.NewDesc(
+ prometheus.BuildFQName(namespace, subsystem, "state"),
+ "Number of processes in each state.",
+ []string{"state"}, nil,
+ ),
+ pidUsed: prometheus.NewDesc(prometheus.BuildFQName(namespace, subsystem, "pids"),
+ "Number of PIDs", nil, nil,
+ ),
+ pidMax: prometheus.NewDesc(prometheus.BuildFQName(namespace, subsystem, "max_processes"),
+ "Number of max PIDs limit", nil, nil,
+ ),
+ }, nil
+}
+func (t *processCollector) Update(ch chan<- prometheus.Metric) error {
+ pids, states, threads, err := getAllocatedThreads()
+ if err != nil {
+ return fmt.Errorf("Unable to retrieve number of allocated threads %v\n", err)
+ }
+
+ ch <- prometheus.MustNewConstMetric(t.threadAlloc, prometheus.GaugeValue, float64(threads))
+ maxThreads, err := readUintFromFile(procFilePath("sys/kernel/threads-max"))
+ if err != nil {
+ return fmt.Errorf("Unable to retrieve limit number of threads %v\n", err)
+ }
+ ch <- prometheus.MustNewConstMetric(t.threadLimit, prometheus.GaugeValue, float64(maxThreads))
+
+ for state := range states {
+ ch <- prometheus.MustNewConstMetric(t.procsState, prometheus.GaugeValue, float64(states[state]), state)
+ }
+
+ pidM, err := readUintFromFile(procFilePath("sys/kernel/pid_max"))
+ if err != nil {
+ return fmt.Errorf("Unable to retrieve limit number of maximum pids alloved %v\n", err)
+ }
+ ch <- prometheus.MustNewConstMetric(t.pidUsed, prometheus.GaugeValue, float64(pids))
+ ch <- prometheus.MustNewConstMetric(t.pidMax, prometheus.GaugeValue, float64(pidM))
+
+ return nil
+}
+
+func getAllocatedThreads() (int, map[string]int32, int, error) {
+ fs, err := procfs.NewFS(*procPath)
+ if err != nil {
+ return 0, nil, 0, err
+ }
+ p, err := fs.AllProcs()
+ if err != nil {
+ return 0, nil, 0, err
+ }
+ thread := 0
+ procStates := make(map[string]int32)
+ for _, pid := range p {
+ stat, err := pid.NewStat()
+ if err != nil {
+ return 0, nil, 0, err
+ }
+ procStates[stat.State] += 1
+ thread += stat.NumThreads
+ }
+ return len(p), procStates, thread, nil
+}
diff --git a/collector/processes_linux_test.go b/collector/processes_linux_test.go
new file mode 100644
index 00000000..1cf9e4f0
--- /dev/null
+++ b/collector/processes_linux_test.go
@@ -0,0 +1,47 @@
+// Copyright 2018 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.
+
+// +build !noprocesses
+
+package collector
+
+import (
+ "testing"
+
+ "gopkg.in/alecthomas/kingpin.v2"
+)
+
+func TestReadProcessStatus(t *testing.T) {
+ if _, err := kingpin.CommandLine.Parse([]string{"--path.procfs", "fixtures/proc"}); err != nil {
+ t.Fatal(err)
+ }
+ want := 1
+ pids, states, threads, err := getAllocatedThreads()
+ if err != nil {
+ t.Fatalf("Cannot retrieve data from procfs getAllocatedThreads function: %v ", err)
+ }
+ if threads < want {
+ t.Fatalf("Current threads: %d Shouldn't be less than wanted %d", threads, want)
+ }
+ if states == nil {
+
+ t.Fatalf("Process states cannot be nil %v:", states)
+ }
+ maxPid, err := readUintFromFile(procFilePath("sys/kernel/pid_max"))
+ if err != nil {
+ t.Fatalf("Unable to retrieve limit number of maximum pids alloved %v\n", err)
+ }
+ if uint64(pids) > maxPid || pids == 0 {
+ t.Fatalf("Total running pids cannot be greater than %d or equals to 0", maxPid)
+ }
+}
diff --git a/end-to-end-test.sh b/end-to-end-test.sh
index ec99cae5..469d2441 100755
--- a/end-to-end-test.sh
+++ b/end-to-end-test.sh
@@ -36,6 +36,7 @@ enabled_collectors=$(cat << COLLECTORS
wifi
xfs
zfs
+ processes
COLLECTORS
)
disabled_collectors=$(cat << COLLECTORS