mirror of
https://github.com/optim-enterprises-bv/vault.git
synced 2025-11-01 11:08:10 +00:00
Add SIGUSR2 pprof to agent and proxy (#27510)
* Add SIGUSR2 pprof to agent * changelog * Update command/agent.go Co-authored-by: Violet Hynes <violet.hynes@hashicorp.com> * Update command/agent.go Co-authored-by: Violet Hynes <violet.hynes@hashicorp.com> * Add to proxy, update tests * Fix path * Changelog * dry * choose one error style --------- Co-authored-by: Violet Hynes <violet.hynes@hashicorp.com>
This commit is contained in:
6
changelog/27510.txt
Normal file
6
changelog/27510.txt
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
```release-note:improvement
|
||||||
|
agent: Add the ability to dump pprof to the filesystem using SIGUSR2
|
||||||
|
```
|
||||||
|
```release-note:improvement
|
||||||
|
proxy: Add the ability to dump pprof to the filesystem using SIGUSR2
|
||||||
|
```
|
||||||
@@ -13,6 +13,7 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
@@ -74,6 +75,7 @@ type AgentCommand struct {
|
|||||||
|
|
||||||
ShutdownCh chan struct{}
|
ShutdownCh chan struct{}
|
||||||
SighupCh chan struct{}
|
SighupCh chan struct{}
|
||||||
|
SigUSR2Ch chan struct{}
|
||||||
|
|
||||||
tlsReloadFuncsLock sync.RWMutex
|
tlsReloadFuncsLock sync.RWMutex
|
||||||
tlsReloadFuncs []reloadutil.ReloadFunc
|
tlsReloadFuncs []reloadutil.ReloadFunc
|
||||||
@@ -758,6 +760,16 @@ func (c *AgentCommand) Run(args []string) int {
|
|||||||
case c.reloadedCh <- struct{}{}:
|
case c.reloadedCh <- struct{}{}:
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
|
case <-c.SigUSR2Ch:
|
||||||
|
pprofPath := filepath.Join(os.TempDir(), "vault-agent-pprof")
|
||||||
|
cpuProfileDuration := time.Second * 1
|
||||||
|
err := WritePprofToFile(pprofPath, cpuProfileDuration)
|
||||||
|
if err != nil {
|
||||||
|
c.logger.Error(err.Error())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
c.logger.Info(fmt.Sprintf("Wrote pprof files to: %s", pprofPath))
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -91,6 +91,7 @@ func testAgentCommand(tb testing.TB, logger hclog.Logger) (*cli.MockUi, *AgentCo
|
|||||||
},
|
},
|
||||||
ShutdownCh: MakeShutdownCh(),
|
ShutdownCh: MakeShutdownCh(),
|
||||||
SighupCh: MakeSighupCh(),
|
SighupCh: MakeSighupCh(),
|
||||||
|
SigUSR2Ch: MakeSigUSR2Ch(),
|
||||||
logger: logger,
|
logger: logger,
|
||||||
startedCh: make(chan struct{}, 5),
|
startedCh: make(chan struct{}, 5),
|
||||||
reloadedCh: make(chan struct{}, 5),
|
reloadedCh: make(chan struct{}, 5),
|
||||||
|
|||||||
@@ -192,6 +192,7 @@ func initCommands(ui, serverCmdUi cli.Ui, runOpts *RunOptions) map[string]cli.Co
|
|||||||
},
|
},
|
||||||
ShutdownCh: MakeShutdownCh(),
|
ShutdownCh: MakeShutdownCh(),
|
||||||
SighupCh: MakeSighupCh(),
|
SighupCh: MakeSighupCh(),
|
||||||
|
SigUSR2Ch: MakeSigUSR2Ch(),
|
||||||
}, nil
|
}, nil
|
||||||
},
|
},
|
||||||
"agent generate-config": func() (cli.Command, error) {
|
"agent generate-config": func() (cli.Command, error) {
|
||||||
@@ -576,6 +577,7 @@ func initCommands(ui, serverCmdUi cli.Ui, runOpts *RunOptions) map[string]cli.Co
|
|||||||
},
|
},
|
||||||
ShutdownCh: MakeShutdownCh(),
|
ShutdownCh: MakeShutdownCh(),
|
||||||
SighupCh: MakeSighupCh(),
|
SighupCh: MakeSighupCh(),
|
||||||
|
SigUSR2Ch: MakeSigUSR2Ch(),
|
||||||
}, nil
|
}, nil
|
||||||
},
|
},
|
||||||
"policy": func() (cli.Command, error) {
|
"policy": func() (cli.Command, error) {
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
@@ -71,6 +72,7 @@ type ProxyCommand struct {
|
|||||||
|
|
||||||
ShutdownCh chan struct{}
|
ShutdownCh chan struct{}
|
||||||
SighupCh chan struct{}
|
SighupCh chan struct{}
|
||||||
|
SigUSR2Ch chan struct{}
|
||||||
|
|
||||||
tlsReloadFuncsLock sync.RWMutex
|
tlsReloadFuncsLock sync.RWMutex
|
||||||
tlsReloadFuncs []reloadutil.ReloadFunc
|
tlsReloadFuncs []reloadutil.ReloadFunc
|
||||||
@@ -715,6 +717,16 @@ func (c *ProxyCommand) Run(args []string) int {
|
|||||||
case c.reloadedCh <- struct{}{}:
|
case c.reloadedCh <- struct{}{}:
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
|
case <-c.SigUSR2Ch:
|
||||||
|
pprofPath := filepath.Join(os.TempDir(), "vault-proxy-pprof")
|
||||||
|
cpuProfileDuration := time.Second * 1
|
||||||
|
err := WritePprofToFile(pprofPath, cpuProfileDuration)
|
||||||
|
if err != nil {
|
||||||
|
c.logger.Error(err.Error())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
c.logger.Info(fmt.Sprintf("Wrote pprof files to: %s", pprofPath))
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ func testProxyCommand(tb testing.TB, logger hclog.Logger) (*cli.MockUi, *ProxyCo
|
|||||||
},
|
},
|
||||||
ShutdownCh: MakeShutdownCh(),
|
ShutdownCh: MakeShutdownCh(),
|
||||||
SighupCh: MakeSighupCh(),
|
SighupCh: MakeSighupCh(),
|
||||||
|
SigUSR2Ch: MakeSigUSR2Ch(),
|
||||||
logger: logger,
|
logger: logger,
|
||||||
startedCh: make(chan struct{}, 5),
|
startedCh: make(chan struct{}, 5),
|
||||||
reloadedCh: make(chan struct{}, 5),
|
reloadedCh: make(chan struct{}, 5),
|
||||||
|
|||||||
@@ -1782,41 +1782,13 @@ func (c *ServerCommand) Run(args []string) int {
|
|||||||
// into a state where it cannot process requests so we can get pprof outputs
|
// into a state where it cannot process requests so we can get pprof outputs
|
||||||
// via SIGUSR2.
|
// via SIGUSR2.
|
||||||
pprofPath := filepath.Join(os.TempDir(), "vault-pprof")
|
pprofPath := filepath.Join(os.TempDir(), "vault-pprof")
|
||||||
err := os.MkdirAll(pprofPath, os.ModePerm)
|
cpuProfileDuration := time.Second * 1
|
||||||
|
err := WritePprofToFile(pprofPath, cpuProfileDuration)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.logger.Error("Could not create temporary directory for pprof", "error", err)
|
c.logger.Error(err.Error())
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
dumps := []string{"goroutine", "heap", "allocs", "threadcreate", "profile"}
|
|
||||||
for _, dump := range dumps {
|
|
||||||
pFile, err := os.Create(filepath.Join(pprofPath, dump))
|
|
||||||
if err != nil {
|
|
||||||
c.logger.Error("error creating pprof file", "name", dump, "error", err)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
if dump != "profile" {
|
|
||||||
err = pprof.Lookup(dump).WriteTo(pFile, 0)
|
|
||||||
if err != nil {
|
|
||||||
c.logger.Error("error generating pprof data", "name", dump, "error", err)
|
|
||||||
pFile.Close()
|
|
||||||
break
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// CPU profiles need to run for a duration so we're going to run it
|
|
||||||
// just for one second to avoid blocking here.
|
|
||||||
if err := pprof.StartCPUProfile(pFile); err != nil {
|
|
||||||
c.logger.Error("could not start CPU profile: ", err)
|
|
||||||
pFile.Close()
|
|
||||||
break
|
|
||||||
}
|
|
||||||
time.Sleep(time.Second * 1)
|
|
||||||
pprof.StopCPUProfile()
|
|
||||||
}
|
|
||||||
pFile.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
c.logger.Info(fmt.Sprintf("Wrote pprof files to: %s", pprofPath))
|
c.logger.Info(fmt.Sprintf("Wrote pprof files to: %s", pprofPath))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime/pprof"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -201,3 +203,40 @@ func (r *recordingRoundTripper) RoundTrip(req *http.Request) (*http.Response, er
|
|||||||
StatusCode: 200,
|
StatusCode: 200,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WritePprofToFile will create a temporary directory at the specified path
|
||||||
|
// and generate pprof files at that location. CPU requires polling over a
|
||||||
|
// duration. For most situations 1 second is enough.
|
||||||
|
func WritePprofToFile(path string, cpuProfileDuration time.Duration) error {
|
||||||
|
err := os.MkdirAll(path, os.ModePerm)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not create temporary directory for pprof: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
dumps := []string{"goroutine", "heap", "allocs", "threadcreate", "profile"}
|
||||||
|
for _, dump := range dumps {
|
||||||
|
pFile, err := os.Create(filepath.Join(path, dump))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error creating pprof file %s: %v", dump, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if dump != "profile" {
|
||||||
|
err = pprof.Lookup(dump).WriteTo(pFile, 0)
|
||||||
|
if err != nil {
|
||||||
|
pFile.Close()
|
||||||
|
return fmt.Errorf("error generating pprof data for %s: %v", dump, err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// CPU profiles need to run for a duration so we're going to run it
|
||||||
|
// just for one second to avoid blocking here.
|
||||||
|
if err := pprof.StartCPUProfile(pFile); err != nil {
|
||||||
|
pFile.Close()
|
||||||
|
return fmt.Errorf("could not start CPU profile: %v", err)
|
||||||
|
}
|
||||||
|
time.Sleep(cpuProfileDuration)
|
||||||
|
pprof.StopCPUProfile()
|
||||||
|
}
|
||||||
|
pFile.Close()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user