Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
liming6
dcu-process-montor
Commits
b87220dd
Commit
b87220dd
authored
Dec 03, 2025
by
liming6
Browse files
feature 添加单个进程视图
parent
b670ea3c
Changes
11
Show whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
396 additions
and
131 deletions
+396
-131
cmd/hytop/backend/dcu.go
cmd/hytop/backend/dcu.go
+4
-4
cmd/hytop/tui/main.go
cmd/hytop/tui/main.go
+39
-5
cmd/hytop/tui/process.go
cmd/hytop/tui/process.go
+10
-6
cmd/hytop/tui/process_detail.go
cmd/hytop/tui/process_detail.go
+232
-1
cmd/hytop/tui/process_env.go
cmd/hytop/tui/process_env.go
+4
-0
cmd/hytop/tui/pstree.go
cmd/hytop/tui/pstree.go
+1
-1
cmd/hytop/tui/sysload.go
cmd/hytop/tui/sysload.go
+76
-95
cmd/hytop/tui/timechart.go
cmd/hytop/tui/timechart.go
+10
-9
cmd/hytop/tui/utils.go
cmd/hytop/tui/utils.go
+14
-0
utils/char.go
utils/char.go
+0
-10
utils/utils_test.go
utils/utils_test.go
+6
-0
No files found.
cmd/hytop/backend/dcu.go
View file @
b87220dd
...
...
@@ -271,7 +271,7 @@ func (m *DCUInfoMap) GetQuitInfo() (map[int]*DCUQuickInfo, sync.Locker) {
type
DCUProcessInfo
struct
{
DCU
int
// 使用的dcu号
DCUMem
string
// 使用的dcu内存容量
DCUMem
utils
.
MemorySize
// 使用的dcu内存容量
SDMA
int
Info
ProcessInfo
// 通用进程信息
}
...
...
@@ -345,7 +345,7 @@ func (m *DCUInfoMap) GetDCUProcessInfo() map[int][]DCUProcessInfo {
item
:=
DCUProcessInfo
{
DCU
:
i
}
item
.
Info
=
pinfo
[
int32
(
v
.
Pid
)]
mem
.
Num
=
v
.
VarmUsage
item
.
DCUMem
=
mem
.
HumanReadStr
(
1
)
item
.
DCUMem
=
mem
item
.
SDMA
=
int
(
v
.
SdmaUsage
)
l
=
append
(
l
,
item
)
result
[
i
]
=
l
...
...
@@ -384,7 +384,7 @@ func (m *DCUInfoMap) GetDCUProcessInfo2() map[int][]DCUProcessInfo {
}
item
:=
DCUProcessInfo
{
DCU
:
i
}
item
.
Info
=
pinfo
[
int32
(
v
.
Pid
)]
item
.
DCUMem
=
v
.
VRamUsed
.
HumanReadStr
(
1
)
item
.
DCUMem
=
v
.
VRamUsed
item
.
SDMA
=
v
.
SDMAUsed
l
=
append
(
l
,
item
)
...
...
cmd/hytop/tui/main.go
View file @
b87220dd
...
...
@@ -102,6 +102,7 @@ type ModelMain struct {
actionMsg
*
ActionMsg
dialog
*
Dialog
// 对话框
pstree
*
ModelPsTree
// 进程树视图
processDetail
*
ModelProcessDetail
}
func
NewModelMain
(
width
,
height
int
)
ModelMain
{
...
...
@@ -174,7 +175,8 @@ func (m *ModelMain) Update(inputMsg tea.Msg) (tea.Model, tea.Cmd) {
cmd
:=
m
.
handleKeyDown
()
return
m
,
cmd
case
"enter"
:
return
m
,
tea
.
Quit
cmd
:=
m
.
handleKeyEnter
()
return
m
,
cmd
case
"h"
:
return
m
,
tea
.
Quit
case
"left"
,
"right"
:
...
...
@@ -205,6 +207,10 @@ func (m *ModelMain) Update(inputMsg tea.Msg) (tea.Model, tea.Cmd) {
m
.
SysLoad
=
sysLoad
.
(
*
ModelSysLoad
)
m
.
ProcessInfo
=
pidinfo
.
(
*
ModelProcessInfo
)
m
.
pstree
=
pstree
.
(
*
ModelPsTree
)
if
m
.
processDetail
!=
nil
{
detail
,
_
:=
m
.
processDetail
.
Update
(
m
.
modelMsg
)
m
.
processDetail
=
detail
.
(
*
ModelProcessDetail
)
}
return
m
,
tickCmd
()
case
ModelMsg
:
// 初始化
header
,
_
:=
m
.
Header
.
Update
(
m
.
modelMsg
)
...
...
@@ -231,6 +237,18 @@ func (m *ModelMain) Update(inputMsg tea.Msg) (tea.Model, tea.Cmd) {
return
m
,
nil
}
func
(
m
*
ModelMain
)
handleKeyEnter
()
tea
.
Cmd
{
if
m
.
actionMsg
.
VM
!=
VMMain
||
m
.
actionMsg
.
PointPid
==
nil
{
return
nil
}
pid
:=
*
m
.
actionMsg
.
PointPid
m
.
actionMsg
.
PidView
=
&
pid
m
.
processDetail
=
NewModelProcessDetail
(
m
.
width
,
m
.
height
,
pid
)
m
.
processDetail
.
Update
(
m
.
modelMsg
)
m
.
actionMsg
.
VM
=
VMOneProcess
return
nil
}
func
(
m
*
ModelMain
)
handleKeyT
()
tea
.
Cmd
{
// 在主视图下,没有选择任何进程时,可以进入进程树视图,且
if
m
.
actionMsg
.
VM
==
VMMain
&&
len
(
m
.
actionMsg
.
SelectPids
)
==
0
{
...
...
@@ -361,6 +379,10 @@ func (m *ModelMain) handleKeyEsc() tea.Cmd {
pstree
,
_
:=
m
.
pstree
.
Update
(
m
.
actionMsg
)
m
.
pstree
=
pstree
.
(
*
ModelPsTree
)
return
nil
case
VMOneProcess
:
m
.
actionMsg
.
VM
=
VMMain
m
.
processDetail
=
nil
return
nil
default
:
return
nil
}
...
...
@@ -417,6 +439,8 @@ func (m *ModelMain) View() string {
return
StackPosition
(
up
,
down
,
lipgloss
.
Center
,
lipgloss
.
Center
)
}
return
m
.
pstree
.
View
()
case
VMOneProcess
:
return
m
.
processDetail
.
View
()
default
:
return
""
}
...
...
@@ -470,8 +494,13 @@ func (m *ModelMain) handleKeyUp() tea.Cmd {
}
switch
m
.
actionMsg
.
VM
{
case
VMMain
:
processes
:=
make
([]
backend
.
DCUProcessInfo
,
0
)
for
index
:=
range
64
{
processes
:=
make
([]
backend
.
DCUProcessInfo
,
0
,
16
)
keys
:=
make
([]
int
,
0
,
16
)
for
k
:=
range
m
.
modelMsg
.
DCUPidInfo
{
keys
=
append
(
keys
,
k
)
}
slices
.
Sort
(
keys
)
for
_
,
index
:=
range
keys
{
p
,
have
:=
m
.
modelMsg
.
DCUPidInfo
[
index
]
if
have
&&
len
(
p
)
>
0
{
processes
=
append
(
processes
,
p
...
)
...
...
@@ -543,8 +572,13 @@ func (m *ModelMain) handleKeyDown() tea.Cmd {
}
switch
m
.
actionMsg
.
VM
{
case
VMMain
:
processes
:=
make
([]
backend
.
DCUProcessInfo
,
0
)
for
index
:=
range
64
{
processes
:=
make
([]
backend
.
DCUProcessInfo
,
0
,
16
)
keys
:=
make
([]
int
,
0
,
16
)
for
key
:=
range
m
.
modelMsg
.
DCUPidInfo
{
keys
=
append
(
keys
,
key
)
}
slices
.
Sort
(
keys
)
for
_
,
index
:=
range
keys
{
p
,
have
:=
m
.
modelMsg
.
DCUPidInfo
[
index
]
if
have
&&
len
(
p
)
>
0
{
processes
=
append
(
processes
,
p
...
)
...
...
cmd/hytop/tui/process.go
View file @
b87220dd
...
...
@@ -4,6 +4,7 @@ import (
"fmt"
"get-container/cmd/hytop/backend"
"maps"
"slices"
"strings"
tea
"github.com/charmbracelet/bubbletea"
...
...
@@ -88,11 +89,14 @@ func (m *ModelProcessInfo) View() string {
lines
:=
make
([]
string
,
0
)
sb
:=
strings
.
Builder
{}
sbInner
:=
strings
.
Builder
{}
for
index
:=
range
64
{
processes
,
have
:=
m
.
Cache
[
index
]
if
!
hav
e
{
continue
keys
:=
make
([]
int
,
0
,
len
(
m
.
Cache
))
for
k
:=
range
m
.
Cach
e
{
keys
=
append
(
keys
,
k
)
}
slices
.
Sort
(
keys
)
for
_
,
index
:=
range
keys
{
processes
:=
m
.
Cache
[
index
]
for
_
,
process
:=
range
processes
{
haveProcess
=
true
sb
.
WriteString
(
myBorder
.
Left
)
...
...
@@ -103,7 +107,7 @@ func (m *ModelProcessInfo) View() string {
sbInner
.
WriteByte
(
' '
)
sbInner
.
WriteString
(
FormatStr
(
process
.
Info
.
User
,
8
,
lipgloss
.
Right
))
sbInner
.
WriteByte
(
' '
)
sbInner
.
WriteString
(
FormatStr
(
process
.
DCUMem
,
8
,
lipgloss
.
Right
))
sbInner
.
WriteString
(
FormatStr
(
process
.
DCUMem
.
HumanReadStr
(
1
)
,
8
,
lipgloss
.
Right
))
sbInner
.
WriteByte
(
' '
)
sbInner
.
WriteString
(
FormatStr
(
fmt
.
Sprintf
(
"%d"
,
process
.
SDMA
),
4
,
lipgloss
.
Right
))
sbInner
.
WriteByte
(
' '
)
...
...
cmd/hytop/tui/process_detail.go
View file @
b87220dd
package
tui
import
(
"fmt"
"get-container/cmd/hytop/backend"
"get-container/cmd/hytop/tchart"
"get-container/utils"
"math"
"strings"
tea
"github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
linkedlist
"github.com/emirpasic/gods/v2/lists/doublylinkedlist"
)
const
(
MPDHeader
=
" DCU PID USER DCU-MEM %CPU %MEM TIME Docker Command"
MPDProcess
=
"This process has finished running."
)
type
ModelProcessDetail
struct
{
CPU
,
Mem
,
DCU
,
DCUMem
*
MyTimeChart
Pid
int32
dcu
int
Header
,
Cmd
,
docker
string
width
,
height
int
// 界面的尺寸
modelMsg
*
ModelMsg
//
maxVal
[
4
]
float64
// 分别代表CPU、Mem、DCU、DCUMem的最大值
maxValThreshold
[
4
]
float64
cpuPoints
*
linkedlist
.
List
[
tchart
.
TimePoint
]
cpuPointsNum
int
// axeYStr [4]string // 纵坐标字符串
h
,
w
[
2
]
int
// h[0]代表上边两个表的高度、h[1]代表下边两个表的高度,w[0]表示左边两个表的宽度,w[1]表示右边两个表的宽度
lines
[
5
]
string
// 5条横线
DCUMemTotal
,
MemTotal
utils
.
MemorySize
// 内存总量
DCUMemMax
,
MemMax
utils
.
MemorySize
// 内存使用最大值
DCUMemUsed
utils
.
MemorySize
CPUPercent
,
MemPercent
float64
t
string
}
func
NewModelProcessDetail
(
width
,
height
int
,
pid
int32
)
*
ModelProcessDetail
{
result
:=
ModelProcessDetail
{}
if
(
width
-
3
)
%
2
==
0
{
result
.
w
[
0
]
=
(
width
-
3
)
/
2
result
.
w
[
1
]
=
result
.
w
[
0
]
}
else
{
result
.
w
[
0
]
=
(
width
-
3
)
/
2
result
.
w
[
1
]
=
result
.
w
[
0
]
+
1
}
if
(
height
-
8
)
%
2
==
0
{
result
.
h
[
0
]
=
(
height
-
8
)
/
2
result
.
h
[
1
]
=
result
.
h
[
0
]
}
else
{
result
.
h
[
0
]
=
(
height
-
8
)
/
2
result
.
h
[
1
]
=
result
.
h
[
0
]
+
1
}
var
lines
[
5
]
string
lines
[
0
]
=
myBorder
.
TopLeft
+
strings
.
Repeat
(
myBorder
.
Top
,
width
-
2
)
+
myBorder
.
TopRight
lines
[
1
]
=
"╞"
+
strings
.
Repeat
(
"═"
,
width
-
2
)
+
"╡"
lines
[
2
]
=
"╞"
+
strings
.
Repeat
(
"═"
,
result
.
w
[
0
])
+
"╤"
+
strings
.
Repeat
(
"═"
,
result
.
w
[
1
])
+
"╡"
lines
[
3
]
=
myBorder
.
MiddleLeft
+
genXAxis
(
result
.
w
[
0
])
+
myBorder
.
Middle
+
genXAxis
(
result
.
w
[
1
])
+
myBorder
.
MiddleRight
lines
[
4
]
=
myBorder
.
BottomLeft
+
strings
.
Repeat
(
"═"
,
result
.
w
[
0
])
+
"╧"
+
strings
.
Repeat
(
"═"
,
result
.
w
[
1
])
+
myBorder
.
BottomRight
result
.
lines
=
lines
result
.
Pid
=
pid
result
.
cpuPoints
=
linkedlist
.
New
[
tchart
.
TimePoint
]()
result
.
height
=
height
result
.
width
=
width
style
:=
lipgloss
.
NewStyle
()
uah
:=
fmt
.
Sprintf
(
"%s@%s "
,
style
.
Foreground
(
lipgloss
.
Color
(
"#edff2cff"
))
.
Render
(
backend
.
User
),
style
.
Foreground
(
lipgloss
.
Color
(
"#a3ff2bff"
))
.
Render
(
backend
.
HostName
))
sb
:=
strings
.
Builder
{}
sb
.
WriteString
(
result
.
lines
[
0
])
sb
.
WriteByte
(
'\n'
)
sb
.
WriteString
(
myBorder
.
Left
)
sb
.
WriteString
(
" Process:"
)
sb
.
WriteString
(
strings
.
Repeat
(
" "
,
width
-
2
-
lipgloss
.
Width
(
uah
)
-
lipgloss
.
Width
(
" Process:"
)))
sb
.
WriteString
(
uah
)
sb
.
WriteString
(
myBorder
.
Left
)
sb
.
WriteByte
(
'\n'
)
sb
.
WriteString
(
myBorder
.
Left
)
sb
.
WriteString
(
MPDHeader
)
sb
.
WriteString
(
strings
.
Repeat
(
" "
,
width
-
2
-
lipgloss
.
Width
(
MPDHeader
)))
sb
.
WriteString
(
myBorder
.
Left
)
sb
.
WriteByte
(
'\n'
)
sb
.
WriteString
(
result
.
lines
[
1
])
sb
.
WriteByte
(
'\n'
)
result
.
Header
=
sb
.
String
()
result
.
CPU
=
NewTimeChart
(
result
.
w
[
0
],
result
.
h
[
0
],
0
,
100
,
[]
lipgloss
.
Color
{
lipgloss
.
Color
(
"#00fffbff"
)})
result
.
Mem
=
NewTimeChart
(
result
.
w
[
0
],
result
.
h
[
1
],
0
,
100
,
[]
lipgloss
.
Color
{
lipgloss
.
Color
(
"#8800ffff"
)})
result
.
DCU
=
NewTimeChart
(
result
.
w
[
1
],
result
.
h
[
1
],
0
,
100
,
[]
lipgloss
.
Color
{
lipgloss
.
Color
(
"#0dff00ff"
)})
result
.
DCUMem
=
NewTimeChart
(
result
.
w
[
1
],
result
.
h
[
0
],
0
,
100
,
[]
lipgloss
.
Color
{
lipgloss
.
Color
(
"#ff1e00ff"
)})
result
.
cpuPointsNum
=
result
.
w
[
0
]
*
2
+
1
for
k
:=
range
result
.
maxVal
{
result
.
maxVal
[
k
]
=
0
}
for
k
:=
range
result
.
maxValThreshold
{
result
.
maxValThreshold
[
k
]
=
100
}
return
&
result
}
func
(
m
*
ModelProcessDetail
)
Init
()
tea
.
Cmd
{
return
nil
}
func
(
m
*
ModelProcessDetail
)
Update
(
imsg
tea
.
Msg
)
(
tea
.
Model
,
tea
.
Cmd
)
{
switch
msg
:=
imsg
.
(
type
)
{
case
*
ModelMsg
:
m
.
modelMsg
=
msg
m
.
handleModelMsg
(
msg
)
}
return
m
,
nil
}
func
(
m
*
ModelProcessDetail
)
handleModelMsg
(
msg
*
ModelMsg
)
{
find
:=
false
var
process
*
backend
.
DCUProcessInfo
for
dcu
,
pids
:=
range
msg
.
DCUPidInfo
{
for
_
,
v
:=
range
pids
{
if
v
.
Info
.
Pid
==
m
.
Pid
{
find
=
true
process
=
&
v
if
m
.
Cmd
==
""
{
m
.
Cmd
=
v
.
Info
.
Cmd
m
.
dcu
=
dcu
}
if
v
.
Info
.
ContInfo
!=
nil
{
m
.
docker
=
v
.
Info
.
ContInfo
.
Name
}
break
}
}
if
find
{
break
}
}
var
cpu
,
mem
,
dcu
,
dcumem
float64
if
process
==
nil
{
m
.
Cmd
=
MPDProcess
m
.
CPUPercent
=
0
m
.
MemPercent
=
0
cpu
=
0
mem
=
0
}
else
{
cpu
=
process
.
Info
.
CPU
mem
=
float64
(
process
.
Info
.
Mem
)
m
.
CPUPercent
=
cpu
m
.
MemPercent
=
mem
m
.
t
=
process
.
Info
.
Time
}
qinfo
,
lock
:=
msg
.
DCUInfo
.
GetQuitInfo
()
dcuInfo
,
have
:=
qinfo
[
m
.
dcu
]
if
!
have
{
dcu
=
0
dcumem
=
0
}
else
{
dcumem
=
float64
(
dcuInfo
.
MemUsedPerent
)
dcu
=
float64
(
dcuInfo
.
DCUUTil
)
}
m
.
DCUMemMax
=
utils
.
MemorySize
{
Unit
:
utils
.
Byte
,
Num
:
dcuInfo
.
MemTotal
}
m
.
DCUMemUsed
=
utils
.
MemorySize
{
Unit
:
utils
.
Byte
,
Num
:
dcuInfo
.
MemUsed
}
lock
.
Unlock
()
m
.
MemMax
=
utils
.
MemorySize
{
Unit
:
utils
.
Byte
,
Num
:
msg
.
systemInfo
.
MemTotal
}
m
.
maxVal
[
0
]
=
max
(
m
.
maxVal
[
0
],
cpu
)
m
.
maxVal
[
1
]
=
max
(
m
.
maxVal
[
1
],
mem
)
m
.
maxVal
[
2
]
=
max
(
m
.
maxVal
[
2
],
dcu
)
m
.
maxVal
[
3
]
=
max
(
m
.
maxVal
[
3
],
dcumem
)
m
.
DCU
.
Update
(
MyTimeChartMsg
{
Reset
:
false
,
Points
:
[]
tchart
.
TimePoint
{{
Time
:
msg
.
t
,
Value
:
dcu
}}})
m
.
DCUMem
.
Update
(
MyTimeChartMsg
{
Reset
:
false
,
Points
:
[]
tchart
.
TimePoint
{{
Time
:
msg
.
t
,
Value
:
dcumem
}}})
m
.
Mem
.
Update
(
MyTimeChartMsg
{
Reset
:
false
,
Points
:
[]
tchart
.
TimePoint
{{
Time
:
msg
.
t
,
Value
:
mem
}}})
m
.
cpuPoints
.
Add
(
tchart
.
TimePoint
{
Time
:
msg
.
t
,
Value
:
cpu
})
if
m
.
cpuPoints
.
Size
()
>
m
.
cpuPointsNum
{
m
.
cpuPoints
.
Remove
(
0
)
}
if
m
.
CPU
.
max
<
cpu
{
// 需要更新
if
int
(
math
.
Ceil
(
cpu
))
%
50
==
0
{
m
.
maxValThreshold
[
0
]
=
math
.
Ceil
(
cpu
)
}
else
{
m
.
maxValThreshold
[
0
]
=
float64
(
int
(
math
.
Ceil
(
cpu
))
/
50
)
*
50
+
50
}
m
.
CPU
=
NewTimeChart
(
m
.
w
[
0
],
m
.
h
[
0
],
0
,
m
.
maxValThreshold
[
0
],
[]
lipgloss
.
Color
{
lipgloss
.
Color
(
"#00fffbff"
)})
m
.
CPU
.
PutPoint
(
m
.
cpuPoints
.
Values
())
}
else
{
m
.
CPU
.
Update
(
MyTimeChartMsg
{
Reset
:
false
,
Points
:
[]
tchart
.
TimePoint
{{
Time
:
msg
.
t
,
Value
:
cpu
}}})
}
}
func
(
m
*
ModelProcessDetail
)
View
()
string
{
sb
:=
strings
.
Builder
{}
sb
.
WriteString
(
m
.
Header
)
sb
.
WriteString
(
myBorder
.
Left
)
sb
.
WriteString
(
FormatStr
(
fmt
.
Sprintf
(
"%d"
,
m
.
dcu
),
4
,
lipgloss
.
Right
))
sb
.
WriteByte
(
' '
)
sb
.
WriteString
(
FormatStr
(
fmt
.
Sprintf
(
"%d"
,
m
.
Pid
),
7
,
lipgloss
.
Left
))
sb
.
WriteByte
(
' '
)
sb
.
WriteString
(
FormatStr
(
backend
.
User
,
8
,
lipgloss
.
Left
))
sb
.
WriteByte
(
' '
)
sb
.
WriteString
(
FormatStr
(
m
.
DCUMemUsed
.
HumanReadStr
(
1
),
9
,
lipgloss
.
Left
))
sb
.
WriteByte
(
' '
)
sb
.
WriteString
(
FormatStr
(
fmt
.
Sprintf
(
"%.1f"
,
m
.
CPUPercent
),
5
,
lipgloss
.
Left
))
sb
.
WriteByte
(
' '
)
sb
.
WriteString
(
FormatStr
(
fmt
.
Sprintf
(
"%.1f"
,
m
.
MemPercent
),
5
,
lipgloss
.
Left
))
sb
.
WriteByte
(
' '
)
sb
.
WriteString
(
FormatStr
(
m
.
t
,
8
,
lipgloss
.
Left
))
sb
.
WriteByte
(
' '
)
if
m
.
docker
==
""
{
sb
.
WriteString
(
FormatStr
(
" - "
,
19
,
lipgloss
.
Left
))
}
else
{
sb
.
WriteString
(
FormatStr
(
m
.
docker
,
19
,
lipgloss
.
Left
))
}
sb
.
WriteByte
(
' '
)
sb
.
WriteString
(
FormatStr
(
m
.
Cmd
,
m
.
width
-
3
-
lipgloss
.
Width
(
MPDHeader
)
+
lipgloss
.
Width
(
"Command"
),
lipgloss
.
Left
))
sb
.
WriteByte
(
' '
)
sb
.
WriteString
(
myBorder
.
Left
)
sb
.
WriteByte
(
'\n'
)
sb
.
WriteString
(
m
.
lines
[
2
])
style
:=
lipgloss
.
NewStyle
()
CPU
:=
style
.
Border
(
lipgloss
.
NormalBorder
(),
false
,
true
,
false
)
.
Render
(
m
.
CPU
.
View
())
DCUM
:=
style
.
Border
(
lipgloss
.
NormalBorder
(),
false
,
true
,
false
,
false
)
.
Render
(
m
.
DCUMem
.
View
())
Mem
:=
style
.
Border
(
lipgloss
.
NormalBorder
(),
false
,
true
,
false
)
.
Render
(
m
.
Mem
.
View
())
DCU
:=
style
.
Border
(
lipgloss
.
NormalBorder
(),
false
,
true
,
false
,
false
)
.
Render
(
m
.
DCU
.
View
())
sb
.
WriteString
(
lipgloss
.
JoinVertical
(
lipgloss
.
Left
,
lipgloss
.
JoinHorizontal
(
lipgloss
.
Top
,
CPU
,
DCUM
),
m
.
lines
[
3
],
lipgloss
.
JoinHorizontal
(
lipgloss
.
Top
,
Mem
,
DCU
),
m
.
lines
[
4
]))
return
sb
.
String
()
}
cmd/hytop/tui/process_env.go
0 → 100644
View file @
b87220dd
package
tui
type
ModelProcessEnv
struct
{
}
cmd/hytop/tui/pstree.go
View file @
b87220dd
...
...
@@ -172,7 +172,7 @@ func (m *ModelPsTree) View() string {
}
}
downStr
=
strings
.
Join
(
lines
,
"
\n
"
)
finalStr
:=
lipgloss
.
JoinVertical
(
lipgloss
.
Left
,
m
.
header
[
0
],
downStr
,
strings
.
Repeat
(
"
\n
"
,
m
.
height
-
1
-
lipgloss
.
Height
(
downStr
))
)
finalStr
:=
lipgloss
.
JoinVertical
(
lipgloss
.
Left
,
m
.
header
[
0
],
downStr
)
return
ModelPsFullHeightStyle
.
Render
(
finalStr
)
}
else
{
return
m
.
header
[
0
]
+
"
\n
There is no DCU process running"
...
...
cmd/hytop/tui/sysload.go
View file @
b87220dd
...
...
@@ -4,7 +4,6 @@ import (
"fmt"
"get-container/cmd/hytop/tchart"
"get-container/utils"
"image/color"
"strings"
"sync"
"sync/atomic"
...
...
@@ -12,8 +11,7 @@ import (
tea
"github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
"github.com/emirpasic/gods/v2/maps/treemap"
"github.com/emirpasic/gods/v2/trees/binaryheap"
linkedlist
"github.com/emirpasic/gods/v2/lists/doublylinkedlist"
"github.com/lucasb-eyer/go-colorful"
"github.com/muesli/gamut"
)
...
...
@@ -38,19 +36,21 @@ type ModelSysLoad struct {
SysCPU
*
MyTimeChart
DCU
*
MyTimeChart
DCUMem
*
MyTimeChart
sysInfoL
ock
sync
.
Mutex
// sysinfo和keys的保护
锁,防止并发写
sysInfo
*
treemap
.
Map
[
time
.
Time
,
SysLoadInfo
]
keys
*
binaryheap
.
Heap
[
time
.
Time
]
current
*
SysLoadInfo
dataMapl
ock
sync
.
RW
Mutex
// 保护下面两个map的
锁,防止并发
读
写
dcuMemData
map
[
int
]
*
linkedlist
.
List
[
tchart
.
TimePoint
]
// 记录dcu的内存使用信息点
dcuUsgData
map
[
int
]
*
linkedlist
.
List
[
tchart
.
TimePoint
]
// 记录dcu的使用率信息点
current
*
SysLoadInfo
// 当前的全量信息
topLine
,
topL
string
bottomLine
,
bottomL
string
style
lipgloss
.
Style
width
int
// 组件总宽度
colors
[]
color
.
Color
// 时序图的颜色表
colorsLow
[]
color
.
Color
// 低亮度模式的颜色表
colors
[]
lipgloss
.
Color
// 时序图的颜色表
colorsLow
[]
lipgloss
.
Color
// 低亮度模式的颜色表
actionMsg
*
ActionMsg
// 动作消息
modelMsg
*
ModelMsg
// 模型周期性消息
DCUToShow
atomic
.
Int32
// 要显示的DCU信息的索引,如果为-1,表示显示dcu的平均数据
dataNumToStore
int
// 维持DCU和DCUMem图表需要的最多数据点的数量
}
type
SysLoadInfo
struct
{
...
...
@@ -69,28 +69,13 @@ type SysLoadInfo struct {
func
NewModelSysLoad
(
width
int
)
*
ModelSysLoad
{
result
:=
ModelSysLoad
{}
result
.
width
=
width
result
.
sysInfo
=
treemap
.
NewWith
[
time
.
Time
,
SysLoadInfo
](
func
(
x
,
y
time
.
Time
)
int
{
if
x
.
Before
(
y
)
{
return
-
1
}
if
x
.
After
(
y
)
{
return
1
}
return
0
})
result
.
keys
=
binaryheap
.
NewWith
(
func
(
a
,
b
time
.
Time
)
int
{
if
a
.
After
(
b
)
{
return
1
}
if
a
.
Before
(
b
)
{
return
-
1
}
return
0
})
result
.
colors
=
gamut
.
Blends
(
lipgloss
.
Color
(
"#ff0000"
),
lipgloss
.
Color
(
"#00ff00ff"
),
SysLoadHeight
)
result
.
colorsLow
=
gamut
.
Blends
(
gamut
.
Darker
(
lipgloss
.
Color
(
"#ff0000"
),
0.8
),
gamut
.
Darker
(
lipgloss
.
Color
(
"#00ff00ff"
),
0.8
),
SysLoadHeight
)
result
.
dcuMemData
=
make
(
map
[
int
]
*
linkedlist
.
List
[
tchart
.
TimePoint
])
result
.
dcuUsgData
=
make
(
map
[
int
]
*
linkedlist
.
List
[
tchart
.
TimePoint
])
result
.
colors
=
ConvertColor
(
gamut
.
Blends
(
lipgloss
.
Color
(
"#ff0000"
),
lipgloss
.
Color
(
"#00ff00ff"
),
SysLoadHeight
))
result
.
colorsLow
=
ConvertColor
(
gamut
.
Blends
(
gamut
.
Darker
(
lipgloss
.
Color
(
"#ff0000"
),
0.8
),
gamut
.
Darker
(
lipgloss
.
Color
(
"#00ff00ff"
),
0.8
),
SysLoadHeight
))
subLine
:=
width
-
StaticWidth
-
1
result
.
dataNumToStore
=
subLine
*
2
+
1
result
.
SysMem
=
NewTimeChart
(
SysLoadWidth
,
SysLoadHeight
,
0
,
100
,
result
.
colors
)
result
.
SysCPU
=
NewTimeChart
(
SysLoadWidth
,
SysLoadHeight
,
0
,
100
,
result
.
colors
)
result
.
DCU
=
NewTimeChart
(
subLine
,
SysLoadHeight
,
0
,
100
,
result
.
colors
)
...
...
@@ -186,7 +171,7 @@ func (m *ModelSysLoad) Update(inputMsg tea.Msg) (tea.Model, tea.Cmd) {
// handleAction 处理ActionMsg
func
(
m
*
ModelSysLoad
)
handleAction
()
{
if
m
.
actionMsg
==
nil
||
m
.
actionMsg
.
TargetDCUIndex
==
nil
{
if
m
.
actionMsg
==
nil
||
m
.
actionMsg
.
TargetDCUIndex
==
nil
||
m
.
actionMsg
.
VM
!=
VMMain
{
return
}
targetIndex
:=
int32
(
*
m
.
actionMsg
.
TargetDCUIndex
)
...
...
@@ -198,31 +183,11 @@ func (m *ModelSysLoad) handleAction() {
wg
:=
sync
.
WaitGroup
{}
wg
.
Add
(
1
)
index
:=
int
(
targetIndex
)
i
:=
m
.
sysInfo
.
Iterator
()
mem
:=
make
([]
tchart
.
TimePoint
,
0
,
m
.
sysInfo
.
Size
())
usage
:=
make
([]
tchart
.
TimePoint
,
0
,
m
.
sysInfo
.
Size
())
for
{
if
!
i
.
Next
()
{
break
}
key
:=
i
.
Key
()
var
valM
,
valU
float32
vU
,
haveU
:=
i
.
Value
()
.
DCUUsage
[
index
]
vM
,
haveM
:=
i
.
Value
()
.
DCUMemUsage
[
index
]
if
haveU
{
valU
=
vU
}
else
{
valU
=
0
}
if
haveM
{
valM
=
vM
}
else
{
valM
=
0
}
mem
=
append
(
mem
,
tchart
.
TimePoint
{
Time
:
key
,
Value
:
float64
(
valM
)})
usage
=
append
(
usage
,
tchart
.
TimePoint
{
Time
:
key
,
Value
:
float64
(
valU
)})
}
rl
:=
m
.
dataMaplock
.
RLocker
()
rl
.
Lock
()
mem
:=
m
.
dcuMemData
[
index
]
.
Values
()
usage
:=
m
.
dcuUsgData
[
index
]
.
Values
()
rl
.
Unlock
()
go
func
()
{
defer
wg
.
Done
()
m3
,
_
:=
m
.
DCU
.
Update
(
NewTimeCharMsg
(
usage
,
true
))
...
...
@@ -294,7 +259,12 @@ func (m *ModelSysLoad) View() string {
// updateInfo 向ModelSysLoad中添加数据
func
(
m
*
ModelSysLoad
)
updateInfo
(
t
*
ModelMsg
)
{
sysInfo
,
_
:=
utils
.
GetSysInfo
()
var
sysInfo
*
utils
.
SysInfo
if
t
.
systemInfo
!=
nil
{
sysInfo
=
t
.
systemInfo
}
else
{
sysInfo
,
_
=
utils
.
GetSysInfo
()
}
s
:=
SysLoadInfo
{}
s
.
Load1
=
sysInfo
.
LoadAverage1
s
.
Load5
=
sysInfo
.
LoadAverage5
...
...
@@ -312,11 +282,26 @@ func (m *ModelSysLoad) updateInfo(t *ModelMsg) {
s
.
DCUMemUsageAvg
,
s
.
DCUUsageAvg
=
0
,
0
qinfo
,
lock
:=
t
.
DCUInfo
.
GetQuitInfo
()
m
.
dataMaplock
.
Lock
()
for
k
,
v
:=
range
qinfo
{
s
.
DCUMemUsageAvg
+=
v
.
MemUsedPerent
s
.
DCUMemUsage
[
k
]
=
v
.
MemUsedPerent
s
.
DCUUsageAvg
+=
v
.
DCUUTil
s
.
DCUUsage
[
k
]
=
v
.
DCUUTil
l1
,
have
:=
m
.
dcuMemData
[
k
]
if
!
have
{
l1
=
linkedlist
.
New
[
tchart
.
TimePoint
]()
m
.
dcuMemData
[
k
]
=
l1
}
l1
.
Add
(
tchart
.
TimePoint
{
Time
:
t
.
t
,
Value
:
float64
(
v
.
MemUsedPerent
)})
l2
,
have
:=
m
.
dcuUsgData
[
k
]
if
!
have
{
l2
=
linkedlist
.
New
[
tchart
.
TimePoint
]()
m
.
dcuUsgData
[
k
]
=
l2
}
l2
.
Add
(
tchart
.
TimePoint
{
Time
:
t
.
t
,
Value
:
float64
(
v
.
DCUUTil
)})
}
l
:=
len
(
qinfo
)
lock
.
Unlock
()
...
...
@@ -324,6 +309,32 @@ func (m *ModelSysLoad) updateInfo(t *ModelMsg) {
s
.
DCUUsageAvg
/=
float32
(
l
)
s
.
DCUMemUsage
[
-
1
]
=
s
.
DCUMemUsageAvg
s
.
DCUUsage
[
-
1
]
=
s
.
DCUUsageAvg
dcu
,
have
:=
m
.
dcuUsgData
[
-
1
]
if
!
have
{
dcu
=
linkedlist
.
New
[
tchart
.
TimePoint
]()
m
.
dcuUsgData
[
-
1
]
=
dcu
}
dcu
.
Add
(
tchart
.
TimePoint
{
Time
:
t
.
t
,
Value
:
float64
(
s
.
DCUUsageAvg
)})
mem
,
have
:=
m
.
dcuMemData
[
-
1
]
if
!
have
{
mem
=
linkedlist
.
New
[
tchart
.
TimePoint
]()
m
.
dcuMemData
[
-
1
]
=
mem
}
mem
.
Add
(
tchart
.
TimePoint
{
Time
:
t
.
t
,
Value
:
float64
(
s
.
DCUMemUsageAvg
)})
// 判断是否要丢弃多余的点
if
dcu
.
Size
()
>
m
.
dataNumToStore
{
for
_
,
v
:=
range
m
.
dcuMemData
{
v
.
Remove
(
0
)
}
for
_
,
v
:=
range
m
.
dcuUsgData
{
v
.
Remove
(
0
)
}
}
m
.
dataMaplock
.
Unlock
()
m
.
current
=
&
s
needUpdate
,
index
:=
false
,
-
1
...
...
@@ -354,44 +365,14 @@ func (m *ModelSysLoad) updateInfo(t *ModelMsg) {
m2
,
_
:=
m
.
SysCPU
.
Update
(
NewTimeCharMsg
([]
tchart
.
TimePoint
{{
Time
:
t
.
t
,
Value
:
s
.
CPUPercent
}},
false
))
m
.
SysCPU
=
m2
.
(
*
MyTimeChart
)
}()
// 存放数据
m
.
sysInfoLock
.
Lock
()
m
.
sysInfo
.
Put
(
t
.
t
,
s
)
m
.
keys
.
Push
(
t
.
t
)
if
m
.
keys
.
Size
()
>
SysLoadCap
{
delKey
,
have
:=
m
.
keys
.
Pop
()
if
have
{
m
.
sysInfo
.
Remove
(
delKey
)
}
}
m
.
sysInfoLock
.
Unlock
()
if
needUpdate
{
// 准备全量数据
i
:=
m
.
sysInfo
.
Iterator
()
mem
:=
make
([]
tchart
.
TimePoint
,
0
,
m
.
sysInfo
.
Size
())
usage
:=
make
([]
tchart
.
TimePoint
,
0
,
m
.
sysInfo
.
Size
())
for
{
if
!
i
.
Next
()
{
break
}
key
:=
i
.
Key
()
var
valM
,
valU
float32
vU
,
haveU
:=
i
.
Value
()
.
DCUUsage
[
index
]
vM
,
haveM
:=
i
.
Value
()
.
DCUMemUsage
[
index
]
if
haveU
{
valU
=
vU
}
else
{
valU
=
0
}
if
haveM
{
valM
=
vM
}
else
{
valM
=
0
}
mem
=
append
(
mem
,
tchart
.
TimePoint
{
Time
:
key
,
Value
:
float64
(
valM
)})
usage
=
append
(
usage
,
tchart
.
TimePoint
{
Time
:
key
,
Value
:
float64
(
valU
)})
}
rl
:=
m
.
dataMaplock
.
RLocker
()
rl
.
Lock
()
mem
:=
m
.
dcuMemData
[
index
]
.
Values
()
usage
:=
m
.
dcuUsgData
[
index
]
.
Values
()
rl
.
Unlock
()
go
func
()
{
defer
wg
.
Done
()
m3
,
_
:=
m
.
DCU
.
Update
(
NewTimeCharMsg
(
usage
,
true
))
...
...
cmd/hytop/tui/timechart.go
View file @
b87220dd
package
tui
import
(
"image/color"
"sort"
"strconv"
"strings"
...
...
@@ -15,7 +14,6 @@ import (
tea
"github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
zone
"github.com/lrstanley/bubblezone"
"github.com/lucasb-eyer/go-colorful"
)
const
(
...
...
@@ -107,11 +105,11 @@ type MyTimeChart struct {
width
,
height
int
// 图表的高度和宽度
max
,
min
float64
// y轴的最值
lockPoints
sync
.
RWMutex
// 保护dataSet的并发读写
color
[]
color
.
Color
color
[]
lipgloss
.
Color
}
// New 新建图表,其中dataSet的Key为数据集名称,value为数据集的颜色
func
NewTimeChart
(
width
,
height
int
,
vmin
,
vmax
float64
,
color
[]
color
.
Color
)
*
MyTimeChart
{
func
NewTimeChart
(
width
,
height
int
,
vmin
,
vmax
float64
,
color
[]
lipgloss
.
Color
)
*
MyTimeChart
{
result
:=
MyTimeChart
{}
result
.
max
=
vmax
result
.
min
=
vmin
...
...
@@ -247,7 +245,7 @@ func (m *MyTimeChart) Update(inputMsg tea.Msg) (tea.Model, tea.Cmd) {
return
m
,
nil
}
func
(
m
*
MyTimeChart
)
ViewWithColor
(
color
[]
color
.
Color
)
string
{
func
(
m
*
MyTimeChart
)
ViewWithColor
(
color
[]
lipgloss
.
Color
)
string
{
str
:=
m
.
zM
.
Scan
(
m
.
chart
.
View
())
sb
:=
strings
.
Builder
{}
sb
.
WriteString
(
str
[
m
.
width
+
1
:
])
...
...
@@ -334,10 +332,10 @@ func (m *MyTimeChart) ViewWithColor(color []color.Color) string {
for
k
,
v
:=
range
runes
{
resultLines
[
k
]
=
string
(
v
)
}
style
:=
lipgloss
.
NewStyle
()
if
len
(
color
)
==
height
{
for
k
,
v
:=
range
resultLines
{
c
,
_
:=
colorful
.
MakeColor
(
color
[
k
])
resultLines
[
k
]
=
lipgloss
.
NewStyle
()
.
Foreground
(
lipgloss
.
Color
(
c
.
Hex
()))
.
Render
(
v
)
resultLines
[
k
]
=
style
.
Foreground
(
color
[
k
])
.
Render
(
v
)
}
}
return
strings
.
Join
(
resultLines
,
"
\n
"
)
...
...
@@ -431,10 +429,13 @@ func (m *MyTimeChart) View() string {
resultLines
[
k
]
=
string
(
v
)
}
if
len
(
m
.
color
)
==
height
{
style
:=
lipgloss
.
NewStyle
()
for
k
,
v
:=
range
resultLines
{
c
,
_
:=
colorful
.
MakeColor
(
m
.
color
[
k
])
resultLines
[
k
]
=
lipgloss
.
NewStyle
()
.
Foreground
(
lipgloss
.
Color
(
c
.
Hex
()))
.
Render
(
v
)
resultLines
[
k
]
=
style
.
Foreground
(
m
.
color
[
k
])
.
Render
(
v
)
}
}
else
if
len
(
m
.
color
)
==
1
{
style
:=
lipgloss
.
NewStyle
()
.
Foreground
(
m
.
color
[
0
])
return
style
.
Render
(
strings
.
Join
(
resultLines
,
"
\n
"
))
}
return
strings
.
Join
(
resultLines
,
"
\n
"
)
}
cmd/hytop/tui/utils.go
View file @
b87220dd
package
tui
import
(
"image/color"
"strings"
"github.com/charmbracelet/lipgloss"
"github.com/charmbracelet/x/ansi"
"github.com/lucasb-eyer/go-colorful"
)
func
absInt
(
a
int
)
int
{
...
...
@@ -151,3 +153,15 @@ func FormatStr(str string, length int, hPos lipgloss.Position) string {
}
return
str
}
func
ConvertColor
(
color
[]
color
.
Color
)
[]
lipgloss
.
Color
{
if
len
(
color
)
==
0
{
return
nil
}
result
:=
make
([]
lipgloss
.
Color
,
len
(
color
))
for
k
,
v
:=
range
color
{
c
,
_
:=
colorful
.
MakeColor
(
v
)
result
[
k
]
=
lipgloss
.
Color
(
c
.
Hex
())
}
return
result
}
utils/char.go
View file @
b87220dd
...
...
@@ -44,16 +44,6 @@ var (
RightOnlyMW
=
[]
rune
(
"⠈⠐⠠⢀⠘⠰⠨⢈⢐⢠⢘⢨⠸⢰⢸"
)
)
/*
├─────────────120s├──────────────────────────60s├───────────30s├──────────────┼──────────────────────360s├─────────────────────────300s├─────────────────────────240s├─────────────────────────180s├─────────────────────────120s├──────────────────────────60s├───────────30s├──────────────┤
│ │ ⡏⠉⡇⣿⢸⢹ │
│ │ ⡇ ⢣⠃⠇⠈⡆ │
│ │ ⡇ ⠸ ⡇ │
│ │ ⡇ ⢣ │
│ MEM: 93.0GiB (6.2%)⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⠤⠤⠤⠤⠤⠤⠤⠤⠄│ AVG DCU UTL: 0.0%⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⡇ ⢸⣀⣀⣀⣀⣀⡀│
*/
func
Full
()
map
[
rune
]
rune
{
result
:=
make
(
map
[
rune
]
rune
)
str
:=
strings
.
ReplaceAll
(
strings
.
ReplaceAll
(
strings
.
Trim
(
MW
,
"
\n
"
),
"|"
,
""
),
"
\n
"
,
""
)
...
...
utils/utils_test.go
View file @
b87220dd
...
...
@@ -195,3 +195,9 @@ func TestChild(t *testing.T) {
t
.
Logf
(
"%d ms
\n
"
,
d
.
Milliseconds
())
t
.
Logf
(
"childs num: %d"
,
len
(
childs
))
}
func
TestCHar
(
t
*
testing
.
T
)
{
c1
:=
"⣿⣿⣿⣿⣿⣿⣿⣿"
c2
:=
"├"
t
.
Log
(
lipgloss
.
JoinHorizontal
(
lipgloss
.
Top
,
c2
,
c1
))
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment