To use pprof
to analyze performance and memory consumption in Envoy, you can
use the built-in statically linked profiler, or dynamically link it in to a
specific place yourself.
Static linking is already available (because of a HeapProfilerDump()
call
inside
Envoy::Profiler::Heap::stopProfiler())
).
Build the static binary using bazel:
$ bazel build //source/exe:envoy-static
To collect a heap profile, run a statically-linked Envoy with pprof
and run the binary with a CPUPROFILE
or HEAPPROFILE
environment variable, like so:
$ CPUPROFILE=/tmp/mybin.cpuprof bazel-bin/source/exe/envoy-static <args>
$ HEAPPROFILE=/tmp/mybin.heapprof bazel-bin/source/exe/envoy-static <args>
CPUPROFILE
or HEAPPROFILE
sets a location for the profiler output. (See Methodology.)
There are several other environment variables that can be set to tweak the behavior of gperftools. See https://gperftools.github.io/gperftools/ for more details.
pprof can be used to symbolize CPU and heap profiles. For example:
$ pprof -text bazel-bin/source/exe/envoy-static /tmp/mybin.cpuprof
The profiler library is automatically linked into envoy_cc_test targets.
Run a test with heap profiling enabled, like so:
$ bazel test --test_env=HEAPPROFILE=/tmp/heapprof <test target>
Run a test with CPU profiling enabled, like so:
$ bazel test --test_env=CPUPROFILE=/tmp/cpuprof <test target>
Note that heap checks and heap profile collection in tests have noticiable performance implications. Use the following command to collect a CPU profile from a test target with heap check and heap profile collection disabled:
$ bazel test --test_env=CPUPROFILE=/tmp/cpuprof --test_env=HEAPPROFILE= --test_env=HEAPCHECK= <test target>
It is possible to start/stop the CPU or heap profiler programmatically.
The Gperftools CPU Profiler
is controlled by ProfilerStart()
/ProfilerStop()
, and the
Gperftools Heap Profiler
is controlled by HeapProfilerStart()
, HeapProfilerStop()
and HeapProfilerDump()
.
These functions are wrapped by Envoy objects defined in source/common/profiler/profiler.h
).
To enable profiling programmatically:
- Add a library dependency on "//source/common/profiler:profiler_lib" to your envoy_cc_library build rule.
- Use the
startProfiler
/stopProfiler
methods ofEnvoy::Profiler::Cpu
orEnvoy::Profiler::Heap
to collect a profile.
Note that startProfiler
should only be called if no other profile of that type is currently active (e.i. profilerEnabled()
returns false).
Example:
// includes
#include "common/profiler/profiler.h"
...
Function(...) {
if (!Profiler::Cpu::startProfiler(profile_path)) {
// Error handling
}
...
Do expensive stuff in one or more threads.
...
// Stop the profiler and dump output to the `profile_path` specified when profile was started.
Profiler::Cpu::stopProfiler();
}
To support memory leaks detection, tests are built with gperftools dependencies enabled by default.
Use HeapProfilerStart()
, HeapProfilerStop()
, and HeapProfilerDump()
to start, stop, and persist
memory dumps, respectively. Please see above for more details.
By default, bazel executes tests in a sandbox, which will be deleted together with memory dumps
after the test run. To preserve memory dumps, bazel can be forced to run tests without
sandboxing, by setting the TestRunner
parameter to local
:
bazel test --strategy=TestRunner=local ...
An alternative is to set HEAPPROFILE
environment variable for the test runner:
bazel test --test_env=HEAPPROFILE=/tmp/testprofile ...
For consistent testing, it makes sense to run Envoy for a constant amount of time across trials:
$ timeout <num_seconds> bazel-bin/source/exe/envoy <options>
Envoy will print to stdout something like:
Starting tracking the heap
And then a series of stdouts like:
Dumping heap profile to <heap file 0001> (100 MB currently in use)
Dumping heap profile to <heap file 0002> (200 MB currently in use)
...
This will generate a series of files; if you statically-linked, these are
wherever HEAPPROFILE
points to. Otherwise, they are in the current directory
by default. They'll be named something like main_common_base.0001.heap
,
main_common_base.0002.heap
, etc.
NB: There is no reason this needs to be titled main_common_base
. Whatever
flag you supply HeapProfilerStart
/ HeapProfilerDump
will become the
filename. Multiple sections of code could be profiled simultaneously by setting
multiple HeapProfilerStart()
/ HeapProfilerStop()
breakpoints with unique
identifiers.
pprof can read these heap files in a
number of ways. Most convenient for first-order inspection might be pprof -top
or pprof -text
:
$ pprof -text bazel-bin/source/exe/envoy main_common_base* | head -n5
File: envoy
Build ID: ...
Type: inuse_space
Showing nodes accounting for 6402800.62kB, 98.59% of 6494044.58kB total
Dropped ... nodes (cum <= ...kB)
More complex flame/graph charts can be generated and viewed in a browser, which is often more helpful than text-based output:
$ pprof -http=localhost:9999 bazel-bin/source/exe/envoy main_common_base*