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
You need to sign in or sign up before continuing.
Commit
fbb93034
authored
Nov 12, 2025
by
liming6
Browse files
feature 完成表格部分
parent
687d3e07
Changes
11
Hide 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"
...
@@ -14,12 +15,11 @@ const (
...
@@ -14,12 +15,11 @@ const (
// ModelMsg 模型信息,在父组件和各个子组件间共享信息
// ModelMsg 模型信息,在父组件和各个子组件间共享信息
type
ModelMsg
struct
{
type
ModelMsg
struct
{
t
time
.
Time
// 当前时间
t
time
.
Time
// 当前时间
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