それが僕には楽しかったんです。

僕と MySQL と時々 MariaDB

gdbを使ってコアダンプの原因を解析

コアダンプは嫌いだ。

大学やその他情報系専門科のある学校に通ったことがある人が一度は触ったことがあるであろう、C言語
こいつは近年の言語に比べてものすごく面倒くさい書き方をするし、手間もかかる。
中でも最悪なのが「コアダンプ」の文字。
これは、多くのC言語ユーザを苦しめてきただろう。
一応エラーの部類に入ると思うが、こいつはどこで問題が起きているか普通は出力してくれないから直すのがすごく大変。


というわけで、今回はコアダンプの原因をgdbというデバッガを使用して解析してみる(※linuxユーザ向け)

それでは解析の準備

これはこの前自分が実際にコアダンプを発生させてしまったコード。
このファイルをここでは「quicksort.c」とする。

#include<stdio.h>

void quicksort(int array[],int left_index,int right_index){
    
    int left = left_index,right = right_index,tmp;
    
    //基点を左右の中間から選択
    int pivot = array[(left+right)/2];

    while(1){

        //左側で基点より大きい値を探す
        while(array[left] < pivot)
            left++;
        //右側で基点より小さい点を探す
        while(pivot < array[right])
            right++;//ここが間違っている

        if(right < left)
            break;

        //値の入れ替え
        //基点より小さい数値を左、大きい数値を右へ移動させる
        tmp = array[left];
        array[left] = array[right];
        array[right] = tmp;

        left++;
        right--;
    }

    //再帰関数による分割統治法を行う
    if(left_index < right)
        quicksort(array,left_index,right);
    
    if(left < right_index)
        quicksort(array,left,right_index);

}

int main(){
    int i;
    int array[] = {2,4,5,3,1,6,3,1,9};
    quicksort(array,0,9-1);
    for(i = 0; i < 9; i++)
        printf("%d\n",array[i]);

    return 0;
}

こいつを実行させると、もちろんこのメッセージがターミナルに出力される。

Segmentation fault (コアダンプ)

それでは解析を始める

まずコアダンプを解析するためにコンパイラオプションの「-g」をつける。

gcc quicksort.c -g -o quicksort
  • gオプションをつけることで、デバッグ情報を出力するようにする。

つぎに、大抵コアダンプ機能が無効にされているの以下のコマンドで有効にする

ulimit -c unlimited

それが終わったら、問題のある(コアダンプのある)実行ファイルを実行する

./quicksort
Segmentation fault (コアダンプ)

となる。

この時lsでカレントディレクトリを確認すると「core」や「core.xxxxx(xは数字)」といったファイルが生成される。


ここまでできたら、以下のコマンドでデバッグする

gdb ./quicksort core

そうすると、こんな出力が見えるはず(プログラムによって違う)

GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.04) 7.11.1
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./quicksort...done.

warning: core file may not match specified executable file.
[New LWP 13685]
Core was generated by `./base1'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0  0x0000000000400616 in quicksort (array=0x7ffe0656abc0, left_index=0, 
    right_index=8) at quicksort.c:26
26	        while(pivot < array[right])
(gdb) 

これで大体わかってしまうんだけど、今回はちゃんと手順を踏む。

まずはBackTraceする。

(gdb) bt

#0  0x0000000000400616 in quicksort (array=0x7ffe0656abc0, left_index=0, 
    right_index=8) at quicksort.c:26
#1  0x0000000000400747 in main () at quicksort.c:56

これで問題のありそうな関数の番号を選択すると

#今回は#0のquicksort関数に問題がありそうだから0を選択
(gdb) frame 0

#0  0x0000000000400616 in quicksort (array=0x7ffe0656abc0, left_index=0, 
    right_index=8) at quicksort.c:26
26	        while(pivot < array[right])

というわけでこの26行目になにやら問題があることがわかった。
今回は

right++;

で配列の範囲外を参照してしまうことが問題になっていた。

というわけで、解析完了!


これがポインタなどの場合は以下の様に確かめることができる

(gdb)p [ここに確かめたい変数名、引数名など]

そうするとそれの値やメモリアドレスを指してくれる。
よくあるのは「0x0」というメモリアドレス。これはメモリが確保されていないことを指すので該当部分のポインタやアドレスまわりの処理を見なおそう。

コアダンプの原因

ここでさくっと、どうしてコアダンプが発生するかまとめておく。

  • メモリ違反
    • これが一番多いと思う
    • 配列の範囲外や未代入ポインタの参照など
  • 無限再帰や深すぎる再帰
    • 再帰から脱出できないときによくおきる

最後に

メモリ関連を特に見なおそう、配列やポインタなど。
gdbをうまく使えればコアダンプを瞬殺できるから使えると便利だけど、面倒くさいのでなるべくコアダンプが発生しないコードを書こう。