summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cgswap.go70
1 files changed, 50 insertions, 20 deletions
diff --git a/cgswap.go b/cgswap.go
index 3491f17..6fab8ae 100644
--- a/cgswap.go
+++ b/cgswap.go
@@ -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