package main import ( "sync" "fmt" "os" "bufio" "strings" "strconv" ) func handleErr(err error) { if err != nil { panic(err) } } type pidinfo struct { VmSwap int Cgroup string } func in_array(needle string, haystack []string) bool { for _, straw := range haystack { if needle == straw { return true } } return false } func main() { dir, err := os.Open("/proc") handleErr(err) fileinfos, err := dir.Readdir(-1) handleErr(err) ch := make(chan pidinfo) dat := make(map[string]int) var consumers sync.WaitGroup consumers.Add(1) go func() { for info := range ch { cur, _ := dat[info.Cgroup] dat[info.Cgroup] = cur + info.VmSwap } consumers.Done() }() var producers sync.WaitGroup for _, fileinfo := range fileinfos { if pid, err := strconv.Atoi(fileinfo.Name()); fileinfo.IsDir() && err == nil { producers.Add(1) go func(pid int) { defer producers.Done() statusFile, err := os.Open(fmt.Sprintf("/proc/%d/status", pid)) if err != nil { return } cgroupFile, err := os.Open(fmt.Sprintf("/proc/%d/cgroup", pid)) buf := bufio.NewScanner(statusFile) for buf.Scan() { line := buf.Text() if strings.HasPrefix(line, "VmSwap:") { swap, err := strconv.Atoi(strings.TrimSpace(strings.TrimSuffix(strings.TrimPrefix(line, "VmSwap:"), "kB"))) if err != nil || swap == 0 { return } buf := bufio.NewScanner(cgroupFile) for buf.Scan() { parts := strings.SplitN(buf.Text(), ":", 3) if len(parts) != 3 { continue } heir := parts[0] controllers := strings.Split(parts[1], ",") cgroup := parts[2] if heir == "0" || in_array("name=systemd", controllers) { ch <- pidinfo{VmSwap: swap, Cgroup: cgroup} return } } return } } }(pid) } } producers.Wait() close(ch) consumers.Wait() for cgroup, vmswap := range dat { fmt.Println(vmswap, "kB", cgroup) } } // grep VmSwap /proc/*/status|sed -rn 's|^/proc/([0-9]+)/status:VmSwap:\s*(\S+ \S+)$|\2 \1|p'|while read -r kb _ pid; do echo "$kb $(systemctl status "$pid"|head -n1|awk '{print $2}')"; done