-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.go
143 lines (123 loc) · 2.76 KB
/
main.go
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
// Copyright © 2022 The Gomon Project.
package main
import (
"cmp"
"context"
"fmt"
"path/filepath"
"strconv"
"strings"
"github.com/zosmac/gocore"
)
type (
// Pid is the type for the process identifier.
Pid int
// process info.
process struct {
Pid
Ppid Pid
CommandLine
}
// CommandLine contains a process' command line arguments.
CommandLine struct {
Executable string `json:"executable" gomon:"property"`
Args []string `json:"args" gomon:"property"`
Envs []string `json:"envs" gomon:"property"`
}
// table alias defines a process table as a map of pids to processes.
table = gocore.Table[Pid, *process]
)
// String renders the pid.
func (pid Pid) String() string {
return strconv.Itoa(int(pid))
}
// main
func main() {
gocore.Main(Main)
}
// Main builds and displays the process tree.
func Main(ctx context.Context) error {
tb := buildTable()
tr := tb.BuildTree()
var pids []Pid
for _, pid := range flags.pids {
if _, ok := tb[pid]; ok {
pids = append(pids, pid)
}
}
if len(pids) > 0 {
pt := table{}
for _, pid := range pids {
for _, pid := range tr.Family(pid).All() {
pt[pid] = tb[pid]
}
}
tr = pt.BuildTree()
}
for depth, pid := range tr.Ordered(func(a, b Pid) int {
return cmp.Or(
cmp.Compare(filepath.Base(tb[a].Executable), filepath.Base(tb[b].Executable)),
cmp.Compare(a, b),
)
}) {
display(depth, pid, tb[pid])
}
return nil
}
// buildTable builds a process table and captures current process state.
func buildTable() table {
pids, err := getPids()
if err != nil {
panic(fmt.Errorf("could not build process table %v", err))
}
tb := make(map[Pid]*process, len(pids))
for _, pid := range pids {
if p := pid.process(); p != nil {
tb[pid] = p
}
}
return tb
}
// display shows the pid, command, arguments, and environment variables for a process.
func display(indent int, _ Pid, p *process) {
tab := strings.Repeat("|\t", indent)
var s string
if flags.verbose {
bg := 44 // blue background for pid
for _, pid := range flags.pids {
if pid == p.Pid {
bg = 41 // red background for pid
}
}
s = fmt.Sprintf("%s\033[97;%2dm%7d", tab, bg, p.Pid)
} else {
s = fmt.Sprintf("%s%7d", tab, p.Pid)
}
tab += "|\t"
var cmd, args, envs string
if len(p.Args) > 0 {
cmd = p.Args[0]
}
if flags.verbose {
if len(p.Args) > 1 {
guide := "\033[m\n" + tab + "\033[34m"
args = guide + strings.Join(p.Args[1:], guide)
}
if len(p.Envs) > 0 {
guide := "\033[m\n" + tab + "\033[35m"
envs = guide + strings.Join(p.Envs, guide)
}
} else {
cmd = filepath.Base(cmd)
}
fmt.Printf("%s\033[m %s%s%s\033[m\n", s, cmd, args, envs)
}
func (p *process) HasParent() bool {
return p.Ppid > 0
}
func (p *process) Parent() (pid Pid) {
if p.Ppid > 0 {
pid = p.Ppid
}
return
}