mirror of
https://github.com/duke-git/lancet.git
synced 2025-10-07 08:20:57 +08:00
feat: add GetProcessInfo for system package
This commit is contained in:
@@ -31,6 +31,11 @@ import (
|
|||||||
- [CompareOsEnv](#CompareOsEnv)
|
- [CompareOsEnv](#CompareOsEnv)
|
||||||
- [ExecCommand](#ExecCommand)
|
- [ExecCommand](#ExecCommand)
|
||||||
- [GetOsBits](#GetOsBits)
|
- [GetOsBits](#GetOsBits)
|
||||||
|
- [StartProcess](#StartProcess)
|
||||||
|
- [StopProcess](#StopProcess)
|
||||||
|
- [KillProcess](#KillProcess)
|
||||||
|
- [GetProcessInfo](#GetProcessInfo)
|
||||||
|
|
||||||
|
|
||||||
<div STYLE="page-break-after: always;"></div>
|
<div STYLE="page-break-after: always;"></div>
|
||||||
|
|
||||||
@@ -404,3 +409,36 @@ func main() {
|
|||||||
// <nil>
|
// <nil>
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### <span id="GetProcessInfo">GetProcessInfo</span>
|
||||||
|
|
||||||
|
<p>根据进程id获取进程信息。</p>
|
||||||
|
|
||||||
|
<b>函数签名:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func GetProcessInfo(pid int) (*ProcessInfo, error)
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>示例:<span style="float:right;display:inline-block">[运行](todo)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/system"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
pid, err := system.StartProcess("ls", "-a")
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
processInfo, err := system.GetProcessInfo(pid)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(processInfo)
|
||||||
|
}
|
||||||
|
```
|
@@ -34,6 +34,8 @@ import (
|
|||||||
- [StartProcess](#StartProcess)
|
- [StartProcess](#StartProcess)
|
||||||
- [StopProcess](#StopProcess)
|
- [StopProcess](#StopProcess)
|
||||||
- [KillProcess](#KillProcess)
|
- [KillProcess](#KillProcess)
|
||||||
|
- [GetProcessInfo](#GetProcessInfo)
|
||||||
|
|
||||||
|
|
||||||
<div STYLE="page-break-after: always;"></div>
|
<div STYLE="page-break-after: always;"></div>
|
||||||
|
|
||||||
@@ -408,3 +410,36 @@ func main() {
|
|||||||
// <nil>
|
// <nil>
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### <span id="GetProcessInfo">GetProcessInfo</span>
|
||||||
|
|
||||||
|
<p>Retrieves detailed process information by pid.</p>
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func GetProcessInfo(pid int) (*ProcessInfo, error)
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>Example:<span style="float:right;display:inline-block">[Run](todo)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/system"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
pid, err := system.StartProcess("ls", "-a")
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
processInfo, err := system.GetProcessInfo(pid)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(processInfo)
|
||||||
|
}
|
||||||
|
```
|
156
system/os.go
156
system/os.go
@@ -6,9 +6,12 @@ package system
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
|
|
||||||
"github.com/duke-git/lancet/v2/validator"
|
"github.com/duke-git/lancet/v2/validator"
|
||||||
@@ -166,3 +169,156 @@ func KillProcess(pid int) error {
|
|||||||
|
|
||||||
return process.Kill()
|
return process.Kill()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ProcessInfo contains detailed information about a process.
|
||||||
|
type ProcessInfo struct {
|
||||||
|
PID int
|
||||||
|
CPU string
|
||||||
|
Memory string
|
||||||
|
State string
|
||||||
|
User string
|
||||||
|
Cmd string
|
||||||
|
Threads []string
|
||||||
|
IOStats string
|
||||||
|
StartTime string
|
||||||
|
ParentPID int
|
||||||
|
NetworkConnections string
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetProcessInfo retrieves detailed process information by pid.
|
||||||
|
// Play: todo
|
||||||
|
func GetProcessInfo(pid int) (*ProcessInfo, error) {
|
||||||
|
var cmd *exec.Cmd
|
||||||
|
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
cmd = exec.Command("tasklist", "/FI", fmt.Sprintf("PID eq %d", pid), "/FO", "CSV", "/V")
|
||||||
|
} else {
|
||||||
|
cmd = exec.Command("ps", "-p", strconv.Itoa(pid), "-o", "pid,%cpu,%mem,state,user,comm")
|
||||||
|
}
|
||||||
|
|
||||||
|
output, err := cmd.Output()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
processInfo, err := parseProcessInfo(output, pid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if runtime.GOOS != "windows" {
|
||||||
|
processInfo.Threads, _ = getThreadsInfo(pid)
|
||||||
|
processInfo.IOStats, _ = getIOStats(pid)
|
||||||
|
processInfo.StartTime, _ = getProcessStartTime(pid)
|
||||||
|
processInfo.ParentPID, _ = getParentProcess(pid)
|
||||||
|
processInfo.NetworkConnections, _ = getNetworkConnections(pid)
|
||||||
|
}
|
||||||
|
|
||||||
|
return processInfo, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseProcessInfo parses the output of `ps` or `tasklist` to fill the ProcessInfo structure.
|
||||||
|
func parseProcessInfo(output []byte, pid int) (*ProcessInfo, error) {
|
||||||
|
lines := strings.Split(string(output), "\n")
|
||||||
|
|
||||||
|
if len(lines) < 2 {
|
||||||
|
return nil, fmt.Errorf("no process found with PID %d", pid)
|
||||||
|
}
|
||||||
|
|
||||||
|
var processInfo ProcessInfo
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
fields := strings.Split(lines[1], "\",\"")
|
||||||
|
if len(fields) < 9 {
|
||||||
|
return nil, fmt.Errorf("unexpected tasklist output format")
|
||||||
|
}
|
||||||
|
|
||||||
|
processInfo = ProcessInfo{
|
||||||
|
PID: pid,
|
||||||
|
CPU: "N/A",
|
||||||
|
Memory: fields[4], // Memory usage in K
|
||||||
|
State: fields[5],
|
||||||
|
User: "N/A",
|
||||||
|
Cmd: fields[8],
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fields := strings.Fields(lines[1])
|
||||||
|
if len(fields) < 6 {
|
||||||
|
return nil, fmt.Errorf("unexpected ps output format")
|
||||||
|
}
|
||||||
|
|
||||||
|
processInfo = ProcessInfo{
|
||||||
|
PID: pid,
|
||||||
|
CPU: fields[1],
|
||||||
|
Memory: fields[2],
|
||||||
|
State: fields[3],
|
||||||
|
User: fields[4],
|
||||||
|
Cmd: fields[5],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &processInfo, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getThreadsInfo(pid int) ([]string, error) {
|
||||||
|
cmd := exec.Command("ps", "-T", "-p", strconv.Itoa(pid))
|
||||||
|
output, err := cmd.Output()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
lines := strings.Split(string(output), "\n")
|
||||||
|
|
||||||
|
var threads []string
|
||||||
|
for _, line := range lines[1:] {
|
||||||
|
if strings.TrimSpace(line) != "" {
|
||||||
|
threads = append(threads, line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return threads, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getIOStats(pid int) (string, error) {
|
||||||
|
filePath := fmt.Sprintf("/proc/%d/io", pid)
|
||||||
|
data, err := os.ReadFile(filePath)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(data), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getProcessStartTime(pid int) (string, error) {
|
||||||
|
cmd := exec.Command("ps", "-p", strconv.Itoa(pid), "-o", "lstart=")
|
||||||
|
output, err := cmd.Output()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.TrimSpace(string(output)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getParentProcess(pid int) (int, error) {
|
||||||
|
cmd := exec.Command("ps", "-o", "ppid=", "-p", strconv.Itoa(pid))
|
||||||
|
output, err := cmd.Output()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ppid, err := strconv.Atoi(strings.TrimSpace(string(output)))
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return ppid, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getNetworkConnections(pid int) (string, error) {
|
||||||
|
cmd := exec.Command("lsof", "-p", strconv.Itoa(pid), "-i")
|
||||||
|
output, err := cmd.Output()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(output), nil
|
||||||
|
}
|
||||||
|
@@ -117,3 +117,17 @@ func ExampleKillProcess() {
|
|||||||
// Output:
|
// Output:
|
||||||
// <nil>
|
// <nil>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ExampleGetProcessInfo() {
|
||||||
|
pid, err := StartProcess("ls", "-a")
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
processInfo, err := GetProcessInfo(pid)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(processInfo)
|
||||||
|
}
|
||||||
|
@@ -4,6 +4,7 @@ import (
|
|||||||
"os/exec"
|
"os/exec"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/duke-git/lancet/v2/internal"
|
"github.com/duke-git/lancet/v2/internal"
|
||||||
)
|
)
|
||||||
@@ -125,3 +126,19 @@ func TestStopProcess(t *testing.T) {
|
|||||||
err = StopProcess(pid)
|
err = StopProcess(pid)
|
||||||
assert.IsNil(err)
|
assert.IsNil(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGetProcessInfo(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
assert := internal.NewAssert(t, "TestGetProcessInfo")
|
||||||
|
|
||||||
|
pid, err := StartProcess("ls", "-a")
|
||||||
|
assert.IsNil(err)
|
||||||
|
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
|
||||||
|
processInfo, err := GetProcessInfo(pid)
|
||||||
|
assert.IsNil(err)
|
||||||
|
|
||||||
|
t.Log(processInfo)
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user