diff options
Diffstat (limited to 'test/internal/testsuite/proc.go')
| -rw-r--r-- | test/internal/testsuite/proc.go | 353 |
1 files changed, 0 insertions, 353 deletions
diff --git a/test/internal/testsuite/proc.go b/test/internal/testsuite/proc.go deleted file mode 100644 index e7ef1aed..00000000 --- a/test/internal/testsuite/proc.go +++ /dev/null @@ -1,353 +0,0 @@ -package testsuite - -import ( - "bytes" - "errors" - "fmt" - "os" - "path/filepath" - "strconv" - "strings" - "syscall" - "unsafe" - - "hakurei.app/fhs" -) - -// Stat represents status information read from /proc/pid/stat. -type Stat struct { - // The process ID. - PID int - // The filename of the executable, with parenthesis stripped. - Comm string - // One of the following characters, indicating process state: - // - // R Running - // - // S Sleeping in an interruptible wait - // - // D Waiting in uninterruptible disk sleep - // - // Z Zombie - // - // T Stopped (on a signal) or (before Linux - // 2.6.33) trace stopped - // - // t Tracing stop (Linux 2.6.33 onward) - // - // W Paging (only before Linux 2.6.0) - // - // X Dead (from Linux 2.6.0 onward) - // - // x Dead (Linux 2.6.33 to 3.13 only) - // - // K Wakekill (Linux 2.6.33 to 3.13 only) - // - // W Waking (Linux 2.6.33 to 3.13 only) - // - // P Parked (Linux 3.9 to 3.13 only) - // - // I Idle (Linux 4.14 onward) - State byte - // The process ID of the parent of this process. - PPID int - // The process group ID of the process. - PGRP int - // The session ID of the process. - Session int - // The controlling terminal of the process. - TTYNR int - // The ID of the foreground process group of the controlling terminal of the - // process. - TPGID int - // The kernel flags word of the process. For bit meanings, see the PF_* - // defines in the Linux kernel source file include/linux/sched.h. - Flags uint - // The number of minor faults the process has made which have not required - // loading a memory page from disk. - MinFlt uint - // The number of minor faults that the process's waited-for children have - // made. - CMinFlt uint - // The number of major faults the process has made which have required - // loading a memory page from disk. - MajFlt uint - // The number of major faults that the process's waited-for children have - // made. - CMajFlt uint - // Amount of time that this process has been scheduled in user mode, - // measured in clock ticks. - UTime uint - // Amount of time that this process has been scheduled in kernel mode, - // measured in clock ticks. - STime uint - // Amount of time that this process's waited-for children have been - // scheduled in user mode, measured in clock ticks. - CUTime int - // Amount of time that this process's waited-for children have been - // scheduled in kernel mode, measured in clock ticks. - CSTime int - // For processes running a real-time scheduling policy, this is the negated - // scheduling priority, minus one. - Priority int - // The nice value, a value in the range 19 (low priority) to -20 (high - // priority). - Nice int - // Number of threads in this process. - NumThreads int - - // unmaintained field: itrealvalue - - // The time the process started after system boot. Since Linux 2.6, the - // value is expressed in clock ticks. - StartTime uint64 - // Virtual memory size in bytes. - VSize uint - // Resident set size in pages. - RSS int - // Soft limit in bytes on the rss of the process. - RSSLim uint64 - // The address above which program text can run. - StartCode uint64 - // The address below which program text can run. - EndCode uint64 - // The address of the start (i.e., bottom) of the stack. - StartStack uint64 - // The current value of ESP (stack pointer), as found in the kernel stack - // page for the process. - KSTKESP uint64 - // The current EIP (instruction pointer). - KSTKEIP uint64 - - // obsolete fields: signal, blocked, sigignore, sigcatch - - // This is the "channel" in which the process is waiting. It is the address - // of a location in the kernel where the process is sleeping. - WChan uint64 - - // unmaintained fields: nswap, cnswap - - // Signal to be sent to parent when we die. - ExitSignal int - // CPU number last executed on. - Processor int - // Real-time scheduling priority, a number in the range 1 to 99 for processes - // scheduled under a real-time policy, or 0, for non-real-time processes. - RTPriority uint - // Scheduling policy (see sched_setscheduler(2)). Decode using the SCHED_* - // constants in linux/sched.h. - Policy uint - // Aggregated block I/O delays, measured in clock ticks (centiseconds). - DelayAcctBlkIOTicks uint64 - // Guest time of the process (time spent running a virtual CPU for a guest - // operating system), measured in clock ticks. - GuestTime int - // Guest time of the process's children, measured in clock ticks. - CGuestTime int -} - -// Executable is like [os.Executable], but for the process referred to by s. -func (s *Stat) Executable() (string, error) { - path, err := os.Readlink(filepath.Join(fhs.Proc, strconv.Itoa(s.PID), "exe")) - - // When the executable has been deleted then Readlink returns a - // path appended with " (deleted)". - return strings.TrimSuffix(path, " (deleted)"), err -} - -// Stat populates stat with the proc filesystem entry referred to by s. -func (s *Stat) Stat(stat *syscall.Stat_t) (err error) { - err = syscall.Stat(filepath.Join(fhs.Proc, strconv.Itoa(s.PID)), stat) - if err != nil { - err = os.NewSyscallError("stat", err) - } - return -} - -// Args reads arguments of the process referred to by s. -func (s *Stat) Args() ([]string, error) { - p, err := os.ReadFile(filepath.Join(fhs.Proc, strconv.Itoa(s.PID), "cmdline")) - if err != nil { - return nil, err - } - a := bytes.Split(p, []byte{0}) - if len(a) > 0 && len(a[len(a)-1]) == 0 { - a = a[:len(a)-1] - } - - args := make([]string, len(a)) - for i, arg := range a { - args[i] = unsafe.String(unsafe.SliceData(arg), len(arg)) - } - return args, nil -} - -// ErrBadDelimiters is returned by [Stat.UnmarshalText] if one or both bytes of -// the comm delimiter pair were missing or misplaced. -var ErrBadDelimiters = errors.New("missing comm delimiters") - -// UnmarshalText populates the structure pointed to by s from text. -func (s *Stat) UnmarshalText(text []byte) (err error) { - var ( - discard uint64 - _uint64 = &discard - _int64 = (*int64)(unsafe.Pointer(&discard)) - - ld = bytes.Index(text, []byte("(")) - rd = bytes.LastIndex(text, []byte(")")) - ) - - if ld <= 0 || rd < 0 { - return ErrBadDelimiters - } - - if s.PID, err = strconv.Atoi( - unsafe.String(unsafe.SliceData(text), ld-1), - ); err != nil { - return - } - - s.Comm = string(text[ld+1 : rd]) - - var ( - n int - - state string - ) - n, err = fmt.Fscan( - bytes.NewBuffer(text[rd+2:]), - &state, - &s.PPID, - &s.PGRP, - &s.Session, - &s.TTYNR, - &s.TPGID, - &s.Flags, - &s.MinFlt, - &s.CMinFlt, - &s.MajFlt, - &s.CMajFlt, - &s.UTime, - &s.STime, - &s.CUTime, - &s.CSTime, - &s.Priority, - &s.Nice, - &s.NumThreads, - _int64, - &s.StartTime, - &s.VSize, - &s.RSS, - &s.RSSLim, - &s.StartCode, - &s.EndCode, - &s.StartStack, - &s.KSTKESP, - &s.KSTKEIP, - _uint64, - _uint64, - _uint64, - _uint64, - &s.WChan, - _uint64, - _uint64, - &s.ExitSignal, - &s.Processor, - &s.RTPriority, - &s.Policy, - &s.DelayAcctBlkIOTicks, - &s.GuestTime, - &s.CGuestTime, - ) - if err != nil { - err = fmt.Errorf("field %d: %w", n, err) - } else if len(state) != 1 { - err = fmt.Errorf("invalid state %q", state) - } else { - s.State = state[0] - } - return -} - -// A StatScanner continuously scans the proc filesystem for process status -// information in /proc/pid/stat. -type StatScanner struct { - // Current entry. - stat Stat - // Cached top-level /proc entries. - dents []os.DirEntry - // Current progress through dents. - i int - // Whether the previous call to Scan had repopulated dents. - wrapped bool - // First stored error: a non-nil err disables the scanner. - err error -} - -// IsNotExist returns whether an error is [os.ErrNotExist] or ESRCH. -func IsNotExist(err error) bool { - return errors.Is(err, os.ErrNotExist) || errors.Is(err, syscall.ESRCH) -} - -// Scan reads a process status information entry. It returns false if an -// unrecoverable error is encountered, after which Scan no longer scans new -// entries. -func (s *StatScanner) Scan() bool { - if s.err != nil { - return false - } - - if s.wrapped = s.i == len(s.dents); s.wrapped { - if s.dents, s.err = os.ReadDir(fhs.Proc); s.err != nil { - return false - } - s.i = 0 - if len(s.dents) == 0 { - s.err = syscall.ENOTRECOVERABLE - return false - } - } - - for s.i < len(s.dents) { - dent := s.dents[s.i] - s.i++ - if !dent.IsDir() { - continue - } - - pid, err := strconv.Atoi(dent.Name()) - if err != nil { - continue - } - - var p []byte - p, err = os.ReadFile(filepath.Join(fhs.Proc, dent.Name(), "stat")) - if err != nil { - if IsNotExist(err) { - continue - } - s.err = err - return false - } - - s.err = s.stat.UnmarshalText(p) - if s.err == nil && pid != s.stat.PID { - s.err = fmt.Errorf( - "bad status information: dent=%d, stat=%d", - pid, s.stat.PID, - ) - } - return s.err == nil - } - return s.Scan() -} - -// Stat returns the address of the [Stat] structure populated by the last call -// to Scan. -func (s *StatScanner) Stat() *Stat { return &s.stat } - -// Err returns the stored error value. -func (s *StatScanner) Err() error { return s.err } - -// Repopulated returns whether the last Scan call had re-read the proc filesystem. -func (s *StatScanner) Repopulated() bool { return s.wrapped } |
