GLIBC:メモリリークのデバッグ:mtrace()の出力の解釈方法
-
03-07-2019 - |
質問
メモリリークの問題をデバッグしようとしています。 mtrace()を使用していますa> malloc / free / reallocトレースを取得します。プログラムを実行して、巨大なログファイルが作成されました。ここまでは順調ですね。しかし、ファイルの解釈に問題があります。これらの行を見てください:
@ /usr/java/ibm-java2-x86_64-50/jre/bin/libj9prt23.so:[0x2b270a384a34] + 0x1502570 0x68
@ /usr/java/ibm-java2-x86_64-50/jre/bin/libj9prt23.so:[0x2b270a384a34] + 0x1502620 0x30
@ /usr/java/ibm-java2-x86_64-50/jre/bin/libj9prt23.so:[0x2b270a384a34] + 0x2aaab43a1700 0xa80
@ /usr/java/ibm-java2-x86_64-50/jre/bin/libj9prt23.so:[0x2b270a384a34] + 0x1501460 0xa64
これについて奇妙なのは、1つの呼び出し(同じ戻りアドレス)が4つの割り当てを担当することです。
見知らぬ人:
@ /usr/java/ibm-java2-x86_64-50/jre/bin/libj9prt23.so:[0x2b270a384a34] + 0x2aaab43a1700 0xa2c
…
@ /usr/java/ibm-java2-x86_64-50/jre/bin/libj9prt23.so:[0x2b270a384a34] + 0x2aaab43a1700 0xa80
これらの2行の間で、ブロック0x2aaab43a1700は解放されません。
これを説明する方法を知っている人はいますか? 1回の呼び出しで4つの割り当てが発生する可能性はありますか?また、mallocは、以前に割り当てられたアドレスをどのように返すことができますか?
編集2008/09/30: GLIBC(mtrace.pl)が提供するmtrace()出力を分析するスクリプトは、ここでは役に立ちません。それはただ言う:Alloc 0x2aaab43a1700複製。しかし、これはどうして起こるのでしょうか?
解決
メモリを割り当てている関数が複数回呼び出されています。呼び出し元のアドレスは、割り当てを行ったコードを指し、そのコードは単に複数回実行されています。
Cの例を次に示します。
void *allocate (void)
{
return (malloc(1000));
}
int main()
{
mtrace();
allocate();
allocate();
}
mtraceの出力は次のとおりです。
Memory not freed: ----------------- Address Size Caller 0x0000000000601460 0x3e8 at 0x4004f6 0x0000000000601850 0x3e8 at 0x4004f6
発信者のアドレスがどのように同一であるかに注意してください?これが、mtrace分析スクリプトが同じであると言っている理由です。同じバグが何度も見られ、いくつかのメモリリークが発生しているためです。
デバッグフラグ(-g)を使用してコンパイルすると、次のことが可能になります。
Memory not freed: ----------------- Address Size Caller 0x0000000000601460 0x3e8 at /home/andrjohn/development/playground/test.c:6 0x0000000000601850 0x3e8 at /home/andrjohn/development/playground/test.c:6
他のヒント
mtraceの直接出力を見ていますが、これは非常にわかりにくく直感に反します。幸いなことに、この出力の解析を非常に簡単に支援できるperlスクリプト(glibc-utils内にあるmtraceと呼ばれる)があります。
デバッグをオンにしてビルドをコンパイルし、次のようなmtraceを実行します。
$ gcc -g -o test test.c
$ MALLOC_TRACE=mtrace.out ./test
$ mtrace test mtrace.out
Memory not freed:
-----------------
Address Size Caller
0x094d9378 0x400 at test.c:6
出力は、消化しやすいロットである必要があります。
1つの可能な説明は、同じ関数が異なるバッファサイズを割り当てているということですか?そのような例の1つはstrdupです。
2番目の質問では、ランタイムが一部の「静的」を割り当てている可能性があります。プロセスが終了するまで解放されないスクラッチ領域。そして、その時点で、とにかくOSはプロセスの後にクリーンアップします。
このように考えてください。Javaにはデストラクタがなく、どのオブジェクトに対してもファイナライズが呼び出されるという保証はありません。
valgrindでアプリを実行してみてください。実際に何がリークされているかについて、より良い見方が得られるかもしれません。