Skip to content

Commit aaa232f

Browse files
authored
Merge pull request #220 from rst0git/task-state-parsing
crit: Add support for TaskState decoding
2 parents e74e3dd + 6b7c800 commit aaa232f

File tree

4 files changed

+173
-0
lines changed

4 files changed

+173
-0
lines changed

crit/explore.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,25 @@ import (
1313
"github.com/checkpoint-restore/go-criu/v8/crit/images/pstree"
1414
)
1515

16+
// CRIU uses 4 task states to represent the state of a process. A process can be
17+
// Alive, Dead, Stopped or Zombie. Only Alive and Stopped processes have
18+
// associated state (memory pages, file descriptors, sockets, etc). The function
19+
// checks whether a process with the given pID has associated state.
20+
func (c *crit) taskHasState(pID uint32) (bool, error) {
21+
coreImg, err := getImg(
22+
filepath.Join(c.inputDirPath, fmt.Sprintf("core-%d.img", pID)),
23+
&criu_core.CoreEntry{},
24+
)
25+
if err != nil {
26+
return false, err
27+
}
28+
29+
coreData := coreImg.Entries[0].Message.(*criu_core.CoreEntry)
30+
state := TaskState(coreData.GetTc().GetTaskState())
31+
32+
return state.IsAliveOrStopped(), nil
33+
}
34+
1635
// PsTree represents the process tree
1736
type PsTree struct {
1837
PID uint32 `json:"pid"`
@@ -97,6 +116,15 @@ func (c *crit) ExploreFds() ([]*Fd, error) {
97116
for _, entry := range psTreeImg.Entries {
98117
process := entry.Message.(*pstree.PstreeEntry)
99118
pID := process.GetPid()
119+
120+
ok, err := c.taskHasState(pID)
121+
if err != nil {
122+
return nil, err
123+
}
124+
if !ok {
125+
continue
126+
}
127+
100128
// Get file with object IDs
101129
idsImg, err := getImg(filepath.Join(c.inputDirPath, fmt.Sprintf("ids-%d.img", pID)), &criu_core.TaskKobjIdsEntry{})
102130
if err != nil {
@@ -199,6 +227,15 @@ func (c *crit) ExploreMems() ([]*MemMap, error) {
199227
for _, entry := range psTreeImg.Entries {
200228
process := entry.Message.(*pstree.PstreeEntry)
201229
pID := process.GetPid()
230+
231+
ok, err := c.taskHasState(pID)
232+
if err != nil {
233+
return nil, err
234+
}
235+
if !ok {
236+
continue
237+
}
238+
202239
// Get memory mappings
203240
mmImg, err := getImg(filepath.Join(c.inputDirPath, fmt.Sprintf("mm-%d.img", pID)), &mm.MmEntry{})
204241
if err != nil {
@@ -318,6 +355,15 @@ func (c *crit) ExploreRss() ([]*RssMap, error) {
318355
for _, entry := range psTreeImg.Entries {
319356
process := entry.Message.(*pstree.PstreeEntry)
320357
pID := process.GetPid()
358+
359+
ok, err := c.taskHasState(pID)
360+
if err != nil {
361+
return nil, err
362+
}
363+
if !ok {
364+
continue
365+
}
366+
321367
// Get virtual memory addresses
322368
mmImg, err := getImg(filepath.Join(c.inputDirPath, fmt.Sprintf("mm-%d.img", pID)), &mm.MmEntry{})
323369
if err != nil {
@@ -418,6 +464,15 @@ func (c *crit) ExploreSk() ([]*Sk, error) {
418464
for _, entry := range psTreeImg.Entries {
419465
process := entry.Message.(*pstree.PstreeEntry)
420466
pID := process.GetPid()
467+
468+
ok, err := c.taskHasState(pID)
469+
if err != nil {
470+
return nil, err
471+
}
472+
if !ok {
473+
continue
474+
}
475+
421476
// Get file with object IDs
422477
idsImg, err := getImg(filepath.Join(c.inputDirPath, fmt.Sprintf("ids-%d.img", pID)), &criu_core.TaskKobjIdsEntry{})
423478
if err != nil {

crit/task_state.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package crit
2+
3+
type TaskState uint32
4+
5+
// Task state constants from CRIU (compel/include/uapi/task-state.h)
6+
const (
7+
TaskAlive TaskState = 0x01
8+
TaskDead TaskState = 0x02
9+
TaskStopped TaskState = 0x03
10+
TaskZombie TaskState = 0x06
11+
)
12+
13+
func (s TaskState) String() string {
14+
if v, ok := map[TaskState]string{
15+
TaskAlive: "Alive",
16+
TaskDead: "Dead",
17+
TaskStopped: "Stopped",
18+
TaskZombie: "Zombie",
19+
}[s]; ok {
20+
return v
21+
}
22+
return "Unknown"
23+
}
24+
25+
// A checkpointed process has state (memory pages, file descriptors, sockets),
26+
// only if it is "alive" or "stopped". Note that "dead" means the task has
27+
// exited during checkpointing and CRIU observed the exit reported by waitpid().
28+
func (s TaskState) IsAliveOrStopped() bool {
29+
switch s {
30+
case TaskAlive, TaskStopped:
31+
return true
32+
default:
33+
return false
34+
}
35+
}
36+
37+
func (s TaskState) IsAlive() bool { return s == TaskAlive }
38+
func (s TaskState) IsDead() bool { return s == TaskDead }
39+
func (s TaskState) IsStopped() bool { return s == TaskStopped }
40+
func (s TaskState) IsZombie() bool { return s == TaskZombie }

crit/task_state_test.go

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
package crit
2+
3+
import "testing"
4+
5+
func TestTaskStateString(t *testing.T) {
6+
tests := []struct {
7+
state TaskState
8+
want string
9+
}{
10+
{TaskAlive, "Alive"},
11+
{TaskDead, "Dead"},
12+
{TaskStopped, "Stopped"},
13+
{TaskZombie, "Zombie"},
14+
{TaskState(0xFF), "Unknown"}, // Unknown state
15+
}
16+
17+
for _, test := range tests {
18+
got := test.state.String()
19+
if got != test.want {
20+
t.Errorf("TaskState(%d).String() = %q, want %q", test.state, got, test.want)
21+
}
22+
}
23+
}
24+
25+
func TestTaskStateIsAliveOrStopped(t *testing.T) {
26+
tests := []struct {
27+
state TaskState
28+
want bool
29+
}{
30+
{TaskAlive, true},
31+
{TaskDead, false},
32+
{TaskStopped, true},
33+
{TaskZombie, false},
34+
{TaskState(0xFF), false}, // Unknown state
35+
}
36+
37+
for _, test := range tests {
38+
got := test.state.IsAliveOrStopped()
39+
if got != test.want {
40+
t.Errorf("TaskState(%d).IsAliveOrStopped() = %v, want %v", test.state, got, test.want)
41+
}
42+
}
43+
}
44+
45+
func TestTaskStateChecks(t *testing.T) {
46+
tests := []struct {
47+
state TaskState
48+
isAlive bool
49+
isDead bool
50+
isStopped bool
51+
isZombie bool
52+
}{
53+
{TaskAlive, true, false, false, false},
54+
{TaskDead, false, true, false, false},
55+
{TaskStopped, false, false, true, false},
56+
{TaskZombie, false, false, false, true},
57+
{TaskState(0xFF), false, false, false, false}, // Unknown
58+
}
59+
60+
for _, test := range tests {
61+
if got := test.state.IsAlive(); got != test.isAlive {
62+
t.Errorf("TaskState(%d).IsAlive() = %v, want %v", test.state, got, test.isAlive)
63+
}
64+
if got := test.state.IsDead(); got != test.isDead {
65+
t.Errorf("TaskState(%d).IsDead() = %v, want %v", test.state, got, test.isDead)
66+
}
67+
if got := test.state.IsStopped(); got != test.isStopped {
68+
t.Errorf("TaskState(%d).IsStopped() = %v, want %v", test.state, got, test.isStopped)
69+
}
70+
if got := test.state.IsZombie(); got != test.isZombie {
71+
t.Errorf("TaskState(%d).IsZombie() = %v, want %v", test.state, got, test.isZombie)
72+
}
73+
}
74+
}

test/loop/loop.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,10 @@ int main(void)
5454
write(start_pipe[1], &res, sizeof(res));
5555
close(start_pipe[1]);
5656

57+
/* Create a process with task_state="dead" */
58+
if (fork() == 0)
59+
_exit(0);
60+
5761
while (1) {
5862
sleep(1);
5963
}

0 commit comments

Comments
 (0)