GDBでGoをデバッグしてみました

このエントリーをはてなブックマークに追加
こんにちは。sou.hです。

Goを興味で触ってみました。
Golangの内部的な動きを見たくて、GDBでGoプログラムをデバッグしてみました。

GDB7.1以降が必要です。GDB7.1以降はコンパイルしたBinファイルはDWARFv3を含まれてます。

デバッグシンボルの削除:
go build -dflags "-s -w"

  • -s: 記号を除く
  • -w: DWARFを除く


インライン関数のオプションをオフにします。
go build -gcflags "-N -l"

デブッグの関数:

  • runtime.Breakpoint():ブレークポイント
  • runtime/debug.PrintStack():デブッグスタック
  • log:デバッグ情報


GDBデバッグサーポート:

  • 引数代入:gdb -d $GCROOT 。
  • 手動代入:source pkg/runtime/runtime-gdb.py。

詳しくはこちらを参考してください: http://golang.org/doc/gdb


デバッグ(環境:Centos6, Go 1.4.2, GDB7.2)

ソースコード:
package main

import (
    "fmt"
    "runtime"
)

func test(s string, x int) (r string) {
    r = fmt.Sprintf("test: %s %d", s, x)
    runtime.Breakpoint()
    return r
}
func main() {
    s := "hoge"
    i := 1234
    println(test(s, i))
}


$ go build -gcflags "-N -l" // コンパイル

$ sudo gdb demo // gdbを起動して,Go Runtimeをロードする 。
GNU gdb (GDB) 7.5.1
Reading symbols from demo…done.
(gdb) source /usr/local/go/src/pkg/runtime/runtime-gdb.py
Loading Go Runtime support.

(gdb) l main.main // ドットでコードを参照する。
9  r = fmt.Sprintf("test: %s %d", s, x)
10 runtime.Breakpoint()
11 return r
12 }
13
14 func main() {
15 s := "hoge"
16 i := 1234
17 println(test(s, i))
18 }

(gdb) l main.go:8 // コロンでコードを参照する。
3  import (
4  "fmt"
5  "runtime"
6  )
7
8  func test(s string, x int) (r string) {
9  r = fmt.Sprintf("test: %s %d", s, x)
10 runtime.Breakpoint()
11 return r
12 }

(gdb) b main.main // ドットでブレークポイントを設定する。
Breakpoint 1 at 0×2131: file main.go, line 14.

(gdb) b main.go:17 // コロンでブレークポイントを設定する。
Breakpoint 2 at 0×2167: file main.go, line 17.

(gdb) info breakpoints // ブレークポイントのリスト
Num Type Disp Enb Address What
1 breakpoint keep y 0×0000000000002131 in main.main at main.go:14
2 breakpoint keep y 0×0000000000002167 in main.main at main.go:17

(gdb) r // デバッグ実行、1個目のブレークポイントまで
Starting program: demo
[New Thread 0x1c03 of process 4088]
[Switching to Thread 0x1c03 of process 4088]
Breakpoint 1, main.main () at main.go:14
14 func main() {

(gdb) info goroutines // goroutinesを表示する。goroutine (ゴルーチン) は Go ランタイムによって管理される軽量スレッドです。
* 1 running runtime.gosched
* 2 syscall runtime.entersyscall

(gdb) goroutine 1 bt // goroutineスタックを参照する。
#0 0x000000000000f6c0 in runtime.gosched () at pkg/runtime/proc.c:927
#1 0x000000000000e44c in runtime.main () at pkg/runtime/proc.c:244
#2 0x000000000000e4ef in schedunlock () at pkg/runtime/proc.c:267
#3 0×0000000000000000 in ?? ()

(gdb) goroutine 2 bt // GC関連のgoroutineスタックを参照する。
#0 runtime.entersyscall () at pkg/runtime/proc.c:989
#1 0x000000000000d01d in runtime.MHeap_Scavenger () at pkg/runtime/mheap.c:363
#2 0x000000000000e4ef in schedunlock () at pkg/runtime/proc.c:267
#3 0×0000000000000000 in ?? ()

(gdb) c / / 次のブレークポイントに行く。
Continuing.
Breakpoint 2, main.main () at main.go:17
17! ! println(test(s, i))

(gdb) info goroutines // 現在のgoroutineの番号は1です
* 1 running runtime.gosched
2 runnable runtime.gosched

(gdb) goroutine 1 bt // 現在のgoroutineのスタック情報です。
#0 0x000000000000f6c0 in runtime.gosched () at pkg/runtime/proc.c:927
#1 0x000000000000e44c in runtime.main () at pkg/runtime/proc.c:244
#2 0x000000000000e4ef in schedunlock () at pkg/runtime/proc.c:267
#3 0×0000000000000000 in ?? ()

(gdb) bt // goroutineの比較
#0 main.main () at main.go:17
#1 0x000000000000e44c in runtime.main () at pkg/runtime/proc.c:244
#2 0x000000000000e4ef in schedunlock () at pkg/runtime/proc.c:267
#3 0×0000000000000000 in ?? ()

(gdb) info frame // スタック情報
Stack level 0, frame at 0x442139f88:
rip = 0×2167 in main.main (main.go:17); saved rip 0xe44c
called by frame at 0x442139fb8
source language go.
Arglist at 0x442139f28, args:
Locals at 0x442139f28, Previous frame’s sp is 0x442139f88
Saved registers:
rip at 0x442139f80

(gdb) info locals // ローカル変数を調べる。
i = 1234
s = "hoge"

(gdb) p s // Pretty-Printでローカル変数調べる。
$1 = "hoge"

(gdb) p $len(s) // オブジェクトの長さを調べる($cap)
$2 = 4

(gdb) whatis i // タイプを調べる
type = int

(gdb) c // 次のブレークポイントへ行く
Continuing.
Program received signal SIGTRAP, Trace/breakpoint trap.
runtime.breakpoint () at pkg/runtime/asm_amd64.s:81
81 RET

(gdb) n // ブレークポイントから出て、実行を続く。
main.test (s="hoge", x=1234, r="test: hoge 1234") at main.go:11
11 return r

(gdb) info args // パラメーターを参照する。
s = "hoge"
x = 1234
r = "test: hoge 1234"

(gdb) x/3xw &r // rのメモリ情報を調べる。(ポイント 8 + 長さ 4)
0x442139f48: 0×42121240 0×00000000 0x0000000f
(gdb) x/15xb 0×42121240
0×42121240: 0×74 0×65 0×73 0×74 0x3a 0×20 0×68 0×61
0×42121248: 0×68 0×61 0×20 0×31 0×32 0×33 0×34

(gdb) c // 次へ。プログラムが終了。

Continuing.
test: hoge 1234
[Inferior 1 (process 4088) exited normally]

(gdb) q // GDBから退出。


通常のGDBと同じ操作です。
次の記事
« Prev Post
前の記事
Next Post »
Related Posts Plugin for WordPress, Blogger...