delve
delve copied to clipboard
What should we do if a linespec does not resolve to any instruction
- What version of Delve are you using (
dlv version)? derekparker/delve@82d142d - What version of Go are you using? (
go version)? go 1.8.1 - What operating system and processor architecture are you using? Windows 10 / amd64
- What did you do? demo.go
package main
type TableModel struct {}
func (TableModel) TableName() string {
return ""
}
func demo(a interface{}) (v1 string, v2 bool) {
if tm, ok := a.(TableModel); ok {
return tm.TableName(), ok
}
return "", false
}
func main() {
a := TableModel{} // b demo.go:17
demo(a)
}
go build -gcflags "-N -l" -o tmp.exe demo.go
dlv.exe --backend=default exec ./tmp.exe
- What did you expect to see? I would expect to have a breakpoint on that line and the debugger to stop on it.
- What did you see instead?
Command failed: no code at demo.go:17
These are the instructions produced for that main function:
TEXT main.main(SB) /home/a/temp/demo.go
demo.go:16 0x44d6c0 64488b0c25f8ffffff FS MOVQ FS:0xfffffff8, CX
demo.go:16 0x44d6c9 483b6110 CMPQ 0x10(CX), SP
demo.go:16 0x44d6cd 7632 JBE 0x44d701
demo.go:16 0x44d6cf 4883ec30 SUBQ $0x30, SP
demo.go:16 0x44d6d3 48896c2428 MOVQ BP, 0x28(SP)
demo.go:16 0x44d6d8 488d6c2428 LEAQ 0x28(SP), BP
demo.go:18 0x44d6dd 488d05bced0000 LEAQ 0xedbc(IP), AX
demo.go:18 0x44d6e4 48890424 MOVQ AX, 0(SP)
demo.go:18 0x44d6e8 488d442428 LEAQ 0x28(SP), AX
demo.go:18 0x44d6ed 4889442408 MOVQ AX, 0x8(SP)
demo.go:18 0x44d6f2 e8e9feffff CALL main.demo(SB)
demo.go:19 0x44d6f7 488b6c2428 MOVQ 0x28(SP), BP
demo.go:19 0x44d6fc 4883c430 ADDQ $0x30, SP
demo.go:19 0x44d700 c3 RET
demo.go:16 0x44d701 e8ea84ffff CALL runtime.morestack_noctxt(SB)
demo.go:16 0x44d706 ebb8 JMP main.main(SB)
Nothing is produced for line 17, the reason this happens is that TableModel is a zero sized struct and no work is required to initialize it. It doesn't have anything to do with :=. Gdb puts a breakpoint on the first line containing code at or after the line specified, we could do that somewhere. Otherwise there's nothing to do here, I don't think this even counts as a upstream bug.
Hmm, I understand this but I'd rather see as an upstream problem. It took me a bit of time to understand what's happening and I've had assistance from @dominikh and @ScottMansfield for this, not sure how many people will be this lucky. The issue is about expectations. If I set a breakpoint on a line, I expect the debugger to stop there, especially when I'm aware what optimizations are turned of for the compiled target. I think this maybe should be discussed with the Go team and see what they think about it?
As for maybe stopping the debugger on the following line it would be a good thing? I got surprised the first time the debugger didn't stopped at all and this is part of a bigger code, reproduced in smaller scale for example sake.
Thank you for your time and help.
The issue is about expectations. If I set a breakpoint on a line, I expect the debugger to stop there
Of course. OTOH if you try to set a breakpoint on an empty line, a comment or a close brace you would expect the debugger to complain (or maybe to set the breakpoint on the next available line?). This case is a lot less obvious, but it's also an fairly rare edge case and the only way to have a debugger stop on that line would be for the compiler to remember to emit a NOP for that line.
This case is a lot less obvious, but it's also an fairly rare edge case and the only way to have a debugger stop on that line would be for the compiler to remember to emit a NOP for that line.
As far as I can tell, running go build -gcflags="-S -N -l" demo.go will generate the NOP for that line and we already know that for debugging you should build with something like this: go build -gcflags="-N -l" demo.go. As far as I understand how this works is that -S doesn't (or shouldn't) have any effect over the final generated assembly whereas -N -l should (as they do already).
"".main t=1 size=72 args=0x0 locals=0x30
0x0000 00000 (/home/florin/go/src/github.com/dlsniper/u/tmp/demo.go:16) TEXT "".main(SB), $48-0
0x0000 00000 (/home/florin/go/src/github.com/dlsniper/u/tmp/demo.go:16) MOVQ (TLS), CX
0x0009 00009 (/home/florin/go/src/github.com/dlsniper/u/tmp/demo.go:16) CMPQ SP, 16(CX)
0x000d 00013 (/home/florin/go/src/github.com/dlsniper/u/tmp/demo.go:16) JLS 65
0x000f 00015 (/home/florin/go/src/github.com/dlsniper/u/tmp/demo.go:16) SUBQ $48, SP
0x0013 00019 (/home/florin/go/src/github.com/dlsniper/u/tmp/demo.go:16) MOVQ BP, 40(SP)
0x0018 00024 (/home/florin/go/src/github.com/dlsniper/u/tmp/demo.go:16) LEAQ 40(SP), BP
0x001d 00029 (/home/florin/go/src/github.com/dlsniper/u/tmp/demo.go:16) FUNCDATA $0, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
0x001d 00029 (/home/florin/go/src/github.com/dlsniper/u/tmp/demo.go:16) FUNCDATA $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
0x001d 00029 (/home/florin/go/src/github.com/dlsniper/u/tmp/demo.go:17) NOP
0x001d 00029 (/home/florin/go/src/github.com/dlsniper/u/tmp/demo.go:18) NOP
0x001d 00029 (/home/florin/go/src/github.com/dlsniper/u/tmp/demo.go:18) LEAQ type."".TableModel(SB), AX
0x0024 00036 (/home/florin/go/src/github.com/dlsniper/u/tmp/demo.go:18) MOVQ AX, (SP)
0x0028 00040 (/home/florin/go/src/github.com/dlsniper/u/tmp/demo.go:18) LEAQ ""..autotmp_3+40(SP), AX
0x002d 00045 (/home/florin/go/src/github.com/dlsniper/u/tmp/demo.go:18) MOVQ AX, 8(SP)
0x0032 00050 (/home/florin/go/src/github.com/dlsniper/u/tmp/demo.go:18) PCDATA $0, $0
0x0032 00050 (/home/florin/go/src/github.com/dlsniper/u/tmp/demo.go:18) CALL "".demo(SB)
0x0037 00055 (/home/florin/go/src/github.com/dlsniper/u/tmp/demo.go:18) NOP
0x0037 00055 (/home/florin/go/src/github.com/dlsniper/u/tmp/demo.go:19) MOVQ 40(SP), BP
0x003c 00060 (/home/florin/go/src/github.com/dlsniper/u/tmp/demo.go:19) ADDQ $48, SP
0x0040 00064 (/home/florin/go/src/github.com/dlsniper/u/tmp/demo.go:19) RET
0x0041 00065 (/home/florin/go/src/github.com/dlsniper/u/tmp/demo.go:19) NOP
0x0041 00065 (/home/florin/go/src/github.com/dlsniper/u/tmp/demo.go:16) PCDATA $0, $-1
0x0041 00065 (/home/florin/go/src/github.com/dlsniper/u/tmp/demo.go:16) CALL runtime.morestack_noctxt(SB)
0x0046 00070 (/home/florin/go/src/github.com/dlsniper/u/tmp/demo.go:16) JMP 0
Thank you.
Judging by the PCDATA/FUNCDATA stuff, I think that's actually printing go's intermediate representation rather than assembly. I imagine NOPs get compiled into nothing. BTW tip doesn't emit them in the intermediate representation either.
That being the case, what would it take for getting the NOP to be compiled in? This use case is probably important enough that a switch could be added for it. :wink:
Hmmm, is this something which should be opened upstream instead? :smile:
I've opened up https://github.com/golang/go/issues/20487 as a follow-up. Regarding the suggestion to stop on the next closest line, I guess that could work meanwhile, even if the users will be a bit more confused?
Regarding the suggestion to stop on the next closest line, I guess that could work meanwhile, even if the users will be a bit more confused?
If the user is trying to set a breakpoint on a line that contains only a comment (or whatever) then they are already confused... if it was me I'd want a breakpoint set. Is it better to set a breakpoint and emit a warning or only emit an error. (It seems to me a lot of folks don't read the normal messages, and so would easily miss any error or warning, unless it be colourised!)
Hmmm, there's also the situation where the user deletes a few lines of existing code with a breakpoint in it somewhere. Some IDE's might still leave the breakpoint at the same line number, which could be non-code after the delete.
Whether or not that's good behaviour on the IDE's part I guess would depend on the IDE's general way of doing things. :smile:
We didn't change anything and this issue hasn't really received any attention in the past 7 years. Our current behavior is probably fine.