go-delve / delve

Delve is a debugger for the Go programming language.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

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:

  1. Clone this branch of xlog https://github.com/emad-elsaid/xlog/tree/delve-issue
  2. The branch has a runtime.Breakpoint() call here
  3. Run the command line app from go run ./cmd/xlog/
  4. Get the process number: ps a it should look something like: 162843 pts/2 Sl+ 0:00 /tmp/go-build2627955907/b001/exe/xlog
  5. Attach delve to it dlv attach 162843
  6. Hit the breakpoint by opening localhost:3000
  7. 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:

  1. What version of Delve are you using (dlv version)?

Delve Debugger
Version: 1.22.1
Build: $Id: 0c3470054da6feac5f6dcf3e3e5144a64f7a9a48 $

  1. What version of Go are you using? (go version)?

go version go1.22.1 linux/amd64

  1. 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