diff --git a/pkg/sentry/fsimpl/proc/tasks.go b/pkg/sentry/fsimpl/proc/tasks.go index 45837f4d1..4d4126d1f 100644 --- a/pkg/sentry/fsimpl/proc/tasks.go +++ b/pkg/sentry/fsimpl/proc/tasks.go @@ -66,17 +66,18 @@ var _ kernfs.Inode = (*tasksInode)(nil) func (fs *filesystem) newTasksInode(ctx context.Context, k *kernel.Kernel, pidns *kernel.PIDNamespace, fakeCgroupControllers map[string]string) *tasksInode { root := auth.NewRootCredentials(pidns.UserNamespace()) contents := map[string]kernfs.Inode{ - "cmdline": fs.newInode(ctx, root, 0444, &cmdLineData{}), - "cpuinfo": fs.newInode(ctx, root, 0444, newStaticFileSetStat(cpuInfoData(k))), - "filesystems": fs.newInode(ctx, root, 0444, &filesystemsData{}), - "loadavg": fs.newInode(ctx, root, 0444, &loadavgData{}), - "sys": fs.newSysDir(ctx, root, k), - "meminfo": fs.newInode(ctx, root, 0444, &meminfoData{}), - "mounts": kernfs.NewStaticSymlink(ctx, root, linux.UNNAMED_MAJOR, fs.devMinor, fs.NextIno(), "self/mounts"), - "net": kernfs.NewStaticSymlink(ctx, root, linux.UNNAMED_MAJOR, fs.devMinor, fs.NextIno(), "self/net"), - "stat": fs.newInode(ctx, root, 0444, &statData{}), - "uptime": fs.newInode(ctx, root, 0444, &uptimeData{}), - "version": fs.newInode(ctx, root, 0444, &versionData{}), + "cmdline": fs.newInode(ctx, root, 0444, &cmdLineData{}), + "cpuinfo": fs.newInode(ctx, root, 0444, newStaticFileSetStat(cpuInfoData(k))), + "filesystems": fs.newInode(ctx, root, 0444, &filesystemsData{}), + "loadavg": fs.newInode(ctx, root, 0444, &loadavgData{}), + "sys": fs.newSysDir(ctx, root, k), + "meminfo": fs.newInode(ctx, root, 0444, &meminfoData{}), + "mounts": kernfs.NewStaticSymlink(ctx, root, linux.UNNAMED_MAJOR, fs.devMinor, fs.NextIno(), "self/mounts"), + "net": kernfs.NewStaticSymlink(ctx, root, linux.UNNAMED_MAJOR, fs.devMinor, fs.NextIno(), "self/net"), + "sentry-meminfo": fs.newInode(ctx, root, 0444, &sentryMeminfoData{}), + "stat": fs.newInode(ctx, root, 0444, &statData{}), + "uptime": fs.newInode(ctx, root, 0444, &uptimeData{}), + "version": fs.newInode(ctx, root, 0444, &versionData{}), } // If fakeCgroupControllers are provided, don't create a cgroupfs backed // /proc/cgroup as it will not match the fake controllers. diff --git a/pkg/sentry/fsimpl/proc/tasks_files.go b/pkg/sentry/fsimpl/proc/tasks_files.go index 93868fec7..a28f46a7b 100644 --- a/pkg/sentry/fsimpl/proc/tasks_files.go +++ b/pkg/sentry/fsimpl/proc/tasks_files.go @@ -17,6 +17,7 @@ package proc import ( "bytes" "fmt" + "runtime" "strconv" "gvisor.dev/gvisor/pkg/abi/linux" @@ -420,3 +421,29 @@ func kernelVersion(ctx context.Context) kernel.Version { } return init.Leader().SyscallTable().Version } + +// sentryMeminfoData implements vfs.DynamicBytesSource for /proc/sentry-meminfo. +// +// +stateify savable +type sentryMeminfoData struct { + dynamicBytesFileSetAttr +} + +var _ dynamicInode = (*sentryMeminfoData)(nil) + +// Generate implements vfs.DynamicBytesSource.Generate. +func (*sentryMeminfoData) Generate(ctx context.Context, buf *bytes.Buffer) error { + var sentryMeminfo runtime.MemStats + runtime.ReadMemStats(&sentryMeminfo) + + fmt.Fprintf(buf, "Alloc: %8d kB\n", sentryMeminfo.Alloc/1024) + fmt.Fprintf(buf, "TotalAlloc: %8d kB\n", sentryMeminfo.TotalAlloc/1024) + fmt.Fprintf(buf, "Sys: %8d kB\n", sentryMeminfo.Sys/1024) + fmt.Fprintf(buf, "Mallocs: %8d\n", sentryMeminfo.Mallocs) + fmt.Fprintf(buf, "Frees: %8d\n", sentryMeminfo.Frees) + fmt.Fprintf(buf, "Live Objects: %8d\n", sentryMeminfo.Mallocs-sentryMeminfo.Frees) + fmt.Fprintf(buf, "HeapAlloc: %8d kB\n", sentryMeminfo.HeapAlloc/1024) + fmt.Fprintf(buf, "HeapSys: %8d kB\n", sentryMeminfo.HeapSys/1024) + fmt.Fprintf(buf, "HeapObjects: %8d\n", sentryMeminfo.HeapObjects) + return nil +} diff --git a/pkg/sentry/fsimpl/proc/tasks_test.go b/pkg/sentry/fsimpl/proc/tasks_test.go index 14f806c3c..ca5e46d56 100644 --- a/pkg/sentry/fsimpl/proc/tasks_test.go +++ b/pkg/sentry/fsimpl/proc/tasks_test.go @@ -47,19 +47,20 @@ var ( var ( tasksStaticFiles = map[string]testutil.DirentType{ - "cmdline": linux.DT_REG, - "cpuinfo": linux.DT_REG, - "filesystems": linux.DT_REG, - "loadavg": linux.DT_REG, - "meminfo": linux.DT_REG, - "mounts": linux.DT_LNK, - "net": linux.DT_LNK, - "self": linux.DT_LNK, - "stat": linux.DT_REG, - "sys": linux.DT_DIR, - "thread-self": linux.DT_LNK, - "uptime": linux.DT_REG, - "version": linux.DT_REG, + "cmdline": linux.DT_REG, + "cpuinfo": linux.DT_REG, + "filesystems": linux.DT_REG, + "loadavg": linux.DT_REG, + "meminfo": linux.DT_REG, + "mounts": linux.DT_LNK, + "net": linux.DT_LNK, + "self": linux.DT_LNK, + "sentry-meminfo": linux.DT_REG, + "stat": linux.DT_REG, + "sys": linux.DT_DIR, + "thread-self": linux.DT_LNK, + "uptime": linux.DT_REG, + "version": linux.DT_REG, } tasksStaticFilesNextOffs = map[string]int64{ "self": selfLink.NextOff, diff --git a/test/syscalls/linux/proc.cc b/test/syscalls/linux/proc.cc index 52921334e..e3fe2bc08 100644 --- a/test/syscalls/linux/proc.cc +++ b/test/syscalls/linux/proc.cc @@ -1337,6 +1337,28 @@ TEST(ProcMeminfo, ContainsBasicFields) { ContainsRegex(R"(MemFree:\s+[0-9]+ kB)"))); } +TEST(ProcSentryMeminfo, ContainsFieldsAndEndsWithNewline) { + SKIP_IF(!IsRunningOnGvisor()); + + std::string proc_sentry_meminfo = + ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/sentry-meminfo")); + + // Assert that all expected fields are present. + EXPECT_THAT(proc_sentry_meminfo, + AllOf(ContainsRegex(R"(Alloc:\s+[0-9]+ kB)"), + ContainsRegex(R"(TotalAlloc:\s+[0-9]+ kB)"), + ContainsRegex(R"(Sys:\s+[0-9]+ kB)"), + ContainsRegex(R"(Mallocs:\s+[0-9]+)"), + ContainsRegex(R"(Frees:\s+[0-9]+)"), + ContainsRegex(R"(Live Objects:\s+[0-9]+)"), + ContainsRegex(R"(HeapAlloc:\s+[0-9]+ kB)"), + ContainsRegex(R"(HeapSys:\s+[0-9]+ kB)"), + ContainsRegex(R"(HeapObjects:\s+[0-9]+)"))); + + // Assert that /proc/sentry-meminfo ends with a new line. + EXPECT_EQ(proc_sentry_meminfo.back(), '\n'); +} + TEST(ProcStat, ContainsBasicFields) { std::string proc_stat = ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/stat"));