diff --git a/pkg/sentry/kernel/task_cgroup.go b/pkg/sentry/kernel/task_cgroup.go index ce62c48ec..dd9b7ef31 100644 --- a/pkg/sentry/kernel/task_cgroup.go +++ b/pkg/sentry/kernel/task_cgroup.go @@ -177,20 +177,21 @@ func (t *Task) MigrateCgroup(dst Cgroup) error { return nil } -// taskCgroupEntry represents a line in /proc//cgroup, and is used to +// TaskCgroupEntry represents a line in /proc//cgroup, and is used to // format a cgroup for display. -type taskCgroupEntry struct { - hierarchyID uint32 - controllers string - path string +type TaskCgroupEntry struct { + HierarchyID uint32 `json:"hierarchy_id,omitempty"` + Controllers string `json:"controllers,omitempty"` + Path string `json:"path,omitempty"` } -// GenerateProcTaskCgroup writes the contents of /proc//cgroup for t to buf. -func (t *Task) GenerateProcTaskCgroup(buf *bytes.Buffer) { +// GetCgroupEntries generates the contents of /proc//cgroup as +// a TaskCgroupEntry array. +func (t *Task) GetCgroupEntries() []TaskCgroupEntry { t.mu.Lock() defer t.mu.Unlock() - cgEntries := make([]taskCgroupEntry, 0, len(t.cgroups)) + cgEntries := make([]TaskCgroupEntry, 0, len(t.cgroups)) for c := range t.cgroups { ctls := c.Controllers() ctlNames := make([]string, 0, len(ctls)) @@ -208,16 +209,22 @@ func (t *Task) GenerateProcTaskCgroup(buf *bytes.Buffer) { ctlNames = append(ctlNames, string(ctl.Type())) } - cgEntries = append(cgEntries, taskCgroupEntry{ - hierarchyID: c.HierarchyID(), - controllers: strings.Join(ctlNames, ","), - path: c.Path(), + cgEntries = append(cgEntries, TaskCgroupEntry{ + HierarchyID: c.HierarchyID(), + Controllers: strings.Join(ctlNames, ","), + Path: c.Path(), }) } - sort.Slice(cgEntries, func(i, j int) bool { return cgEntries[i].hierarchyID > cgEntries[j].hierarchyID }) + sort.Slice(cgEntries, func(i, j int) bool { return cgEntries[i].HierarchyID > cgEntries[j].HierarchyID }) + return cgEntries +} + +// GenerateProcTaskCgroup writes the contents of /proc//cgroup for t to buf. +func (t *Task) GenerateProcTaskCgroup(buf *bytes.Buffer) { + cgEntries := t.GetCgroupEntries() for _, cgE := range cgEntries { - fmt.Fprintf(buf, "%d:%s:%s\n", cgE.hierarchyID, cgE.controllers, cgE.path) + fmt.Fprintf(buf, "%d:%s:%s\n", cgE.HierarchyID, cgE.Controllers, cgE.Path) } } diff --git a/runsc/boot/procfs/dump.go b/runsc/boot/procfs/dump.go index 17c2829be..3cc627607 100644 --- a/runsc/boot/procfs/dump.go +++ b/runsc/boot/procfs/dump.go @@ -61,6 +61,8 @@ type ProcessProcfsDump struct { // Limits constains resource limits for this process. Currently only // RLIMIT_NOFILE is supported. Limits map[string]limits.Limit `json:"limits,omitempty"` + // Cgroup is /proc/[pid]/cgroup split into an array. + Cgroup []kernel.TaskCgroupEntry `json:"cgroup,omitempty"` } // getMM returns t's MemoryManager. On success, the MemoryManager's users count @@ -208,5 +210,8 @@ func Dump(t *kernel.Task, pid kernel.ThreadID) (ProcessProcfsDump, error) { Limits: map[string]limits.Limit{ "RLIMIT_NOFILE": fdLimit, }, + // We don't need to worry about fake cgroup controllers as that is not + // supported in runsc. + Cgroup: t.GetCgroupEntries(), }, nil } diff --git a/runsc/container/trace_test.go b/runsc/container/trace_test.go index c492ba032..fc0844b99 100644 --- a/runsc/container/trace_test.go +++ b/runsc/container/trace_test.go @@ -23,6 +23,7 @@ import ( specs "github.com/opencontainers/runtime-spec/specs-go" "google.golang.org/protobuf/proto" + "gvisor.dev/gvisor/pkg/sentry/kernel" "gvisor.dev/gvisor/pkg/sentry/limits" "gvisor.dev/gvisor/pkg/sentry/seccheck" "gvisor.dev/gvisor/pkg/sentry/seccheck/checkers/remote/test" @@ -304,6 +305,7 @@ func TestProcfsDump(t *testing.T) { spec.Process.Rlimits = []specs.POSIXRlimit{ {Type: "RLIMIT_NOFILE", Hard: fdLimit.Max, Soft: fdLimit.Cur}, } + conf.Cgroupfs = true _, bundleDir, cleanup, err := testutil.SetupContainer(spec, conf) if err != nil { t.Fatalf("error setting up container: %v", err) @@ -396,4 +398,18 @@ func TestProcfsDump(t *testing.T) { if got := procfsDump[0].Limits["RLIMIT_NOFILE"]; got != fdLimit { t.Errorf("expected FD limit to be %+v, but got %+v", fdLimit, got) } + + wantCgroup := []kernel.TaskCgroupEntry{ + kernel.TaskCgroupEntry{HierarchyID: 2, Controllers: "memory", Path: "/"}, + kernel.TaskCgroupEntry{HierarchyID: 1, Controllers: "cpu", Path: "/"}, + } + if len(procfsDump[0].Cgroup) != len(wantCgroup) { + t.Errorf("expected 2 cgroup controllers, got %+v", procfsDump[0].Cgroup) + } else { + for i, cgroup := range procfsDump[0].Cgroup { + if cgroup != wantCgroup[i] { + t.Errorf("expected %+v, got %+v", wantCgroup[i], cgroup) + } + } + } }