coreファイルを使ったgoのデバッグ

以下の記事を参考にcore dumpからgoプログラムのデバッグする方法をためしてみた。

Analyzing Core Dump Generated By Go Program - Speaker Deck

C言語の場合gdbを使うが、go言語の場合、dlvを使うようだ。

覚えておいて損はなさそう。ただし、macosではcoreファイルが作成されないようだ。残念。

$ ulimit -c unlimited
$ cat main.go
package main

func main() {
    var p *int
    p = nil
    *p = 1
}

$ export GOTRACEBACK=crash
$ go build main.go
$ ./main
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x44ebb2]

goroutine 1 [running]:
panic(0x45d560, 0x4b7ce0)
    /usr/local/go/src/runtime/panic.go:556 +0x2cb fp=0xc000032720 sp=0xc000032690 pc=0x421edb
runtime.panicmem()
    /usr/local/go/src/runtime/panic.go:82 +0x5e fp=0xc000032740 sp=0xc000032720 pc=0x420f1e
runtime.sigpanic()
    /usr/local/go/src/runtime/signal_unix.go:390 +0x182 fp=0xc000032790 sp=0xc000032740 pc=0x4342f2
main.main()
    /home/unokun/work/go/main.go:6 +0x2 fp=0xc000032798 sp=0xc000032790 pc=0x44ebb2
runtime.main()
    /usr/local/go/src/runtime/proc.go:201 +0x207 fp=0xc0000327e0 sp=0xc000032798 pc=0x423bc7
runtime.goexit()
    /usr/local/go/src/runtime/asm_amd64.s:1333 +0x1 fp=0xc0000327e8 sp=0xc0000327e0 pc=0x448d21

goroutine 2 [force gc (idle)]:
runtime.gopark(0x470bd8, 0x4b9ae0, 0x1410, 0x1)
    /usr/local/go/src/runtime/proc.go:302 +0xeb fp=0xc000032f80 sp=0xc000032f60 pc=0x423fab
runtime.goparkunlock(0x4b9ae0, 0x1410, 0x1)
    /usr/local/go/src/runtime/proc.go:308 +0x53 fp=0xc000032fb0 sp=0xc000032f80 pc=0x424053
runtime.forcegchelper()
    /usr/local/go/src/runtime/proc.go:251 +0xb3 fp=0xc000032fe0 sp=0xc000032fb0 pc=0x423e23
runtime.goexit()
    /usr/local/go/src/runtime/asm_amd64.s:1333 +0x1 fp=0xc000032fe8 sp=0xc000032fe0 pc=0x448d21
created by runtime.init.4
    /usr/local/go/src/runtime/proc.go:240 +0x35

goroutine 3 [GC sweep wait]:
runtime.gopark(0x470bd8, 0x4b9ba0, 0x41140c, 0x1)
    /usr/local/go/src/runtime/proc.go:302 +0xeb fp=0xc000033780 sp=0xc000033760 pc=0x423fab
runtime.goparkunlock(0x4b9ba0, 0x47140c, 0x1)
    /usr/local/go/src/runtime/proc.go:308 +0x53 fp=0xc0000337b0 sp=0xc000033780 pc=0x424053
runtime.bgsweep(0xc000052000)
    /usr/local/go/src/runtime/mgcsweep.go:52 +0x8f fp=0xc0000337d8 sp=0xc0000337b0 pc=0x418a9f
runtime.goexit()
    /usr/local/go/src/runtime/asm_amd64.s:1333 +0x1 fp=0xc0000337e0 sp=0xc0000337d8 pc=0x448d21
created by runtime.gcenable
    /usr/local/go/src/runtime/mgc.go:216 +0x58
[1]    29625 abort (core dumped)  ./main

$ ls
core.29625  main  main.go

$ dlv core main core.29625
Type 'help' for list of commands.
(dlv) bt
 0  0x000000000044a624 in runtime.raise
    at /usr/local/go/src/runtime/sys_linux_amd64.s:146
 1  0x00000000004344ab in runtime.dieFromSignal
    at /usr/local/go/src/runtime/signal_unix.go:424
 2  0x0000000000434928 in runtime.sigfwdgo
    at /usr/local/go/src/runtime/signal_unix.go:629
 3  0x0000000000433d08 in runtime.sigtrampgo
    at /usr/local/go/src/runtime/signal_unix.go:289
 4  0x000000000044a913 in runtime.sigtramp
    at /usr/local/go/src/runtime/sys_linux_amd64.s:353
 5  0x000000000044aa00 in runtime.sigreturn
    at /usr/local/go/src/runtime/sys_linux_amd64.s:445
 6  0x000000000043466a in runtime.crash
    at /usr/local/go/src/runtime/signal_unix.go:518
 7  0x00000000004224d7 in runtime.fatalpanic
    at /usr/local/go/src/runtime/panic.go:708
 8  0x0000000000421edb in runtime.gopanic
    at /usr/local/go/src/runtime/panic.go:556
 9  0x0000000000420f1e in runtime.panicmem
    at /usr/local/go/src/runtime/panic.go:82
10  0x00000000004342f2 in runtime.sigpanic
    at /usr/local/go/src/runtime/signal_unix.go:390
11  0x000000000044ebb2 in main.main
    at ./main.go:6
12  0x0000000000423bc7 in runtime.main
    at /usr/local/go/src/runtime/proc.go:201
13  0x0000000000448d21 in runtime.goexit
    at /usr/local/go/src/runtime/asm_amd64.s:1333
(dlv) frame 11
> runtime.raise() /usr/local/go/src/runtime/sys_linux_amd64.s:146 (PC: 0x44a624)
Warning: debugging optimized function
Frame 11: ./main.go:6 (PC: 44ebb2)
     1: package main
     2:
     3: func main() {
     4:     var p *int
     5:     p = nil
=>   6:      *p = 1
     7: }
(dlv) locals
p = (unreadable empty OP stack)
(dlv) disass
TEXT main.main(SB) /home/unokun/work/go/main.go
    main.go:6   0x44ebb0    31c0        xor eax, eax
    main.go:6   0x44ebb2    48c70001000000  mov qword ptr [rax], 0x1
    main.go:7   0x44ebb9    c3      ret
(dlv) regs
...
     Rax = 0x0000000000000000
...
(dlv)