I'm seeing two different behaviors that seem bugs (I don't know if bugs in Delve or faulty debug info); they're both around the same runtime function, so I'm reporting both of them here and I'll let someone else split them in different issues if they are indeed different.
Problem 1: placing a breakpoint by line number in traceback.go:457
(this line) doesn't work. The breakpoint is never hit. The pc at which Delve places the breakpoint is jumped over in some executions. Placing the breakpoint by hand at different pc's associated with the same source line works.
Repro:
Consider the following program:
package main
import (
"net/http"
_ "net/http/pprof" // For the pprof http handler
)
func main() {
http.ListenAndServe("localhost:8080", nil)
}
Compile it with go 1.19.4 (default gcflags, optimizations enabled), on Linux, and then
$ dlv attach `pidof repro`
(dlv) break traceback.go:457
Breakpoint 1 set at 0x45f260 for runtime.gentraceback() /home/andrei/goroot/src/runtime/traceback.go:457
Notice the 0x45f260
. A reference from objdump --dwarf=decodedline testprog | grep 'traceback.go.'
:
traceback.go 457 0x45f1d9
traceback.go 453 0x45f1e6 x
traceback.go 453 0x45f1eb
traceback.go 453 0x45f200 x
traceback.go 453 0x45f205
traceback.go 454 0x45f219 x
traceback.go 454 0x45f21e
traceback.go 457 0x45f260 x
traceback.go 457 0x45f262
traceback.go 457 0x45f26a x
traceback.go 457 0x45f26f
traceback.go 457 0x45f277 x
traceback.go 457 0x45f27c
0x45f260
is not the first PC associated with line 457, but seems to be the first pc that is marked as Stmt
(in case that's relevant).
Now exercise the code through the pprof endpoint, for example through:
http://localhost:8080/debug/pprof/goroutine?debug=2
Our breakpoint is in a helper function around printing the goroutine dump, and it should be hit. However, you'll see that it is not hit.
If we place our breakpoint slightly earlier in this function, we can trace and see how the PC progresses:
(dlv) break traceback.go:454
Breakpoint 2 set at 0x45f219 for runtime.gentraceback() /home/andrei/goroot/src/runtime/traceback.go:454
(dlv) c
> runtime.gentraceback() /home/andrei/goroot/src/runtime/traceback.go:454 (hits goroutine(12):1 total:1) (PC: 0x45f219)
si
si
...
The PC goes:
0x45f219
0x45f21e
0x45f220
0x45f224 -> The instruction here is jnz 0x45f262
0x45f262 -> So we jumped over 0x45f260
0x45f26a -> This is where an `n` brings me, probably because this instruction is the next one marked as `Stmt`.
So, it seems to me that the pc 0x45f260
is the wrong one to put the breakpoint in; some code paths jump over it.
Problem 2: Delve seems to not generate a correct backtrace when starting from inside this runtime.genbacktrace()
function. If you do bt
from the pc's referenced above, you get the following. Stack frames are missing between the runtime.gentraceback
and the runtime.systemstack_switch
frames: there's supposed to be a couple of functions in there.
By poking around with other tools, the stack unwinding debug info seems to exist, and to be correct for the gentraceback
locations. But don't take my word for it.
0 0x000000000045f26a in runtime.gentraceback
at /home/andrei/goroot/src/runtime/traceback.go:457
1 0x0000000000469020 in runtime.systemstack_switch
at /home/andrei/goroot/src/runtime/asm_amd64.s:459
2 0x00000000004313af in runtime.Stack
at /home/andrei/goroot/src/runtime/mprof.go:1203
3 0x0000000000661a90 in runtime/pprof.writeGoroutineStacks
at /home/andrei/goroot/src/runtime/pprof/pprof.go:692
4 0x00000000006619cb in runtime/pprof.writeGoroutine
at /home/andrei/goroot/src/runtime/pprof/pprof.go:681
5 0x000000000065e94b in runtime/pprof.(*Profile).WriteTo
at /home/andrei/goroot/src/runtime/pprof/pprof.go:330
6 0x000000000066db25 in net/http/pprof.handler.ServeHTTP
at /home/andrei/goroot/src/net/http/pprof/pprof.go:253
...