ここにあるパッチは、プログラムを遅くしますが、gdb上でプログラムを一行ごとに実行して変数の検査をできるようにします。Fedora Core 4上で、使えます。i386-softmmuターゲットだけのものです。
Windowsホストのパッチ。AIOが使われるようになってから、SIGALRMとSIGUSR2がLinuxホストでは使われます。しかし、gdbにとっては、デバッグの障害になります。上のパッチは、SIGUSR2を使わなくし、AIOがSIGALRMを使うようにしています。また、タイマー割り込みをしなくしています。
Windowsホストでは、シグナルの問題はありませんので、デバッグはやさしいです。
gccの-O0オプションで最適化を行わないようにします。-fomit-frame-pointerを取ることで、通常の関数呼び出しを用いて、gdb上でのデバッグを容易にできます。ただ、これらはOP_CFLAGSには使えません。op.oというオブジェクトファイルは、コンパイル時にdyngenによって解析されるので使えません。-O2オプションのみでは他のソースコードに対して最適化されすぎますので、-O2 -fno-gcse もしくは -O1 オプションが作成環境(gccのバージョンなど)によりつかえます。
-O0オプションを使うには、exec.hにあるenvとT0からT3までの変数をレジスタ変数に指定しなくする必要があります。これで、struct CPUX86Stateを調べたりできます。
target-i386/op.c内のASM_SOFTMMUは使えません。プログラムがコンパイルできないので。
以下に、Linuxホストでのデバッグ方法を示します。
(1)現在のCVSのコードにパッチをあて、makeします。
$ cvs -z3 -d:pserver:anonymous@cvs.savannah.nongnu.org:/sources/qemu co qemu $ cd qemu qemu$ patch -p0 <../qemu-20061108-debug-on-linux.patch qemu$ ./configure --target-list=i386-softmmu --cc=gcc32 qemu$ make(2) ディレクトリをi386-softmmuに変え、.gdbinitファイルを編集します。
qemu$ cd i386-softmmu i386-softmmu$ vi .gdbinit以下に.gdbinitファイルを示します。
file qemu set args -L ../pc-bios -hda ../../linux.img b main define hook-stop handle SIGALRM nopass end define hook-run handle SIGALRM pass end defilen hook-continue handle SIGALRM pass end run-----------------
i386-softmmu$ gdbすると、プログラムはmainルーチンで止まります。
[Fedora Core 4] LD_ASSUME_KERNEL=2.2.5 [Fedora Core 4] gdb
上記のパッチをあてて、プログラムを作ったあと次のように起動します。
i386-softmmu$ gdb qemu.exe GNU gdb 5.2.1 Copyright 2002 Free Software Foundaton, Inc. etc. (gdb)
set args で起動オプションと、b(break)でブレークポイントを設定します。
(gdb) set args -L ../pc-bios -hda ../linux.img (gdb) b main
r(run)でプログラムを起動し、n(next)で1行ずつ実行し、p(print)で変数を検査できます。
(gdb)r Starting prgram: C:\qemu\i386-softmmu\qemu.exe -L ../pc-bios -hda ../linux.img Breakpoint 1, main (argc=5, argv=0x1584a08) at C:/qemu/vl.c:2758 2758 DisplayState *ds = &display_state; (これが次に実行されるソースコードです。)
(gdb)p display_state (display_stateの内容が表示されます。)
$1 = {data = 0x0, linesize =0, depth = 0, width = 0, height = 0,
dpy_update = 0, dpy_size = 0, dpy_refresh = 0}
(gdb)p &display_state (display_stateのメモリーアドレスが表示されます。) $2 = (DisplayState *) 0x4b6018 (この値は環境により異なります。)
(gdb)p ds (dsの値が表示されます。) $3 = (DisplayState *) 0x7800bd6 (この値は今は初期化されてません。)
(gdb)n (vl.cの2758行目が実行されます。)
(gdb)p ds $4 = (DisplayState *) 0x4b6018 (この値はdisplay_stateのメモリーアドレスと同じ値になります。)
(gdb)p *ds
$5 = {data = 0x0, linesize =0, depth = 0, width = 0, height = 0,
dpy_update = 0, dpy_size = 0, dpy_refresh = 0}
(これらの値は、display_stateの内容と同じです。)
これで、display_stateのメモリーアドレスがdsに入ったことを確認することができます。
あと、main_loopという関数の先頭にブレークポイントを設定し、そこまで実行するにはc(continue)。ソースコードの表示には、l(list)。
(gdb)b main_loop
Breakpoint 2 at 0xxxxxx: file C:/qemu/vl.c, line 2459.
(gdb) c
Contnuing.
Breakpoint 2, main_loop () at C:/qemu/vl.c, line 2459
2459 CPUState *env = global_env;
(gdb) list
2454 }
2455
2456 int main_loop(void)
2457 {
2458 int ret, timeout;
2459 CPUState *env = global_env;
2460
(gdb) n
2462 if (vm_running) {
(gdb) p env
$7 = (CPUX86State *) 0xa520048 (CPUStateは、マクロによりCPUX86Stateになっています。)
(gdb) p *env
$8 = {regs = {0, 0, 1536, etc.....
vl.cの2463行目にブレークポイントを設定したりするには、b vl.c:2463を使います。関数に入るには、s(step)で入ることができます。finishで関数の終わりまで実行して抜け出せます。
(gdb) b vl.c:2463
Breakpoint 3 at 0x403f5e: file c:/qemu/vl.c, line 2463.
(gdb) c
Continuing.
Breakpoint 3, main_loop () at C:/qemu/vl.c:2463
2463 ret = cpu_exec(env);
(gdb) s
cpu_x86_exec (env1=0xa520048) at C:/qemu/cpu-exec.c:113
113 saved_T0 = T0;
(gdb) finish
Run till exit from #0 cpu_x86_exec (env1=0xa520048)
at C:/qemu/cpu-exec.c:113
0x00403f69 in main_loop () at C:/qemu/vl.c:2463
2463 ret = cpu_exec(env);
Value returned is $10 = 256
(gdb)
ブレークポイントの表示は、i(info) b(break)。設定解除は、del(delete) number。有効化、無効化には、enab(enable) number、 dis(disable) number。
(gdb) i b (gdb) del 1 (gdb) enab 1 (gdb) dis 1
プログラムを終了するには、q(quit)。
(gdb) q The program is running. Exit anyway? (y or n) y
よく使うのはこんなとこだと思います。あとは、h(help)で調べてみてください。
(gdb) h (gdb) h running
.gdbinitというファイルを置くことでgdbの初期化時の設定をすることができます。
file qemu.exe set args -L ../pc-bios -hda ../linux.img b main r
NoConsoleというプログラムが、既にある実行ファイルにコンソールウィンドウをつけたりとったりできます。
http://lists.gnu.org/archive/html/qemu-devel/2005-04/msg00437.html
http://lists.gnu.org/archive/html/qemu-devel/2005-10/msg00326.html
ソースコードはこちら。
http://dliboon.freeshell.org/?view=programming.bcx
Windows 2000ゲストをWindows XPホストからデバッグする場合について紹介します。
Debugging Tools for Windowsをマイクロソフトのサイトからダウンロードして、ホストOSにインストールします。
windbgを起動して、[File]->[Symbol File Path]メニューを選びます。これで、ゲストOSのシンボルパスを設定します。
ダイアログに、srv*c:\tmp*http://msdl.microsoft.com/download/symbolsを設定します。これで、マイクロソフトのシンボルサーバからゲストOSのシンボルをダウンロードし、c:\tmpフォルダに保存することになります。もし、c:\tmpフォルダがなければ、作成してください。

デスクトップにwindbgのショートカットを作ります。スタート−>すべてのプログラム−>Debugging Tools for Windows−>windbgと選んで、その上で右クリックをします。そして、送る−>デスクトップ(ショートカットを作成)を選びます。
作成したショートカット上で右クリックし、プロパティを選びます。
リンク先というところに、-k com:pipe,port=\\.\pipe\com_1,resets=0,reconnect というスタートオプションを付け加えます。com_1というのが、名前つきパイプの名前です。
"C:\Program Files\Debugging Tools for Windows\windbg.exe" -k com:pipe,port=\\.\pipe\com_1,resets=0,reconnect

ゲストOSがWindows 2000の場合は、起動して、コマンドプロンプトに行きます。boot.iniの属性を変更して、notepad.exeで開きます。
C:\> attrib -r -s -h boot.ini
C:\> notepad boot.ini
そして、エントリをコピーして/debug /debugport=com1 /baudrate=115200を加えます。
multi(0)disk(0)rdisk(0)partition(1)\WINDOWS="Microsoft Windows 2000 Professional" /fastdetect multi(0)disk(0)rdisk(0)partition(1)\WINDOWS="Microsoft Windows 2000 Professional" /fastdetect /debug /debugport=com1 /baudrate=115200それぞれのエントリは1行です。

-serial pipe:com_1オプションでゲストOSを起動します。com_1というのは、上で説明した名前つきパイプの名前です。
qemu.exe -L . -hda win2k.img -serial pipe:com_1
Qemuは、windbgの接続を待って止まります。

そのあと、windbgのショートカットをクリックします。

Windows 2000の画面に、[debugger enabled]を選択できるメニューが表示されるはずです。
[debugger enable]を選択します。
Microsoft Windows 2000 Professional
Microsoft Windows 2000 Professional [debugger enabled]

すると、windbgの画面に接続されたというメッセージが現れます。もし、このメッセージがでない場合は、ゲストOSとwindbgを閉じて、もう一度やり直してください。
Connected to Windows 2000 2195 x86 compatible target, ptr64 FALSE Kernel Debugger connection established. Symbol search path is: srv*c:\tmp*http://msdl.microsoft.com/download/symbols Executable search path is:

Windows 2000 Kernel Version 2195 UP Free x86 compatible Kernel base = 0x80400000 PsLoadedModuleList = 0x8046a4c0 System Uptime: not available

まず、windbgをスタートします。そして、Ctrl-Alt-kを2回押します。そうすると、次のようなメッセージが出ます。
Will request initial breakpoint at next boot. Will breakin on first symbol load at next boot.そのあと、ゲストOSをスタートします。すると、ntoskrnl.exeのロード時にブレークすることができます。
ドライバのリスト。
kd> lm t nモジュールのリスト。
kd> x *!モジュールのシンボルのリスト(たとえば、ntoskrnl.exe)。
kd> x nt!*逆アセンブル。
kd> uリスタート。
kd> g
MinGWで、dllのデバッグをしようとすると、ブレークポイントを設定するのにちょっとしたコツが必要になります。
具体的には、QEMUでSDL.dllのデバッグをしようとしたのですが、まず、-gオプション付でSDLのコンパイルをします。
SDL-1.2.9>$ CFLAGS="-O0 -g" configureSDL.dllをQEMUのある場所にコピーします。そして、gdbでのデバッグを開始します。
$ gdb qemu (gdb) set args -L ../pc-bios -hda ../../linux.img (gdb) b main (gdb) run止まったら、調べたい関数を指定します。
(gdb) b SDL_VideoInit (gdb) continueこれで、DLL内で止まると思います。
$ gdb qemu (gdb) set args -L ../pc-bios -hda ../../linux.img (gdb) dll-symbols SDL.dlllistで調べたい関数を表示します。
(gdb) list SDL_VideoInit
141
142 /*
143 * Initialize the video and event subsystems -- determine native pixel format
144 */
145 int SDL_VideoInit (const char *driver_name, Uint32 flags)
146 {
147 SDL_VideoDevice *video;
148 int index;
149 int i;
150 SDL_PixelFormat vformat;
表示された行番号をブレークポイントに指定します。(gdb) break 145 (gdb) runこれで、runをすればブレークポイントで止まるはずです。ソースコードが表示されない場合、directoryでソースコードの位置を指定する必要があるかもしれません。
(gdb) directory ~/sdl-1.2.9/src/video/なぜQEMUを実行してからでないと、ブレークポイントを設定できないかというと、SDL.dllがメモリーにロードされないとアドレスが解決できないせいだと思います。