Skip to content

Commit a83f5ba

Browse files
committed
Fix issue in GetProcessStartTime
This fix tries to address the issue raised in docker: moby/moby#27540 The issue was that `GetProcessStartTime` use space `" "` to split the `/proc/[pid]/stat` and take the `22`th value. However, the `2`th value is inside `(` and `)`, and could contain space. The following are two examples: ``` ubuntu@ubuntu:~/runc$ cat /proc/90286/stat 90286 (bash) S 90271 90286 90286 34818 90286 4194560 1412 1130576 4 0 2 1 2334 438 20 0 1 0 3093098 20733952 823 18446744073709551615 1 1 0 0 0 0 0 3670020 1266777851 0 0 0 17 1 0 0 0 0 0 0 0 0 0 0 0 0 0 ubuntu@ubuntu:~/runc$ cat /proc/89653/stat 89653 (gunicorn: maste) S 89630 89653 89653 0 -1 4194560 29689 28896 0 3 146 32 76 19 20 0 1 0 2971844 52965376 3920 18446744073709551615 1 1 0 0 0 0 0 16781312 137447943 0 0 0 17 1 0 0 0 0 0 0 0 0 0 0 0 0 0 ``` This fix fixes this issue by removing the prefix before `)`, then finding the `20`th value (instead of `22`th value). Signed-off-by: Yong Tang <[email protected]>
1 parent 2a5001c commit a83f5ba

File tree

2 files changed

+38
-2
lines changed

2 files changed

+38
-2
lines changed

libcontainer/system/proc.go

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,30 @@ func GetProcessStartTime(pid int) (string, error) {
1414
if err != nil {
1515
return "", err
1616
}
17+
return parseStartTime(string(data))
18+
}
1719

18-
parts := strings.Split(string(data), " ")
20+
func parseStartTime(stat string) (string, error) {
1921
// the starttime is located at pos 22
2022
// from the man page
2123
//
2224
// starttime %llu (was %lu before Linux 2.6)
2325
// (22) The time the process started after system boot. In kernels before Linux 2.6, this
2426
// value was expressed in jiffies. Since Linux 2.6, the value is expressed in clock ticks
2527
// (divide by sysconf(_SC_CLK_TCK)).
26-
return parts[22-1], nil // starts at 1
28+
//
29+
// NOTE:
30+
// pos 2 could contain space and is inside `(` and `)`:
31+
// (2) comm %s
32+
// The filename of the executable, in parentheses.
33+
// This is visible whether or not the executable is
34+
// swapped out.
35+
//
36+
// the following is an example:
37+
// 89653 (gunicorn: maste) S 89630 89653 89653 0 -1 4194560 29689 28896 0 3 146 32 76 19 20 0 1 0 2971844 52965376 3920 18446744073709551615 1 1 0 0 0 0 0 16781312 137447943 0 0 0 17 1 0 0 0 0 0 0 0 0 0 0 0 0 0
38+
39+
// get parts after last `)`:
40+
s := strings.Split(stat, ")")
41+
parts := strings.Split(strings.TrimSpace(s[len(s)-1]), " ")
42+
return parts[22-3], nil // starts at 3 (after the filename pos `2`)
2743
}

libcontainer/system/proc_test.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package system
2+
3+
import "testing"
4+
5+
func TestParseStartTime(t *testing.T) {
6+
data := map[string]string{
7+
"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": "9126532",
8+
"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": "9214966",
9+
"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": "8722075",
10+
}
11+
for line, startTime := range data {
12+
st, err := parseStartTime(line)
13+
if err != nil {
14+
t.Fatal(err)
15+
}
16+
if startTime != st {
17+
t.Fatalf("expected start time %q but received %q", startTime, st)
18+
}
19+
}
20+
}

0 commit comments

Comments
 (0)