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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
|
package main
import (
"container/list"
"fmt"
"github.com/buetow/gstat/diskstats"
"github.com/buetow/gstat/process"
"golang.org/x/crypto/ssh/terminal"
"log"
"os"
"os/signal"
"syscall"
"time"
)
var interval time.Duration
var footer string
type twoP struct {
first process.Process
second process.Process
diff int
}
type mapP map[string]twoP
func timedGather(timerChan <-chan bool, dRxChan chan<- diskstats.Diskstats, pRxChan chan<- process.Process) {
for {
switch <-timerChan {
case false:
{
break
}
case true:
{
go diskstats.Gather(dRxChan)
go process.Gather(pRxChan)
}
}
}
close(dRxChan)
close(pRxChan)
}
func receiveD(dRxChan <-chan diskstats.Diskstats) {
for d := range dRxChan {
//diskstats.Print()
// Implemented later
_ = d
}
}
func sortP(lastP *mapP) *list.List {
remove := list.New()
sorted := list.New()
for _, val := range *lastP {
nowTimestamp := int32(time.Now().Unix())
if val.first.Timestamp+int32(interval)*2 < nowTimestamp {
// Schedule remove obsolete pids from lastP
remove.PushBack(val.first.Id)
} else if val.diff > 0 {
// Insertion sort
if sorted.Len() > 0 {
for e := sorted.Front(); e != nil; e = e.Next() {
diff := e.Value.(twoP).diff
if diff < val.diff {
//fmt.Printf("Inserting %d before %d\n", val.diff, diff)
sorted.InsertBefore(val, e)
break
}
}
} else {
sorted.PushFront(val)
}
}
}
// Rremove obsolete pids from lastP
for e := remove.Front(); e != nil; e = e.Next() {
id := e.Value.(twoP).first.Id
//fmt.Println("Removing stale process: " + id)
delete(*lastP, id)
}
return sorted
}
func printP(sortedP *list.List) {
tWidth, tHeight, err := terminal.GetSize(0)
if err != nil {
log.Fatal(err)
}
// Clear the screen + print header
fmt.Println("\033[H\033[2J")
fmt.Printf("%5s %5s %s\n", "Value", "PID", "Command")
// Print the results
row := 2
for e := sortedP.Front(); e != nil; e = e.Next() {
row++
if row > tHeight {
break
}
val := e.Value.(twoP)
outstr := fmt.Sprintf("%5d %5d %s", val.diff, val.first.Pid, val.first.Cmdline)
l := len(outstr)
if l > tWidth {
l = tWidth
}
fmt.Printf("%s\n", outstr[0:l])
}
// Fill up the other rows + print footer
for ; row < tHeight; row++ {
fmt.Println()
}
fmt.Printf(footer)
}
func receiveP(pRxChan <-chan process.Process) {
lastP := make(mapP)
flag := false
makeDiff := func(first, second process.Process) twoP {
// TODO: make "syscr" choosable/configurable
firstVal := first.Count["syscr"]
secondVal := second.Count["syscr"]
diff := firstVal - secondVal
if diff < 0 {
diff = -diff
}
return twoP{first, second, diff}
}
for p := range pRxChan {
if p.Last {
if flag {
printP(sortP(&lastP))
}
flag = !flag
} else {
if val, ok := lastP[p.Id]; ok {
if flag {
lastP[p.Id] = makeDiff(val.first, p)
} else {
lastP[p.Id] = makeDiff(p, val.second)
}
} else {
lastP[p.Id] = twoP{first: p}
}
}
}
}
func main() {
timerChan := make(chan bool)
dRxChan := make(chan diskstats.Diskstats)
pRxChan := make(chan process.Process)
interval = 2
footer = "gstat 0.1 (C) 2015 Paul Buetow <http://github.com/buetow/gstat>"
go timedGather(timerChan, dRxChan, pRxChan)
go receiveD(dRxChan)
go receiveP(pRxChan)
termChan := make(chan os.Signal, 1)
signal.Notify(termChan, os.Interrupt)
signal.Notify(termChan, syscall.SIGTERM)
go func() {
<-termChan
timerChan <- false
fmt.Println("Good bye")
os.Exit(1)
}()
for {
timerChan <- true
time.Sleep(time.Second * interval)
}
}
|