diff options
-rw-r--r-- | cgswap.go | 70 |
1 files changed, 50 insertions, 20 deletions
@@ -1,25 +1,34 @@ +// Copyright (C) 2017 Luke Shumaker <lukeshu@sbcglobal.net> + +// Command cgswap displays which cgroups are using swap space. package main import ( - "sync" + "bufio" "fmt" "os" - "bufio" - "strings" + "sort" "strconv" + "strings" + "sync" ) -func handleErr(err error) { - if err != nil { - panic(err) - } -} +// You may be wondering why this program is so parallel. Needless +// complexity, right? Well, because processes may be very short +// lived, we want to read each process's info as soon as possible, +// before it goes away. -type pidinfo struct { - VmSwap int +type cginfo struct { Cgroup string + VmSwap int } +type cginfos []cginfo + +func (l cginfos) Len() int { return len(l) } +func (l cginfos) Less(i, j int) bool { return l[i].VmSwap < l[j].VmSwap } +func (l cginfos) Swap(i, j int) { l[i], l[j] = l[j], l[i] } + func in_array(needle string, haystack []string) bool { for _, straw := range haystack { if needle == straw { @@ -31,18 +40,30 @@ func in_array(needle string, haystack []string) bool { func main() { dir, err := os.Open("/proc") - handleErr(err) + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } fileinfos, err := dir.Readdir(-1) - handleErr(err) + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } - ch := make(chan pidinfo) - dat := make(map[string]int) + ch := make(chan cginfo) + var infos cginfos + cgroup2idx := 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 + idx, ok := cgroup2idx[info.Cgroup] + if ok { + infos[idx].VmSwap += info.VmSwap + } else { + cgroup2idx[info.Cgroup] = len(infos) + infos = append(infos, info) + } } consumers.Done() }() @@ -77,7 +98,7 @@ func main() { controllers := strings.Split(parts[1], ",") cgroup := parts[2] if heir == "0" || in_array("name=systemd", controllers) { - ch <- pidinfo{VmSwap: swap, Cgroup: cgroup} + ch <- cginfo{VmSwap: swap, Cgroup: cgroup} return } } @@ -90,8 +111,17 @@ func main() { producers.Wait() close(ch) consumers.Wait() - for cgroup, vmswap := range dat { - fmt.Println(vmswap, "kB", cgroup) + + sort.Sort(infos) + + total := 0 + for _, info := range infos { + total += info.VmSwap + } + vmswap_width := len(strconv.Itoa(total)) + + for _, info := range infos { + fmt.Printf("%[1]*d kB %s\n", vmswap_width, info.VmSwap, info.Cgroup) } + fmt.Printf("%[1]*d kB %s\n", vmswap_width, total, "total") } -// 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 |