1
0
mirror of https://github.com/cjbassi/gotop.git synced 2025-04-27 13:48:54 +08:00
gotop/src/widgets/proc.go
2019-01-30 15:20:09 -08:00

256 lines
5.2 KiB
Go

package widgets
import (
"fmt"
"log"
"os/exec"
"sort"
"strconv"
"sync"
"time"
ui "github.com/cjbassi/gotop/src/termui"
"github.com/cjbassi/gotop/src/utils"
"github.com/gizak/termui"
psCPU "github.com/shirou/gopsutil/cpu"
)
const (
UP = "▲"
DOWN = "▼"
)
// Process represents each process.
type Process struct {
PID int
Command string
CPU float64
Mem float64
Args string
}
type Proc struct {
*ui.Table
cpuCount float64
interval time.Duration
sortMethod string
groupedProcs []Process
ungroupedProcs []Process
group bool
}
func NewProc(renderLock *sync.RWMutex) *Proc {
cpuCount, err := psCPU.Counts(false)
if err != nil {
log.Printf("failed to get CPU count from gopsutil: %v", err)
}
self := &Proc{
Table: ui.NewTable(),
interval: time.Second,
cpuCount: float64(cpuCount),
sortMethod: "c",
group: true,
}
self.Title = " Processes "
self.ColResizer = self.ColResize
self.Cursor = true
self.Gap = 3
self.PadLeft = 2
self.UniqueCol = 0
if self.group {
self.UniqueCol = 1
}
self.update()
go func() {
for range time.NewTicker(self.interval).C {
renderLock.RLock()
self.update()
renderLock.RUnlock()
}
}()
return self
}
// Sort sorts either the grouped or ungrouped []Process based on the sortMethod.
// Called with every update, when the sort method is changed, and when processes are grouped and ungrouped.
func (self *Proc) Sort() {
self.Header = []string{"Count", "Command", "CPU%", "Mem%"}
if !self.group {
self.Header[0] = "PID"
}
processes := &self.ungroupedProcs
if self.group {
processes = &self.groupedProcs
}
switch self.sortMethod {
case "c":
sort.Sort(sort.Reverse(ProcessByCPU(*processes)))
self.Header[2] += DOWN
case "p":
if self.group {
sort.Sort(sort.Reverse(ProcessByPID(*processes)))
} else {
sort.Sort(ProcessByPID(*processes))
}
self.Header[0] += DOWN
case "m":
sort.Sort(sort.Reverse(ProcessByMem(*processes)))
self.Header[3] += DOWN
}
self.Rows = FieldsToStrings(*processes, self.group)
}
// ColResize overrides the default ColResize in the termui table.
func (self *Proc) ColResize() {
self.ColWidths = []int{
5, utils.Max(self.Inner.Dx()-26, 10), 4, 4,
}
}
func (self *Proc) ChangeSort(e termui.Event) {
if self.sortMethod != e.ID {
self.sortMethod = e.ID
self.Top()
self.Sort()
}
}
func (self *Proc) Tab() {
self.group = !self.group
if self.group {
self.UniqueCol = 1
} else {
self.UniqueCol = 0
}
self.Sort()
self.Top()
}
// 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 {
groupedP := make(map[string]Process)
for _, process := range P {
val, ok := groupedP[process.Command]
if ok {
groupedP[process.Command] = Process{
val.PID + 1,
val.Command,
val.CPU + process.CPU,
val.Mem + process.Mem,
"",
}
} else {
groupedP[process.Command] = Process{
1,
process.Command,
process.CPU,
process.Mem,
"",
}
}
}
groupList := make([]Process, len(groupedP))
var i int
for _, val := range groupedP {
groupList[i] = val
i++
}
return groupList
}
// FieldsToStrings converts a []Process to a [][]string
func FieldsToStrings(P []Process, grouped bool) [][]string {
strings := make([][]string, len(P))
for i, p := range P {
strings[i] = make([]string, 4)
strings[i][0] = strconv.Itoa(int(p.PID))
if grouped {
strings[i][1] = p.Command
} else {
strings[i][1] = p.Args
}
strings[i][2] = fmt.Sprintf("%4s", strconv.FormatFloat(p.CPU, 'f', 1, 64))
strings[i][3] = fmt.Sprintf("%4s", strconv.FormatFloat(float64(p.Mem), 'f', 1, 64))
}
return strings
}
// Kill kills process or group of processes.
func (self *Proc) Kill() {
self.SelectedItem = ""
command := "kill"
if self.UniqueCol == 1 {
command = "pkill"
}
cmd := exec.Command(command, self.Rows[self.SelectedRow][self.UniqueCol])
cmd.Start()
cmd.Wait()
}
/////////////////////////////////////////////////////////////////////////////////
// []Process 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
}