In OTP 25 and earlier you need to include the tools
application for it to be available, which is often excluded from releases, so that’s probably why (it’s been moved to runtime_tools
in OTP 26)
Looking at the source code for the appsignal
NIF I think it would be a good idea to temporarily include the tools
application to check this.
The NIF seems to allocate a tiny resource (just a pointer) for each transaction which won’t make much of a dent in erlang:memory()
unless tons of them have been allocated, but the pointed-to memory seems to be allocated using system allocators (malloc(2)
et al) which will hide it from view: what looks like a megabyte or two in erlang:memory()
might actually be hundreds.
If this is the culprit it’ll stick out like a sore thumb in the instrument:allocations()
output.
Aside from using instrument
you can try running your test cases under valgrind, though it’ll take some editing to make it work with Elixir.
Make sure valgrind is installed, and build OTP from source with the special valgrind emulator type:
$ export ERL_TOP=`pwd`
$ export MAKEFLAGS=-j
$ ./otp_build setup -a
$ (cd erts/emulator/; make valgrind)
# Save this for later
$ readlink -f bin/cerl
Then edit your bin/elixir
script to use it, changing the following line…
set -- "$ERTS_BIN$ERL_EXEC" -noshell -elixir_root ... et cetera et cetera
To:
set -- "the path you got from readlink" -valgrind -noshell -elixir_root ... et cetera et cetera
If all goes well, you should see something like the following starting iex
:
$ iex
==607653== Memcheck, a memory error detector
==607653== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==607653== Using Valgrind-3.18.1 and LibVEX; rerun with -h for copyright info
==607653== Command: xyz
==607653==
==607653== Warning: set address range perms: large range [0x521b000, 0x4521b000) (noaccess)
Erlang/OTP 27 [DEVELOPMENT] [erts-14.0] [source-5776496e72] [64-bit] [smp:12:12] [ds:12:12:10] [async-threads:1] [jit] [valgrind-compiled]
Interactive Elixir (1.15.0-dev) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)>
It will run like a snail going uphill but if there’s a regular memory leak it’ll tell you all about it. Unfortunately it can’t tell you about things like a forever-expanding queue where all elements are reachable but never end up being freed, though.