-
Notifications
You must be signed in to change notification settings - Fork 2.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
libct/system.Stat: fix panic, speed up, add benchmarks #2696
Merged
Merged
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,45 +1,180 @@ | ||
package system | ||
|
||
import "testing" | ||
import ( | ||
"errors" | ||
"math/bits" | ||
"os" | ||
"reflect" | ||
"strconv" | ||
"testing" | ||
) | ||
|
||
func TestParseStartTime(t *testing.T) { | ||
data := 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": { | ||
PID: 4902, | ||
Name: "gunicorn: maste", | ||
State: 'S', | ||
StartTime: 9126532, | ||
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) | ||
kolyshkin marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} 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", | ||
}, | ||
"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": { | ||
PID: 9534, | ||
Name: "cat", | ||
State: 'R', | ||
StartTime: 9214966, | ||
{ | ||
"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", | ||
}, | ||
|
||
"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": { | ||
PID: 24767, | ||
Name: "irq/44-mei_me", | ||
State: 'S', | ||
StartTime: 8722075, | ||
{ | ||
") 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", | ||
}, | ||
kolyshkin marked this conversation as resolved.
Show resolved
Hide resolved
|
||
{ | ||
"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 line, expected := range data { | ||
st, err := parseStat(line) | ||
if err != nil { | ||
t.Fatal(err) | ||
for _, c := range cases { | ||
st, err := parseStat(c.input) | ||
if err == nil { | ||
t.Errorf("case %q, expected error, got nil, %+v", c.desc, st) | ||
} | ||
if st.PID != expected.PID { | ||
t.Fatalf("expected PID %q but received %q", expected.PID, st.PID) | ||
} | ||
} | ||
|
||
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 st.State != expected.State { | ||
t.Fatalf("expected state %q but received %q", expected.State, st.State) | ||
} | ||
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) | ||
} | ||
if st.Name != expected.Name { | ||
t.Fatalf("expected name %q but received %q", expected.Name, st.Name) | ||
names, err := fd.Readdirnames(-1) | ||
if err != nil { | ||
b.Fatal(err) | ||
} | ||
if st.StartTime != expected.StartTime { | ||
t.Fatalf("expected start time %q but received %q", expected.StartTime, st.StartTime) | ||
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) | ||
} |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here's hoping that they don't add a new field in a few kernel versions that contains
)
grimace. Why on earth doesn't/proc/self/status
contain StartTime? If it did we could just use that rather than this unparseable crap./rant
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I heard they will switch to YAML, because it seems to be popular 🤪