ProcessStatCollector: continue if PID disappears between opening and reading file

Signed-off-by: Dustin Hooten <dustinhooten@gmail.com>
pull/1948/head
Dustin Hooten 2021-01-24 17:15:59 -07:00 committed by Johannes 'fish' Ziemke
parent 88ee42742e
commit b7626ecdbf
2 changed files with 35 additions and 2 deletions

View File

@ -19,6 +19,8 @@ import (
"errors" "errors"
"fmt" "fmt"
"os" "os"
"strings"
"syscall"
"github.com/go-kit/kit/log" "github.com/go-kit/kit/log"
"github.com/go-kit/kit/log/level" "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 { for _, pid := range p {
stat, err := pid.Stat() stat, err := pid.Stat()
// PIDs can vanish between getting the list and getting stats. // 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) level.Debug(c.logger).Log("msg", "file not found when retrieving stats for pid", "pid", pid, "err", err)
continue continue
} }
@ -125,3 +127,7 @@ func (c *processCollector) getAllocatedThreads() (int, map[string]int32, int, er
} }
return pids, procStates, thread, nil return pids, procStates, thread, nil
} }
func errorContains(err, target error) bool {
return err != nil && target != nil && strings.Contains(err.Error(), target.Error())
}

View File

@ -16,9 +16,11 @@
package collector package collector
import ( import (
"github.com/go-kit/kit/log" "errors"
"syscall"
"testing" "testing"
"github.com/go-kit/kit/log"
"github.com/prometheus/procfs" "github.com/prometheus/procfs"
kingpin "gopkg.in/alecthomas/kingpin.v2" 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) 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)
}
}
}