diff --git a/stat.go b/stat.go index 593ad0f6..8e47ecf8 100644 --- a/stat.go +++ b/stat.go @@ -143,6 +143,22 @@ func parseSoftIRQStat(line string) (SoftIRQStat, uint64, error) { return softIRQStat, total, nil } +func parseProcessCreated(value string) (uint64, error) { + if v, err := strconv.ParseUint(value, 10, 64); err == nil { + return v, nil + } + + // Some kernels have been observed to expose the wrapped fork counter as a + // signed 32-bit decimal string (for example "-2045677862"). Recover the + // underlying uint32 value instead of failing the entire /proc/stat parse. + v, err := strconv.ParseInt(value, 10, 32) + if err != nil { + return 0, err + } + + return uint64(uint32(v)), nil +} + // NewStat returns information about current cpu/process statistics. // See https://www.kernel.org/doc/Documentation/filesystems/proc.txt // @@ -220,7 +236,7 @@ func parseStat(r io.Reader, fileName string) (Stat, error) { return Stat{}, fmt.Errorf("%w: couldn't parse %q (ctxt): %w", ErrFileParse, parts[1], err) } case parts[0] == "processes": - if stat.ProcessCreated, err = strconv.ParseUint(parts[1], 10, 64); err != nil { + if stat.ProcessCreated, err = parseProcessCreated(parts[1]); err != nil { return Stat{}, fmt.Errorf("%w: couldn't parse %q (processes): %w", ErrFileParse, parts[1], err) } case parts[0] == "procs_running": diff --git a/stat_test.go b/stat_test.go index 3707a481..84169c30 100644 --- a/stat_test.go +++ b/stat_test.go @@ -13,7 +13,10 @@ package procfs -import "testing" +import ( + "strings" + "testing" +) func TestStat(t *testing.T) { s, err := getProcFixtures(t).Stat() @@ -70,5 +73,15 @@ func TestStat(t *testing.T) { if want, have := uint64(508444), s.SoftIRQ.Rcu; want != have { t.Errorf("want softirq RCU %d, have %d", want, have) } +} + +func TestStatProcessesCounterWrap(t *testing.T) { + s, err := parseStat(strings.NewReader("processes -2045677862\n"), "stat") + if err != nil { + t.Fatal(err) + } + if want, have := uint64(2249289434), s.ProcessCreated; want != have { + t.Fatalf("want wrapped process counter %d, have %d", want, have) + } }