今回は gperftools(Google Performance Tools) を使ってメモリリークを検出してみたいと思う。
下のコードをコンパイルして試す。関数aと関数bでわざとメモリリークを発生させている。
Makefile は以下のとおり。リンクフラグに -ltcmalloc を追加している。
make が済んだら、以下のようにして実行する。1行目は libtcmalloc.so.4 がないと怒られるため、パスを通している。そして3行目の環境変数 HEAPCHECK に値をセットすることで、実行時にメモリリークのチェックが行われるようになる。
続いて gperftool の結果を処理する。gvのところをdotに変える。
出力された画像が次だ。

以上。
下のコードをコンパイルして試す。関数aと関数bでわざとメモリリークを発生させている。
#include <stdio.h> #include <stdlib.h> #include <sys/time.h> int a(void) { char* x = new char[4]; // memory leak x[0]='x'; int i=0,g=0; while(i++<100000) { g+=i; } return g; } int b(void) { char* y = new char[4]; // memory leak y[0]='y'; char* z = new char[4]; z[0]='z'; int i=0,g=0; while(i++<400000) { g+=i; } delete[] z; return g; } int main(int argc, char** argv) { int iterations; if(argc != 2) { printf("Usage %s <No of Iterations>\n", argv[0]); exit(-1); } else iterations = atoi(argv[1]); printf("No of iterations = %d\n", iterations); struct timeval s, e; gettimeofday(&s, NULL); while(iterations--) { a(); b(); } gettimeofday(&e, NULL); printf("time = %lf\n", (e.tv_sec - s.tv_sec) + (e.tv_usec - s.tv_usec)*1.0E-6); return 0; }
Makefile は以下のとおり。リンクフラグに -ltcmalloc を追加している。
# Makefile program = gpttest objs = gpttest.o CC = g++ CFLAGS = -g -Wall LD = g++ LDFLAGS = -ltcmalloc .SUFFIXES: .c .o $(program): $(objs) $(LD) -o $(program) $(LDFLAGS) $^ .c.o: $(CC) $(CFLAGS) -c $< .PHONY: clean clean: $(RM) $(program) $(objs)
make が済んだら、以下のようにして実行する。1行目は libtcmalloc.so.4 がないと怒られるため、パスを通している。そして3行目の環境変数 HEAPCHECK に値をセットすることで、実行時にメモリリークのチェックが行われるようになる。
> export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib > export PPROF_PATH=/usr/local/bin/pprof > export HEAPCHECK=strict > ./gpttest 3実行後、以下のような表示が行われ、メモリリークが発生していることがわかる。
[user1@localhost gperftools-test]$ sh gpttest.sh WARNING: Perftools heap leak checker is active -- Performance may suffer No of iterations = 3 time = 0.002770 Leak check _main_ detected leaks of 24 bytes in 6 objects The 2 largest leaks: Using local file ./gpttest. Leak of 12 bytes in 3 objects allocated from: @ 4007f6 a @ 40091a main @ 309721ea4d __libc_start_main Leak of 12 bytes in 3 objects allocated from: @ 400840 b @ 40091f main @ 309721ea4d __libc_start_main If the preceding stack traces are not enough to find the leaks, try running THIS shell command: pprof ./gpttest "/tmp/gpttest.4870._main_-end.heap" --inuse_objects --lines --heapcheck --edgefraction=1e-10 --nodefraction=1e-10 --gv If you are still puzzled about why the leaks are there, try rerunning this program with HEAP_CHECK_TEST_POINTER_ALIGNMENT=1 and/or with HEAP_CHECK_MAX_POINTER_OFFSET=-1 If the leak report occurs in a small fraction of runs, try running with TCMALLOC_MAX_FREE_QUEUE_SIZE of few hundred MB or with TCMALLOC_RECLAIM_MEMORY=false, it might help find leaks more repea Exiting with error code (instead of crashing) because of whole-program memory leaks中ほどにpprof で始まる行があり、このコマンドを実行すればgvでメモリリークの発生場所を表示してくれる。のだが、gv は入っていないため text で表示してみる。最後の --gv を --text に変える。
> pprof ./gpttest "/tmp/gpttest.4870._main_-end.heap" --inuse_objects --lines --heapcheck --edgefraction=1e-10 --nodefraction=1e-10 --text実行結果は以下のとおり。メモリリークの原因となったファイルと行番号を教えてくれる。
Using local file ./gpttest. Using local file /tmp/gpttest.4870._main_-end.heap. Total: 6 objects 3 50.0% 50.0% 3 50.0% a /home/yamashita/gperftools-test/gpttest.c:6 3 50.0% 100.0% 3 50.0% b /home/yamashita/gperftools-test/gpttest.c:16 0 0.0% 100.0% 6 100.0% __libc_start_main ??:0 0 0.0% 100.0% 3 50.0% main /home/yamashita/gperftools-test/gpttest.c:48 0 0.0% 100.0% 3 50.0% main /home/yamashita/gperftools-test/gpttest.c:49graphviz を使うことでグラフィカルに表示することもできる。 まずはgraphviz のインストール。
> yum install graphviz
続いて gperftool の結果を処理する。gvのところをdotに変える。
> pprof ./gpttest "/tmp/gpttest.4870._main_-end.heap" --inuse_objects --lines --heapcheck --edgefraction=1e-10 --nodefraction=1e-10 --dot > gpttest.dot > dot -T png gpttest.dot > gpttest.png
出力された画像が次だ。

以上。