valgrindを使用してプログラムのメモリリークを見つけるにはどうすればよいですか?
誰かが私を助けて、手順を実行する手順を説明してください。
Ubuntu 10.04を使用していますが、プログラムがありa.c
ます。手伝ってください。
valgrindを使用してプログラムのメモリリークを見つけるにはどうすればよいですか?
誰かが私を助けて、手順を実行する手順を説明してください。
Ubuntu 10.04を使用していますが、プログラムがありa.c
ます。手伝ってください。
回答:
OPを侮辱するのではなく、この質問に来てもまだLinux を初めて使う人のために、システムにValgrindをインストールする必要があるかもしれません。
sudo apt install valgrind # Ubuntu, Debian, etc.
sudo yum install valgrind # RHEL, CentOS, Fedora, etc.
ValgrindはC / C ++コードですぐに使用できますが、適切に構成されていれば、他の言語でも使用できます(Python についてはこちらを参照)。
Valgrindを実行するには、実行可能ファイルを引数として渡します(プログラムへのパラメーターと共に)。
valgrind --leak-check=full \
--show-leak-kinds=all \
--track-origins=yes \
--verbose \
--log-file=valgrind-out.txt \
./executable exampleParam1
フラグは、要するに:
--leak-check=full
:「各個別リークが詳細に表示されます」--show-leak-kinds=all
:「完全」レポートで「明確、間接、可能、到達可能」なリークの種類をすべて表示します。--track-origins=yes
:速度よりも有用な出力を優先します。これは、初期化されていない値の原因を追跡します。これは、メモリエラーに非常に役立ちます。Valgrindが許容できないほど遅い場合は、オフにすることを検討してください。--verbose
:プログラムの異常な動作について説明できます。冗長性を高めるために繰り返します。--log-file
:ファイルに書き込みます。出力が端末スペースを超える場合に役立ちます。最後に、次のようなValgrindレポートを表示します。
HEAP SUMMARY:
in use at exit: 0 bytes in 0 blocks
total heap usage: 636 allocs, 636 frees, 25,393 bytes allocated
All heap blocks were freed -- no leaks are possible
ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
つまり、メモリリークがあり、Valgrindは意味のあることを何も言っていません。おそらく、このようなもの:
5 bytes in 1 blocks are definitely lost in loss record 1 of 1
at 0x4C29BE3: malloc (vg_replace_malloc.c:299)
by 0x40053E: main (in /home/Peri461/Documents/executable)
私が書いたCコードも見てみましょう。
#include <stdlib.h>
int main() {
char* string = malloc(5 * sizeof(char)); //LEAK: not freed!
return 0;
}
まあ、失われた5バイトがありました。どうやってそうなった?エラーレポートはちょうど言う
main
とmalloc
。より大きなプログラムでは、それを探すのは非常に面倒です。これは、実行可能ファイルのコンパイル方法が原因です。何が問題だったかに関する詳細を1行ずつ取得できます。デバッグフラグを使用してプログラムを再コンパイルします(私はgcc
ここで使用しています):
gcc -o executable -std=c11 -Wall main.c # suppose it was this at first
gcc -o executable -std=c11 -Wall -ggdb3 main.c # add -ggdb3 to it
このデバッグビルドで、Valgrind はリークしたメモリを割り当てるコードの正確な行をポイントします!(言い回しは重要です。リークの正確な場所ではないかもしれませんが、リークされたものです。トレースは、どこを見つけるのに役立ちます 。)
5 bytes in 1 blocks are definitely lost in loss record 1 of 1
at 0x4C29BE3: malloc (vg_replace_malloc.c:299)
by 0x40053E: main (main.c:4)
IndexOutOfBoundsException
タイプの問題です。IDEが閉じ括弧をまだ入力していないことに気付くように、リーク/エラーが相互にリンクする場合があります。1つの問題を解決することで他の問題を解決できるため、良い原因であると思われる問題を探し、これらのアイデアのいくつかを適用します。
gdb
)、前提条件/事後条件エラーを探します。アイデアは、割り当てられたメモリの寿命に焦点を当てながら、プログラムの実行を追跡することです。60 bytes in 1 blocks are definitely lost in loss record 1 of 1
at 0x4C2BB78: realloc (vg_replace_malloc.c:785)
by 0x4005E4: resizeArray (main.c:12)
by 0x40062E: main (main.c:19)
そしてコード:
#include <stdlib.h>
#include <stdint.h>
struct _List {
int32_t* data;
int32_t length;
};
typedef struct _List List;
List* resizeArray(List* array) {
int32_t* dPtr = array->data;
dPtr = realloc(dPtr, 15 * sizeof(int32_t)); //doesn't update array->data
return array;
}
int main() {
List* array = calloc(1, sizeof(List));
array->data = calloc(10, sizeof(int32_t));
array = resizeArray(array);
free(array->data);
free(array);
return 0;
}
教員として、私はこの間違いをよく見ました。生徒はローカル変数を使用し、元のポインタを更新するのを忘れています。ここでのエラーはrealloc
、割り当てられたメモリを実際に別の場所に移動し、ポインタの場所を変更できることに気づいています。次に、配列がどこに移動されたかresizeArray
を知らせずに
去りarray->data
ます。
1 errors in context 1 of 1:
Invalid write of size 1
at 0x4005CA: main (main.c:10)
Address 0x51f905a is 0 bytes after a block of size 26 alloc'd
at 0x4C2B975: calloc (vg_replace_malloc.c:711)
by 0x400593: main (main.c:5)
そしてコード:
#include <stdlib.h>
#include <stdint.h>
int main() {
char* alphabet = calloc(26, sizeof(char));
for(uint8_t i = 0; i < 26; i++) {
*(alphabet + i) = 'A' + i;
}
*(alphabet + 26) = '\0'; //null-terminate the string?
free(alphabet);
return 0;
}
Valgrindが上記のコメント行を示していることに注意してください。サイズ26の配列には、インデックス[0,25]が付けられています。これが*(alphabet + 26)
無効な書き込みである理由です。これは範囲外です。無効な書き込みは、off-by-oneエラーの一般的な結果です。割り当て操作の左側を見てください。
1 errors in context 1 of 1:
Invalid read of size 1
at 0x400602: main (main.c:9)
Address 0x51f90ba is 0 bytes after a block of size 26 alloc'd
at 0x4C29BE3: malloc (vg_replace_malloc.c:299)
by 0x4005E1: main (main.c:6)
そしてコード:
#include <stdlib.h>
#include <stdint.h>
int main() {
char* destination = calloc(27, sizeof(char));
char* source = malloc(26 * sizeof(char));
for(uint8_t i = 0; i < 27; i++) {
*(destination + i) = *(source + i); //Look at the last iteration.
}
free(destination);
free(source);
return 0;
}
Valgrindは、上記のコメント行を示しています。ここで最後の反復、つまりを見てください
*(destination + 26) = *(source + 26);
。ただし、*(source + 26)
無効な書き込みと同様に、再び範囲外です。無効な読み取りも、off-by-oneエラーの一般的な結果です。割り当て操作の右側を見てください。
リークが私の場合はどうすればわかりますか?他の人のコードを使用しているときにリークを見つけるにはどうすればよいですか?私ではないリークを発見しました。何かすべきですか すべてが正当な質問です。最初に、一般的な遭遇の2つのクラスを示す2つの実際の例。
#include <jansson.h>
#include <stdio.h>
int main() {
char* string = "{ \"key\": \"value\" }";
json_error_t error;
json_t* root = json_loads(string, 0, &error); //obtaining a pointer
json_t* value = json_object_get(root, "key"); //obtaining a pointer
printf("\"%s\" is the value field.\n", json_string_value(value)); //use value
json_decref(value); //Do I free this pointer?
json_decref(root); //What about this one? Does the order matter?
return 0;
}
これは単純なプログラムです。JSON文字列を読み取って解析します。作成では、ライブラリー呼び出しを使用して構文解析を行います。JSONはそれ自体のネストされた構造を含むことができるため、Janssonは必要な割り当てを動的に行います。ただし、これはdecref
、すべての関数から与えられたメモリを私たちまたは「解放」することを意味するものではありません。実際、私が上で書いたこのコードは、「無効な読み取り」と「無効な書き込み」の両方をスローします。これらのエラーは、のdecref
行を削除すると解消されますvalue
。
どうして?変数value
は、Jansson APIでは「借用参照」と見なされます。Janssonはメモリを追跡し、decref
JSON構造を互いに独立させなければなりません。ここでのレッスン:
ドキュメントを読んでください。本当に。時々理解するのは難しいですが、彼らはなぜこれらのことが起こるのかを教えてくれます。代わりに、
このメモリエラーに関する既存の質問があります。
#include "SDL2/SDL.h"
int main(int argc, char* argv[]) {
if (SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO) != 0) {
SDL_Log("Unable to initialize SDL: %s", SDL_GetError());
return 1;
}
SDL_Quit();
return 0;
}
このコードの何が問題になっていますか?常に212 KiBのメモリをリークします。少し考えてみてください。SDLをオンにしてからオフにします。回答?何も問題はありません。
最初は奇妙に聞こえるかもしれません。正直なところ、グラフィックは乱雑であり、一部のリークを標準ライブラリの一部として受け入れる必要がある場合があります。ここでの教訓:すべてのメモリリークを抑制する必要はありません。リーク は既知の問題であり、何も実行できないため、リークを抑制する必要がある場合があります。(これはあなた自身のリークを無視する私の許可ではありません!)
リークが私のものである場合、どうすればわかりますか?
そうです。(とにかく99%確実)
他人のコードを使用している場合、どのようにリークを見つけるのですか?
他の誰かがすでにそれを見つけている可能性があります。Googleをお試しください!それが失敗した場合は、上記で提供したスキルを使用してください。それが失敗し、API呼び出しがほとんど表示され、独自のスタックトレースがほとんど表示されない場合は、次の質問を参照してください。
私ではないリークを発見しました。何かすべきですか
はい!ほとんどのAPIには、バグや問題を報告する方法があります。それらを使用してください!プロジェクトで使用しているツールへの還元にご協力ください!
この間、私と一緒にいてくれてありがとう。私がこの答えに到達する人々の幅広いスペクトルに傾倒しようとしたので、あなたが何かを学んだことを願っています。途中であなたが聞いたことがあると思います:Cのメモリアロケータはどのように機能しますか?実際にメモリリークとメモリエラーは何ですか?それらはsegfaultsとどう違うのですか?Valgrindはどのように機能しますか?これらのいずれかをお持ちの場合は、好奇心を養ってください:
memcheck
ツールは、デフォルトで有効になっていますか?
memcheck
デフォルトのツールである:--tool=<toolname> [default: memcheck]
これを試して:
valgrind --leak-check=full -v ./your_program
valgrindがインストールされている限り、それはあなたのプログラムを通過し、何が問題なのかを教えてくれます。それはあなたの漏れが発見されるかもしれないポインタとおおよその場所をあなたに与えることができます。セグメンテーション違反が発生している場合は、を実行してみてくださいgdb
。
your_program
==アプリケーションの実行に使用する実行可能ファイルの名前またはコマンド。
以下を実行できます。
valgrind --leak-check=full --log-file="logfile.out" -v [your_program(and its arguments)]