diff --git a/cmd/agent/main.go b/cmd/agent/main.go index 5a8c90176bee5..ed3611a8d625a 100644 --- a/cmd/agent/main.go +++ b/cmd/agent/main.go @@ -16,6 +16,8 @@ import ( "github.com/DataDog/datadog-agent/cmd/agent/command" "github.com/DataDog/datadog-agent/cmd/agent/subcommands" + + "github.com/DataDog/datadog-agent/cmd/internal/rssshrinker" "github.com/DataDog/datadog-agent/cmd/internal/runcmd" "github.com/spf13/cobra" ) @@ -37,6 +39,10 @@ func init() { } func main() { + if err := rssshrinker.ReleaseMemory(); err != nil { + fmt.Fprintf(os.Stderr, "Failed to release memory: %s\n", err) + } + process := strings.TrimSpace(os.Getenv("DD_BUNDLED_AGENT")) if process == "" { diff --git a/cmd/internal/rssshrinker/rssshrinker_linux.go b/cmd/internal/rssshrinker/rssshrinker_linux.go new file mode 100644 index 0000000000000..dbb4e70d1d038 --- /dev/null +++ b/cmd/internal/rssshrinker/rssshrinker_linux.go @@ -0,0 +1,76 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2024-present Datadog, Inc. + +//go:build linux + +// Package rssshrinker provides functions to reduce the process’ RSS +package rssshrinker + +import ( + "bufio" + "fmt" + "os" + "strconv" + "strings" + "syscall" + "unsafe" +) + +//revive:disable:var-naming + +// MADV_PAGEOUT Reclaim these pages. +const MADV_PAGEOUT = 21 + +//revive:enable:var-naming + +// ReleaseMemory releases memory by calling madvise with MADV_PAGEOUT on all +func ReleaseMemory() error { + selfMap, err := os.Open("/proc/self/maps") + if err != nil { + return fmt.Errorf("failed to open /proc/self/maps: %w", err) + } + defer selfMap.Close() + + scanner := bufio.NewScanner(selfMap) + for scanner.Scan() { + fields := strings.Fields(scanner.Text()) + + if len(fields) != 6 { + continue + } + + address, perms, _ /* offset */, _ /* device */, _ /* inode */, pathname := fields[0], fields[1], fields[2], fields[3], fields[4], fields[5] + + if strings.HasPrefix(pathname, "[") { + continue + } + + if len(perms) != 4 || perms[0] != 'r' || perms[1] != '-' { + continue + } + + beginStr, endStr, found := strings.Cut(address, "-") + if !found { + continue + } + + begin, err := strconv.ParseUint(beginStr, 16, 64) + if err != nil { + return fmt.Errorf("failed to parse begin address %q: %w", beginStr, err) + } + + end, err := strconv.ParseUint(endStr, 16, 64) + if err != nil { + return fmt.Errorf("failed to parse end address %q: %w", endStr, err) + } + + // nolint:govet:unsafeptr + if err := syscall.Madvise(unsafe.Slice((*byte)(unsafe.Pointer(uintptr(begin))), end-begin), MADV_PAGEOUT); err != nil { + return fmt.Errorf("failed to madvise: %w", err) + } + } + + return nil +} diff --git a/cmd/internal/rssshrinker/rssshrinker_stub.go b/cmd/internal/rssshrinker/rssshrinker_stub.go new file mode 100644 index 0000000000000..c1b3236ef5b6e --- /dev/null +++ b/cmd/internal/rssshrinker/rssshrinker_stub.go @@ -0,0 +1,14 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2024-present Datadog, Inc. + +//go:build !linux + +// Package rssshrinker provides functions to reduce the process’ RSS +package rssshrinker + +// ReleaseMemory isn’t implemented on this platform +func ReleaseMemory() error { + return nil +} diff --git a/cmd/process-agent/main.go b/cmd/process-agent/main.go index 4c79969cf1fdd..b144bceb2f0e8 100644 --- a/cmd/process-agent/main.go +++ b/cmd/process-agent/main.go @@ -7,9 +7,11 @@ package main import ( + "fmt" _ "net/http/pprof" "os" + "github.com/DataDog/datadog-agent/cmd/internal/rssshrinker" "github.com/DataDog/datadog-agent/cmd/internal/runcmd" "github.com/DataDog/datadog-agent/cmd/process-agent/command" "github.com/DataDog/datadog-agent/cmd/process-agent/subcommands" @@ -18,6 +20,10 @@ import ( // main is the main application entry point func main() { + if err := rssshrinker.ReleaseMemory(); err != nil { + fmt.Fprintf(os.Stderr, "Failed to release memory: %s\n", err) + } + flavor.SetFlavor(flavor.ProcessAgent) os.Args = command.FixDeprecatedFlags(os.Args, os.Stdout) diff --git a/cmd/security-agent/main_nix.go b/cmd/security-agent/main_nix.go index ad6a9f2f1b4be..270a6db7cb734 100644 --- a/cmd/security-agent/main_nix.go +++ b/cmd/security-agent/main_nix.go @@ -9,11 +9,13 @@ package main import ( + "fmt" "os" _ "expvar" // Blank import used because this isn't directly used in this file _ "net/http/pprof" // Blank import used because this isn't directly used in this file + "github.com/DataDog/datadog-agent/cmd/internal/rssshrinker" "github.com/DataDog/datadog-agent/cmd/internal/runcmd" "github.com/DataDog/datadog-agent/cmd/security-agent/command" "github.com/DataDog/datadog-agent/cmd/security-agent/subcommands" @@ -21,6 +23,10 @@ import ( ) func main() { + if err := rssshrinker.ReleaseMemory(); err != nil { + fmt.Fprintf(os.Stderr, "Failed to release memory: %s\n", err) + } + // set the Agent flavor flavor.SetFlavor(flavor.SecurityAgent) diff --git a/cmd/system-probe/main.go b/cmd/system-probe/main.go index 9397dac36e99b..5d415b02758cf 100644 --- a/cmd/system-probe/main.go +++ b/cmd/system-probe/main.go @@ -9,14 +9,20 @@ package main import ( + "fmt" "os" + "github.com/DataDog/datadog-agent/cmd/internal/rssshrinker" "github.com/DataDog/datadog-agent/cmd/internal/runcmd" "github.com/DataDog/datadog-agent/cmd/system-probe/command" "github.com/DataDog/datadog-agent/cmd/system-probe/subcommands" ) func main() { + if err := rssshrinker.ReleaseMemory(); err != nil { + fmt.Fprintf(os.Stderr, "Failed to release memory: %s\n", err) + } + rootCmd := command.MakeCommand(subcommands.SysprobeSubcommands()) command.SetDefaultCommandIfNonePresent(rootCmd) os.Exit(runcmd.Run(rootCmd)) diff --git a/cmd/trace-agent/main.go b/cmd/trace-agent/main.go index 79a71a3a9cfa0..80497bec87b7e 100644 --- a/cmd/trace-agent/main.go +++ b/cmd/trace-agent/main.go @@ -7,14 +7,20 @@ package main import ( + "fmt" "os" + "github.com/DataDog/datadog-agent/cmd/internal/rssshrinker" "github.com/DataDog/datadog-agent/cmd/internal/runcmd" "github.com/DataDog/datadog-agent/cmd/trace-agent/command" "github.com/DataDog/datadog-agent/pkg/util/flavor" ) func main() { + if err := rssshrinker.ReleaseMemory(); err != nil { + fmt.Fprintf(os.Stderr, "Failed to release memory: %s\n", err) + } + flavor.SetFlavor(flavor.TraceAgent) os.Args = command.FixDeprecatedFlags(os.Args, os.Stdout)