Skip to content
quarry Artifact [38492545af]
Login

Artifact [38492545af]

Artifact 38492545aff198be72ee19057efe8607632231593afef40981278c858ddc370b:


package api

import (
	"bytes"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"time"
)

// clockTicks is USER_HZ, which /proc's start times are counted in. It
// is 100 on every Linux this forge runs on; reading it needs cgo.
const clockTicks = 100

// childProcsIn lists a process's own zit children from /proc: verb,
// start time and resident memory. A process that ends mid-read is
// skipped, which is the truth by the time anyone reads the panel.
// childProcsIn reads a /proc tree rooted at root for the zit children
// of self. Split from childProcs so a test can hand it a tree of its own.
func childProcsIn(root string, self int) []childProc {
	boot := bootTime(root)
	if boot.IsZero() {
		return nil
	}
	ents, err := os.ReadDir(root)
	if err != nil {
		return nil
	}
	page := uint64(os.Getpagesize())
	var out []childProc
	for _, e := range ents {
		pid, err := strconv.Atoi(e.Name())
		if err != nil {
			continue
		}
		dir := filepath.Join(root, e.Name())
		stat, err := os.ReadFile(filepath.Join(dir, "stat"))
		if err != nil {
			continue
		}
		// The command name is parenthesised and may hold spaces, so the
		// fields are read after its LAST closing parenthesis.
		i := bytes.LastIndexByte(stat, ')')
		if i < 0 {
			continue
		}
		f := strings.Fields(string(stat[i+1:]))
		// f[0] is the state, f[1] the parent, f[19] the start time.
		if len(f) < 20 {
			continue
		}
		if ppid, _ := strconv.Atoi(f[1]); ppid != self {
			continue
		}
		args := bytes.Split(bytes.TrimRight(mustRead(filepath.Join(dir, "cmdline")), "\x00"), []byte{0})
		if len(args) < 2 || filepath.Base(string(args[0])) != "zit" {
			continue
		}
		ticks, _ := strconv.ParseUint(f[19], 10, 64)
		p := childProc{PID: pid, Verb: string(args[1]),
			Start: boot.Add(time.Duration(ticks) * time.Second / clockTicks)}
		if sm := strings.Fields(string(mustRead(filepath.Join(dir, "statm")))); len(sm) > 1 {
			if res, err := strconv.ParseUint(sm[1], 10, 64); err == nil {
				p.RSS = res * page
			}
		}
		out = append(out, p)
	}
	return out
}

func mustRead(path string) []byte {
	b, _ := os.ReadFile(path)
	return b
}

// bootTime is when the machine booted, from /proc/stat's btime.
func bootTime(root string) time.Time {
	b, err := os.ReadFile(filepath.Join(root, "stat"))
	if err != nil {
		return time.Time{}
	}
	for _, line := range strings.Split(string(b), "\n") {
		if rest, ok := strings.CutPrefix(line, "btime "); ok {
			if s, err := strconv.ParseInt(strings.TrimSpace(rest), 10, 64); err == nil {
				return time.Unix(s, 0)
			}
		}
	}
	return time.Time{}
}