web-dev-qa-db-ja.com

セグメンテーション違反を引き起こすコード行を特定しますか?

セグメンテーションフォールト を引き起こすコードのどこに間違いがあるのか​​をどのように判断しますか?

いくつかのコードを書いた後、セグメンテーションフォールトのある場所を特定するために、コンパイラ(gcc)でプログラムのフォールトの場所を表示できますか?

131
user319824

GCCではできませんが、GDB( debugger )はできます。次のように、-gスイッチを使用してプログラムをコンパイルします。

gcc program.c -g

次に、gdbを使用します。

$ gdb ./a.out
(gdb) run
<segfault happens here>
(gdb) backtrace
<offending code is shown here>

ここ は、GDBを使い始めるための素晴らしいチュートリアルです。

180
nc3b

また、Valgrindを試してみることができます:Valgrindをインストールしてvalgrind --leak-check = fullを実行すると、プログラムが実行され、セグメンテーション違反のスタックトレース、無効なメモリの読み取りまたは書き込み、メモリリークが表示されます。本当に便利です。

37
jwkpiano1

コアダンプを使用して、gdbで調べることもできます。有用な情報を取得するには、-gフラグを使用してコンパイルする必要もあります。

メッセージを受け取るたびに:

 Segmentation fault (core dumped)

コアファイルが現在のディレクトリに書き込まれます。そして、あなたはコマンドでそれを調べることができます

 gdb your_program core_file

ファイルには、プログラムがクラッシュしたときのメモリの状態が含まれています。コアダンプは、ソフトウェアの展開中に役立ちます。

システムがコアダンプファイルのサイズをゼロに設定しないようにしてください。次の方法で無制限に設定できます。

ulimit -c unlimited

でも気をつけて!そのコアダンプは巨大になる可能性があります。

17
Lucas

セグメンテーションフォールトのデバッグに役立つ多くのツールが用意されています。お気に入りのツールをリストに追加したいと思います: Address Sanitizers(略称ASAN)

現代の¹コンパイラには便利な-fsanitize=addressフラグが付いており、コンパイル時間と実行時のオーバーヘッドが追加され、エラーチェックが増えます。

ドキュメント によれば、これらのチェックにはデフォルトでセグメンテーション違反の捕捉が含まれます。ここでの利点は、gdbの出力に似たスタックトレースを取得できることですが、デバッガー内でプログラムを実行する必要はありません。例:

int main() {
  volatile int *ptr = (int*)0;
  *ptr = 0;
}
$ gcc -g -fsanitize=address main.c
$ ./a.out
AddressSanitizer:DEADLYSIGNAL
=================================================================
==4848==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000 (pc 0x5654348db1a0 bp 0x7ffc05e39240 sp 0x7ffc05e39230 T0)
==4848==The signal is caused by a WRITE memory access.
==4848==Hint: address points to the zero page.
    #0 0x5654348db19f in main /tmp/tmp.s3gwjqb8zT/main.c:3
    #1 0x7f0e5a052b6a in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x26b6a)
    #2 0x5654348db099 in _start (/tmp/tmp.s3gwjqb8zT/a.out+0x1099)

AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV /tmp/tmp.s3gwjqb8zT/main.c:3 in main
==4848==ABORTING

出力はgdbが出力するものよりも少し複雑ですが、利点があります:

  • スタックトレースを受信するために問題を再現する必要はありません。開発中にフラグを有効にするだけで十分です。

  • ASANは、単なるセグメンテーションフォールト以上のものをキャッチします。そのメモリ領域がプロセスにアクセス可能であったとしても、多くの範囲外アクセスがキャッチされます。


¹つまり、 Clang 3.1 + および GCC 4.8 + です。

4
asynts

コアダンプに関するルーカスの答えは良いです。私の.cshrcには:

alias core 'ls -lt core; echo where | gdb -core=core -silent; echo "\n"'

「core」と入力してバックトレースを表示します。日付スタンプ、正しいファイルを見ていることを確認するために:(。

追加stack破損バグがある場合、コアダンプに適用されるバックトレースは多くの場合ゴミです。この場合、gdb内でプログラムを実行すると、受け入れられた答えに従って、より良い結果が得られます(障害が容易に再現可能であると仮定)。また、コアを同時にダンプする複数のプロセスにも注意してください。一部のOSでは、コアファイルの名前にPIDが追加されます。

2
Joseph Quinsey