From 632c311a7f2e593c2e3baac65c17efea3f4cbc83 Mon Sep 17 00:00:00 2001 From: Alec Holmes Date: Thu, 29 Jul 2021 14:03:55 -0400 Subject: [PATCH] integration pprof into go-control-plane for benchmarking and resource profiling Signed-off-by: Alec Holmes --- .gitignore | 6 +++ Makefile | 19 ++++++++- benchmarks/README.md | 65 +++++++++++++++++++++++++++++++ benchmarks/client/README.md | 3 ++ benchmarks/client/main.go | 47 ++++++++++++++++++++++ benchmarks/client/xds/opts.go | 48 +++++++++++++++++++++++ benchmarks/client/xds/xds.go | 45 +++++++++++++++++++++ benchmarks/client/xds/xds_test.go | 1 + build/do_benchmarks.sh | 53 +++++++++++++++++++++++++ build/integration.sh | 6 +-- go.mod | 7 ++++ go.sum | 17 ++++++++ pkg/test/main/README.md | 15 ------- pkg/test/main/main.go | 53 +++++++++++++------------ 14 files changed, 338 insertions(+), 47 deletions(-) create mode 100644 benchmarks/README.md create mode 100644 benchmarks/client/README.md create mode 100644 benchmarks/client/main.go create mode 100644 benchmarks/client/xds/opts.go create mode 100644 benchmarks/client/xds/xds.go create mode 100644 benchmarks/client/xds/xds_test.go create mode 100755 build/do_benchmarks.sh diff --git a/.gitignore b/.gitignore index 947b51001e..7996a18f43 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,9 @@ examples/dyplomat/dyplomat *_gen_test.go /envoy*.log + +# Benchmarking artifacts +*.gz +*.pprof +/benchmarks/reports +/benchmarks/pngs diff --git a/Makefile b/Makefile index b7d7db39d1..c4a4776e44 100644 --- a/Makefile +++ b/Makefile @@ -4,8 +4,8 @@ # Variables #------------------------------------------------------------------------------ -SHELL := /bin/bash -BINDIR := bin +SHELL := /bin/bash +BINDIR := bin PKG := github.com/envoyproxy/go-control-plane .PHONY: build @@ -19,6 +19,9 @@ clean: @go mod tidy @rm -rf $(BINDIR) @rm -rf *.log + @rm -rf *.pprof + @rm -rf benchmarks/reports + @rm -rf benchmarks/pngs # TODO(mattklein123): See the note in TestLinearConcurrentSetWatch() for why we set -parallel here # This should be removed. @@ -79,6 +82,18 @@ integration.xds.delta: $(BINDIR)/test $(BINDIR)/upstream integration.ads.delta: $(BINDIR)/test $(BINDIR)/upstream env XDS=delta-ads build/integration.sh + +#------------------------------------------------------------------------------ +# Benchmarks utilizing intergration tests +#------------------------------------------------------------------------------ +.PHONY: benchmark docker_benchmarks +benchmark: $(BINDIR)/test $(BINDIR)/upstream + env XDS=xds build/integration.sh + +docker_benchmarks: + docker build --pull -f Dockerfile.ci . -t gcp_ci && \ + docker run -v $$(pwd):/go-control-plane $$(tty -s && echo "-it" || echo) gcp_ci /bin/bash -c /go-control-plane/build/do_benchmarks.sh + #-------------------------------------- #-- example xDS control plane server #-------------------------------------- diff --git a/benchmarks/README.md b/benchmarks/README.md new file mode 100644 index 0000000000..ea9489ca3c --- /dev/null +++ b/benchmarks/README.md @@ -0,0 +1,65 @@ +# Benchmarks + +These benchmarks are designed to run off the integration test suite, and provide detailed profiling artifacts generated with pprof. + +## Prerequisites +- [Go 1.16+](https://golang.org/dl/) +- [Graphviz](https://graphviz.org/download/) +- [pprof](https://github.com/google/pprof) +- [Docker](https://www.docker.com/) + +## Running Locally + +To run the benchmarks locally, we take a similar apprach to the integration tests. + +### Requirements + +* Envoy binary `envoy` available: set `ENVOY` environment variable to the + location of the binary, or use the default value `/usr/local/bin/envoy` +* `go-control-plane` builds successfully +* Local installation of pprof and graphviz for profiler output + +### Steps + +To run the benchmark: +``` +make benchmark MODE=0 +``` + +There are 5 different modes all corresponding to various profiling outputs: +```go +const ( + PPROF_CPU int = iota + PPROF_HEAP + PPROF_MUTEX + PPROF_BLOCK + PPROF_GOROUTINE +) +``` +To specifiy the mode, use `MODE` environment variable that corresponds to the output you wish to evaluate. + +The output file will be stored at the project root with .pprof extension (e.g. `cpu.pprof`). + +Run `pprof` tool like so which will open up a shell. From there, you can run command such as `web` to visualize the result: + +```bash +go tool pprof cpu.pprof +(pprof) web +``` +For more information, run `help`. + +## Running With Docker + +To run the benchmarks, we just require the prerequisite of docker and go. + +### Steps + +To run the benchmarks: + +``` +make docker_benchmarks +``` + +This will generate all profile artifacts in the `./benchmarks/reports`. Graphical profile anaylsis is located in `/.benchmarks/pngs`. + +For more information on how to interpret these reports/graphs, [click here](https://github.com/google/pprof/blob/master/doc/README.md#graphical-reports) diff --git a/benchmarks/client/README.md b/benchmarks/client/README.md new file mode 100644 index 0000000000..3e39e0e22b --- /dev/null +++ b/benchmarks/client/README.md @@ -0,0 +1,3 @@ +# Benchmark Client + +This test client provides simulation of various workloads when communicating with the go-control-plane management server. diff --git a/benchmarks/client/main.go b/benchmarks/client/main.go new file mode 100644 index 0000000000..1d5c3c7ff9 --- /dev/null +++ b/benchmarks/client/main.go @@ -0,0 +1,47 @@ +package main + +import ( + "log" + "os" + "runtime" + "time" + + "github.com/envoyproxy/go-control-plane/benchmarks/client/xds" + "github.com/urfave/cli/v2" +) + +func main() { + // Statically set the max procs + runtime.GOMAXPROCS(runtime.NumCPU()) + + app := &cli.App{ + Name: "xds-benchmark", + Usage: "xds benchmarking tool that simulates client workload", + Commands: []*cli.Command{ + { + Name: "run", + Aliases: []string{"r"}, + Usage: "run the benchmark with the provided duration", + Action: func(c *cli.Context) error { + arg := c.Args().First() + dur, err := time.ParseDuration(arg) + if err != nil { + return err + } + + sess, err := xds.NewSession("localhost:50000") + if err != nil { + return err + } + + return sess.Simulate(dur) + }, + }, + }, + } + + err := app.Run(os.Args) + if err != nil { + log.Fatal(err) + } +} diff --git a/benchmarks/client/xds/opts.go b/benchmarks/client/xds/opts.go new file mode 100644 index 0000000000..f34a2015f7 --- /dev/null +++ b/benchmarks/client/xds/opts.go @@ -0,0 +1,48 @@ +package xds + +// Options are configuration settings for the discovery object +type Options struct { + NodeID string + Zone string + Cluster string + ResourceNames []string // List of Envoy resource names to subscribe to + ResourceType string // ex: type.googleapis.com/envoy.api.v2.ClusterLoadAssignment +} + +// Option follows the functional opts pattern +type Option func(*Options) + +// WithNode will inject the node id into the configuration object +func WithNode(id string) Option { + return func(o *Options) { + o.NodeID = id + } +} + +// WithZone will specificy which zone to use in the xDS discovery request +func WithZone(zone string) Option { + return func(o *Options) { + o.Zone = zone + } +} + +// WithCluster will specificy which cluster the request is announcing as +func WithCluster(cluster string) Option { + return func(o *Options) { + o.Cluster = cluster + } +} + +// WithResourceNames will inject a list of resources the user wants to place watches on +func WithResourceNames(names []string) Option { + return func(o *Options) { + o.ResourceNames = names + } +} + +// WithResourceType will inject the specific resource type that a user wants to stream +func WithResourceType(resource string) Option { + return func(o *Options) { + o.ResourceType = resource + } +} diff --git a/benchmarks/client/xds/xds.go b/benchmarks/client/xds/xds.go new file mode 100644 index 0000000000..63cd25a102 --- /dev/null +++ b/benchmarks/client/xds/xds.go @@ -0,0 +1,45 @@ +package xds + +import ( + "time" + + "google.golang.org/grpc" +) + +// Sess holds a grpc connection as well as config options to use during the simulation +type Sess struct { + Session *grpc.ClientConn + Opts Options +} + +// NewSession will dial a new benchmarking session with the configured options +func NewSession(url string, opts ...Option) (*Sess, error) { + var options Options + for _, o := range opts { + o(&options) + } + + conn, err := grpc.Dial(url, grpc.WithBlock(), grpc.WithInsecure()) + if err != nil { + return nil, err + } + + return &Sess{ + Session: conn, + Opts: options, + }, nil +} + +// Simulate will start an xDS stream which provides simulatest clients communicating with an xDS server +func (s *Sess) Simulate(target time.Duration) error { + // Create a loop that will continually do work until the elapsed time as passed + for timeout := time.After(target); ; { + select { + case <-timeout: + return nil + default: + // Do some work + + } + } +} diff --git a/benchmarks/client/xds/xds_test.go b/benchmarks/client/xds/xds_test.go new file mode 100644 index 0000000000..5b4b0d8c19 --- /dev/null +++ b/benchmarks/client/xds/xds_test.go @@ -0,0 +1 @@ +package xds diff --git a/build/do_benchmarks.sh b/build/do_benchmarks.sh new file mode 100755 index 0000000000..5257a3bad9 --- /dev/null +++ b/build/do_benchmarks.sh @@ -0,0 +1,53 @@ +#!/usr/bin/env bash + +set -e +set -x + +go install golang.org/x/tools/cmd/goimports@latest +go get -u github.com/google/pprof +apt update && apt install -y graphviz + +cd /go-control-plane + +NUM_DIFF_LINES=`/go/bin/goimports -local github.com/envoyproxy/go-control-plane -d pkg | wc -l` +if [[ $NUM_DIFF_LINES > 0 ]]; then + echo "Failed format check. Run make format" + exit 1 +fi + +make benchmark MODE=0 +make benchmark MODE=1 +make benchmark MODE=2 +make benchmark MODE=3 +make benchmark MODE=4 + +mkdir -p benchmarks/reports +mkdir -p benchmarks/pngs + +# generate our consumable pprof profiles +pprof -text bin/test cpu.pprof > benchmarks/reports/cpu_text.txt +pprof -tree bin/test cpu.pprof > benchmarks/reports/cpu_tree.txt +pprof -traces bin/test cpu.pprof > benchmarks/reports/cpu_trace.txt + +pprof -text bin/test goroutine.pprof > benchmarks/reports/goroutine_text.txt +pprof -tree bin/test goroutine.pprof > benchmarks/reports/goroutine_tree.txt +pprof -traces bin/test goroutine.pprof > benchmarks/reports/goroutine_trace.txt + +pprof -text bin/test block.pprof > benchmarks/reports/block_text.txt +pprof -tree bin/test block.pprof > benchmarks/reports/block_tree.txt +pprof -traces bin/test block.pprof > benchmarks/reports/block_trace.txt + +pprof -text bin/test mutex.pprof > benchmarks/reports/mutex_text.txt +pprof -tree bin/test mutex.pprof > benchmarks/reports/mutex_tree.txt +pprof -traces bin/test mutex.pprof > benchmarks/reports/mutex_trace.txt + +pprof -text bin/test mem.pprof > benchmarks/reports/mem_text.txt +pprof -tree bin/test mem.pprof > benchmarks/reports/mem_tree.txt +pprof -traces bin/test mem.pprof > benchmarks/reports/mem_trace.txt + +# generate some cool pprof graphs +pprof -png bin/test cpu.pprof > benchmarks/pngs/cpu_graph.png +pprof -png bin/test goroutine.pprof > benchmarks/pngs/goroutine_graph.png +pprof -png bin/test block.pprof > benchmarks/pngs/block_graph.png +pprof -png bin/test mutex.pprof > benchmarks/pngs/mutex_graph.png +pprof -png bin/test mem.pprof > benchmarks/pngs/mem_graph.png diff --git a/build/integration.sh b/build/integration.sh index 811b547e03..a1e8184dfb 100755 --- a/build/integration.sh +++ b/build/integration.sh @@ -13,8 +13,8 @@ MESSAGE=$'Hi, there!\n' # Management server type. Valid values are "ads", "xds", "rest", "delta", or "delta-ads" XDS=${XDS:-ads} -# pprof profiler. True means turn on profiling -PPROF=${PPROF:-false} +# pprof profiler mode +MODE=${MODE:-0} # Number of RTDS layers. if [ "$XDS" = "ads" ]; then @@ -43,4 +43,4 @@ function cleanup() { trap cleanup EXIT # run the test suite (which also contains the control plane) -bin/test --xds=${XDS} --runtimes=${RUNTIMES} --pprof=${PPROF} -debug -message="$MESSAGE" "$@" +bin/test --xds=${XDS} --runtimes=${RUNTIMES} --pprofMode=${MODE} -debug -message="$MESSAGE" "$@" diff --git a/go.mod b/go.mod index 0c10b6d613..a16d3ee995 100644 --- a/go.mod +++ b/go.mod @@ -8,8 +8,10 @@ require ( github.com/envoyproxy/protoc-gen-validate v0.9.1 github.com/golang/protobuf v1.5.2 github.com/google/go-cmp v0.5.9 + github.com/pkg/profile v1.7.0 github.com/prometheus/client_model v0.3.0 github.com/stretchr/testify v1.8.1 + github.com/urfave/cli/v2 v2.24.1 go.opentelemetry.io/proto/otlp v0.19.0 google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6 google.golang.org/grpc v1.52.0 @@ -17,8 +19,13 @@ require ( ) require ( + github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/felixge/fgprof v0.9.3 // indirect + github.com/google/pprof v0.0.0-20211214055906-6f57359322fd // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/russross/blackfriday/v2 v2.1.0 // indirect + github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect golang.org/x/net v0.4.0 // indirect golang.org/x/sys v0.3.0 // indirect golang.org/x/text v0.5.0 // indirect diff --git a/go.sum b/go.sum index 4261e1ebdd..85a8e6da58 100644 --- a/go.sum +++ b/go.sum @@ -364,6 +364,7 @@ cloud.google.com/go/workflows v1.8.0/go.mod h1:ysGhmEajwZxGn1OhGOGKsTXc5PyxOc0vf cloud.google.com/go/workflows v1.9.0/go.mod h1:ZGkj1aFIOd9c8Gerkjjq7OW7I5+l6cSvT3ujaO/WwSA= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= @@ -387,6 +388,8 @@ github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWH github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20220314180256-7f1daf1720fc h1:PYXxkRUBGUMa5xgMVMDl62vEklZvKpVaxQeN9ie7Hfk= github.com/cncf/xds/go v0.0.0-20220314180256-7f1daf1720fc/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= +github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -402,6 +405,8 @@ github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go. github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v0.9.1 h1:PS7VIOgmSVhWUEeZwTe7z7zouA22Cr590PzXKbZHOVY= github.com/envoyproxy/protoc-gen-validate v0.9.1/go.mod h1:OKNgG7TCp5pF4d6XftA0++PMirau2/yoOwVac3AbF2w= +github.com/felixge/fgprof v0.9.3 h1:VvyZxILNuCiUCSXtPtYmmtGvb65nqXh2QFWc0Wpf2/g= +github.com/felixge/fgprof v0.9.3/go.mod h1:RdbpDgzqYVh/T9fPELJyV7EYJuHB55UTEULNun8eiPw= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= @@ -476,6 +481,8 @@ github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20211214055906-6f57359322fd h1:1FjCyPC+syAzJ5/2S8fqdZK1R22vvA0J7JZKcuOIQ7Y= +github.com/google/pprof v0.0.0-20211214055906-6f57359322fd/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -501,6 +508,7 @@ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/ianlancetaylor/demangle v0.0.0-20210905161508-09a460cdf81d/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= @@ -513,6 +521,8 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/lyft/protoc-gen-star v0.6.1/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/profile v1.7.0 h1:hnbDkaNWPCLMO9wGLdBFTIZvzDrDfBM2072E1S9gJkA= +github.com/pkg/profile v1.7.0/go.mod h1:8Uer0jas47ZQMJ7VD+OHknK4YDY07LPUC6dEvqDjvNo= github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -522,6 +532,8 @@ github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvq github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= @@ -536,6 +548,10 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/urfave/cli/v2 v2.24.1 h1:/QYYr7g0EhwXEML8jO+8OYt5trPnLHS0p3mrgExJ5NU= +github.com/urfave/cli/v2 v2.24.1/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc= +github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= +github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -737,6 +753,7 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/pkg/test/main/README.md b/pkg/test/main/README.md index 61b9e5a5db..29c235a3f4 100644 --- a/pkg/test/main/README.md +++ b/pkg/test/main/README.md @@ -37,18 +37,3 @@ eventually converges to use the latest pushed configuration) for each run. You can run ```bin/test -help``` to get a list of the cli flags that the test program accepts. There are also comments in ```main.go```. - -## Using the pprof profiler - -One customization is to run the go language profiler [pprof](https://github.com/DataDog/go-profiler-notes/blob/main/pprof.md). See also . - -The profiler is normally off because it adds overhead to the tests. You can turn -it on with the command line option `--pprof`. There is an environment variable -`PPROF` for the `make` commands shown above. For example: - - (export PPROF=true; make integration.xds) - -The test will then write files of the form `block_profile_xds.pb.gz`. The files -get written to the root of the project, in the same place as the envoy logs. - -You can use `go tool pprof bin/test ` to analyze the profile data. diff --git a/pkg/test/main/main.go b/pkg/test/main/main.go index 02acc22637..f856aafccf 100644 --- a/pkg/test/main/main.go +++ b/pkg/test/main/main.go @@ -24,10 +24,10 @@ import ( "log" "net/http" "os" - "runtime" - "runtime/pprof" "time" + "github.com/pkg/profile" + "github.com/envoyproxy/go-control-plane/pkg/cache/v3" "github.com/envoyproxy/go-control-plane/pkg/server/v3" "github.com/envoyproxy/go-control-plane/pkg/test" @@ -35,6 +35,14 @@ import ( testv3 "github.com/envoyproxy/go-control-plane/pkg/test/v3" ) +const ( + PPROF_CPU int = iota + PPROF_HEAP + PPROF_MUTEX + PPROF_BLOCK + PPROF_GOROUTINE +) + var ( debug bool @@ -62,11 +70,11 @@ var ( nodeID string - pprofEnabled bool + pprofMode int ) func init() { - flag.BoolVar(&debug, "debug", false, "Use debug logging") + flag.BoolVar(&debug, "debug", false, "Use debug logging and enable pprof") // // These parameters control the ports that the integration test @@ -151,8 +159,8 @@ func init() { // These parameters control the the use of the pprof profiler // - // Enable use of the pprof profiler - flag.BoolVar(&pprofEnabled, "pprof", false, "Enable use of the pprof profiler") + // Pprof mode for which profiling method to use + flag.IntVar(&pprofMode, "pprofMode", 0, "Mode for pprof to run in: 0 -> CPU,1 -> heap, 2 -> mutex, 3 -> block, 4 -> goroutines") } @@ -161,13 +169,18 @@ func main() { flag.Parse() ctx := context.Background() - if pprofEnabled { - runtime.SetBlockProfileRate(1) - for _, prof := range []string{"block", "goroutine", "mutex"} { - log.Printf("turn on pprof %s profiler", prof) - if pprof.Lookup(prof) == nil { - pprof.NewProfile(prof) - } + if debug { + switch pprofMode { + case PPROF_CPU: + defer profile.Start(profile.CPUProfile, profile.ProfilePath("."), profile.NoShutdownHook).Stop() + case PPROF_HEAP: + defer profile.Start(profile.MemProfile, profile.ProfilePath(".")).Stop() + case PPROF_MUTEX: + defer profile.Start(profile.MutexProfile, profile.ProfilePath(".")).Stop() + case PPROF_BLOCK: + defer profile.Start(profile.BlockProfile, profile.ProfilePath(".")).Stop() + case PPROF_GOROUTINE: + defer profile.Start(profile.GoroutineProfile, profile.ProfilePath(".")).Stop() } } @@ -286,20 +299,6 @@ func main() { } } - if pprofEnabled { - for _, prof := range []string{"block", "goroutine", "mutex"} { - p := pprof.Lookup(prof) - filePath := fmt.Sprintf("%s_profile_%s.pb.gz", prof, mode) - log.Printf("storing %s profile for %s in %s", prof, mode, filePath) - f, err := os.Create(filePath) - if err != nil { - log.Fatalf("could not create %s profile %s: %s", prof, filePath, err) - } - p.WriteTo(f, 1) // nolint:errcheck - f.Close() - } - } - log.Printf("Test for %s passed!\n", mode) }