VMによるカーネルモジュールのデバッグ


VMによるカーネルモジュールのデバッグ
本論文では,仮想マシンにおいてKGDBデュアルマシンを用いてNBD駆動を調整する準備手順と,より高い周波数のデバッグコマンドの使用について述べる.これを例に、Linuxカーネルおよびカーネルモジュールをデバッグする一般的な方法について説明します.
カーネルデバッグを行う場合、システムはアプリケーションのユーザ状態プログラムを鳴らすことがないため、2台のコンピュータを使用してシリアルポートまたはネットワークを使用してデュアルマシンで調整する必要があります.本稿では、シリアルポートを使用して調整することを紹介します.
デバッグの準備とデバッグ手順について詳しく説明します.
1準備
  • 仮想マシンを新規作成し、linuxシステムをインストールします.このチュートリアルでは、SUSE Linux Enterprise 11を使用します.
  • kernel.orgは最新のカーネルを取得し、2011-02-11まで、最新のカーネルバージョン番号は2.6.37です.

  • 以下に、nbd-serverプログラムをコンパイルする際に必要なライブラリパッケージを示します.他のドライバをデバッグする場合は、必要に応じてダウンロードとインストールを行います.
  • gettext、本明細書で使用するバージョンは0.18.1.1です.
  • libxml、本明細書で使用するバージョンは2.7.8です.
  • zlib、本明細書で使用するバージョンは1.2.5です.
  • Glib、本明細書で使用するバージョンは2.26.1です.
  • NBD、本明細書で使用されるバージョンは2.9.20である.

  • 2 linuxのカーネルデバッグ機能をオンにする
    2.1カーネルを更新し、KGDBを開く
    得られたlinuxソースコードをlinuxの/usr/srcパスの下に解凍し、同じディレクトリの下でlinux接続を確立し、コードパスの下に接続します:(緑の字はシステム出力メッセージで、赤の字は入力する必要がある命令です.以下同じ)
    localhost:/usr/src# ln -s linux-2.6.37 linux
    /bootのconfigファイルを/usr/src/linuxにコピーし、名前を変更します.config. linuxソースパスの下でmake menuconfigコマンドを実行し、カーネルコンパイルオプションの構成を開始します.
    localhost:/usr/src/linux # make menuconfig
    次のコンパイルオプションがオンになっていることを確認します.
    CONFIG_FRAME_POINTER=y
    CONFIG_KGDB=y
    CONFIG_KGDB_SERIAL_CONSOLE=y

    カーネルのコンパイルと更新:
    localhost:/usr/src/linux # make && make modules_install && make install
    カーネルのコンパイルと更新が完了すると、コンピュータを再起動し、起動メニューで「新しいカーネルから起動」を選択します.
    4シリアルポートの構成
    hypervisor managerに2台の仮想マシンのシリアルポートを構成します.
    5必要なライブラリのインストール
    この順序で必要なライブラリファイルをインストールします.gettextl->libxml->zlib->glib注:nbd-serverプログラムをコンパイルするときに必要なライブラリファイルパッケージです.他のドライバをデバッグする場合は、必要に応じて適宜インストールします.
    6カーネルデバッグを開始
    6.1サービス・エンドのオープン(デバッグ対象)
    シリアル・ポートを使用してシリアル・メッセージを送信および受信するには、次の手順に従います.
    localhost:/# echo ttyS0 >/sys/module/kgdboc/parameters/kgdboc
    カーネルデバッグブレークポイントの開始:
    localhost:/# echo g >/proc/sysrq-trigger
    サービス側で上記の2つのコマンドを実行すると、システムをデバッグ状態にします.サービス側は、ユーザー状態応答を停止しました.
    6.2クライアントを起動する(デバッガ)
    コマンドラインで、現在のディレクトリをlinuxのソースディレクトリの下に設定します.
    localhost:/# cd usr/src/linux
    gdbデバッガを起動するには、次の手順に従います.
    localhost:/usr/src/linux # gdb ./vmlinux
    gdbの起動が完了するのを待って、gdb接続サービスを設定します.
    (gdb) set remotebaud 115200 (gdb) target remote/dev/ttyS0
    gdbが次のメッセージを出力するのを待って、デバッグ環境が成功したことを示します.
    kgdb_breakpoint () at kernel/debug/debug_core.c:960 960 wmb();/* Sync point after breakpoint */
    7デバッグ開始
    7.1 NBDのシンボルファイルをロードする
    運用サービスを再開するには、次の手順に従います.
    (gdb) c
    サービス側でNBDモジュールのロードアドレスを取得する:
    localhost:/# cat/proc/modules
    サービス側は、次のような情報を入力します.
    nbd 12427 1 - Live 0xd8831000 xt_tcpudp 2632 3 - Live 0xd8b50000 xt_pkttype 796 3 - Live 0xd8b17000
    上から得られたnbdのロードアドレスは0 xd 8831000である.サービス側のカーネルデバッグブレークポイントの開始を続行します.
    localhost:/# echo g >/proc/sysrq-trigger
    クライアントにnbdのシンボルファイルをロードするには:
    (gdb) add-symbol-file drivers/block/nbd.ko 0xd8831000
    gdbは以下の情報を出力し、yを選択し、車に戻る.
    add symbol table from file “drivers/block/nbd.ko” at .text_addr = 0xd8831000 (y or n) y
    7.2 NBDモジュールをデバッグする簡単な例
    nbd.cコードの237行のブレークポイント:
    (gdb) b nbd.c:237
    gdb出力は、ブレークポイント設定に成功したことを示す情報です.
    (gdb) Breakpoint 1 at 0xd88316b1: file drivers/block/nbd.c, line 237
    運用サービスを再開するには、次の手順に従います.
    (gdb)c
    サービス側でnbdの操作を行うと、システムはさっきのブレークポイントで中断します.
    Breakpoint 1, nbd_send_req (lo=0xd5634000, req=0xd4ea02d8) at drivers/block/nbd.c:237 237 unsigned long size = blk_rq_bytes(req);
    単一ステップ実行:(n:単一ステップスキップ.s:単一ステップ進む)
    (gdb) n
    gdb出力:
    239 request.magic = htonl(NBD_REQUEST_MAGIC);
    この時点で237行のsize変数の値を表示します.
    (gdb) p size
    gdb出力:
    $1 = 1024
    変数sizeの値を変更するには、次の手順に従います.
    (gdb) set size=1025
    この時点で237行のsize変数の値を表示します.
    (gdb) p size
    gdb出力:
    $1 = 1025
    nbd.cコードの239行の下の条件ブレークポイント(条件は237行のsizeが1024):
    (gdb) b nbd.c:239 if size==1024
    運用サービスを再開するには、次の手順に従います.
    (gdb) c
    sizeの値が1024の場合、システムは中断し、gdbは以下の情報を出力する.
    Breakpoint 2, nbd_send_req (lo=0xd5634000, req=0xd4ea02d8) at drivers/block/nbd.c:239 239 request.magic = htonl(NBD_REQUEST_MAGIC);
    sizeの値は1024で表示されます.
    (gdb) p size $3 = 1024
    呼び出しスタックを表示するには、次の手順に従います.
    (gdb) bt #0 nbd_send_req (lo=0xd5634000, req=0xd4ea02d8) at drivers/block/nbd.c:239 #1 0xd8832210 in nbd_handle_req (lo=0xd5634000, req=0xd4ea02d8) at drivers/block/nbd.c:478 #2 0xd8832452 in nbd_thread (data=0xd5634000) at drivers/block/nbd.c:523 #3 0xc025b8f4 in kthread (_create=) at kernel/kthread.c:95 #4 0xc0203336 in ?? () at arch/x86/kernel/entry_32.S:1009 #5 0x00000000 in ?? ()
    ブレークポイント付近のコードを表示するには、次の手順に従います.
    (gdb) l 234 { 235 int result, flags; 236 struct nbd_request request; 237 unsigned long size = blk_rq_bytes(req); 238 239 request.magic = htonl(NBD_REQUEST_MAGIC); 240 request.type = htonl(nbd_cmd(req)); 241 request.from = cpu_to_be64((u64)blk_rq_pos(req) << 9); 242 request.len = htonl(size); 243 memcpy(request.handle, &req, sizeof(req));
    8デバッグの終了
    まず、すべてのブレークポイントを削除します.
    (gdb) delete Delete all breakpoints? (y or n) y
    サービス・エンドの実行を再開するには、次の手順に従います.
    (gdb) c Continuing.
    ctrl+cを押します.
    Give up (and stop debugging it)? (y or n) y
    終了コマンドを入力:
    (gdb) quit localhost:/usr/src/linux #