Skip to content

Commit 9ebd458

Browse files
authored
[AGENTRUN-1124] Replace gopsutil fork with upstream where possible (#47700)
### What does this PR do? Replace the gopsutil fork with upstream where possible, and isolate the remaining uses. See below for the impact of some of the changes. The fork is now only used on Darwin for live process collection, I'll look into that in a follow-up. ### Motivation Remove the fork as much as possible. ### Describe how you validated your changes CI ### Additional Notes Changes with behavioral impact: - `host.PlatformInformation()` on Darwin: `PlatformFamily` changes from always "darwin" to either "Standalone Workstation" or "Server". - `host.PlatformInformation()` on Windows: version field previously returned "10.0.19041 Build 19041" (`WMI`). Now calls `KernelVersion()` instead, returning "10.0.19041.x Build 19041.x" (`RtlGetVersion`, same format, adds UBR patch revision). - `host.BootTime()` on Linux (test only): upstream uses `/proc/uptime` float arithmetic in Docker/LXC containers instead of the integer btime from `/proc/stat`, so the test assertion is relaxed to within 1 second. No behavior changes: - `host.BootTime()` on Windows: different underlying API (`GetTickCount64` vs `WMI`), same result. - `host.KernelVersion()` on Linux: `uname()` syscall vs `/proc/sys/kernel/osrelease`, same value. - `host.PlatformInformation()` on Linux: upstream v4 respects `HOST_ETC` identically to the fork, same results for all supported distros. - `host.Info()` on Linux: same fields and values, stale comment about missing containerized environment support removed. - `process.MemoryMaps()`: struct literal replaced with `NewProcess()`, same behavior. Co-authored-by: pierre.gimalac <pierre.gimalac@datadoghq.com>
1 parent f833cb1 commit 9ebd458

File tree

10 files changed

+144
-132
lines changed

10 files changed

+144
-132
lines changed

comp/logonduration/impl/impl_windows.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import (
1414
"sync"
1515
"time"
1616

17-
"github.com/DataDog/gopsutil/host"
17+
"github.com/shirou/gopsutil/v4/host"
1818

1919
configcomp "github.com/DataDog/datadog-agent/comp/core/config"
2020
hostname "github.com/DataDog/datadog-agent/comp/core/hostname/hostnameinterface"

pkg/network/tracer/offsetguess/tracer.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import (
2323
"time"
2424

2525
manager "github.com/DataDog/ebpf-manager"
26-
"github.com/DataDog/gopsutil/host"
26+
"github.com/shirou/gopsutil/v4/host"
2727
"golang.org/x/sys/unix"
2828

2929
ddebpf "github.com/DataDog/datadog-agent/pkg/ebpf"

pkg/process/checks/system_info.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,8 @@
88
package checks
99

1010
import (
11-
// waiting for upstream fix to platform collection in containerized environments
12-
"github.com/DataDog/gopsutil/host"
1311
"github.com/shirou/gopsutil/v4/cpu"
12+
"github.com/shirou/gopsutil/v4/host"
1413
"github.com/shirou/gopsutil/v4/mem"
1514

1615
model "github.com/DataDog/agent-payload/v5/process"

pkg/process/checks/system_info_darwin.go

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,8 @@ import (
1111
"errors"
1212

1313
model "github.com/DataDog/agent-payload/v5/process"
14-
// difference between methods for collecting macOS platform, kernel version
15-
// between shirou and Datadog psutil
16-
"github.com/DataDog/gopsutil/host"
1714
"github.com/shirou/gopsutil/v4/cpu"
15+
"github.com/shirou/gopsutil/v4/host"
1816
"github.com/shirou/gopsutil/v4/mem"
1917
"golang.org/x/sys/unix"
2018
)

pkg/process/procutil/process_linux_test.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,7 @@ import (
2020
"github.com/stretchr/testify/assert"
2121
"github.com/stretchr/testify/require"
2222

23-
// relying upon fork BootTime behavior
24-
"github.com/DataDog/gopsutil/host"
23+
"github.com/shirou/gopsutil/v4/host"
2524
// using process.AllProcesses()
2625
"github.com/DataDog/gopsutil/process"
2726
)
@@ -976,7 +975,9 @@ func TestBootTimeLocalFS(t *testing.T) {
976975
defer probe.Close()
977976
expectT, err := host.BootTime()
978977
assert.NoError(t, err)
979-
assert.Equal(t, expectT, probe.bootTime.Load())
978+
// upstream BootTime may use /proc/uptime (float arithmetic) in container environments
979+
// rather than the integer btime from /proc/stat, so allow 1 second of difference
980+
assert.InDelta(t, float64(expectT), float64(probe.bootTime.Load()), 1.0)
980981
}
981982

982983
func TestBootTimeRefresh(t *testing.T) {

pkg/process/procutil/process_model.go

Lines changed: 0 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,8 @@ import (
1111
"strconv"
1212
"strings"
1313

14-
"github.com/DataDog/gopsutil/cpu"
15-
1614
"github.com/DataDog/datadog-agent/pkg/discovery/tracermetadata"
1715
"github.com/DataDog/datadog-agent/pkg/languagedetection/languagemodels"
18-
19-
// using process.FilledProcess
20-
"github.com/DataDog/gopsutil/process"
2116
)
2217

2318
// InjectionState represents the APM injection state of a process
@@ -325,112 +320,3 @@ type NumCtxSwitchesStat struct {
325320
Voluntary int64
326321
Involuntary int64
327322
}
328-
329-
// ConvertAllFilledProcesses takes a group of FilledProcess objects and convert them into Process
330-
func ConvertAllFilledProcesses(processes map[int32]*process.FilledProcess) map[int32]*Process {
331-
result := make(map[int32]*Process, len(processes))
332-
for pid, p := range processes {
333-
result[pid] = ConvertFromFilledProcess(p)
334-
}
335-
return result
336-
}
337-
338-
// ConvertAllFilledProcessesToStats takes a group of FilledProcess objects and convert them into Stats
339-
func ConvertAllFilledProcessesToStats(processes map[int32]*process.FilledProcess) map[int32]*Stats {
340-
stats := make(map[int32]*Stats, len(processes))
341-
for pid, p := range processes {
342-
stats[pid] = ConvertFilledProcessesToStats(p)
343-
}
344-
return stats
345-
}
346-
347-
// ConvertFilledProcessesToStats takes a group of FilledProcess objects and convert them into Stats
348-
func ConvertFilledProcessesToStats(p *process.FilledProcess) *Stats {
349-
return &Stats{
350-
CreateTime: p.CreateTime,
351-
Status: p.Status,
352-
Nice: p.Nice,
353-
OpenFdCount: p.OpenFdCount,
354-
NumThreads: p.NumThreads,
355-
CPUTime: ConvertFromCPUStat(p.CpuTime),
356-
MemInfo: ConvertFromMemInfo(p.MemInfo),
357-
MemInfoEx: ConvertFromMemInfoEx(p.MemInfoEx),
358-
IOStat: ConvertFromIOStats(p.IOStat),
359-
CtxSwitches: ConvertFromCtxSwitches(p.CtxSwitches),
360-
}
361-
}
362-
363-
// ConvertFromFilledProcess takes a FilledProcess object and convert it into Process
364-
func ConvertFromFilledProcess(p *process.FilledProcess) *Process {
365-
return &Process{
366-
Pid: p.Pid,
367-
Ppid: p.Ppid,
368-
NsPid: p.NsPid,
369-
Name: p.Name,
370-
Cwd: p.Cwd,
371-
Exe: p.Exe,
372-
Cmdline: p.Cmdline,
373-
Username: p.Username,
374-
Uids: p.Uids,
375-
Gids: p.Gids,
376-
Stats: ConvertFilledProcessesToStats(p),
377-
}
378-
}
379-
380-
// ConvertFromCPUStat converts gopsutil TimesStat object to CPUTimesStat in procutil
381-
func ConvertFromCPUStat(s cpu.TimesStat) *CPUTimesStat {
382-
return &CPUTimesStat{
383-
User: s.User,
384-
System: s.System,
385-
Idle: s.Idle,
386-
Nice: s.Nice,
387-
Iowait: s.Iowait,
388-
Irq: s.Irq,
389-
Softirq: s.Softirq,
390-
Steal: s.Steal,
391-
Guest: s.Guest,
392-
GuestNice: s.GuestNice,
393-
Stolen: s.Stolen,
394-
Timestamp: s.Timestamp,
395-
}
396-
}
397-
398-
// ConvertFromMemInfo converts gopsutil MemoryInfoStat object to MemoryInfoStat in procutil
399-
func ConvertFromMemInfo(s *process.MemoryInfoStat) *MemoryInfoStat {
400-
return &MemoryInfoStat{
401-
RSS: s.RSS,
402-
VMS: s.VMS,
403-
Swap: s.Swap,
404-
}
405-
}
406-
407-
// ConvertFromMemInfoEx converts gopsutil MemoryInfoExStat object to MemoryInfoExStat in procutil
408-
func ConvertFromMemInfoEx(s *process.MemoryInfoExStat) *MemoryInfoExStat {
409-
return &MemoryInfoExStat{
410-
RSS: s.RSS,
411-
VMS: s.VMS,
412-
Shared: s.Shared,
413-
Text: s.Text,
414-
Lib: s.Lib,
415-
Data: s.Data,
416-
Dirty: s.Dirty,
417-
}
418-
}
419-
420-
// ConvertFromIOStats converts gopsutil IOCountersStat object to IOCounterStat in procutil
421-
func ConvertFromIOStats(s *process.IOCountersStat) *IOCountersStat {
422-
return &IOCountersStat{
423-
ReadCount: int64(s.ReadCount),
424-
WriteCount: int64(s.WriteCount),
425-
ReadBytes: int64(s.ReadBytes),
426-
WriteBytes: int64(s.WriteBytes),
427-
}
428-
}
429-
430-
// ConvertFromCtxSwitches converts gopsutil NumCtxSwitchesStat object to NumCtxSwitchesStat in procutil
431-
func ConvertFromCtxSwitches(s *process.NumCtxSwitchesStat) *NumCtxSwitchesStat {
432-
return &NumCtxSwitchesStat{
433-
Voluntary: s.Voluntary,
434-
Involuntary: s.Involuntary,
435-
}
436-
}
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
// Unless explicitly stated otherwise all files in this repository are licensed
2+
// under the Apache License Version 2.0.
3+
// This product includes software developed at Datadog (https://www.datadoghq.com/).
4+
// Copyright 2016-present Datadog, Inc.
5+
6+
//go:build (!linux && !windows) || test
7+
8+
package procutil
9+
10+
import (
11+
"github.com/DataDog/gopsutil/cpu"
12+
"github.com/DataDog/gopsutil/process"
13+
)
14+
15+
// ConvertAllFilledProcesses takes a group of FilledProcess objects and convert them into Process
16+
func ConvertAllFilledProcesses(processes map[int32]*process.FilledProcess) map[int32]*Process {
17+
result := make(map[int32]*Process, len(processes))
18+
for pid, p := range processes {
19+
result[pid] = ConvertFromFilledProcess(p)
20+
}
21+
return result
22+
}
23+
24+
// ConvertAllFilledProcessesToStats takes a group of FilledProcess objects and convert them into Stats
25+
func ConvertAllFilledProcessesToStats(processes map[int32]*process.FilledProcess) map[int32]*Stats {
26+
stats := make(map[int32]*Stats, len(processes))
27+
for pid, p := range processes {
28+
stats[pid] = ConvertFilledProcessesToStats(p)
29+
}
30+
return stats
31+
}
32+
33+
// ConvertFilledProcessesToStats takes a group of FilledProcess objects and convert them into Stats
34+
func ConvertFilledProcessesToStats(p *process.FilledProcess) *Stats {
35+
return &Stats{
36+
CreateTime: p.CreateTime,
37+
Status: p.Status,
38+
Nice: p.Nice,
39+
OpenFdCount: p.OpenFdCount,
40+
NumThreads: p.NumThreads,
41+
CPUTime: ConvertFromCPUStat(p.CpuTime),
42+
MemInfo: ConvertFromMemInfo(p.MemInfo),
43+
MemInfoEx: ConvertFromMemInfoEx(p.MemInfoEx),
44+
IOStat: ConvertFromIOStats(p.IOStat),
45+
CtxSwitches: ConvertFromCtxSwitches(p.CtxSwitches),
46+
}
47+
}
48+
49+
// ConvertFromFilledProcess takes a FilledProcess object and convert it into Process
50+
func ConvertFromFilledProcess(p *process.FilledProcess) *Process {
51+
return &Process{
52+
Pid: p.Pid,
53+
Ppid: p.Ppid,
54+
NsPid: p.NsPid,
55+
Name: p.Name,
56+
Cwd: p.Cwd,
57+
Exe: p.Exe,
58+
Cmdline: p.Cmdline,
59+
Username: p.Username,
60+
Uids: p.Uids,
61+
Gids: p.Gids,
62+
Stats: ConvertFilledProcessesToStats(p),
63+
}
64+
}
65+
66+
// ConvertFromCPUStat converts gopsutil TimesStat object to CPUTimesStat in procutil
67+
func ConvertFromCPUStat(s cpu.TimesStat) *CPUTimesStat {
68+
return &CPUTimesStat{
69+
User: s.User,
70+
System: s.System,
71+
Idle: s.Idle,
72+
Nice: s.Nice,
73+
Iowait: s.Iowait,
74+
Irq: s.Irq,
75+
Softirq: s.Softirq,
76+
Steal: s.Steal,
77+
Guest: s.Guest,
78+
GuestNice: s.GuestNice,
79+
Stolen: s.Stolen,
80+
Timestamp: s.Timestamp,
81+
}
82+
}
83+
84+
// ConvertFromMemInfo converts gopsutil MemoryInfoStat object to MemoryInfoStat in procutil
85+
func ConvertFromMemInfo(s *process.MemoryInfoStat) *MemoryInfoStat {
86+
return &MemoryInfoStat{
87+
RSS: s.RSS,
88+
VMS: s.VMS,
89+
Swap: s.Swap,
90+
}
91+
}
92+
93+
// ConvertFromMemInfoEx converts gopsutil MemoryInfoExStat object to MemoryInfoExStat in procutil
94+
func ConvertFromMemInfoEx(s *process.MemoryInfoExStat) *MemoryInfoExStat {
95+
return &MemoryInfoExStat{
96+
RSS: s.RSS,
97+
VMS: s.VMS,
98+
Shared: s.Shared,
99+
Text: s.Text,
100+
Lib: s.Lib,
101+
Data: s.Data,
102+
Dirty: s.Dirty,
103+
}
104+
}
105+
106+
// ConvertFromIOStats converts gopsutil IOCountersStat object to IOCounterStat in procutil
107+
func ConvertFromIOStats(s *process.IOCountersStat) *IOCountersStat {
108+
return &IOCountersStat{
109+
ReadCount: int64(s.ReadCount),
110+
WriteCount: int64(s.WriteCount),
111+
ReadBytes: int64(s.ReadBytes),
112+
WriteBytes: int64(s.WriteBytes),
113+
}
114+
}
115+
116+
// ConvertFromCtxSwitches converts gopsutil NumCtxSwitchesStat object to NumCtxSwitchesStat in procutil
117+
func ConvertFromCtxSwitches(s *process.NumCtxSwitchesStat) *NumCtxSwitchesStat {
118+
return &NumCtxSwitchesStat{
119+
Voluntary: s.Voluntary,
120+
Involuntary: s.Involuntary,
121+
}
122+
}

pkg/sbom/collectors/host/host_wmi.go

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,7 @@ import (
2020
"github.com/DataDog/datadog-agent/pkg/util/option"
2121
"github.com/DataDog/datadog-agent/pkg/util/pointer"
2222
"github.com/DataDog/datadog-agent/pkg/util/winutil"
23-
"github.com/DataDog/gopsutil/host"
24-
25-
host2 "github.com/shirou/gopsutil/v4/host"
23+
"github.com/shirou/gopsutil/v4/host"
2624
"github.com/yusufpapurcu/wmi"
2725
)
2826

@@ -124,12 +122,17 @@ func (c *Collector) Init(_ config.Component, _ option.Option[workloadmeta.Compon
124122
return err
125123
}
126124

127-
c.platform, c.family, c.build, err = host.PlatformInformation()
125+
c.platform, c.family, _, err = host.PlatformInformation()
126+
if err != nil {
127+
return err
128+
}
129+
130+
c.build, err = host.KernelVersion()
128131
if err != nil {
129132
return err
130133
}
131134

132-
c.arch, err = host2.KernelArch()
135+
c.arch, err = host.KernelArch()
133136
if err != nil {
134137
return err
135138
}

pkg/security/security_profile/activity_tree/process_node_snapshot_test.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,18 @@ import (
1212
"os"
1313
"testing"
1414

15-
legacyprocess "github.com/DataDog/gopsutil/process"
15+
"github.com/shirou/gopsutil/v4/process"
1616
"github.com/stretchr/testify/assert"
1717
)
1818

1919
func TestSnapshotMemoryMappedFiles(t *testing.T) {
2020
pid := os.Getpid()
2121

2222
// gopsutil
23-
fakeprocess := legacyprocess.Process{Pid: int32(pid)}
23+
fakeprocess, err := process.NewProcess(int32(pid))
24+
if err != nil {
25+
t.Fatal(err)
26+
}
2427
smapsPtr, err := fakeprocess.MemoryMaps(false)
2528
if err != nil {
2629
t.Fatal(err)

pkg/util/kernel/platform.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
package kernel
99

1010
import (
11-
gopsutilhost "github.com/DataDog/gopsutil/host"
11+
gopsutilhost "github.com/shirou/gopsutil/v4/host"
1212

1313
"github.com/DataDog/datadog-agent/pkg/util/funcs"
1414
)

0 commit comments

Comments
 (0)