| package system |
| |
| import ( |
| "errors" |
| "math/bits" |
| "os" |
| "reflect" |
| "strconv" |
| "testing" |
| ) |
| |
| var procdata = map[string]Stat_t{ |
| "4902 (gunicorn: maste) S 4885 4902 4902 0 -1 4194560 29683 29929 61 83 78 16 96 17 20 0 1 0 9126532 52965376 1903 18446744073709551615 4194304 7461796 140733928751520 140733928698072 139816984959091 0 0 16781312 137447943 1 0 0 17 3 0 0 9 0 0 9559488 10071156 33050624 140733928758775 140733928758945 140733928758945 140733928759264 0": { |
| Name: "gunicorn: maste", |
| State: 'S', |
| StartTime: 9126532, |
| }, |
| "9534 (cat) R 9323 9534 9323 34828 9534 4194304 95 0 0 0 0 0 0 0 20 0 1 0 9214966 7626752 168 18446744073709551615 4194304 4240332 140732237651568 140732237650920 140570710391216 0 0 0 0 0 0 0 17 1 0 0 0 0 0 6340112 6341364 21553152 140732237653865 140732237653885 140732237653885 140732237656047 0": { |
| Name: "cat", |
| State: 'R', |
| StartTime: 9214966, |
| }, |
| "12345 ((ugly )pr()cess() R 9323 9534 9323 34828 9534 4194304 95 0 0 0 0 0 0 0 20 0 1 0 9214966 7626752 168 18446744073709551615 4194304 4240332 140732237651568 140732237650920 140570710391216 0 0 0 0 0 0 0 17 1 0 0 0 0 0 6340112 6341364 21553152 140732237653865 140732237653885 140732237653885 140732237656047 0": { |
| Name: "(ugly )pr()cess(", |
| State: 'R', |
| StartTime: 9214966, |
| }, |
| "24767 (irq/44-mei_me) S 2 0 0 0 -1 2129984 0 0 0 0 0 0 0 0 -51 0 1 0 8722075 0 0 18446744073709551615 0 0 0 0 0 0 0 2147483647 0 0 0 0 17 1 50 1 0 0 0 0 0 0 0 0 0 0 0": { |
| Name: "irq/44-mei_me", |
| State: 'S', |
| StartTime: 8722075, |
| }, |
| "0 () I 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0": { |
| Name: "", |
| State: 'I', |
| StartTime: 0, |
| }, |
| // Not entirely correct, but minimally viable input (StartTime and a space after). |
| "1 (woo hoo) S 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 ": { |
| Name: "woo hoo", |
| State: 'S', |
| StartTime: 4, |
| }, |
| } |
| |
| func TestParseStat(t *testing.T) { |
| for line, exp := range procdata { |
| st, err := parseStat(line) |
| if err != nil { |
| t.Errorf("input %q, unexpected error %v", line, err) |
| } else if !reflect.DeepEqual(st, exp) { |
| t.Errorf("input %q, expected %+v, got %+v", line, exp, st) |
| } |
| } |
| } |
| |
| func TestParseStatBadInput(t *testing.T) { |
| cases := []struct { |
| desc, input string |
| }{ |
| { |
| "no (", |
| "123 ) S 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0", |
| }, |
| { |
| "no )", |
| "123 ( S 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0", |
| }, |
| { |
| ") at end", |
| "123 (cmd) S 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)", |
| }, |
| { |
| "misplaced ()", |
| "123 )one( S 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0", |
| }, |
| { |
| "misplaced empty ()", |
| "123 )( S 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0", |
| }, |
| { |
| "empty line", |
| "", |
| }, |
| { |
| "short line", |
| "123 (cmd) S 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0", |
| }, |
| { |
| "short line (no space after stime)", |
| "123 (cmd) S 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 42", |
| }, |
| { |
| "bad stime", |
| "123 (cmd) S 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -1 ", |
| }, |
| { |
| "bad stime 2", // would be valid if not -1 |
| "123 (cmd) S -1 ", |
| }, |
| { |
| "a tad short", |
| "1234 (cmd) ", |
| }, |
| { |
| "bad stime", |
| "123 (cmd) S 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1", |
| }, |
| } |
| for _, c := range cases { |
| st, err := parseStat(c.input) |
| if err == nil { |
| t.Errorf("case %q, expected error, got nil, %+v", c.desc, st) |
| } |
| } |
| } |
| |
| func BenchmarkParseStat(b *testing.B) { |
| var ( |
| st, exp Stat_t |
| line string |
| err error |
| ) |
| |
| for i := 0; i != b.N; i++ { |
| for line, exp = range procdata { |
| st, err = parseStat(line) |
| } |
| } |
| if err != nil { |
| b.Fatal(err) |
| } |
| if !reflect.DeepEqual(st, exp) { |
| b.Fatal("wrong result") |
| } |
| } |
| |
| func BenchmarkParseRealStat(b *testing.B) { |
| var ( |
| st Stat_t |
| err error |
| total int |
| ) |
| b.StopTimer() |
| fd, err := os.Open("/proc") |
| if err != nil { |
| b.Fatal(err) |
| } |
| defer fd.Close() |
| |
| for i := 0; i != b.N; i++ { |
| count := 0 |
| if _, err := fd.Seek(0, 0); err != nil { |
| b.Fatal(err) |
| } |
| names, err := fd.Readdirnames(-1) |
| if err != nil { |
| b.Fatal(err) |
| } |
| for _, n := range names { |
| pid, err := strconv.ParseUint(n, 10, bits.UintSize) |
| if err != nil { |
| continue |
| } |
| b.StartTimer() |
| st, err = Stat(int(pid)) |
| b.StopTimer() |
| if err != nil { |
| // Ignore a process that just finished. |
| if errors.Is(err, os.ErrNotExist) { |
| continue |
| } |
| b.Fatal(err) |
| } |
| count++ |
| } |
| total += count |
| } |
| b.Logf("N: %d, parsed %d pids, last stat: %+v, err: %v", b.N, total, st, err) |
| } |