mirror of
https://github.com/hanzoai/visor.git
synced 2026-09-24 17:04:14 +00:00
Migrates all (except 3) seqfile implementations to the vfs.DynamicBytesSource interface. There should not be any change in functionality due to this migration itself. Please note that the following seqfile implementations have not been migrated: - /proc/filesystems in proc/filesystems.go - /proc/[pid]/mountinfo in proc/mounts.go - /proc/[pid]/mounts in proc/mounts.go This is because these depend on pending changes in /pkg/senty/vfs. PiperOrigin-RevId: 263880719
262 lines
8.3 KiB
Go
262 lines
8.3 KiB
Go
// Copyright 2019 The gVisor Authors.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package proc
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
|
|
"gvisor.dev/gvisor/pkg/abi/linux"
|
|
"gvisor.dev/gvisor/pkg/sentry/context"
|
|
"gvisor.dev/gvisor/pkg/sentry/kernel"
|
|
"gvisor.dev/gvisor/pkg/sentry/limits"
|
|
"gvisor.dev/gvisor/pkg/sentry/mm"
|
|
"gvisor.dev/gvisor/pkg/sentry/usage"
|
|
"gvisor.dev/gvisor/pkg/sentry/usermem"
|
|
"gvisor.dev/gvisor/pkg/sentry/vfs"
|
|
)
|
|
|
|
// mapsCommon is embedded by mapsData and smapsData.
|
|
type mapsCommon struct {
|
|
t *kernel.Task
|
|
}
|
|
|
|
// mm gets the kernel task's MemoryManager. No additional reference is taken on
|
|
// mm here. This is safe because MemoryManager.destroy is required to leave the
|
|
// MemoryManager in a state where it's still usable as a DynamicBytesSource.
|
|
func (md *mapsCommon) mm() *mm.MemoryManager {
|
|
var tmm *mm.MemoryManager
|
|
md.t.WithMuLocked(func(t *kernel.Task) {
|
|
if mm := t.MemoryManager(); mm != nil {
|
|
tmm = mm
|
|
}
|
|
})
|
|
return tmm
|
|
}
|
|
|
|
// mapsData implements vfs.DynamicBytesSource for /proc/[pid]/maps.
|
|
//
|
|
// +stateify savable
|
|
type mapsData struct {
|
|
mapsCommon
|
|
}
|
|
|
|
var _ vfs.DynamicBytesSource = (*mapsData)(nil)
|
|
|
|
// Generate implements vfs.DynamicBytesSource.Generate.
|
|
func (md *mapsData) Generate(ctx context.Context, buf *bytes.Buffer) error {
|
|
if mm := md.mm(); mm != nil {
|
|
mm.ReadMapsDataInto(ctx, buf)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// smapsData implements vfs.DynamicBytesSource for /proc/[pid]/smaps.
|
|
//
|
|
// +stateify savable
|
|
type smapsData struct {
|
|
mapsCommon
|
|
}
|
|
|
|
var _ vfs.DynamicBytesSource = (*smapsData)(nil)
|
|
|
|
// Generate implements vfs.DynamicBytesSource.Generate.
|
|
func (sd *smapsData) Generate(ctx context.Context, buf *bytes.Buffer) error {
|
|
if mm := sd.mm(); mm != nil {
|
|
mm.ReadSmapsDataInto(ctx, buf)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// +stateify savable
|
|
type taskStatData struct {
|
|
t *kernel.Task
|
|
|
|
// If tgstats is true, accumulate fault stats (not implemented) and CPU
|
|
// time across all tasks in t's thread group.
|
|
tgstats bool
|
|
|
|
// pidns is the PID namespace associated with the proc filesystem that
|
|
// includes the file using this statData.
|
|
pidns *kernel.PIDNamespace
|
|
}
|
|
|
|
var _ vfs.DynamicBytesSource = (*taskStatData)(nil)
|
|
|
|
// Generate implements vfs.DynamicBytesSource.Generate.
|
|
func (s *taskStatData) Generate(ctx context.Context, buf *bytes.Buffer) error {
|
|
fmt.Fprintf(buf, "%d ", s.pidns.IDOfTask(s.t))
|
|
fmt.Fprintf(buf, "(%s) ", s.t.Name())
|
|
fmt.Fprintf(buf, "%c ", s.t.StateStatus()[0])
|
|
ppid := kernel.ThreadID(0)
|
|
if parent := s.t.Parent(); parent != nil {
|
|
ppid = s.pidns.IDOfThreadGroup(parent.ThreadGroup())
|
|
}
|
|
fmt.Fprintf(buf, "%d ", ppid)
|
|
fmt.Fprintf(buf, "%d ", s.pidns.IDOfProcessGroup(s.t.ThreadGroup().ProcessGroup()))
|
|
fmt.Fprintf(buf, "%d ", s.pidns.IDOfSession(s.t.ThreadGroup().Session()))
|
|
fmt.Fprintf(buf, "0 0 " /* tty_nr tpgid */)
|
|
fmt.Fprintf(buf, "0 " /* flags */)
|
|
fmt.Fprintf(buf, "0 0 0 0 " /* minflt cminflt majflt cmajflt */)
|
|
var cputime usage.CPUStats
|
|
if s.tgstats {
|
|
cputime = s.t.ThreadGroup().CPUStats()
|
|
} else {
|
|
cputime = s.t.CPUStats()
|
|
}
|
|
fmt.Fprintf(buf, "%d %d ", linux.ClockTFromDuration(cputime.UserTime), linux.ClockTFromDuration(cputime.SysTime))
|
|
cputime = s.t.ThreadGroup().JoinedChildCPUStats()
|
|
fmt.Fprintf(buf, "%d %d ", linux.ClockTFromDuration(cputime.UserTime), linux.ClockTFromDuration(cputime.SysTime))
|
|
fmt.Fprintf(buf, "%d %d ", s.t.Priority(), s.t.Niceness())
|
|
fmt.Fprintf(buf, "%d ", s.t.ThreadGroup().Count())
|
|
|
|
// itrealvalue. Since kernel 2.6.17, this field is no longer
|
|
// maintained, and is hard coded as 0.
|
|
fmt.Fprintf(buf, "0 ")
|
|
|
|
// Start time is relative to boot time, expressed in clock ticks.
|
|
fmt.Fprintf(buf, "%d ", linux.ClockTFromDuration(s.t.StartTime().Sub(s.t.Kernel().Timekeeper().BootTime())))
|
|
|
|
var vss, rss uint64
|
|
s.t.WithMuLocked(func(t *kernel.Task) {
|
|
if mm := t.MemoryManager(); mm != nil {
|
|
vss = mm.VirtualMemorySize()
|
|
rss = mm.ResidentSetSize()
|
|
}
|
|
})
|
|
fmt.Fprintf(buf, "%d %d ", vss, rss/usermem.PageSize)
|
|
|
|
// rsslim.
|
|
fmt.Fprintf(buf, "%d ", s.t.ThreadGroup().Limits().Get(limits.Rss).Cur)
|
|
|
|
fmt.Fprintf(buf, "0 0 0 0 0 " /* startcode endcode startstack kstkesp kstkeip */)
|
|
fmt.Fprintf(buf, "0 0 0 0 0 " /* signal blocked sigignore sigcatch wchan */)
|
|
fmt.Fprintf(buf, "0 0 " /* nswap cnswap */)
|
|
terminationSignal := linux.Signal(0)
|
|
if s.t == s.t.ThreadGroup().Leader() {
|
|
terminationSignal = s.t.ThreadGroup().TerminationSignal()
|
|
}
|
|
fmt.Fprintf(buf, "%d ", terminationSignal)
|
|
fmt.Fprintf(buf, "0 0 0 " /* processor rt_priority policy */)
|
|
fmt.Fprintf(buf, "0 0 0 " /* delayacct_blkio_ticks guest_time cguest_time */)
|
|
fmt.Fprintf(buf, "0 0 0 0 0 0 0 " /* start_data end_data start_brk arg_start arg_end env_start env_end */)
|
|
fmt.Fprintf(buf, "0\n" /* exit_code */)
|
|
|
|
return nil
|
|
}
|
|
|
|
// statmData implements vfs.DynamicBytesSource for /proc/[pid]/statm.
|
|
//
|
|
// +stateify savable
|
|
type statmData struct {
|
|
t *kernel.Task
|
|
}
|
|
|
|
var _ vfs.DynamicBytesSource = (*statmData)(nil)
|
|
|
|
// Generate implements vfs.DynamicBytesSource.Generate.
|
|
func (s *statmData) Generate(ctx context.Context, buf *bytes.Buffer) error {
|
|
var vss, rss uint64
|
|
s.t.WithMuLocked(func(t *kernel.Task) {
|
|
if mm := t.MemoryManager(); mm != nil {
|
|
vss = mm.VirtualMemorySize()
|
|
rss = mm.ResidentSetSize()
|
|
}
|
|
})
|
|
|
|
fmt.Fprintf(buf, "%d %d 0 0 0 0 0\n", vss/usermem.PageSize, rss/usermem.PageSize)
|
|
return nil
|
|
}
|
|
|
|
// statusData implements vfs.DynamicBytesSource for /proc/[pid]/status.
|
|
//
|
|
// +stateify savable
|
|
type statusData struct {
|
|
t *kernel.Task
|
|
pidns *kernel.PIDNamespace
|
|
}
|
|
|
|
var _ vfs.DynamicBytesSource = (*statusData)(nil)
|
|
|
|
// Generate implements vfs.DynamicBytesSource.Generate.
|
|
func (s *statusData) Generate(ctx context.Context, buf *bytes.Buffer) error {
|
|
fmt.Fprintf(buf, "Name:\t%s\n", s.t.Name())
|
|
fmt.Fprintf(buf, "State:\t%s\n", s.t.StateStatus())
|
|
fmt.Fprintf(buf, "Tgid:\t%d\n", s.pidns.IDOfThreadGroup(s.t.ThreadGroup()))
|
|
fmt.Fprintf(buf, "Pid:\t%d\n", s.pidns.IDOfTask(s.t))
|
|
ppid := kernel.ThreadID(0)
|
|
if parent := s.t.Parent(); parent != nil {
|
|
ppid = s.pidns.IDOfThreadGroup(parent.ThreadGroup())
|
|
}
|
|
fmt.Fprintf(buf, "PPid:\t%d\n", ppid)
|
|
tpid := kernel.ThreadID(0)
|
|
if tracer := s.t.Tracer(); tracer != nil {
|
|
tpid = s.pidns.IDOfTask(tracer)
|
|
}
|
|
fmt.Fprintf(buf, "TracerPid:\t%d\n", tpid)
|
|
var fds int
|
|
var vss, rss, data uint64
|
|
s.t.WithMuLocked(func(t *kernel.Task) {
|
|
if fdTable := t.FDTable(); fdTable != nil {
|
|
fds = fdTable.Size()
|
|
}
|
|
if mm := t.MemoryManager(); mm != nil {
|
|
vss = mm.VirtualMemorySize()
|
|
rss = mm.ResidentSetSize()
|
|
data = mm.VirtualDataSize()
|
|
}
|
|
})
|
|
fmt.Fprintf(buf, "FDSize:\t%d\n", fds)
|
|
fmt.Fprintf(buf, "VmSize:\t%d kB\n", vss>>10)
|
|
fmt.Fprintf(buf, "VmRSS:\t%d kB\n", rss>>10)
|
|
fmt.Fprintf(buf, "VmData:\t%d kB\n", data>>10)
|
|
fmt.Fprintf(buf, "Threads:\t%d\n", s.t.ThreadGroup().Count())
|
|
creds := s.t.Credentials()
|
|
fmt.Fprintf(buf, "CapInh:\t%016x\n", creds.InheritableCaps)
|
|
fmt.Fprintf(buf, "CapPrm:\t%016x\n", creds.PermittedCaps)
|
|
fmt.Fprintf(buf, "CapEff:\t%016x\n", creds.EffectiveCaps)
|
|
fmt.Fprintf(buf, "CapBnd:\t%016x\n", creds.BoundingCaps)
|
|
fmt.Fprintf(buf, "Seccomp:\t%d\n", s.t.SeccompMode())
|
|
return nil
|
|
}
|
|
|
|
// ioUsage is the /proc/<pid>/io and /proc/<pid>/task/<tid>/io data provider.
|
|
type ioUsage interface {
|
|
// IOUsage returns the io usage data.
|
|
IOUsage() *usage.IO
|
|
}
|
|
|
|
// +stateify savable
|
|
type ioData struct {
|
|
ioUsage
|
|
}
|
|
|
|
var _ vfs.DynamicBytesSource = (*ioData)(nil)
|
|
|
|
// Generate implements vfs.DynamicBytesSource.Generate.
|
|
func (i *ioData) Generate(ctx context.Context, buf *bytes.Buffer) error {
|
|
io := usage.IO{}
|
|
io.Accumulate(i.IOUsage())
|
|
|
|
fmt.Fprintf(buf, "char: %d\n", io.CharsRead)
|
|
fmt.Fprintf(buf, "wchar: %d\n", io.CharsWritten)
|
|
fmt.Fprintf(buf, "syscr: %d\n", io.ReadSyscalls)
|
|
fmt.Fprintf(buf, "syscw: %d\n", io.WriteSyscalls)
|
|
fmt.Fprintf(buf, "read_bytes: %d\n", io.BytesRead)
|
|
fmt.Fprintf(buf, "write_bytes: %d\n", io.BytesWritten)
|
|
fmt.Fprintf(buf, "cancelled_write_bytes: %d\n", io.BytesWriteCancelled)
|
|
return nil
|
|
}
|