Panic when attaching to program executed via `go run`
emad-elsaid opened this issue · comments
When running a go program with go run ./path/to/main
and attaching Delve to the process. when it hits a breakpoint it can't print args.
These are steps to reproduce it:
- Clone this branch of xlog https://github.com/emad-elsaid/xlog/tree/delve-issue
- The branch has a
runtime.Breakpoint()
call here - Run the command line app from
go run ./cmd/xlog/
- Get the process number:
ps a
it should look something like:162843 pts/2 Sl+ 0:00 /tmp/go-build2627955907/b001/exe/xlog
- Attach delve to it
dlv attach 162843
- Hit the breakpoint by opening
localhost:3000
- in Delve step couple of time to reach a function with argument then run command
args
I got the following panic:
➜ dlv attach 162843
Warning: no debug info found, some functionality will be missing such as stack traces and variable evaluation.
Type 'help' for list of commands.
(dlv) c
> [hardcoded-breakpoint] github.com/emad-elsaid/xlog.getPageHandler() /usr/lib/go/src/runtime/proc.go:5155 (hits total:0) (PC: 0xa06c05)
5150: unlock(&sched.gFree.lock)
5151: }
5152:
5153: // Breakpoint executes a breakpoint trap.
5154: func Breakpoint() {
=>5155: breakpoint()
5156: }
5157:
5158: // dolockOSThread is called by LockOSThread and lockOSThread below
5159: // after they modify m.locked. Do not allow preemption during this call,
5160: // or else the m might be different in this function than in the caller.
(dlv) s
> github.com/emad-elsaid/xlog.getPageHandler() ./code/xlog/handlers.go:60 (PC: 0xa06c69)
55:
56: // Shows a page. the page name is the path itself. if the page doesn't exist it
57: // redirect to edit page otherwise will render it to HTML
58: func getPageHandler(w Response, r Request) Output {
59: runtime.Breakpoint()
=> 60: page := NewPage(r.PathValue("page"))
61:
62: if !page.Exists() {
63: if output, err := staticHandler(r); err == nil {
64: return output
65: }
(dlv) s
> github.com/emad-elsaid/xlog.NewPage() ./code/xlog/page_source.go:22 (PC: 0xa09ac0)
17:
18: var sources = []PageSource{
19: &markdownCWDFS{},
20: }
21:
=> 22: func NewPage(name string) (p Page) {
23: for i := range sources {
24: p = sources[i].Page(name)
25: if p != nil && p.Exists() {
26: return
27: }
(dlv) s
> github.com/emad-elsaid/xlog.NewPage() ./code/xlog/page_source.go:23 (PC: 0xa09ad2)
18: var sources = []PageSource{
19: &markdownCWDFS{},
20: }
21:
22: func NewPage(name string) (p Page) {
=> 23: for i := range sources {
24: p = sources[i].Page(name)
25: if p != nil && p.Exists() {
26: return
27: }
28: }
(dlv) args
Command failed: Internal debugger error: runtime error: invalid memory address or nil pointer dereference
runtime.gopanic (0x43cdb1)
/usr/lib/go/src/runtime/panic.go:770
runtime.panicmem (0x455bb7)
/usr/lib/go/src/runtime/panic.go:261
runtime.sigpanic (0x455b85)
/usr/lib/go/src/runtime/signal_unix.go:881
debug/dwarf.(*Reader).Seek (0x647dba)
/usr/lib/go/src/debug/dwarf/entry.go:836
debug/dwarf.(*Data).Reader (0x689347)
/usr/lib/go/src/debug/dwarf/entry.go:814
github.com/go-delve/delve/pkg/dwarf/godwarf.LoadTree (0x689311)
/home/emad/go/pkg/mod/github.com/go-delve/delve@v1.22.1/pkg/dwarf/godwarf/tree.go:93
github.com/go-delve/delve/pkg/proc.(*Image).getDwarfTree (0x7c557b)
/home/emad/go/pkg/mod/github.com/go-delve/delve@v1.22.1/pkg/proc/bininfo.go:1020
github.com/go-delve/delve/pkg/proc.(*EvalScope).Locals (0x7dcb27)
/home/emad/go/pkg/mod/github.com/go-delve/delve@v1.22.1/pkg/proc/eval.go:376
github.com/go-delve/delve/pkg/proc.(*EvalScope).FunctionArguments (0x7de66f)
/home/emad/go/pkg/mod/github.com/go-delve/delve@v1.22.1/pkg/proc/eval.go:607
github.com/go-delve/delve/service/debugger.(*Debugger).FunctionArguments (0x8c31ab)
/home/emad/go/pkg/mod/github.com/go-delve/delve@v1.22.1/service/debugger/debugger.go:1581
github.com/go-delve/delve/service/rpc2.(*RPCServer).ListFunctionArgs (0x90a94f)
/home/emad/go/pkg/mod/github.com/go-delve/delve@v1.22.1/service/rpc2/server.go:511
reflect.Value.call (0x4e10a4)
/usr/lib/go/src/reflect/value.go:596
reflect.Value.Call (0x4e0178)
/usr/lib/go/src/reflect/value.go:380
github.com/go-delve/delve/service/rpccommon.(*ServerImpl).serveJSONCodec.func2 (0x9f54d8)
/home/emad/go/pkg/mod/github.com/go-delve/delve@v1.22.1/service/rpccommon/server.go:340
github.com/go-delve/delve/service/rpccommon.(*ServerImpl).serveJSONCodec (0x9f486c)
/home/emad/go/pkg/mod/github.com/go-delve/delve@v1.22.1/service/rpccommon/server.go:342
runtime.goexit (0x473560)
/usr/lib/go/src/runtime/asm_amd64.s:1695
Env:
- What version of Delve are you using (
dlv version
)?
Delve Debugger
Version: 1.22.1
Build:$Id: 0c3470054da6feac5f6dcf3e3e5144a64f7a9a48 $
- What version of Go are you using? (
go version
)?
go version go1.22.1 linux/amd64
- What operating system and processor architecture are you using?
Linux moon 6.8.2-arch2-1 #1 SMP PREEMPT_DYNAMIC Thu, 28 Mar 2024 17:06:35 +0000 x86_64 GNU/Linux
Model name: 11th Gen Intel(R) Core(TM) i7-1185G7 @ 3.00GHz
I probably missed something in Delve conditions to show args/locals...etc. I expected it to work even if the process ran from go run
command.
I made another experiment where I built the app first and ran it then attached delve to the process and everything is fine. So I'm not sure if this is an expected behavior or is it a bug.
Thanks for reporting! I just sent in #3695 to fix this.
To be clear, the bug is that Delve ends up panic'ing (and recovering) instead of returning a nicer error. As it stands, when a program is built and executed via go run ...
the Go compiler does not include the necessary DWARF debug information in the binary for Delve to be able to locate variables. If you want a better debugging experience I would suggest simply: dlv debug ./cmd/xlog/
instead, which will let Delve compile the binary for you and run it directly under the debugger in one step. From there you can continue
and then make requests to your service.
I see, thanks @derekparker for the clarification. I didn't know go run
doesn't include DWARF data.
Do you have any document that expand on that, I'm looking for flags or env variables to change this behavior in Go. What I'm trying to achieve depends on that.
I can't find the issue where this was explained anymore but it's by design and there is no way to change this behavior.
This is what I was trying to achieve over the past days: https://github.com/emad-elsaid/examine
I accepted the fact that the concept won't work with go run