Skip to content

GDBを用いたデバッグ

Takuya Kojima edited this page Feb 9, 2020 · 2 revisions

概要

printfデバッグなどは面倒だったりするので、GDBを使えるようにしてある。 ふんが研の環境にはすでにMIPS用GDBがインストールされているが、まだの場合はインストール方法を参照されたい。

デバッグ情報を含むバイナリの生成

Geyserのアプリ開発環境ではGDBDEBUGを定義すると(TARGET).elfともにデバッグ情報である(TARGET).debugを生成する。 verilogシミュレーションとの互換性からelfファイルからはデバッグ情報が引き抜かれている。

cube_simをデバッガーモードで起動

-o debugをつけて実行するとデバッガーモードで起動する。 今回はサンプルコードのprintf.cを対象にして起動してみる。

すると、127.0.0.1:45100でgdbserverが起動したことがわかる。

$ ./cube_sim -o debug test_vec/printf.bin
Little-Endian host processor detected.
Mapping Boot ROM image (boot.bin, 7 words) to physical address 0xbfc00000
Use this command to attach debugger: target remote 127.0.0.1:45100
Mapping program binary (test_vec/gdb_test.bin, 8388608 words) to physical address 0x81000000
Mapping RAM module (host=0x7ff17f444010, 1024KB) to physical address 0x0
Mapping Halt device to physical address 0x01010024
Mapping Clock device to physical address 0x01010000
Connected IRQ7 to the Clock device
Succeeded in setup rs232c serial IO
Succeeded in setup cube router
Connected IRQ5 to the Router Interface
Hit Ctrl-\ to halt machine, Ctrl-_ for a debug prompt.

*************RESET*************
Resetting CPU
Resetting Router Interface

Waiting for connection from debugger.

GDBクライアントの起動

次に、ホストマシンでGDBを起動し、先ほど起動したgdbserverに接続する。

まずは、下記のコマンドでgdbを起動する。--execオプションの引数にはプログラム本体のelfファイルを、--symbolsオプションの引数には抽出されたデバッグ情報を指定する。(gdbコマンドのパスは適宜環境に合わせて読み替えてください)

$ /home/wasmii5/mips-cross/bin/mips--elf-gdb --exec=printf.elf --symbols=printf.debug 
GNU gdb 6.8
Copyright (C) 2008 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 "--host=x86_64-unknown-linux-gnu --target=mips--elf"...
(gdb) 

するとgdbのプロンプトが出てくる。

ここでから、gdbserverに接続するために先ほど表示されたサーバーのアドレスとポート番号を用いる。

(gdb) target remote 127.0.0.1:45100
Remote debugging using 127.0.0.1:45100
0xbfc00000 in ?? ()
(gdb) 

ブート時の開始アドレス0xbfc00000にいることがわかる。??となっているのはスタートアップルーチンに関するデバッグ情報を持っていないため。

ブレイクポイントを設定してみる

今回対象とするプログラムコードは

#include <macro.h>
#include <mylib.h>

volatile int main()
{
  extern int _stext, _etext, _srodata, _erodata, _sdata, _edata, _sbss, _ebss;
  extern int _kernelstack, _irqstack, _smemorypool;

  volatile char buf[100];
  volatile short s = -16;
  
  printf("Hello World\n");
  printf("%s\n", "test");
  printf("%c\n", 'a');
  printf("%05x\n", 0x123);
  itoa(15, buf, 2); //to binary
  printf("%s\n", buf);
  printf("%010d\n", 12345);
  printf("%d\n", s);

  __asm__("break");
  
  return 0;
}

このコードを実行する際にprintf("Hello World\n");の直後で止めてみる。 breakポイントを14行目に設定する。

(gdb) break 14
Breakpoint 1 at 0x81000060: file printf.c, line 14.

複数ファイルを利用している場合は

(gdb) break printf.c:17
Breakpoint 2 at 0x8100009c: file printf.c, line 17.

と明示的にファイルを指定すれば良い。

これでcontinue(c)をすればbreakpointに到達するまで、CPUを動かしてくれる。

(gdb) c
Continuing.

Breakpoint 1, main () at printf.c:14
14        printf("%c\n", 'a');

ここで、HelloWorldの行は12行目であるため、その直後の13行目で止めたいところだが、パイプライン化の影響などにより、そのさらに1行先を指定しないとうまくいかない。

cube_sim側で確認するとHelloWorldが表示されて止まっているのがわかると思う。

変数を覗く

上のbreakpointを設定した状態でbufの0番目をのぞいてみる。

(gdb) print buf[0]
$1 = 0 '\0'

ヌル文字が入っているのが確認された。 ちなみに、ある関数内のシンボルを確認するには

(gdb) info scope main
Scope for main:
Symbol buf is a variable with complex or multiple locations (DWARF2), length 100.
Symbol s is a variable with complex or multiple locations (DWARF2), length 2.

とする。 また、現在いる関数内の変数全てをみるには

(gdb) info locals 
buf = '\0' <repeats 99 times>
s = -16

とする。 また、itoaでbufに値が格納される前なのでこの結果は正しい。

次にitoaが実行されるまで、進めてからまたbufの中身を見てみる またbreakpointを設定しても良いのだが、stepを使って5行分進めてみる。

step 5

18        printf("%010d\n", 12345);
(gdb) print buf
$4 = "1111", '\0' <repeats 95 times>

最初に1111(15を2進数に変換した結果)が格納されているのがわかる。

変数の値を弄る

最後に、GDB側から変数の値をいじってみる。

上のbufの内容を確認したところから、bufの0番目の文字を1から2に変更してみる。 コマンドは

(gdb) set variable buf[0] = '2'

これで最後まで実行(continue)してみるとcube_sim側では

Debugger connected.
Hello World
test
a
00123
2111
0000012345
-16
* BREAK instruction reached -- HALTING *
Exception 9 (Breakpoint32 exception (BREAK instr)) triggered, EPC=810000e8
Priority is 4; mem access mode is not applicable

*************HALT*************

3643 cycles in 1739.99959 seconds (1.070 instructions per second) (stall ratio 48.916%)
Instruction Cache Profile
        Access Count 1956
        Cache Miss Ratio 1.63599%
        write back ratio 0.00000%
Data Cache Profile
        Access Count 555
        Cache Miss Ratio 1.08108%
        write back ratio 0.00000%
Goodbye.

本来buf内容を表示(printf("%s\n", buf);)した結果は1111になるはずだが、無理やり書き換えたことで2111となっている。