3

私は多くのプログラミングを行いましたが、C はあまり使用していません。デバッグについてアドバイスが必要です。マルチスレッド プログラム (OS X 10.4 で pthreads を使用) を実行してから約 10 ~ 100 秒後に、静的変数 (ファイル スコープ) が破壊されています。私のコードは次のようになります。

static float some_values[SIZE];
static int * addr;

addrしばらくの間有効なメモリアドレスを指し、その後何らかの値 (0 の場合もあればゼロ以外の場合もある) で上書きされ、逆参照時にセグメンテーション違反が発生します。をいじってみると、期待どおりに がメモリ内に配置されgdbていることが確認されたので、最初に推測したのは、範囲外のインデックスを使用して に書き込んだということです。ただし、これは小さなファイルなので、問題がないことを簡単に確認できます。addrsome_valuessome_values

明らかなデバッグ手法は、変数にウォッチポイントを設定することですaddr。しかし、そうすると、 で不規則で不可解な動作が発生するようですgdb。ウォッチポイントは、への最初の割り当てでトリガーされaddrます。その後、実行を続行すると、すぐに別のスレッドで無意味なセグメンテーション違反が発生します...おそらく、プログラムの別の部分で静的変数のアドレスにアクセスする際のセグメンテーション違反です! しかし、gdbそのメモリ アドレスを対話的に読み書きできるようにします。

プログラムは信号 EXC_BAD_ACCESS を受信しました。メモリにアクセスできませんでした。
理由: アドレス: 0x001d5bd0 の KERN_PROTECTION_FAILURE
mainloop.c:39 の受信 (arg=0x0) で 0x0000678d
39 sample_buf_cleared ++;
(gdb) p &sample_buf_cleared
$17 = (int *) 0x1d5bd0
(gdb) p sample_buf_cleared
$18 = 1
(gdb) sample_buf_cleared = 2 を設定
(gdb)

gdb明らかに混乱しています。誰かが理由を知っていますか?または、ウォッチポイントを使用せずにこのバグをデバッグするための提案はありますか?

4

6 に答える 6

3
  1. uint の配列を some_values と addr の間に配置して、some_values をオーバーランしているかどうか、または破損が最初に考えたよりも多くのアドレスに影響するかどうかを判断できます。パディングを DEADBEEF や、プログラム内で発生する可能性が低く、簡単に区別できるその他の明らかなパターンに初期化します。パディングの値が変更された場合は、それを float にキャストし、数値が float として意味があるかどうかを確認します。

static float some_values[SIZE]; static unsigned int padding[1024]; static int * addr;

  1. プログラムを複数回実行します。実行するたびに別のスレッドを無効にして、問題がいつ解消されるかを確認します。

  2. プログラム プロセス アフィニティをシングル コアに設定してから、ウォッチポイントを試します。値を同時に変更する 2 つのスレッドがなければ、うまくいくかもしれません。注: このソリューションは、それが起こらないようにするものではありません。デバッガーでキャッチしやすくなる場合があります。

于 2009-06-17T05:10:39.737 に答える
2

static変数とマルチスレッドは一般に混在しません。

addrコードが表示されていない場合 (スレッド化されたコードを含める必要があります)、2 つのスレッドが同時に変数に書き込みを行っていると思います。うまくいきません。

次のいずれかが必要です。

  • addrスレッドごとに個別のインスタンスを作成します。また
  • addr2 つのスレッドが同時に値を変更するのを防ぐために、何らかの同期を提供します。
于 2009-06-17T04:15:38.943 に答える
1

valgrind を使ってみてください。私は OS X で valgrind を試したことがなく、あなたの問題を理解していませんが、「破壊された」と言ったときに「valgrind を試してください」というのが最初に思い浮かびます。

于 2009-06-17T04:11:41.470 に答える
1

addrあなたが試すことができることの1つは、の値を監視し、それが変更されたときに中断することだけを目的とする別のスレッドを作成することです。例えば:

static int * volatile addr;  // volatile here is important, and must be after the *
void *addr_thread_proc(void *arg)
{
    while(1)
    {
        int *old_value = addr;
        while(addr == old_value) /* spin */;
        __asm__("int3");  // break the debugger, or raise SIGTRAP if no debugger
    }
}
...
pthread_t spin_thread;
pthread_create(&spin_thread, NULL, &addr_thread_proc, NULL);

次に、値がaddr変更されるたびにint3命令が実行され、デバッガーが中断され、すべてのスレッドが停止します。

于 2009-06-17T04:46:35.487 に答える
0

OSXでデバッグを行ったことはありませんが、LinuxのGDBでも同じ動作が見られます。プログラムがクラッシュしますが、GDBは、プログラムが読み取り/書き込みに失敗したメモリの読み取りと書き込みを行うことができます。

これは必ずしもGDBが混乱していることを意味するわけではありません。むしろ、カーネルは、下位プロセスが読み取りまたは書き込みを許可されていないptrace()を介してGDBがメモリを読み取り/書き込みできるようにしました。IOW、これは(最近修正された)カーネルのバグでした。

それでも、何らかの理由でGDBウォッチポイントが機能していないようです。

使用できる手法の1つは、静的にスペースを割り当てるのではなく、スペースを確保し、配列がmmapページ境界で終了するように配置し、次のページにアクセスできないように配置することです(を介して)。some_valuesmprotect

コードがの終わりを超えてアクセスしようとするsome_valuesと、例外が発生します(事実上、書き込み不可能な「ウォッチポイント」をちょうど過ぎて設定していますsome_values)。

于 2009-06-18T02:22:13.450 に答える
0

gdb は、マルチスレッド プログラムで奇妙な動作をすることがよくあります。別の解決策 (余裕がある場合) は、printf()s をあちこちに配置して、値が壊れる瞬間を見つけようとすることです。あまりエレガントではありませんが、時には効果的です。

于 2009-06-17T04:14:37.523 に答える