Files
Marcus Efraimsson fc8a5cf468 Profiling: Enhance configuration and docs (#90048)
Co-authored-by: Dave Henderson <dave.henderson@grafana.com>
2024-07-09 17:48:16 +02:00

167 lines
4.5 KiB
Go

package commands
import (
"fmt"
"net/http"
"os"
"runtime"
"runtime/trace"
"strconv"
"github.com/grafana/grafana/pkg/infra/log"
)
const (
profilingEnabledEnvName = "GF_DIAGNOSTICS_PROFILING_ENABLED"
profilingAddrEnvName = "GF_DIAGNOSTICS_PROFILING_ADDR"
profilingPortEnvName = "GF_DIAGNOSTICS_PROFILING_PORT"
profilingBlockRateEnvName = "GF_DIAGNOSTICS_PROFILING_BLOCK_RATE"
profilingMutexRateEnvName = "GF_DIAGNOSTICS_PROFILING_MUTEX_RATE"
tracingEnabledEnvName = "GF_DIAGNOSTICS_TRACING_ENABLED"
tracingFileEnvName = "GF_DIAGNOSTICS_TRACING_FILE"
)
type profilingDiagnostics struct {
enabled bool
addr string
port uint64
blockRate int
mutexRate int
}
func newProfilingDiagnostics(enabled bool, addr string, port uint64, blockRate int, mutexRate int) *profilingDiagnostics {
return &profilingDiagnostics{
enabled: enabled,
addr: addr,
port: port,
blockRate: blockRate,
mutexRate: mutexRate,
}
}
func (pd *profilingDiagnostics) overrideWithEnv() error {
enabledEnv := os.Getenv(profilingEnabledEnvName)
if enabledEnv != "" {
enabled, err := strconv.ParseBool(enabledEnv)
if err != nil {
return fmt.Errorf("failed to parse %s environment variable as bool", profilingEnabledEnvName)
}
pd.enabled = enabled
}
addrEnv := os.Getenv(profilingAddrEnvName)
if addrEnv != "" {
pd.addr = addrEnv
}
portEnv := os.Getenv(profilingPortEnvName)
if portEnv != "" {
port, parseErr := strconv.ParseUint(portEnv, 0, 64)
if parseErr != nil {
return fmt.Errorf("failed to parse %s environment variable to unsigned integer", profilingPortEnvName)
}
pd.port = port
}
blockRateEnv := os.Getenv(profilingBlockRateEnvName)
if blockRateEnv != "" {
blockRate, err := strconv.Atoi(blockRateEnv)
if err != nil {
return fmt.Errorf("failed to parse %s environment variable as int", profilingBlockRateEnvName)
}
pd.blockRate = blockRate
}
mutexFractionEnv := os.Getenv(profilingMutexRateEnvName)
if mutexFractionEnv != "" {
mutexProfileFraction, err := strconv.Atoi(mutexFractionEnv)
if err != nil {
return fmt.Errorf("failed to parse %s environment variable as int", profilingMutexRateEnvName)
}
pd.mutexRate = mutexProfileFraction
}
return nil
}
type tracingDiagnostics struct {
enabled bool
file string
}
func newTracingDiagnostics(enabled bool, file string) *tracingDiagnostics {
return &tracingDiagnostics{
enabled: enabled,
file: file,
}
}
func (td *tracingDiagnostics) overrideWithEnv() error {
enabledEnv := os.Getenv(tracingEnabledEnvName)
if enabledEnv != "" {
enabled, err := strconv.ParseBool(enabledEnv)
if err != nil {
return fmt.Errorf("failed to parse %s environment variable as bool", tracingEnabledEnvName)
}
td.enabled = enabled
}
fileEnv := os.Getenv(tracingFileEnvName)
if fileEnv != "" {
td.file = fileEnv
}
return nil
}
func setupProfiling(profile bool, profileAddr string, profilePort uint64, blockRate int, mutexFraction int) error {
profileDiagnostics := newProfilingDiagnostics(profile, profileAddr, profilePort, blockRate, mutexFraction)
if err := profileDiagnostics.overrideWithEnv(); err != nil {
return err
}
if profileDiagnostics.enabled {
fmt.Println("diagnostics: pprof profiling enabled", "addr", profileDiagnostics.addr, "port", profileDiagnostics.port, "blockProfileRate", profileDiagnostics.blockRate, "mutexProfileRate", profileDiagnostics.mutexRate)
runtime.SetBlockProfileRate(profileDiagnostics.blockRate)
runtime.SetMutexProfileFraction(profileDiagnostics.mutexRate)
go func() {
// TODO: We should enable the linter and fix G114 here.
// G114: Use of net/http serve function that has no support for setting timeouts (gosec)
//
//nolint:gosec
err := http.ListenAndServe(fmt.Sprintf("%s:%d", profileDiagnostics.addr, profileDiagnostics.port), nil)
if err != nil {
panic(err)
}
}()
}
return nil
}
func setupTracing(tracing bool, tracingFile string, logger *log.ConcreteLogger) error {
traceDiagnostics := newTracingDiagnostics(tracing, tracingFile)
if err := traceDiagnostics.overrideWithEnv(); err != nil {
return err
}
if traceDiagnostics.enabled {
fmt.Println("diagnostics: tracing enabled", "file", traceDiagnostics.file)
f, err := os.Create(traceDiagnostics.file)
if err != nil {
panic(err)
}
defer func() {
if err := f.Close(); err != nil {
logger.Error("Failed to write trace diagnostics", "path", traceDiagnostics.file, "err", err)
}
}()
if err := trace.Start(f); err != nil {
panic(err)
}
defer trace.Stop()
}
return nil
}