Enable profiler and profile-guided builds

I used the chance to simplify the Makefile, as this is required for the file check to work correctly. Variables should not contain quotes, as these will be included in the value otherwise.
This commit is contained in:
Sebastian Serth
2023-02-26 20:16:08 +01:00
committed by Sebastian Serth
parent 00952ca212
commit 1a378ce640
5 changed files with 90 additions and 18 deletions

3
.gitignore vendored
View File

@ -1,6 +1,9 @@
# Project binary
/poseidon
# CPU profiling information
cmd/poseidon/default.pgo
# Configuration file
configuration.yaml
tests/e2e/configuration.yaml

View File

@ -1,19 +1,23 @@
PROJECT_NAME := "poseidon"
REPOSITORY_OWNER = "openHPI"
PKG := "github.com/$(REPOSITORY_OWNER)/$(PROJECT_NAME)/cmd/$(PROJECT_NAME)"
PROJECT_NAME = poseidon
REPOSITORY_OWNER = openHPI
PKG = github.com/$(REPOSITORY_OWNER)/$(PROJECT_NAME)/cmd/$(PROJECT_NAME)
UNIT_TESTS = $(shell go list ./... | grep -v /e2e | grep -v /recovery)
DOCKER_TAG := "poseidon:latest"
DOCKER_OPTS := -v $(shell pwd)/configuration.yaml:/configuration.yaml
# Define the PGO file to be used for the build
PGO_FILE = ./cmd/$(PROJECT_NAME)/default.pgo
# Docker options
DOCKER_TAG = poseidon:latest
DOCKER_OPTS = -v $(shell pwd)/configuration.yaml:/configuration.yaml
LOWER_REPOSITORY_OWNER = $(shell echo $(REPOSITORY_OWNER) | tr A-Z a-z)
# Define image to be used in e2e tests. Requires `make` to be available.
E2E_TEST_DOCKER_CONTAINER := co_execenv_java
E2E_TEST_DOCKER_TAG := 17
E2E_TEST_DOCKER_IMAGE = "$(LOWER_REPOSITORY_OWNER)/$(E2E_TEST_DOCKER_CONTAINER):$(E2E_TEST_DOCKER_TAG)"
E2E_TEST_DOCKER_CONTAINER = co_execenv_java
E2E_TEST_DOCKER_TAG = 17
E2E_TEST_DOCKER_IMAGE = $(LOWER_REPOSITORY_OWNER)/$(E2E_TEST_DOCKER_CONTAINER):$(E2E_TEST_DOCKER_TAG)
# The base image of the e2e test image. This is used to build the base image as well.
E2E_TEST_BASE_CONTAINER := docker_exec_phusion
E2E_TEST_BASE_IMAGE = "$(LOWER_REPOSITORY_OWNER)/$(E2E_TEST_BASE_CONTAINER)"
E2E_TEST_BASE_IMAGE = $(LOWER_REPOSITORY_OWNER)/$(E2E_TEST_BASE_CONTAINER)
default: help
@ -45,7 +49,13 @@ git-hooks: .git/hooks/pre-commit ## Install the git-hooks
.PHONY: build
build: deps ## Build the binary
ifneq ("$(wildcard $(PGO_FILE))","")
# PGO_FILE exists
@go build -pgo=$(PGO_FILE) -ldflags "-X main.pgoEnabled=true" -o $(PROJECT_NAME) -v $(PKG)
else
# PGO_FILE does not exist
@go build -o $(PROJECT_NAME) -v $(PKG)
endif
.PHONY: clean
clean: ## Remove previous build
@ -101,7 +111,7 @@ deploy/dockerfiles: ## Clone Dockerfiles repository
.PHONY: e2e-test-docker-image
e2e-test-docker-image: deploy/dockerfiles ## Build Docker image that is used in e2e tests
@docker build -t $(E2E_TEST_BASE_IMAGE) -f deploy/dockerfiles/$(E2E_TEST_BASE_CONTAINER)
@docker build -t $(E2E_TEST_BASE_IMAGE) deploy/dockerfiles/$(E2E_TEST_BASE_CONTAINER)
@docker build -t $(E2E_TEST_DOCKER_IMAGE) deploy/dockerfiles/$(E2E_TEST_DOCKER_CONTAINER)/$(E2E_TEST_DOCKER_TAG)
.PHONY: e2e-test

View File

@ -15,6 +15,8 @@ import (
"net/http"
"os"
"os/signal"
"runtime/pprof"
"strconv"
"syscall"
"time"
)
@ -22,9 +24,21 @@ import (
var (
gracefulShutdownWait = 15 * time.Second
log = logging.GetLogger("main")
// If pgoEnabled is true, the binary was built with PGO enabled.
// This is set during compilation with our Makefile as a STRING.
pgoEnabled = "false"
)
func initSentry(options *sentry.ClientOptions) {
func initSentry(options *sentry.ClientOptions, profilingEnabled bool) {
options.BeforeSendTransaction = func(event *sentry.Event, _ *sentry.EventHint) *sentry.Event {
if event.Tags == nil {
event.Tags = make(map[string]string)
}
event.Tags["go_pgo"] = pgoEnabled
event.Tags["go_profiling"] = strconv.FormatBool(profilingEnabled)
return event
}
if err := sentry.Init(*options); err != nil {
log.Errorf("sentry.Init: %s", err)
}
@ -37,6 +51,33 @@ func shutdownSentry() {
}
}
func initProfiling(options config.Profiling) (cancel func()) {
if options.Enabled {
profile, err := os.Create(options.File)
if err != nil {
log.WithError(err).Error("Error while opening the profile file")
}
log.Debug("Starting CPU profiler")
if err := pprof.StartCPUProfile(profile); err != nil {
log.WithError(err).Error("Error while starting the CPU profiler!!")
}
cancel = func() {
if options.Enabled {
log.Debug("Stopping CPU profiler")
pprof.StopCPUProfile()
if err := profile.Close(); err != nil {
log.WithError(err).Error("Error while closing profile file")
}
}
}
} else {
cancel = func() {}
}
return cancel
}
func runServer(server *http.Server) {
log.WithField("address", server.Addr).Info("Starting server")
var err error
@ -139,9 +180,12 @@ func main() {
log.WithError(err).Warn("Could not initialize configuration")
}
logging.InitializeLogging(config.Config.Logger.Level)
initSentry(&config.Config.Sentry)
initSentry(&config.Config.Sentry, config.Config.Profiling.Enabled)
defer shutdownSentry()
stopProfiling := initProfiling(config.Config.Profiling)
defer stopProfiling()
cancel := monitoring.InitializeInfluxDB(&config.Config.InfluxDB)
defer cancel()

View File

@ -61,6 +61,14 @@ logger:
# Log level that is used after reading the config (INFO until then)
level: DEBUG
# Configuration of the embedded profiler
profiling:
# Enables the runtime profiler
enabled: false
# The file to which the profile is written to.
# The default location `cmd/poseidon/default.pgo` will be picked up during the build process to create a profile-guided build.
file: cmd/poseidon/default.pgo
# Configuration of the sentry logging
sentry:
# The DSN of the sentry endpoint to use.

View File

@ -123,6 +123,12 @@ type logger struct {
Level string
}
// Profiling configures the usage of a runtime profiler to create optimized binaries.
type Profiling struct {
Enabled bool
File string
}
// InfluxDB configures the usage of an Influx db monitoring.
type InfluxDB struct {
URL string
@ -134,12 +140,13 @@ type InfluxDB struct {
// configuration contains the complete configuration of Poseidon.
type configuration struct {
Server server
Nomad Nomad
AWS AWS
Logger logger
Sentry sentry.ClientOptions
InfluxDB InfluxDB
Server server
Nomad Nomad
AWS AWS
Logger logger
Profiling Profiling
Sentry sentry.ClientOptions
InfluxDB InfluxDB
}
// InitConfig merges configuration options from environment variables and