Skip to content

Commit

Permalink
Merge pull request #67 from leahneukirchen/readstat
Browse files Browse the repository at this point in the history
internal/proc: parse /proc/$PID/stat correctly
  • Loading branch information
vrothberg authored Apr 6, 2020
2 parents 7383b17 + d8c41f5 commit 3c5f81c
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 10 deletions.
21 changes: 16 additions & 5 deletions internal/proc/stat.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
package proc

import (
"errors"
"fmt"
"io/ioutil"
"strings"
Expand Down Expand Up @@ -112,21 +113,31 @@ type Stat struct {
}

// readStat is used for mocking in unit tests.
var readStat = func(path string) ([]string, error) {
data, err := ioutil.ReadFile(path)
var readStat = func(path string) (string, error) {
rawData, err := ioutil.ReadFile(path)
if err != nil {
return nil, err
return "", err
}
return strings.Fields(string(data)), nil
return string(rawData), nil
}

// ParseStat parses the /proc/$pid/stat file and returns a Stat.
func ParseStat(pid string) (*Stat, error) {
fields, err := readStat(fmt.Sprintf("/proc/%s/stat", pid))
data, err := readStat(fmt.Sprintf("/proc/%s/stat", pid))
if err != nil {
return nil, err
}

firstParen := strings.IndexByte(data, '(')
lastParen := strings.LastIndexByte(data, ')')
if firstParen == -1 || lastParen == -1 {
return nil, errors.New("invalid format in stat")
}
pidstr := data[0 : firstParen-1]
comm := data[firstParen+1 : lastParen]
rest := strings.Fields(data[lastParen+1:])
fields := append([]string{pidstr, comm}, rest...)

fieldAt := func(i int) string {
return fields[i-1]
}
Expand Down
34 changes: 29 additions & 5 deletions internal/proc/stat_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,28 +15,38 @@
package proc

import (
"strings"
"errors"
"testing"

"github.com/stretchr/testify/assert"
)

var statFile = "31404 (gedit) R 2109 2128 2128 0 -1 4194304 13153 328 0 0 590 55 0 0 20 0 6 0 1331588 419667968 19515 18446744073709551615 94120519110656 94120519115256 140737253236304 0 0 0 0 4096 0 0 0 0 17 2 0 0 62588346 0 0 94120521215368 94120521216168 94120544436224 140737253242331 140737253242369 140737253242369 140737253244905 0"
var statFileSpace = "31405 (ge d it) R 2109 2128 2128 0 -1 4194304 13153 328 0 0 590 55 0 0 20 0 6 0 1331588 419667968 19515 18446744073709551615 94120519110656 94120519115256 140737253236304 0 0 0 0 4096 0 0 0 0 17 2 0 0 62588346 0 0 94120521215368 94120521216168 94120544436224 140737253242331 140737253242369 140737253242369 140737253244905 0"
var statFileParen = "31406 (ged)it) R 2109 2128 2128 0 -1 4194304 13153 328 0 0 590 55 0 0 20 0 6 0 1331588 419667968 19515 18446744073709551615 94120519110656 94120519115256 140737253236304 0 0 0 0 4096 0 0 0 0 17 2 0 0 62588346 0 0 94120521215368 94120521216168 94120544436224 140737253242331 140737253242369 140737253242369 140737253244905 0"

func testReadStat(_ string) ([]string, error) {
return strings.Fields(statFile), nil
func testReadStat(file string) (string, error) {
switch file {
case "/proc/31404/stat":
return statFile, nil
case "/proc/31405/stat":
return statFileSpace, nil
case "/proc/31406/stat":
return statFileParen, nil
}
return "", errors.New("unimplemented test case")
}

func TestParseStat(t *testing.T) {
readStat = testReadStat

s, err := ParseStat("")
s, err := ParseStat("31404")

assert.Nil(t, err)
assert.NotNil(t, s)

assert.Equal(t, "31404", s.Pid)
assert.Equal(t, "(gedit)", s.Comm)
assert.Equal(t, "gedit", s.Comm)
assert.Equal(t, "R", s.State)
assert.Equal(t, "2109", s.Ppid)
assert.Equal(t, "2128", s.Pgrp)
Expand All @@ -58,4 +68,18 @@ func TestParseStat(t *testing.T) {
assert.Equal(t, "0", s.Itrealvalue)
assert.Equal(t, "1331588", s.Starttime)
assert.Equal(t, "419667968", s.Vsize)

s2, err := ParseStat("31405")

assert.Nil(t, err)
assert.NotNil(t, s2)

assert.Equal(t, "ge d it", s2.Comm)

s3, err := ParseStat("31406")

assert.Nil(t, err)
assert.NotNil(t, s3)

assert.Equal(t, "ged)it", s3.Comm)
}

0 comments on commit 3c5f81c

Please sign in to comment.