blob: de663c706618ea9ef91a683f7978f7d370eeec3a [file] [log] [blame] [view]
JBD36779cc2017-09-06 14:12:20 -07001Originally published at https://rakyll.org/coredumps/.
2
3---
4
5
6Debugging is highly useful to examine the execution flow
7and to understand the current state of a program.
8
9A core file is a file that contains the memory dump of a running
10process and its process status. It is primarily used for post-mortem
11debugging of a program, as well as to understand a program's state
12while it is still running. These two cases make debugging of core dumps
13a good diagnostics aid to postmortem and analyze production
14services.
15
16I will use a simple hello world web server in this article,
17but in real life our programs might get very
18complicated easily.
19The availability of core dump analysis gives you an
20opportunity to resurrect a program from specific snapshot
21and look into cases that might only reproducible in certain
22conditions/environments.
23
24__Note__: This flow only works on Linux at this point end-to-end,
25I am not quite sure about the other Unixes but it is not
26yet supported on macOS. Windows is not supported at this point.
27
28Before we begin, you need to make sure that your ulimit
29for core dumps are at a reasonable level. It is by default
300 which means the max core file size can only be zero.
31I usually set it to unlimited on my development machine by typing:
32
33```
34$ ulimit -c unlimited
35```
36
37Then, make sure you have [delve](https://github.com/derekparker/delve)
38installed on your machine.
39
40Here is a `main.go` that contains a simple handler and it starts an HTTP server.
41
42```
43$ cat main.go
44package main
45
46import (
47 "fmt"
48 "log"
49 "net/http"
50)
51
52func main() {
53 http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
54 fmt.Fprint(w, "hello world\n")
55 })
56 log.Fatal(http.ListenAndServe("localhost:7777", nil))
57}
58```
59
60Let's build this and have a binary.
61
62```
63$ go build .
64```
65
66Lets assume, in the future, there is something messy going on with
67this server but you are not so sure about what it might be.
68You might have instrumented your program in various ways but it
69might not be enough for getting any clue from the existing
70instrumentation data.
71
72Basically, in a situation like this, it would be nice to have a
73snapshot of the current process, and then use that snapshot to dive
74into to the current state of your program with your existing debugging
75tools.
76
77There are several ways to obtain a core file. You might have been
78already familiar with crash dumps, these are basically core dumps
79written to disk when a program is crashing. Go doesn't enable crash dumps
80by default but gives you this option on Ctrl+backslash when
81`GOTRACEBACK` env variable is set to "crash".
82
83```
84$ GOTRACEBACK=crash ./hello
85(Ctrl+\)
86```
87
88It will crash the program with stack trace printed and core dump file
89will be written.
90
91Another option is to retrieve a core dump from a running process
92without having to kill a process.
93With `gcore`, it is possible to get the core
94files without crashing. Let’s start the server again:
95
96```
97$ ./hello &
98$ gcore 546 # 546 is the PID of hello.
99```
100We have a dump without crashing the process. The next step
101is to load the core file to delve and start analyzing.
102
103```
104$ dlv core ./hello core.546
105```
106
107Alright, this is it! This is no different than the typical delve interactive.
108You can backtrace, list, see variables, and more. Some features will be disabled
109given a core dump is a snapshot and not a currently running process, but
110the execution flow and the program state will be entirely accessible.
111
112```
113(dlv) bt
114 0 0x0000000000457774 in runtime.raise
115 at /usr/lib/go/src/runtime/sys_linux_amd64.s:110
116 1 0x000000000043f7fb in runtime.dieFromSignal
117 at /usr/lib/go/src/runtime/signal_unix.go:323
118 2 0x000000000043f9a1 in runtime.crash
119 at /usr/lib/go/src/runtime/signal_unix.go:409
120 3 0x000000000043e982 in runtime.sighandler
121 at /usr/lib/go/src/runtime/signal_sighandler.go:129
122 4 0x000000000043f2d1 in runtime.sigtrampgo
123 at /usr/lib/go/src/runtime/signal_unix.go:257
124 5 0x00000000004579d3 in runtime.sigtramp
125 at /usr/lib/go/src/runtime/sys_linux_amd64.s:262
126 6 0x00007ff68afec330 in (nil)
127 at :0
128 7 0x000000000040f2d6 in runtime.notetsleep
129 at /usr/lib/go/src/runtime/lock_futex.go:209
130 8 0x0000000000435be5 in runtime.sysmon
131 at /usr/lib/go/src/runtime/proc.go:3866
132 9 0x000000000042ee2e in runtime.mstart1
133 at /usr/lib/go/src/runtime/proc.go:1182
13410 0x000000000042ed04 in runtime.mstart
135 at /usr/lib/go/src/runtime/proc.go:1152
136
137(dlv) ls
138> runtime.raise() /usr/lib/go/src/runtime/sys_linux_amd64.s:110 (PC: 0x457774)
139 105: SYSCALL
140 106: MOVL AX, DI // arg 1 tid
141 107: MOVL sig+0(FP), SI // arg 2
142 108: MOVL $200, AX // syscall - tkill
143 109: SYSCALL
144=> 110: RET
145 111:
146 112: TEXT runtime·raiseproc(SB),NOSPLIT,$0
147 113: MOVL $39, AX // syscall - getpid
148 114: SYSCALL
149 115: MOVL AX, DI // arg 1 pid
150```