From b7626ecdbf4c51c902719cd025a388cdcd45e5e0 Mon Sep 17 00:00:00 2001 From: Dustin Hooten Date: Sun, 24 Jan 2021 17:15:59 -0700 Subject: [PATCH] ProcessStatCollector: continue if PID disappears between opening and reading file Signed-off-by: Dustin Hooten --- collector/processes_linux.go | 8 +++++++- collector/processes_linux_test.go | 29 ++++++++++++++++++++++++++++- 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/collector/processes_linux.go b/collector/processes_linux.go index 3d4e95d2..965d31dc 100644 --- a/collector/processes_linux.go +++ b/collector/processes_linux.go @@ -19,6 +19,8 @@ import ( "errors" "fmt" "os" + "strings" + "syscall" "github.com/go-kit/kit/log" "github.com/go-kit/kit/log/level" @@ -111,7 +113,7 @@ func (c *processCollector) getAllocatedThreads() (int, map[string]int32, int, er for _, pid := range p { stat, err := pid.Stat() // PIDs can vanish between getting the list and getting stats. - if errors.Is(err, os.ErrNotExist) { + if errors.Is(err, os.ErrNotExist) || errorContains(err, syscall.ESRCH) { level.Debug(c.logger).Log("msg", "file not found when retrieving stats for pid", "pid", pid, "err", err) continue } @@ -125,3 +127,7 @@ func (c *processCollector) getAllocatedThreads() (int, map[string]int32, int, er } return pids, procStates, thread, nil } + +func errorContains(err, target error) bool { + return err != nil && target != nil && strings.Contains(err.Error(), target.Error()) +} diff --git a/collector/processes_linux_test.go b/collector/processes_linux_test.go index cb01fbb7..16151962 100644 --- a/collector/processes_linux_test.go +++ b/collector/processes_linux_test.go @@ -16,9 +16,11 @@ package collector import ( - "github.com/go-kit/kit/log" + "errors" + "syscall" "testing" + "github.com/go-kit/kit/log" "github.com/prometheus/procfs" kingpin "gopkg.in/alecthomas/kingpin.v2" ) @@ -52,3 +54,28 @@ func TestReadProcessStatus(t *testing.T) { t.Fatalf("Total running pids cannot be greater than %d or equals to 0", maxPid) } } + +func Test_errorContains(t *testing.T) { + testCases := []struct { + err error + target error + expected bool + }{ + {err: nil, target: nil, expected: false}, + {err: errors.New("e"), target: nil, expected: false}, + {err: nil, target: errors.New("e"), expected: false}, + {err: errors.New("abc"), target: errors.New("def"), expected: false}, + {err: errors.New("abc"), target: errors.New("bc"), expected: true}, + {err: errors.New("read /proc/2054/stat: no such process"), target: syscall.ESRCH, expected: true}, + } + for _, tc := range testCases { + actual := errorContains(tc.err, tc.target) + if actual != tc.expected { + negation := " not" + if tc.expected { + negation = "" + } + t.Fatalf("Expected \"%v\"%s to contain \"%v\", got %v", tc.err, negation, tc.target, actual) + } + } +}