diff --git a/changelog/20609.txt b/changelog/20609.txt new file mode 100644 index 000000000000..fe92833da52d --- /dev/null +++ b/changelog/20609.txt @@ -0,0 +1,4 @@ +```release-note:improvement +command/server: Add support for dumping pprof files to the filesystem via SIGUSR2 when +`VAULT_PPROF_WRITE_TO_FILE=true` is set on the server. +``` \ No newline at end of file diff --git a/command/server.go b/command/server.go index dedc085fdd67..31eab7ef0f88 100644 --- a/command/server.go +++ b/command/server.go @@ -1681,6 +1681,44 @@ func (c *ServerCommand) Run(args []string) int { c.logger.Info(fmt.Sprintf("Wrote stacktrace to: %s", f.Name())) f.Close() } + + // We can only get pprof outputs via the API but sometimes Vault can get + // into a state where it cannot process requests so we can get pprof outputs + // via SIGUSR2. + if os.Getenv("VAULT_PPROF_WRITE_TO_FILE") != "" { + dir := "" + path := os.Getenv("VAULT_PPROF_FILE_PATH") + if path != "" { + if _, err := os.Stat(path); err != nil { + c.logger.Error("Checking pprof path failed", "error", err) + continue + } + dir = path + } else { + dir, err = os.MkdirTemp("", "vault-pprof") + if err != nil { + c.logger.Error("Could not create temporary directory for pprof", "error", err) + continue + } + } + + dumps := []string{"goroutine", "heap", "allocs", "threadcreate"} + for _, dump := range dumps { + pFile, err := os.Create(filepath.Join(dir, dump)) + if err != nil { + c.logger.Error("error creating pprof file", "name", dump, "error", err) + break + } + + err = pprof.Lookup(dump).WriteTo(pFile, 0) + if err != nil { + c.logger.Error("error generating pprof data", "name", dump, "error", err) + break + } + } + + c.logger.Info(fmt.Sprintf("Wrote pprof files to: %s", dir)) + } } } // Notify systemd that the server is shutting down