mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-12-21 23:08:46 +08:00
feat: add more fields to process detail view (#11093)
This commit is contained in:
parent
35c946a064
commit
dc9fdf897b
4 changed files with 182 additions and 18 deletions
|
|
@ -1,11 +1,15 @@
|
||||||
package service
|
package service
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/1Panel-dev/1Panel/agent/app/dto/request"
|
"github.com/1Panel-dev/1Panel/agent/app/dto/request"
|
||||||
"github.com/1Panel-dev/1Panel/agent/utils/common"
|
"github.com/1Panel-dev/1Panel/agent/utils/common"
|
||||||
"github.com/1Panel-dev/1Panel/agent/utils/websocket"
|
"github.com/1Panel-dev/1Panel/agent/utils/websocket"
|
||||||
"github.com/shirou/gopsutil/v4/process"
|
"github.com/shirou/gopsutil/v4/process"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -103,24 +107,20 @@ func (ps *ProcessService) GetProcessInfoByPID(pid int32) (*websocket.PsProcessDa
|
||||||
data.CmdLine = cmdline
|
data.CmdLine = cmdline
|
||||||
}
|
}
|
||||||
|
|
||||||
if memInfo, err := p.MemoryInfo(); err == nil {
|
if memDetail, err := getMemoryDetail(p.Pid); err == nil {
|
||||||
data.Rss = common.FormatBytes(memInfo.RSS)
|
data.Rss = common.FormatBytes(memDetail.RSS)
|
||||||
data.VMS = common.FormatBytes(memInfo.VMS)
|
data.VMS = common.FormatBytes(memDetail.VMS)
|
||||||
data.HWM = common.FormatBytes(memInfo.HWM)
|
data.HWM = common.FormatBytes(memDetail.HWM)
|
||||||
data.Data = common.FormatBytes(memInfo.Data)
|
data.Data = common.FormatBytes(memDetail.Data)
|
||||||
data.Stack = common.FormatBytes(memInfo.Stack)
|
data.Stack = common.FormatBytes(memDetail.Stack)
|
||||||
data.Locked = common.FormatBytes(memInfo.Locked)
|
data.Locked = common.FormatBytes(memDetail.Locked)
|
||||||
data.Swap = common.FormatBytes(memInfo.Swap)
|
data.Swap = common.FormatBytes(memDetail.Swap)
|
||||||
data.RssValue = memInfo.RSS
|
data.Dirty = common.FormatBytes(memDetail.Dirty)
|
||||||
} else {
|
data.RssValue = memDetail.RSS
|
||||||
data.Rss = "--"
|
data.PSS = common.FormatBytes(memDetail.PSS)
|
||||||
data.Data = "--"
|
data.USS = common.FormatBytes(memDetail.USS)
|
||||||
data.VMS = "--"
|
data.Shared = common.FormatBytes(memDetail.Shared)
|
||||||
data.HWM = "--"
|
data.Text = common.FormatBytes(memDetail.Text)
|
||||||
data.Stack = "--"
|
|
||||||
data.Locked = "--"
|
|
||||||
data.Swap = "--"
|
|
||||||
data.RssValue = 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if envs, err := p.Environ(); err == nil {
|
if envs, err := p.Environ(); err == nil {
|
||||||
|
|
@ -133,3 +133,152 @@ func (ps *ProcessService) GetProcessInfoByPID(pid int32) (*websocket.PsProcessDa
|
||||||
|
|
||||||
return data, nil
|
return data, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type MemoryDetail struct {
|
||||||
|
RSS uint64
|
||||||
|
VMS uint64
|
||||||
|
HWM uint64
|
||||||
|
Data uint64
|
||||||
|
Stack uint64
|
||||||
|
Locked uint64
|
||||||
|
Swap uint64
|
||||||
|
|
||||||
|
PSS uint64
|
||||||
|
USS uint64
|
||||||
|
Shared uint64
|
||||||
|
Text uint64
|
||||||
|
Dirty uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
func getMemoryDetail(pid int32) (*MemoryDetail, error) {
|
||||||
|
mem := &MemoryDetail{}
|
||||||
|
|
||||||
|
if err := readStatus(pid, mem); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := readSmapsRollup(pid, mem); err != nil {
|
||||||
|
if err := readSmaps(pid, mem); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mem, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func readStatus(pid int32, mem *MemoryDetail) error {
|
||||||
|
filePath := fmt.Sprintf("/proc/%d/status", pid)
|
||||||
|
file, err := os.Open(filePath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
scanner := bufio.NewScanner(file)
|
||||||
|
|
||||||
|
for scanner.Scan() {
|
||||||
|
line := scanner.Text()
|
||||||
|
fields := strings.Fields(line)
|
||||||
|
if len(fields) < 2 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
key := strings.TrimSuffix(fields[0], ":")
|
||||||
|
value, _ := strconv.ParseUint(fields[1], 10, 64)
|
||||||
|
value *= 1024
|
||||||
|
|
||||||
|
switch key {
|
||||||
|
case "VmRSS":
|
||||||
|
mem.RSS = value
|
||||||
|
case "VmSize":
|
||||||
|
mem.VMS = value
|
||||||
|
case "VmData":
|
||||||
|
mem.Data = value
|
||||||
|
case "VmSwap":
|
||||||
|
mem.Swap = value
|
||||||
|
case "VmExe":
|
||||||
|
mem.Text = value
|
||||||
|
case "RssShmem":
|
||||||
|
mem.Shared = value
|
||||||
|
case "VmHWM":
|
||||||
|
mem.HWM = value
|
||||||
|
case "VmStk":
|
||||||
|
mem.Stack = value
|
||||||
|
case "VmLck":
|
||||||
|
mem.Locked = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return scanner.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
func readSmapsRollup(pid int32, mem *MemoryDetail) error {
|
||||||
|
filePath := fmt.Sprintf("/proc/%d/smaps_rollup", pid)
|
||||||
|
file, err := os.Open(filePath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
scanner := bufio.NewScanner(file)
|
||||||
|
|
||||||
|
for scanner.Scan() {
|
||||||
|
line := scanner.Text()
|
||||||
|
fields := strings.Fields(line)
|
||||||
|
if len(fields) < 2 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
key := strings.TrimSuffix(fields[0], ":")
|
||||||
|
value, _ := strconv.ParseUint(fields[1], 10, 64)
|
||||||
|
value *= 1024
|
||||||
|
|
||||||
|
switch key {
|
||||||
|
case "Pss":
|
||||||
|
mem.PSS = value
|
||||||
|
case "Private_Clean", "Private_Dirty":
|
||||||
|
mem.USS += value
|
||||||
|
case "Shared_Clean", "Shared_Dirty":
|
||||||
|
if mem.Shared == 0 {
|
||||||
|
mem.Shared = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return scanner.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
func readSmaps(pid int32, mem *MemoryDetail) error {
|
||||||
|
filePath := fmt.Sprintf("/proc/%d/smaps", pid)
|
||||||
|
file, err := os.Open(filePath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
scanner := bufio.NewScanner(file)
|
||||||
|
|
||||||
|
for scanner.Scan() {
|
||||||
|
line := scanner.Text()
|
||||||
|
fields := strings.Fields(line)
|
||||||
|
if len(fields) < 2 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
key := strings.TrimSuffix(fields[0], ":")
|
||||||
|
value, _ := strconv.ParseUint(fields[1], 10, 64)
|
||||||
|
value *= 1024
|
||||||
|
|
||||||
|
switch key {
|
||||||
|
case "Pss":
|
||||||
|
mem.PSS += value
|
||||||
|
case "Private_Clean", "Private_Dirty":
|
||||||
|
mem.USS += value
|
||||||
|
case "Shared_Clean", "Shared_Dirty":
|
||||||
|
if mem.Shared == 0 {
|
||||||
|
mem.Shared += value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return scanner.Err()
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -67,6 +67,11 @@ type PsProcessData struct {
|
||||||
Stack string `json:"stack"`
|
Stack string `json:"stack"`
|
||||||
Locked string `json:"locked"`
|
Locked string `json:"locked"`
|
||||||
Swap string `json:"swap"`
|
Swap string `json:"swap"`
|
||||||
|
Dirty string `json:"dirty"`
|
||||||
|
PSS string `json:"pss"`
|
||||||
|
USS string `json:"uss"`
|
||||||
|
Shared string `json:"shared"`
|
||||||
|
Text string `json:"text"`
|
||||||
|
|
||||||
CpuValue float64 `json:"cpuValue"`
|
CpuValue float64 `json:"cpuValue"`
|
||||||
RssValue uint64 `json:"rssValue"`
|
RssValue uint64 `json:"rssValue"`
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,11 @@ export namespace Process {
|
||||||
stack: string;
|
stack: string;
|
||||||
locked: string;
|
locked: string;
|
||||||
swap: string;
|
swap: string;
|
||||||
|
dirty: string;
|
||||||
|
pss: string;
|
||||||
|
uss: string;
|
||||||
|
shared: string;
|
||||||
|
text: string;
|
||||||
|
|
||||||
cpuValue: number;
|
cpuValue: number;
|
||||||
rssValue: number;
|
rssValue: number;
|
||||||
|
|
|
||||||
|
|
@ -38,12 +38,17 @@
|
||||||
<el-tab-pane :label="$t('process.mem')" name="mem">
|
<el-tab-pane :label="$t('process.mem')" name="mem">
|
||||||
<el-descriptions :column="2" border>
|
<el-descriptions :column="2" border>
|
||||||
<el-descriptions-item :label="'rss'">{{ data.rss }}</el-descriptions-item>
|
<el-descriptions-item :label="'rss'">{{ data.rss }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item :label="'pss'">{{ data.pss }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item :label="'uss'">{{ data.uss }}</el-descriptions-item>
|
||||||
<el-descriptions-item :label="'swap'">{{ data.swap }}</el-descriptions-item>
|
<el-descriptions-item :label="'swap'">{{ data.swap }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item :label="'shared'">{{ data.shared }}</el-descriptions-item>
|
||||||
<el-descriptions-item :label="'vms'">{{ data.vms }}</el-descriptions-item>
|
<el-descriptions-item :label="'vms'">{{ data.vms }}</el-descriptions-item>
|
||||||
<el-descriptions-item :label="'hwm'">{{ data.hwm }}</el-descriptions-item>
|
<el-descriptions-item :label="'hwm'">{{ data.hwm }}</el-descriptions-item>
|
||||||
<el-descriptions-item :label="'data'">{{ data.data }}</el-descriptions-item>
|
<el-descriptions-item :label="'data'">{{ data.data }}</el-descriptions-item>
|
||||||
<el-descriptions-item :label="'stack'">{{ data.stack }}</el-descriptions-item>
|
<el-descriptions-item :label="'stack'">{{ data.stack }}</el-descriptions-item>
|
||||||
<el-descriptions-item :label="'locked'">{{ data.locked }}</el-descriptions-item>
|
<el-descriptions-item :label="'locked'">{{ data.locked }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item :label="'text'">{{ data.text }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item :label="'dirty'">{{ data.dirty }}</el-descriptions-item>
|
||||||
</el-descriptions>
|
</el-descriptions>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
<el-tab-pane :label="$t('process.openFiles')" name="openFiles">
|
<el-tab-pane :label="$t('process.openFiles')" name="openFiles">
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue