From 33fee83deabd0cc7040758406a830158f8708b84 Mon Sep 17 00:00:00 2001
From: Valentin Rothberg <rothberg@redhat.com>
Date: Fri, 20 Aug 2021 15:22:51 +0200
Subject: [PATCH] add flag to record memory profiles

Add a new flag `--memory-profile=$path` which creates a memory profile.
The generated profile can later be analyzed via `go tool pprof`.

[NO TESTS NEEDED] since it's a hidden flag, devs-only.

Signed-off-by: Valentin Rothberg <rothberg@redhat.com>
---
 cmd/podman/root.go            | 29 +++++++++++++++++++++++------
 pkg/domain/entities/engine.go |  1 +
 2 files changed, 24 insertions(+), 6 deletions(-)

diff --git a/cmd/podman/root.go b/cmd/podman/root.go
index dc4ebb952a..1275f56313 100644
--- a/cmd/podman/root.go
+++ b/cmd/podman/root.go
@@ -224,14 +224,29 @@ func persistentPostRunE(cmd *cobra.Command, args []string) error {
 		return nil
 	}
 
-	if !registry.IsRemote() {
-		if cmd.Flag("cpu-profile").Changed {
-			pprof.StopCPUProfile()
+	registry.ImageEngine().Shutdown(registry.Context())
+	registry.ContainerEngine().Shutdown(registry.Context())
+
+	if registry.IsRemote() {
+		return nil
+	}
+
+	// CPU and memory profiling.
+	if cmd.Flag("cpu-profile").Changed {
+		pprof.StopCPUProfile()
+	}
+	if cmd.Flag("memory-profile").Changed {
+		f, err := os.Create(registry.PodmanConfig().MemoryProfile)
+		if err != nil {
+			return errors.Wrap(err, "creating memory profile")
+		}
+		defer f.Close()
+		runtime.GC() // get up-to-date GC statistics
+		if err := pprof.WriteHeapProfile(f); err != nil {
+			return errors.Wrap(err, "writing memory profile")
 		}
 	}
 
-	registry.ImageEngine().Shutdown(registry.Context())
-	registry.ContainerEngine().Shutdown(registry.Context())
 	return nil
 }
 
@@ -294,7 +309,8 @@ func rootFlags(cmd *cobra.Command, opts *entities.PodmanConfig) {
 		pFlags.StringVar(&cfg.Engine.CgroupManager, cgroupManagerFlagName, cfg.Engine.CgroupManager, "Cgroup manager to use (\"cgroupfs\"|\"systemd\")")
 		_ = cmd.RegisterFlagCompletionFunc(cgroupManagerFlagName, common.AutocompleteCgroupManager)
 
-		pFlags.StringVar(&opts.CPUProfile, "cpu-profile", "", "Path for the cpu profiling results")
+		pFlags.StringVar(&opts.CPUProfile, "cpu-profile", "", "Path for the cpu-profiling results")
+		pFlags.StringVar(&opts.MemoryProfile, "memory-profile", "", "Path for the memory-profiling results")
 
 		conmonFlagName := "conmon"
 		pFlags.StringVar(&opts.ConmonPath, conmonFlagName, "", "Path of the conmon binary")
@@ -354,6 +370,7 @@ func rootFlags(cmd *cobra.Command, opts *entities.PodmanConfig) {
 			"cpu-profile",
 			"default-mounts-file",
 			"max-workers",
+			"memory-profile",
 			"registries-conf",
 			"trace",
 		} {
diff --git a/pkg/domain/entities/engine.go b/pkg/domain/entities/engine.go
index af996ad1e6..a8023f7cff 100644
--- a/pkg/domain/entities/engine.go
+++ b/pkg/domain/entities/engine.go
@@ -39,6 +39,7 @@ type PodmanConfig struct {
 	EngineMode     EngineMode // ABI or Tunneling mode
 	Identity       string     // ssh identity for connecting to server
 	MaxWorks       int        // maximum number of parallel threads
+	MemoryProfile  string     // Hidden: Should memory profile be taken
 	RegistriesConf string     // allows for specifying a custom registries.conf
 	Remote         bool       // Connection to Podman API Service will use RESTful API
 	RuntimePath    string     // --runtime flag will set Engine.RuntimePath