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
fbb93034
Commit
fbb93034
authored
Nov 12, 2025
by
liming6
Browse files
feature 完成表格部分
parent
687d3e07
Changes
11
Show whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
278 additions
and
46 deletions
+278
-46
cmd/dcutop/backend/main.go
cmd/dcutop/backend/main.go
+11
-0
cmd/dcutop/backend/main_test.go
cmd/dcutop/backend/main_test.go
+15
-1
cmd/dcutop/tui/dcuinfo.go
cmd/dcutop/tui/dcuinfo.go
+119
-7
cmd/dcutop/tui/header.go
cmd/dcutop/tui/header.go
+2
-8
cmd/dcutop/tui/main.go
cmd/dcutop/tui/main.go
+37
-13
cmd/dcutop/tui/tui_test.go
cmd/dcutop/tui/tui_test.go
+22
-11
cmd/dcutop/tui/utils.go
cmd/dcutop/tui/utils.go
+23
-0
go.mod
go.mod
+5
-4
gpu/hy-smi.go
gpu/hy-smi.go
+2
-2
gpu/regexp_test.go
gpu/regexp_test.go
+11
-0
test-data/output.txt
test-data/output.txt
+31
-0
No files found.
cmd/dcutop/backend/main.go
View file @
fbb93034
...
@@ -156,3 +156,14 @@ func UpdateDCUInfo(full bool) {
...
@@ -156,3 +156,14 @@ func UpdateDCUInfo(full bool) {
d
.
PwrMode
=
v
.
PwrMode
d
.
PwrMode
=
v
.
PwrMode
}
}
}
}
func
GetDCUInfo
()
map
[
int
]
DCUInfo
{
result
:=
make
(
map
[
int
]
DCUInfo
)
MapIdDCU
.
Range
(
func
(
key
,
value
any
)
bool
{
id
:=
key
.
(
int
)
val
:=
value
.
(
*
DCUInfo
)
result
[
id
]
=
*
val
return
true
})
return
result
}
cmd/dcutop/backend/main_test.go
View file @
fbb93034
...
@@ -8,8 +8,22 @@ import (
...
@@ -8,8 +8,22 @@ import (
func
TestUpdateDCUInfo
(
t
*
testing
.
T
)
{
func
TestUpdateDCUInfo
(
t
*
testing
.
T
)
{
for
i
:=
0
;
i
<
10
;
i
++
{
for
i
:=
0
;
i
<
10
;
i
++
{
start
:=
time
.
Now
()
start
:=
time
.
Now
()
UpdateDCUInfo
(
tru
e
)
UpdateDCUInfo
(
fals
e
)
end
:=
time
.
Now
()
end
:=
time
.
Now
()
t
.
Logf
(
"%d ms"
,
end
.
Sub
(
start
)
.
Milliseconds
())
t
.
Logf
(
"%d ms"
,
end
.
Sub
(
start
)
.
Milliseconds
())
}
}
}
}
func
TestGetDCUInfo
(
t
*
testing
.
T
)
{
UpdateDCUInfo
(
true
)
info
:=
GetDCUInfo
()
t
.
Logf
(
"%+v"
,
info
)
for
i
:=
range
10
{
time
.
Sleep
(
time
.
Second
)
start
:=
time
.
Now
()
UpdateDCUInfo
(
false
)
info
=
GetDCUInfo
()
tt
:=
time
.
Since
(
start
)
.
Milliseconds
()
t
.
Logf
(
"%d|%d : %+v"
,
i
,
tt
,
info
)
}
}
cmd/dcutop/tui/dcuinfo.go
View file @
fbb93034
package
tui
package
tui
import
(
import
(
"get-container/gpu"
"fmt"
"get-container/cmd/dcutop/backend"
"maps"
"strconv"
"strings"
"github.com/charmbracelet/bubbles/progress"
tea
"github.com/charmbracelet/bubbletea"
tea
"github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
)
)
type
ModelDCUInfo
struct
{
type
ModelDCUInfo
struct
{
DCUNum
int
// dcu数量
DCUNum
int
// dcu数量
width
,
height
int
// 终端的尺寸
width
,
height
int
// 终端的尺寸
DCUStaticInfo
[]
gpu
.
SMIAllOutput
info
map
[
int
]
backend
.
DCUInfo
// dcu相关信息
DCURunningInfo
[]
gpu
.
DCURunningInfo
pro
progress
.
Model
// 进度条
proWidth
int
// 进度条宽度
}
}
const
(
TopLine
=
`╞═══════════════════════════════╪══════════════════════╪══════════════════════╪`
MiddleLine
=
`├───────────────────────────────┼──────────────────────┼──────────────────────┼`
BottomLine
=
`╞═══════════════════════════════╧══════════════════════╧══════════════════════╪`
OtherWidth
=
8
StaticWidth
=
87
// 固定表格的宽度
ProgressMaxWidth
=
105
// 进度条最大宽度
ProgressMinWidth
=
15
// 进度条最小宽度
)
func
(
m
*
ModelDCUInfo
)
Init
()
tea
.
Cmd
{
func
(
m
*
ModelDCUInfo
)
Init
()
tea
.
Cmd
{
if
m
.
width
<
StaticWidth
+
ProgressMinWidth
{
return
tea
.
Quit
}
m
.
proWidth
=
ProgressMinWidth
if
m
.
width
>
StaticWidth
+
ProgressMaxWidth
{
m
.
proWidth
=
ProgressMaxWidth
}
else
{
m
.
proWidth
=
m
.
width
-
StaticWidth
}
m
.
pro
=
progress
.
New
(
progress
.
WithDefaultGradient
(),
progress
.
WithWidth
(
m
.
proWidth
))
return
nil
return
nil
}
}
func
(
m
*
ModelDCUInfo
)
Update
(
msg
tea
.
Msg
)
(
tea
.
Model
,
tea
.
Cmd
)
{
func
(
m
*
ModelDCUInfo
)
Update
(
inputMsg
tea
.
Msg
)
(
tea
.
Model
,
tea
.
Cmd
)
{
switch
msg
:=
inputMsg
.
(
type
)
{
case
*
ModelMsg
:
clear
(
m
.
info
)
maps
.
Copy
(
m
.
info
,
msg
.
DCUInfo
)
return
m
,
nil
}
return
m
,
nil
return
m
,
nil
}
}
func
(
m
*
ModelDCUInfo
)
View
()
string
{
func
(
m
*
ModelDCUInfo
)
View
()
string
{
return
""
lineWidth
:=
0
l
:=
len
(
m
.
info
)
strBuilder
:=
strings
.
Builder
{}
infos
:=
make
([]
string
,
l
)
for
i
:=
range
l
{
ii
:=
m
.
info
[
i
]
strBuilder
.
WriteString
(
myBorder
.
Left
)
strBuilder
.
WriteByte
(
' '
)
strBuilder
.
WriteString
(
FormatStr
(
strconv
.
Itoa
(
ii
.
Id
),
4
,
lipgloss
.
Left
))
strBuilder
.
WriteByte
(
' '
)
strBuilder
.
WriteString
(
FormatStr
(
ii
.
Name
,
6
,
lipgloss
.
Left
))
strBuilder
.
WriteByte
(
' '
)
strBuilder
.
WriteString
(
FormatStr
(
ii
.
PerformanceLevel
,
17
,
lipgloss
.
Right
))
strBuilder
.
WriteByte
(
' '
)
strBuilder
.
WriteString
(
myBorder
.
Left
)
strBuilder
.
WriteByte
(
' '
)
strBuilder
.
WriteString
(
FormatStr
(
ii
.
BusId
,
13
,
lipgloss
.
Left
))
strBuilder
.
WriteByte
(
' '
)
strBuilder
.
WriteString
(
FormatStr
(
"Off"
,
6
,
lipgloss
.
Right
))
strBuilder
.
WriteByte
(
' '
)
strBuilder
.
WriteString
(
myBorder
.
Left
)
strBuilder
.
WriteByte
(
' '
)
if
ii
.
Mig
{
strBuilder
.
WriteString
(
FormatStr
(
"On"
,
16
,
lipgloss
.
Left
))
}
else
{
strBuilder
.
WriteString
(
FormatStr
(
"Off"
,
16
,
lipgloss
.
Left
))
}
strBuilder
.
WriteByte
(
' '
)
if
ii
.
Ecc
{
strBuilder
.
WriteString
(
FormatStr
(
"On"
,
3
,
lipgloss
.
Right
))
}
else
{
strBuilder
.
WriteString
(
FormatStr
(
"Off"
,
3
,
lipgloss
.
Right
))
}
strBuilder
.
WriteByte
(
' '
)
strBuilder
.
WriteString
(
myBorder
.
Left
)
strBuilder
.
WriteString
(
" DCU: "
)
strBuilder
.
WriteString
(
m
.
pro
.
ViewAs
(
float64
(
ii
.
DCUUTil
)
/
100
))
strBuilder
.
WriteByte
(
' '
)
strBuilder
.
WriteString
(
myBorder
.
Left
)
strBuilder
.
WriteByte
(
'\n'
)
strBuilder
.
WriteString
(
myBorder
.
Left
)
strBuilder
.
WriteByte
(
' '
)
strBuilder
.
WriteString
(
FormatStr
(
ii
.
Fan
,
4
,
lipgloss
.
Left
))
strBuilder
.
WriteByte
(
' '
)
strBuilder
.
WriteString
(
FormatStr
(
fmt
.
Sprintf
(
"%.1fC"
,
ii
.
Temp
),
6
,
lipgloss
.
Left
))
strBuilder
.
WriteByte
(
' '
)
// 18
strBuilder
.
WriteString
(
FormatStr
(
fmt
.
Sprintf
(
"%.1fW / %.1fW"
,
ii
.
PwrAvg
,
ii
.
PwrCap
),
17
,
lipgloss
.
Right
))
strBuilder
.
WriteByte
(
' '
)
strBuilder
.
WriteString
(
myBorder
.
Left
)
strBuilder
.
WriteByte
(
' '
)
// 20
strBuilder
.
WriteString
(
FormatStr
(
fmt
.
Sprintf
(
"%dMiB / %dMiB"
,
ii
.
MemUsed
,
ii
.
MemTotal
),
20
,
lipgloss
.
Right
))
strBuilder
.
WriteByte
(
' '
)
strBuilder
.
WriteString
(
myBorder
.
Left
)
strBuilder
.
WriteByte
(
' '
)
strBuilder
.
WriteString
(
FormatStr
(
fmt
.
Sprintf
(
"%.1f%%"
,
ii
.
DCUUTil
),
6
,
lipgloss
.
Left
))
strBuilder
.
WriteByte
(
' '
)
strBuilder
.
WriteString
(
FormatStr
(
ii
.
PwrMode
,
13
,
lipgloss
.
Right
))
strBuilder
.
WriteByte
(
' '
)
strBuilder
.
WriteString
(
myBorder
.
Left
)
strBuilder
.
WriteString
(
" Mem: "
)
strBuilder
.
WriteString
(
m
.
pro
.
ViewAs
(
float64
(
ii
.
MemUsedPerent
)
/
100
))
strBuilder
.
WriteByte
(
' '
)
strBuilder
.
WriteString
(
myBorder
.
Left
)
infos
[
i
]
=
strBuilder
.
String
()
strBuilder
.
Reset
()
}
if
len
(
infos
)
>
0
{
lineWidth
=
lipgloss
.
Width
(
infos
[
0
])
}
subLen
:=
lineWidth
-
StaticWidth
+
OtherWidth
-
1
if
subLen
<=
0
{
subLen
=
0
}
top
:=
TopLine
+
strings
.
Repeat
(
"═"
,
subLen
)
+
"╕
\n
"
middle
:=
"
\n
"
+
MiddleLine
+
strings
.
Repeat
(
"─"
,
subLen
)
+
"┤
\n
"
bottom
:=
"
\n
"
+
BottomLine
+
strings
.
Repeat
(
"═"
,
subLen
)
+
"╡
\n
"
return
top
+
strings
.
Join
(
infos
,
middle
)
+
bottom
}
}
cmd/dcutop/tui/header.go
View file @
fbb93034
...
@@ -23,12 +23,6 @@ func (mh *ModelHeader) Init() tea.Cmd {
...
@@ -23,12 +23,6 @@ func (mh *ModelHeader) Init() tea.Cmd {
func
(
mh
*
ModelHeader
)
Update
(
inputMsg
tea
.
Msg
)
(
tea
.
Model
,
tea
.
Cmd
)
{
func
(
mh
*
ModelHeader
)
Update
(
inputMsg
tea
.
Msg
)
(
tea
.
Model
,
tea
.
Cmd
)
{
switch
msg
:=
inputMsg
.
(
type
)
{
switch
msg
:=
inputMsg
.
(
type
)
{
case
ModelMsg
:
mh
.
t
=
msg
.
t
mh
.
DCUTopVersion
=
msg
.
MyVersion
mh
.
DriverVersion
=
msg
.
Version
.
DriverVersion
mh
.
SMIVersion
=
msg
.
Version
.
SMIVersion
return
mh
,
nil
case
*
ModelMsg
:
case
*
ModelMsg
:
mh
.
t
=
msg
.
t
mh
.
t
=
msg
.
t
mh
.
DCUTopVersion
=
msg
.
MyVersion
mh
.
DCUTopVersion
=
msg
.
MyVersion
...
@@ -42,8 +36,8 @@ func (mh *ModelHeader) Update(inputMsg tea.Msg) (tea.Model, tea.Cmd) {
...
@@ -42,8 +36,8 @@ func (mh *ModelHeader) Update(inputMsg tea.Msg) (tea.Model, tea.Cmd) {
const
(
const
(
Title
=
`
Title
=
`
├───────────────────────────────┬──────────────────────┬──────────────────────┤
├───────────────────────────────┬──────────────────────┬──────────────────────┤
│ DCU
Name Performance Level│ Bus-Id DisP.A │ MIG M. ECC │
│ DCU Name Performance Level
│ Bus-Id DisP.A │ MIG M. ECC │
│ Fan Temp
Pwr:Usage/Cap│ Memory-Usage │ DCU-Util PowerMode │
│ Fan Temp Pwr:Usage/Cap
│ Memory-Usage │ DCU-Util PowerMode │
`
`
)
)
...
...
cmd/dcutop/tui/main.go
View file @
fbb93034
package
tui
package
tui
import
(
import
(
"get-container/cmd/dcutop/backend"
"get-container/gpu"
"get-container/gpu"
"time"
"time"
...
@@ -18,8 +19,7 @@ type ModelMsg struct {
...
@@ -18,8 +19,7 @@ type ModelMsg struct {
index
uint64
// update次数
index
uint64
// update次数
Version
*
gpu
.
HYVersionInfo
// gpu版本相关信息
Version
*
gpu
.
HYVersionInfo
// gpu版本相关信息
MyVersion
string
MyVersion
string
DCUInfo
[]
gpu
.
DCUInfo
// DCU全量信息
DCUInfo
map
[
int
]
backend
.
DCUInfo
// DCU全量信息
DCURunningInfo
[]
gpu
.
DCURunningInfo
// DCU运行时信息
// DCUPidInfo []gpu.DCUPidInfo // 使用dcu的进程信息
// DCUPidInfo []gpu.DCUPidInfo // 使用dcu的进程信息
}
}
...
@@ -41,7 +41,7 @@ func NewModelMain(width, height int) ModelMain {
...
@@ -41,7 +41,7 @@ func NewModelMain(width, height int) ModelMain {
result
.
width
=
width
result
.
width
=
width
result
.
height
=
height
result
.
height
=
height
result
.
Header
=
&
ModelHeader
{}
result
.
Header
=
&
ModelHeader
{}
result
.
DCUInfo
=
&
ModelDCUInfo
{
width
:
width
,
height
:
height
}
result
.
DCUInfo
=
&
ModelDCUInfo
{
width
:
width
,
height
:
height
,
info
:
make
(
map
[
int
]
backend
.
DCUInfo
)
}
return
result
return
result
}
}
...
@@ -59,17 +59,21 @@ func sendMsgCmd(modelMsg *ModelMsg) tea.Cmd {
...
@@ -59,17 +59,21 @@ func sendMsgCmd(modelMsg *ModelMsg) tea.Cmd {
// Init 初始化信息
// Init 初始化信息
func
(
m
*
ModelMain
)
Init
()
tea
.
Cmd
{
func
(
m
*
ModelMain
)
Init
()
tea
.
Cmd
{
modelMsg
:=
&
ModelMsg
{}
modelMsg
:=
ModelMsg
{}
if
err
:=
initModelInfo
(
modelMsg
);
err
!=
nil
{
if
err
:=
initModelInfo
(
&
modelMsg
);
err
!=
nil
{
return
tea
.
Quit
return
tea
.
Quit
}
}
cmds
:=
make
([]
tea
.
Cmd
,
0
)
cmds
:=
make
([]
tea
.
Cmd
,
0
)
if
c
:=
m
.
Header
.
Init
();
c
!=
nil
{
if
c
:=
m
.
Header
.
Init
();
c
!=
nil
{
cmds
=
append
(
cmds
,
c
)
cmds
=
append
(
cmds
,
c
)
}
}
m
.
modelMsg
=
modelMsg
m
.
DCUInfo
.
DCUNum
=
len
(
modelMsg
.
DCUInfo
)
if
c
:=
m
.
DCUInfo
.
Init
();
c
!=
nil
{
cmds
=
append
(
cmds
,
c
)
}
m
.
modelMsg
=
&
modelMsg
// 将调用初始化方法
// 将调用初始化方法
cmds
=
append
(
cmds
,
tickCmd
(),
sendMsgCmd
(
modelMsg
))
cmds
=
append
(
cmds
,
tickCmd
(),
sendMsgCmd
(
&
modelMsg
))
return
tea
.
Batch
(
cmds
...
)
return
tea
.
Batch
(
cmds
...
)
}
}
...
@@ -85,15 +89,23 @@ func (m *ModelMain) Update(inputMsg tea.Msg) (tea.Model, tea.Cmd) {
...
@@ -85,15 +89,23 @@ func (m *ModelMain) Update(inputMsg tea.Msg) (tea.Model, tea.Cmd) {
updateModelInfo
(
m
.
modelMsg
,
m
.
index
,
time
.
Time
(
msg
))
updateModelInfo
(
m
.
modelMsg
,
m
.
index
,
time
.
Time
(
msg
))
cmds
:=
make
([]
tea
.
Cmd
,
0
)
cmds
:=
make
([]
tea
.
Cmd
,
0
)
header
,
cmdHeader
:=
m
.
Header
.
Update
(
m
.
modelMsg
)
header
,
cmdHeader
:=
m
.
Header
.
Update
(
m
.
modelMsg
)
dcuInfo
,
dcuHeader
:=
m
.
DCUInfo
.
Update
(
m
.
modelMsg
)
m
.
Header
=
header
.
(
*
ModelHeader
)
m
.
Header
=
header
.
(
*
ModelHeader
)
cmds
=
append
(
cmds
,
cmdHeader
,
tickCmd
())
m
.
DCUInfo
=
dcuInfo
.
(
*
ModelDCUInfo
)
cmds
=
append
(
cmds
,
cmdHeader
,
dcuHeader
,
tickCmd
())
return
m
,
tea
.
Batch
(
cmds
...
)
return
m
,
tea
.
Batch
(
cmds
...
)
case
ModelMsg
:
// 初始化
header
,
_
:=
m
.
Header
.
Update
(
m
.
modelMsg
)
dcuInfo
,
_
:=
m
.
DCUInfo
.
Update
(
m
.
modelMsg
)
m
.
Header
=
header
.
(
*
ModelHeader
)
m
.
DCUInfo
=
dcuInfo
.
(
*
ModelDCUInfo
)
return
m
,
nil
}
}
return
m
,
nil
return
m
,
nil
}
}
func
(
m
*
ModelMain
)
View
()
string
{
func
(
m
*
ModelMain
)
View
()
string
{
return
m
.
Header
.
View
()
return
m
.
Header
.
View
()
+
m
.
DCUInfo
.
View
()
}
}
// type ModelDCUInfo struct{}
// type ModelDCUInfo struct{}
...
@@ -125,6 +137,12 @@ func initModelInfo(model *ModelMsg) error {
...
@@ -125,6 +137,12 @@ func initModelInfo(model *ModelMsg) error {
return
err
return
err
}
}
model
.
Version
=
ver
model
.
Version
=
ver
if
model
.
index
%
20
==
0
{
backend
.
UpdateDCUInfo
(
true
)
}
else
{
backend
.
UpdateDCUInfo
(
false
)
}
model
.
DCUInfo
=
backend
.
GetDCUInfo
()
return
nil
return
nil
}
}
...
@@ -132,4 +150,10 @@ func initModelInfo(model *ModelMsg) error {
...
@@ -132,4 +150,10 @@ func initModelInfo(model *ModelMsg) error {
func
updateModelInfo
(
modelMsg
*
ModelMsg
,
index
uint64
,
t
time
.
Time
)
{
func
updateModelInfo
(
modelMsg
*
ModelMsg
,
index
uint64
,
t
time
.
Time
)
{
modelMsg
.
index
=
index
modelMsg
.
index
=
index
modelMsg
.
t
=
t
modelMsg
.
t
=
t
if
modelMsg
.
index
%
20
==
0
{
backend
.
UpdateDCUInfo
(
true
)
}
else
{
backend
.
UpdateDCUInfo
(
false
)
}
modelMsg
.
DCUInfo
=
backend
.
GetDCUInfo
()
}
}
cmd/dcutop/tui/tui_test.go
View file @
fbb93034
...
@@ -2,15 +2,17 @@ package tui
...
@@ -2,15 +2,17 @@ package tui
import
(
import
(
"fmt"
"fmt"
"strings"
"testing"
"testing"
"github.com/charmbracelet/lipgloss"
"github.com/charmbracelet/lipgloss"
)
)
const
Border
=
`├───────────────────────────────┬──────────────────────┬──────────────────────┤
const
S
=
`├───────────────────────────────┼──────────────────────┼──────────────────────┼
│ GPU Name Persistence-M│ Bus-Id Disp.A │ MIG M. Uncorr. ECC │
│ 2 BW200, manual │ 0000:5e:00.0 Off │ Off On │ DCU: ░░░░░░░░░░░░░░░░░░░░░░░░░ 0% │`
│ Fan Temp Perf Pwr:Usage/Cap│ Memory-Usage │ GPU-Util Compute M. │`
func
TestLine
(
t
*
testing
.
T
)
{
t
.
Logf
(
"%d"
,
lipgloss
.
Width
(
S
))
}
func
TestHeader
(
t
*
testing
.
T
)
{
func
TestHeader
(
t
*
testing
.
T
)
{
m
:=
ModelHeader
{}
m
:=
ModelHeader
{}
...
@@ -19,13 +21,6 @@ func TestHeader(t *testing.T) {
...
@@ -19,13 +21,6 @@ func TestHeader(t *testing.T) {
fmt
.
Println
(
m
.
View
())
fmt
.
Println
(
m
.
View
())
}
}
func
TestBorder
(
t
*
testing
.
T
)
{
lines
:=
strings
.
Split
(
Border
,
"
\n
"
)
for
_
,
i
:=
range
lines
{
t
.
Logf
(
"%d"
,
lipgloss
.
Width
(
i
))
}
}
func
TestAis
(
t
*
testing
.
T
)
{
func
TestAis
(
t
*
testing
.
T
)
{
for
i
:=
10
;
i
<
180
;
i
++
{
for
i
:=
10
;
i
<
180
;
i
++
{
str
:=
genXAxis
(
i
)
str
:=
genXAxis
(
i
)
...
@@ -35,3 +30,19 @@ func TestAis(t *testing.T) {
...
@@ -35,3 +30,19 @@ func TestAis(t *testing.T) {
fmt
.
Println
(
str
)
fmt
.
Println
(
str
)
}
}
}
}
func
TestFormatStr
(
t
*
testing
.
T
)
{
str
:=
lipgloss
.
NewStyle
()
.
Foreground
(
lipgloss
.
Color
(
"#2b95ffff"
))
.
SetString
(
"hello world!"
)
.
String
()
t
.
Logf
(
"|%s|"
,
FormatStr
(
str
,
5
,
lipgloss
.
Left
))
t
.
Logf
(
"|%s|"
,
FormatStr
(
str
,
5
,
lipgloss
.
Right
))
t
.
Logf
(
"|%s|"
,
FormatStr
(
str
,
20
,
lipgloss
.
Left
))
t
.
Logf
(
"|%s|"
,
FormatStr
(
str
,
20
,
lipgloss
.
Right
))
}
func
TestModel
(
t
*
testing
.
T
)
{
m
:=
NewModelMain
(
200
,
100
)
m
.
Init
()
m
.
DCUInfo
.
Update
(
m
.
modelMsg
)
str
:=
m
.
View
()
t
.
Log
(
str
)
}
cmd/dcutop/tui/utils.go
View file @
fbb93034
...
@@ -128,3 +128,26 @@ func StackPosition(up, down string, vPos, hPos lipgloss.Position) string {
...
@@ -128,3 +128,26 @@ func StackPosition(up, down string, vPos, hPos lipgloss.Position) string {
return
""
return
""
}
}
// FormatStr 格式化字符串,输出的字符串可视长度为length,hPos表示是左对齐还是右对齐
// str必须为单行字符串,没有\n
func
FormatStr
(
str
string
,
length
int
,
hPos
lipgloss
.
Position
)
string
{
realLen
:=
lipgloss
.
Width
(
str
)
if
realLen
<
length
{
switch
hPos
{
case
lipgloss
.
Left
:
return
str
+
strings
.
Repeat
(
" "
,
length
-
realLen
)
case
lipgloss
.
Right
:
return
strings
.
Repeat
(
" "
,
length
-
realLen
)
+
str
}
}
else
if
realLen
>
length
{
// 需要截断
switch
hPos
{
case
lipgloss
.
Left
:
// 左对齐
return
ansi
.
Truncate
(
str
,
length
,
""
)
case
lipgloss
.
Right
:
// 右对齐
return
ansi
.
TruncateLeft
(
str
,
realLen
-
length
,
""
)
}
}
return
str
}
go.mod
View file @
fbb93034
...
@@ -5,14 +5,15 @@ go 1.24.2
...
@@ -5,14 +5,15 @@ go 1.24.2
require (
require (
github.com/charmbracelet/bubbletea
v1.3.10
github.com/charmbracelet/bubbletea
v1.3.10
github.com/charmbracelet/lipgloss
v1.1.0
github.com/charmbracelet/lipgloss
v1.1.0
github.com/lrstanley/bubblezone
v0.0.0-20240914071701-b48c55a5e78e
github.com/moby/moby/api
v1.52.0-beta.2
github.com/moby/moby/api
v1.52.0-beta.2
github.com/moby/moby/client
v0.1.0-beta.2
github.com/moby/moby/client
v0.1.0-beta.2
github.com/shirou/gopsutil/v4
v4.25.9
github.com/shirou/gopsutil/v4
v4.25.9
)
)
require (
require (
github.com/charmbracelet/bubbles
v0.20.0
// indirect
github.com/charmbracelet/bubbles
v0.20.0
github.com/
lrstanley/bubblezone
v0.
0
.0
-20240914071701-b48c55a5e78e
// indirect
github.com/
charmbracelet/harmonica
v0.
2
.0 // indirect
)
)
require (
require (
...
@@ -21,9 +22,9 @@ require (
...
@@ -21,9 +22,9 @@ require (
github.com/NimbleMarkets/ntcharts
v0.3.1
github.com/NimbleMarkets/ntcharts
v0.3.1
github.com/aymanbagabas/go-osc52/v2
v2.0.1 // indirect
github.com/aymanbagabas/go-osc52/v2
v2.0.1 // indirect
github.com/charmbracelet/colorprofile
v0.2.3-0.20250311203215-f60798e515dc // indirect
github.com/charmbracelet/colorprofile
v0.2.3-0.20250311203215-f60798e515dc // indirect
github.com/charmbracelet/x/ansi
v0.10.1
// indirect
github.com/charmbracelet/x/ansi
v0.10.1
github.com/charmbracelet/x/cellbuf
v0.0.13-0.20250311204145-2c3ea96c31dd // indirect
github.com/charmbracelet/x/cellbuf
v0.0.13-0.20250311204145-2c3ea96c31dd // indirect
github.com/charmbracelet/x/term
v0.2.1
// indirect
github.com/charmbracelet/x/term
v0.2.1
github.com/containerd/errdefs
v1.0.0 // indirect
github.com/containerd/errdefs
v1.0.0 // indirect
github.com/containerd/errdefs/pkg
v0.3.0 // indirect
github.com/containerd/errdefs/pkg
v0.3.0 // indirect
github.com/distribution/reference
v0.6.0 // indirect
github.com/distribution/reference
v0.6.0 // indirect
...
...
gpu/hy-smi.go
View file @
fbb93034
...
@@ -51,7 +51,7 @@ var (
...
@@ -51,7 +51,7 @@ var (
RePCIeRelay
=
regexp
.
MustCompile
(
`(?mi)^PCIe\s*Replay\s*Count\s*:\s*([0-9]*)$`
)
RePCIeRelay
=
regexp
.
MustCompile
(
`(?mi)^PCIe\s*Replay\s*Count\s*:\s*([0-9]*)$`
)
ReSerialNum
=
regexp
.
MustCompile
(
`(?mi)^Serial\s*Number\s*:\s*([0-9a-zA-Z]*)$`
)
ReSerialNum
=
regexp
.
MustCompile
(
`(?mi)^Serial\s*Number\s*:\s*([0-9a-zA-Z]*)$`
)
ReVoltage
=
regexp
.
MustCompile
(
`(?mi)^Voltage\s*\((.*)\)\s*:\s*([0-9.]*)$`
)
ReVoltage
=
regexp
.
MustCompile
(
`(?mi)^Voltage\s*\((.*)\)\s*:\s*([0-9.]*)$`
)
RePCIBus
=
regexp
.
MustCompile
(
`(?mi)^PCI\s*Bus\s*:\s*([0-9a-zA-Z.:]*)$`
)
RePCIBus
=
regexp
.
MustCompile
(
`(?mi)^PCI\s*Bus\s*:\s*([0-9a-zA-Z.:]*)
.*
$`
)
ReMECFWVersion
=
regexp
.
MustCompile
(
`(?mi)^MEC\s*Firmware\s*Version\s*:\s*([0-9.]*)$`
)
ReMECFWVersion
=
regexp
.
MustCompile
(
`(?mi)^MEC\s*Firmware\s*Version\s*:\s*([0-9.]*)$`
)
ReMEC2FWVersion
=
regexp
.
MustCompile
(
`(?mi)^MEC2\s*Firmware\s*Version\s*:\s*([0-9.]*)$`
)
ReMEC2FWVersion
=
regexp
.
MustCompile
(
`(?mi)^MEC2\s*Firmware\s*Version\s*:\s*([0-9.]*)$`
)
ReRLCFWVersion
=
regexp
.
MustCompile
(
`(?mi)^RLC\s*Firmware\s*Version\s*:\s*([0-9.]*)$`
)
ReRLCFWVersion
=
regexp
.
MustCompile
(
`(?mi)^RLC\s*Firmware\s*Version\s*:\s*([0-9.]*)$`
)
...
@@ -342,7 +342,7 @@ func parseSMIAllOutput(id int, str string) (*SMIAllOutput, error) {
...
@@ -342,7 +342,7 @@ func parseSMIAllOutput(id int, str string) (*SMIAllOutput, error) {
result
.
Voltage
=
float32
(
p
)
result
.
Voltage
=
float32
(
p
)
}
}
}
}
if
s
:=
regMatch
(
RePCIBus
,
str
,
2
);
s
!=
nil
{
if
s
:=
regMatch
(
RePCIBus
,
str
,
1
);
s
!=
nil
{
result
.
PCIBus
=
s
[
0
]
result
.
PCIBus
=
s
[
0
]
}
}
if
s
:=
regMatch
(
ReMECFWVersion
,
str
,
1
);
s
!=
nil
{
if
s
:=
regMatch
(
ReMECFWVersion
,
str
,
1
);
s
!=
nil
{
...
...
gpu/regexp_test.go
View file @
fbb93034
...
@@ -291,5 +291,16 @@ func TestParseSMIAllOutput(t *testing.T) {
...
@@ -291,5 +291,16 @@ func TestParseSMIAllOutput(t *testing.T) {
for
_
,
i
:=
range
result
{
for
_
,
i
:=
range
result
{
t
.
Logf
(
"%+v"
,
*
i
)
t
.
Logf
(
"%+v"
,
*
i
)
}
}
}
func
TestPCIBus
(
t
*
testing
.
T
)
{
f
:=
RePCIBus
.
FindStringSubmatch
(
"PCI Bus: 0000:9f:00.0 --> Socket id: 1"
)
for
_
,
l
:=
range
f
{
t
.
Logf
(
"%s"
,
l
)
}
if
s
:=
regMatch
(
RePCIBus
,
"PCI Bus: 0000:9f:00.0 --> Socket id: 1"
,
1
);
s
!=
nil
{
t
.
Logf
(
"%+v"
,
s
)
}
}
}
test-data/output.txt
0 → 100644
View file @
fbb93034
2025-11-12 17:04:45 (Press h for help or q for quit)
╒═════════════════════════════════════════════════════════════════════════════╕
│ hytop: 1.0.0 Driver Version: 6.3.16-V1.1.0 SMI Version: 1.20.0 │
├───────────────────────────────┬──────────────────────┬──────────────────────┤
│ DCU Name Performance Level │ Bus-Id DisP.A │ MIG M. ECC │
│ Fan Temp Pwr:Usage/Cap │ Memory-Usage │ DCU-Util PowerMode │
╞═════════════════════════════════════════════════════════════════════════════╪
│ 0 BW200, manual │ 0000:49:00.0 Off │ Off On │ DCU: ░░░░░░░░░░░░░░░░░░░░░░░░░ 0% │
│ N/A 32.0C 132.0W / 800.0W │ 2MiB / 65520MiB │ 0.0% Normal │ Mem: ░░░░░░░░░░░░░░░░░░░░░░░░░ 0% │
├───────────────────────────────┼──────────────────────┼──────────────────────┼
│ 1 BW200, manual │ 0000:54:00.0 Off │ Off On │ DCU: ░░░░░░░░░░░░░░░░░░░░░░░░░ 0% │
│ N/A 32.0C 126.0W / 800.0W │ 2MiB / 65520MiB │ 0.0% Normal │ Mem: ░░░░░░░░░░░░░░░░░░░░░░░░░ 0% │
├───────────────────────────────┼──────────────────────┼──────────────────────┼
│ 2 BW200, manual │ 0000:5e:00.0 Off │ Off On │ DCU: ░░░░░░░░░░░░░░░░░░░░░░░░░ 0% │
│ N/A 33.0C 132.0W / 800.0W │ 2MiB / 65520MiB │ 0.0% Normal │ Mem: ░░░░░░░░░░░░░░░░░░░░░░░░░ 0% │
├───────────────────────────────┼──────────────────────┼──────────────────────┼
│ 3 BW200, manual │ 0000:67:00.0 Off │ Off On │ DCU: ░░░░░░░░░░░░░░░░░░░░░░░░░ 0% │
│ N/A 34.0C 133.0W / 800.0W │ 2MiB / 65520MiB │ 0.0% Normal │ Mem: ░░░░░░░░░░░░░░░░░░░░░░░░░ 0% │
├───────────────────────────────┼──────────────────────┼──────────────────────┼
│ 4 BW200, manual │ 0000:9c:00.0 Off │ Off On │ DCU: ░░░░░░░░░░░░░░░░░░░░░░░░░ 0% │
│ N/A 34.0C 134.0W / 800.0W │ 2MiB / 65520MiB │ 0.0% Normal │ Mem: ░░░░░░░░░░░░░░░░░░░░░░░░░ 0% │
├───────────────────────────────┼──────────────────────┼──────────────────────┼
│ 5 BW200, manual │ 0000:bc:00.0 Off │ Off On │ DCU: ░░░░░░░░░░░░░░░░░░░░░░░░░ 0% │
│ N/A 34.0C 133.0W / 800.0W │ 2MiB / 65520MiB │ 0.0% Normal │ Mem: ░░░░░░░░░░░░░░░░░░░░░░░░░ 0% │
├───────────────────────────────┼──────────────────────┼──────────────────────┼
│ 6 BW200, manual │ 0000:cd:00.0 Off │ Off On │ DCU: ░░░░░░░░░░░░░░░░░░░░░░░░░ 0% │
│ N/A 32.0C 132.0W / 800.0W │ 2MiB / 65520MiB │ 0.0% Normal │ Mem: ░░░░░░░░░░░░░░░░░░░░░░░░░ 0% │
├───────────────────────────────┼──────────────────────┼──────────────────────┼
│ 7 BW200, manual │ 0000:dd:00.0 Off │ Off On │ DCU: ░░░░░░░░░░░░░░░░░░░░░░░░░ 0% │
│ N/A 31.0C 128.0W / 800.0W │ 2MiB / 65520MiB │ 0.0% Normal │ Mem: ░░░░░░░░░░░░░░░░░░░░░░░░░ 0% │
╘═══════════════════════════════╧══════════════════════╧══════════════════════╧
\ No newline at end of file
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