delve icon indicating copy to clipboard operation
delve copied to clipboard

What should we do if a linespec does not resolve to any instruction

Open dlsniper opened this issue 8 years ago • 9 comments
trafficstars

  1. What version of Delve are you using (dlv version)? derekparker/delve@82d142d
  2. What version of Go are you using? (go version)? go 1.8.1
  3. What operating system and processor architecture are you using? Windows 10 / amd64
  4. 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
  1. What did you expect to see? I would expect to have a breakpoint on that line and the debugger to stop on it.
  2. What did you see instead?
Command failed: no code at demo.go:17

dlsniper avatar May 14 '17 15:05 dlsniper

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.

aarzilli avatar May 15 '17 09:05 aarzilli

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.

dlsniper avatar May 15 '17 23:05 dlsniper

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.

aarzilli avatar May 16 '17 13:05 aarzilli

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.

dlsniper avatar May 16 '17 14:05 dlsniper

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.

aarzilli avatar May 16 '17 16:05 aarzilli

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:

justinclift avatar May 18 '17 08:05 justinclift

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?

dlsniper avatar May 24 '17 22:05 dlsniper

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!)

au-phiware avatar Aug 15 '17 01:08 au-phiware

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:

justinclift avatar Aug 15 '17 10:08 justinclift

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.

aarzilli avatar May 13 '24 12:05 aarzilli