1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
|
// Copyright (C) 2017 Luke Shumaker <lukeshu@sbcglobal.net>
// Command cgswap displays which cgroups are using swap space.
package main
import (
"bufio"
"fmt"
"os"
"sort"
"strconv"
"strings"
"sync"
)
// 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 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 {
return true
}
}
return false
}
func main() {
dir, err := os.Open("/proc")
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
fileinfos, err := dir.Readdir(-1)
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
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 {
idx, ok := cgroup2idx[info.Cgroup]
if ok {
infos[idx].VmSwap += info.VmSwap
} else {
cgroup2idx[info.Cgroup] = len(infos)
infos = append(infos, info)
}
}
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 <- cginfo{VmSwap: swap, Cgroup: cgroup}
return
}
}
return
}
}
}(pid)
}
}
producers.Wait()
close(ch)
consumers.Wait()
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")
}
|