xmtop/widgets/proc.go
2018-02-19 02:00:21 -08:00

303 lines
5.4 KiB
Go

package widgets
import (
"sort"
"strconv"
"time"
ui "github.com/cjbassi/gotop/termui"
cpu "github.com/shirou/gopsutil/cpu"
ps "github.com/shirou/gopsutil/process"
)
const (
DOWN = "▼"
UP = "▲"
)
// Process represents each process
type Process struct {
PID int32
Command string
CPU float64
Mem float32
}
// Proc is the widget
type Proc struct {
*ui.Table
cpuCount int
interval time.Duration
sortMethod string
groupedProcs []Process
ungroupedProcs []Process
group bool
KeyPressed chan bool
}
// Creates a new Proc widget
func NewProc(loaded, keyPressed chan bool) *Proc {
cpuCount, _ := cpu.Counts(false)
p := &Proc{
Table: ui.NewTable(),
interval: time.Second,
cpuCount: cpuCount,
sortMethod: "c",
group: true,
KeyPressed: keyPressed,
}
p.Label = "Process List"
p.UniqueCol = 0
if p.group {
p.UniqueCol = 1
}
p.keyBinds()
go func() {
p.update()
loaded <- true
}()
ticker := time.NewTicker(p.interval)
go func() {
for range ticker.C {
p.update()
}
}()
return p
}
func (p *Proc) update() {
psProcs, _ := ps.Processes()
processes := make([]Process, len(psProcs))
for i, pr := range psProcs {
pid := pr.Pid
command, _ := pr.Name()
cpu, _ := pr.CPUPercent()
mem, _ := pr.MemoryPercent()
processes[i] = Process{
pid,
command,
cpu / float64(p.cpuCount),
mem,
}
}
p.ungroupedProcs = processes
p.groupedProcs = Group(processes)
p.Sort()
}
// Sort sorts either the grouped or ungrouped []Process based on the sortMethod
func (p *Proc) Sort() {
p.Header = []string{"Count", "Command", "CPU%", "Mem%"}
if !p.group {
p.Header[0] = "PID"
}
processes := &p.ungroupedProcs
if p.group {
processes = &p.groupedProcs
}
switch p.sortMethod {
case "c":
sort.Sort(sort.Reverse(ProcessByCPU(*processes)))
p.Header[2] += DOWN
case "p":
if p.group {
sort.Sort(sort.Reverse(ProcessByPID(*processes)))
} else {
sort.Sort(ProcessByPID(*processes))
}
p.Header[0] += DOWN
case "m":
sort.Sort(sort.Reverse(ProcessByMem(*processes)))
p.Header[3] += DOWN
}
p.Rows = FieldsToStrings(*processes)
}
func (p *Proc) keyBinds() {
ui.On("MouseLeft", func(e ui.Event) {
p.Click(e.MouseX, e.MouseY)
ui.Render(p)
})
ui.On("MouseWheelUp", "MouseWheelDown", func(e ui.Event) {
switch e.Key {
case "MouseWheelDown":
p.Down()
case "MouseWheelUp":
p.Up()
}
p.KeyPressed <- true
})
ui.On("<up>", "<down>", func(e ui.Event) {
switch e.Key {
case "<up>":
p.Up()
case "<down>":
p.Down()
}
p.KeyPressed <- true
})
viKeys := []string{"j", "k", "gg", "G", "C-d", "C-u", "C-f", "C-b"}
ui.On(viKeys, func(e ui.Event) {
switch e.Key {
case "j":
p.Down()
case "k":
p.Up()
case "gg":
p.Top()
case "G":
p.Bottom()
case "C-d":
p.HalfPageDown()
case "C-u":
p.HalfPageUp()
case "C-f":
p.PageDown()
case "C-b":
p.PageUp()
}
p.KeyPressed <- true
})
ui.On("dd", func(e ui.Event) {
p.Kill()
})
ui.On("<tab>", func(e ui.Event) {
p.group = !p.group
if p.group {
p.UniqueCol = 1
} else {
p.UniqueCol = 0
}
p.sortMethod = "c"
p.Sort()
p.Top()
p.KeyPressed <- true
})
ui.On("m", "c", "p", func(e ui.Event) {
if p.sortMethod != e.Key {
p.sortMethod = e.Key
p.Top()
p.Sort()
p.KeyPressed <- true
}
})
}
// Group groupes a []Process based on command name.
// The first field changes from PID to count.
// CPU and Mem are added together for each Process.
func Group(P []Process) []Process {
groupMap := make(map[string]Process)
for _, p := range P {
val, ok := groupMap[p.Command]
if ok {
newP := Process{
val.PID + 1,
val.Command,
val.CPU + p.CPU,
val.Mem + p.Mem,
}
groupMap[p.Command] = newP
} else {
newP := Process{
1,
p.Command,
p.CPU,
p.Mem,
}
groupMap[p.Command] = newP
}
}
groupList := make([]Process, len(groupMap))
i := 0
for _, val := range groupMap {
groupList[i] = val
i++
}
return groupList
}
// FieldsToStrings converts a []Process to a [][]string
func FieldsToStrings(P []Process) [][]string {
strings := make([][]string, len(P))
for i, p := range P {
strings[i] = make([]string, 4)
strings[i][0] = strconv.Itoa(int(p.PID))
strings[i][1] = p.Command
strings[i][2] = strconv.FormatFloat(p.CPU, 'f', 1, 64)
strings[i][3] = strconv.FormatFloat(float64(p.Mem), 'f', 1, 32)
}
return strings
}
////////////////////////////////////////////////////////////////////////////////
// Sorting
type ProcessByCPU []Process
// Len implements Sort interface
func (P ProcessByCPU) Len() int {
return len(P)
}
// Swap implements Sort interface
func (P ProcessByCPU) Swap(i, j int) {
P[i], P[j] = P[j], P[i]
}
// Less implements Sort interface
func (P ProcessByCPU) Less(i, j int) bool {
return P[i].CPU < P[j].CPU
}
type ProcessByPID []Process
// Len implements Sort interface
func (P ProcessByPID) Len() int {
return len(P)
}
// Swap implements Sort interface
func (P ProcessByPID) Swap(i, j int) {
P[i], P[j] = P[j], P[i]
}
// Less implements Sort interface
func (P ProcessByPID) Less(i, j int) bool {
return P[i].PID < P[j].PID
}
type ProcessByMem []Process
// Len implements Sort interface
func (P ProcessByMem) Len() int {
return len(P)
}
// Swap implements Sort interface
func (P ProcessByMem) Swap(i, j int) {
P[i], P[j] = P[j], P[i]
}
// Less implements Sort interface
func (P ProcessByMem) Less(i, j int) bool {
return P[i].Mem < P[j].Mem
}