feature: Add profiling documentation for backends based on BackendUtil.
This commit is contained in:
parent
356cfb8dca
commit
0efda4b283
4 changed files with 112 additions and 1 deletions
|
@ -18,9 +18,14 @@ type BackendApplicationHelper struct {
|
||||||
|
|
||||||
func (helper *BackendApplicationHelper) Start() error {
|
func (helper *BackendApplicationHelper) Start() error {
|
||||||
log.Debug("BackendApplicationHelper is starting")
|
log.Debug("BackendApplicationHelper is starting")
|
||||||
|
err := configureAndLaunchBackgroundProfilingTasks()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
log.Debug("Currently waiting for Unix socket connection...")
|
log.Debug("Currently waiting for Unix socket connection...")
|
||||||
|
|
||||||
var err error
|
|
||||||
helper.socket, err = net.Dial("unix", helper.SocketPath)
|
helper.socket, err = net.Dial("unix", helper.SocketPath)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
9
backend/backendutil/profiling_disabled.go
Normal file
9
backend/backendutil/profiling_disabled.go
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
//go:build !debug
|
||||||
|
|
||||||
|
package backendutil
|
||||||
|
|
||||||
|
var endProfileFunc func()
|
||||||
|
|
||||||
|
func configureAndLaunchBackgroundProfilingTasks() error {
|
||||||
|
return nil
|
||||||
|
}
|
91
backend/backendutil/profiling_enabled.go
Normal file
91
backend/backendutil/profiling_enabled.go
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
//go:build debug
|
||||||
|
|
||||||
|
package backendutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"runtime/pprof"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/charmbracelet/log"
|
||||||
|
"golang.org/x/exp/rand"
|
||||||
|
)
|
||||||
|
|
||||||
|
func configureAndLaunchBackgroundProfilingTasks() error {
|
||||||
|
profilingMode, err := os.ReadFile("/tmp/hermes.backendlauncher.profilebackends")
|
||||||
|
|
||||||
|
if err != nil && errors.Is(err, os.ErrNotExist) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
switch string(profilingMode) {
|
||||||
|
case "cpu":
|
||||||
|
log.Debug("Starting CPU profiling as a background task")
|
||||||
|
go doCPUProfiling()
|
||||||
|
case "mem":
|
||||||
|
log.Debug("Starting memory profiling as a background task")
|
||||||
|
go doMemoryProfiling()
|
||||||
|
default:
|
||||||
|
log.Warnf("Unknown profiling mode: %s", string(profilingMode))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func doCPUProfiling() {
|
||||||
|
// (imterah) WTF? why isn't this being seeded on its own? according to Go docs, this should be seeded automatically...
|
||||||
|
rand.Seed(uint64(time.Now().UnixNano()))
|
||||||
|
|
||||||
|
profileFileName := fmt.Sprintf("/tmp/hermes.backendlauncher.cpu.prof.%d", rand.Int())
|
||||||
|
profileFile, err := os.Create(profileFileName)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Failed to create CPU profiling file: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debugf("Writing CPU usage profile to '%s'. Will capture when Ctrl+C/SIGTERM is recieved.", profileFileName)
|
||||||
|
pprof.StartCPUProfile(profileFile)
|
||||||
|
|
||||||
|
exitNotification := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(exitNotification, os.Interrupt, syscall.SIGTERM)
|
||||||
|
<-exitNotification
|
||||||
|
|
||||||
|
log.Debug("Recieved SIGTERM. Cleaning up and exiting...")
|
||||||
|
|
||||||
|
pprof.StopCPUProfile()
|
||||||
|
profileFile.Close()
|
||||||
|
|
||||||
|
log.Debug("Exiting...")
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func doMemoryProfiling() {
|
||||||
|
// (imterah) WTF? why isn't this being seeded on its own? according to Go docs, this should be seeded automatically...
|
||||||
|
rand.Seed(uint64(time.Now().UnixNano()))
|
||||||
|
|
||||||
|
profileFileName := fmt.Sprintf("/tmp/hermes.backendlauncher.mem.prof.%d", rand.Int())
|
||||||
|
profileFile, err := os.Create(profileFileName)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Failed to create memory profiling file: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debugf("Writing memory profile to '%s'. Will capture when Ctrl+C/SIGTERM is recieved.", profileFileName)
|
||||||
|
|
||||||
|
exitNotification := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(exitNotification, os.Interrupt, syscall.SIGTERM)
|
||||||
|
<-exitNotification
|
||||||
|
|
||||||
|
log.Debug("Recieved SIGTERM. Cleaning up and exiting...")
|
||||||
|
|
||||||
|
pprof.WriteHeapProfile(profileFile)
|
||||||
|
profileFile.Close()
|
||||||
|
|
||||||
|
log.Debug("Exiting...")
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
6
docs/profiling.md
Normal file
6
docs/profiling.md
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
# Profiling
|
||||||
|
To profile any backend code based on `backendutil`, follow these steps:
|
||||||
|
1. Rebuild the backend with the `debug` flag: `cd $BACKEND_HERE; GOOS=linux go build -tags debug .; cd ..`
|
||||||
|
2. Copy the binary to the target machine (if applicable), and stop the API server.
|
||||||
|
3. If you want to profile the CPU utilization, write `cpu` to the file `/tmp/hermes.backendlauncher.profilebackends`: `echo -n "cpu" > /tmp/hermes.backendlauncher.profilebackends`. Else, replace `cpu` with `mem`.
|
||||||
|
4. Start the API server, with development mode and debug logging enabled.
|
Loading…
Add table
Add a link
Reference in a new issue