2

私は C でプロジェクトに取り組んでおり、プログラムをクラッシュさせるあいまいなバグをデバッグする方法を理解しようとしています。コードの小さなバージョンを作成して問題を切り分けようとする試みは、かなり大規模であり、機能していません。だから私は、メモリリークをデバッグして特定する方法を考え出そうとしています。

私は次の計画を思いつきました: 問題が特定の関数の実行に起因することはわかっており、その関数はそれ自体を再帰的に呼び出します。そこで、プログラムのメモリ割り当てのスナップショットを作成できると考えました。ボンネットの下で何が起こっているかについてジャックを知らないので(この状況で役立つには少し足りないことを知っています):

typedef struct record_mem {
    int num_allocs;
    int num_frees;
    int size_space;
    int num_structure_1;
    ...
    int num_structure_N;
    int num_records;
    struct record_mem *next;
} RECORD;
extern RECORD *top;
void pushmem(RECORD **top)
{
    RECORD *nnew = 0;
    RECORD *nnew = (RECORD *)malloc(sizeof(RECORD));
    nnew->num_allocs=1;
    nnew->num_frees=0;
    nnew->size_space=sizeof(RECORD);
    nnew->num_structure_1=0;
    ...
    nnew->num_structure_N=0;
    nnew->num_records=1;
    nnew->next=0;
    if(*top)
    {
        nnew->num_allocs+=(*top)->num_allocs;
        nnew->num_frees=(*top)->num_frees;
        nnew->size_space+=(*top)->size_space;
            nnew->num_structure_1=(*top)->num_allocs;
            ...
            nnew->num_structure_N=(*top)->num_allocs;
            nnew->num_records+=(*top)->num_records;
        nnew->next=*top;
    }
    *top=nnew;
}

プログラムがクラッシュする瞬間の直前に保持しているメモリレコードの内容を印刷するという考えです(GDBのおかげでクラッシュする場所を知っています)。

そして、プログラム全体で(プログラムの各データ構造に対して、上記のような同様のプッシュ関数があります)、データ構造の割り当てと合計スタック(ヒープ?)のメモリ割り当てを集計する関数を含む1つのライナーを追加するだけです(追跡できます)の)。私は、実行中のプログラムのスナップショットを記録する必要があると感じた場所に、さらに memory_record 構造を作成するだけです。問題は、実際に使用されているメモリの量を何らかの方法で記録できない場合、このメモリバランスシートの記録が役に立たないことです。

しかし、どうすればこれを行うことができますか?さらに、ぶら下がっているポインターとリークをどのように考慮に入れるでしょうか? 私はOS Xを使用していますが、スタックポインタなどを記録する方法を現在調べています。

編集: あなたが尋ねたので: valgrind の出力: (closure() は、悪いポインターを返す main から呼び出される関数です: 二重リンクリストの先頭を返すはずです。私は余分なノードを計算してリンクリストに追加し、ノード間をジャンプする必要があるため、それ自体を再帰的に呼び出します。)

jason-danckss-macbook:project Jason$ valgrind --leak-check=full --tool=memcheck ./testc
Will attempt to compute closure of AB:
Result: testcl: 0x10000d0b0
==7682== Invalid read of size 8
==7682==    at 0x100001D4E: printrelation2 (relation.h:490)
==7682==    by 0x100003CFE: main (test-computation.c:47)
==7682==  Address 0x10000cee8 is 8 bytes inside a block of size 24 free'd
==7682==    at 0xD828: free (vg_replace_malloc.c:450)
==7682==    by 0x100001232: destroyrelation2 (relation.h:161)
==7682==    by 0x100003407: destroyallhashmap (computation.h:333)
==7682==    by 0x1000039E1: closure (computation.h:539)
==7682==    by 0x100003CBE: main (test-computation.c:38)
==7682== 
==7682== 
==7682== HEAP SUMMARY:
==7682==     in use at exit: 5,360 bytes in 48 blocks
==7682==   total heap usage: 99 allocs, 51 frees, 6,640 bytes allocated
==7682== 
==7682== 48 (24 direct, 24 indirect) bytes in 1 blocks are definitely lost in loss record 33 of 37
==7682==    at 0xC283: malloc (vg_replace_malloc.c:274)
==7682==    by 0x100001104: getnewrelation (relation.h:134)
==7682==    by 0x100001848: copyrelation (relation.h:343)
==7682==    by 0x100003991: closure (computation.h:531)
==7682==    by 0x100003CBE: main (test-computation.c:38)
==7682== 
==7682== 1,128 (24 direct, 1,104 indirect) bytes in 1 blocks are definitely lost in loss record 36 of 37
==7682==    at 0xC283: malloc (vg_replace_malloc.c:274)
==7682==    by 0x100002315: getnewholder (dependency.h:129)
==7682==    by 0x100003B17: main (test-computation.c:14)
==7682== 
==7682== LEAK SUMMARY:
==7682==    definitely lost: 48 bytes in 2 blocks
==7682==    indirectly lost: 1,128 bytes in 44 blocks
==7682==      possibly lost: 0 bytes in 0 blocks
==7682==    still reachable: 4,096 bytes in 1 blocks
==7682==         suppressed: 88 bytes in 1 blocks
==7682== Reachable blocks (those to which a pointer was found) are not shown.
==7682== To see them, rerun with: --leak-check=full --show-reachable=yes
==7682== 
==7682== For counts of detected and suppressed errors, rerun with: -v
==7682== ERROR SUMMARY: 3 errors from 3 contexts (suppressed: 0 from 0)
4

3 に答える 3

5

valgrind (およびそのmemcheck )を試しましたか?

$ valgrind --tool=memcheck --leak-check=full ./yourprogram

(そしてできればあなたのプログラムをでコンパイルしてください-g

編集: 申し訳ありませんが、Valgrind を使用したくないということは読みませんでしたが、投稿の dureuill からのコメントで指摘されているように、非常便利であり、学習する価値があります。

別の情報:メモリ リークは、いくつかのorのfree後に欠落が原因で発生します ( C の簡単な例をここで見ることができます)。また、 (withを使用して行を取得し、再帰検索を行う) を使用して、プログラム内のすべてのメモリ割り当て行を一覧表示することもできます。への呼び出しでそれらのそれぞれを一致させようとします。ただし、これは面倒な場合があり、Valgrind を使用した方が高速になると心から信じています。mallocreallocgrep-n-rfree

于 2014-04-13T20:16:07.860 に答える
4

私は Valgrind をすべての提案で見たので、時間の経過とともに有用であることが証明されている他のいくつかのより一般的なものをお勧めします。

コードを絞り込んでバグを探す

まず、ツールを使用したり、大規模なシステムを追跡したりすることが難しくなります。問題を絞り込んでみてください。

たとえば、モジュールをオフにします (コードの一部をコメントアウトして、引き続き問題が発生するかどうかを確認します)。実際に厄介なランダム メモリ破損でない限り、いくつかのヒットと試行により、コードの主要なチャンクを排除できるはずです。

動的メモリを削除するか、少なくともメモリの割り当て解除をコメントアウトします

「メモリのない」呼び出しをコメント アウトしてみてください (システム メモリのオーバーフローを避けることができる場合)。このようにして、少なくとも解放または割り当て解除の問題を排除または絞り込むことができます。できれば、静的に割り当てられたメモリを使用してシステム全体を実行してみてください。あまり実用的ではないことは承知していますが、スコープを限定して一貫してクラッシュを発生させれば、動的メモリを必要とする代わりに十分な大きさの静的メモリを割り当てることができるかもしれません。ノードの配列を作成し、それらをポインターに割り当てることができます。

スタックを呼び出してクラッシュ位置を監視する

クラッシュの時点でコール スタックを確認し、ローカルで使用可能なポインターを検証済みであると仮定します。上記のいずれかを試す前に、それが非常に即時のアプローチであったはずです。

于 2014-04-13T21:59:47.000 に答える