1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
|
package pidpicker
import (
"os"
"path/filepath"
"testing"
)
func TestScanProcessesFrom(t *testing.T) {
root := t.TempDir()
mkProc(t, root, "12", "12 (bash) S 1 1 1 0", "bash\x00-l\x00")
mkProc(t, root, "99", "99 (sshd) S 1 1 1 0", "")
mkProc(t, root, "bad", "bad data", "ignored")
processes, err := scanProcessesFrom(root)
if err != nil {
t.Fatalf("scanProcessesFrom returned error: %v", err)
}
if len(processes) != 2 {
t.Fatalf("expected 2 valid processes, got %d", len(processes))
}
if processes[0].Pid != 12 || processes[0].Comm != "bash" || processes[0].Cmdline != "bash -l" {
t.Fatalf("unexpected first process: %+v", processes[0])
}
if processes[0].ParentPID != 12 {
t.Fatalf("expected first process parent pid=12, got %d", processes[0].ParentPID)
}
if processes[1].Pid != 99 || processes[1].Comm != "sshd" || processes[1].Cmdline != "" {
t.Fatalf("unexpected second process: %+v", processes[1])
}
}
func TestParseCommFromStatInvalid(t *testing.T) {
cases := []string{
"",
"100 no-parens",
"100 () S 1 1 1 0",
"100 (unterminated S 1 1 1 0",
}
for _, tc := range cases {
if _, err := parseCommFromStat(tc); err == nil {
t.Fatalf("expected parseCommFromStat to fail for %q", tc)
}
}
}
func TestNormalizeCmdline(t *testing.T) {
got := normalizeCmdline([]byte("python\x00main.py\x00"))
if got != "python main.py" {
t.Fatalf("unexpected normalized cmdline: %q", got)
}
if normalizeCmdline(nil) != "" {
t.Fatalf("expected empty cmdline for nil bytes")
}
}
func TestScanThreadsFrom(t *testing.T) {
root := t.TempDir()
procDir := filepath.Join(root, "12")
if err := os.MkdirAll(filepath.Join(procDir, "task", "12"), 0o755); err != nil {
t.Fatalf("mkdir: %v", err)
}
if err := os.MkdirAll(filepath.Join(procDir, "task", "120"), 0o755); err != nil {
t.Fatalf("mkdir: %v", err)
}
if err := os.WriteFile(filepath.Join(procDir, "cmdline"), []byte("bash\x00-l\x00"), 0o644); err != nil {
t.Fatalf("write cmdline: %v", err)
}
if err := os.WriteFile(filepath.Join(procDir, "task", "12", "comm"), []byte("bash-main\n"), 0o644); err != nil {
t.Fatalf("write comm: %v", err)
}
if err := os.WriteFile(filepath.Join(procDir, "task", "120", "comm"), []byte("bash-worker\n"), 0o644); err != nil {
t.Fatalf("write comm: %v", err)
}
threads, err := scanThreadsFrom(root, 12)
if err != nil {
t.Fatalf("scanThreadsFrom returned error: %v", err)
}
if len(threads) != 2 {
t.Fatalf("expected 2 threads, got %d", len(threads))
}
if threads[0].Pid != 12 || threads[0].Comm != "bash-main" || threads[0].Cmdline != "bash -l" {
t.Fatalf("unexpected first thread: %+v", threads[0])
}
if threads[0].ParentPID != 12 {
t.Fatalf("unexpected first thread parent pid: %d", threads[0].ParentPID)
}
if threads[1].Pid != 120 || threads[1].Comm != "bash-worker" || threads[1].Cmdline != "bash -l" {
t.Fatalf("unexpected second thread: %+v", threads[1])
}
if threads[1].ParentPID != 12 {
t.Fatalf("unexpected second thread parent pid: %d", threads[1].ParentPID)
}
}
func mkProc(t *testing.T, root, pid, stat, cmdline string) {
t.Helper()
dir := filepath.Join(root, pid)
if err := os.MkdirAll(dir, 0o755); err != nil {
t.Fatalf("mkdir %s: %v", dir, err)
}
if err := os.WriteFile(filepath.Join(dir, "stat"), []byte(stat), 0o644); err != nil {
t.Fatalf("write stat: %v", err)
}
if err := os.WriteFile(filepath.Join(dir, "cmdline"), []byte(cmdline), 0o644); err != nil {
t.Fatalf("write cmdline: %v", err)
}
}
|