go-delve / delve

Delve is a debugger for the Go programming language.
MIT License
23.1k stars 2.15k forks source link

Inconsistent locspec resolution within the same source file. #3318

Closed dvogel closed 1 year ago

dvogel commented 1 year ago

This is a bug about breakpoints not being accepted. I have read the relevant FAQ section. The guidance there does not seem to apply to this issue since this file is partially amenable to breakpoints.

Observed results:

Expected result: Either a breakpoint or an error message that makes more sense within the context of the available source file.

$ dlv version
Delve Debugger
Version: 1.20.1
Build: $Id: 96e65b6c615845d42e0e31d903f6475b0e4ece6e $
$ go version
go version go1.19.6 linux/amd64
$ go env
GO111MODULE=""
GOARCH="amd64"
GOBIN=""
GOCACHE="/home/dvogel/.cache/go-build"
GOENV="/home/dvogel/.config/go/env"
GOEXE=""
GOEXPERIMENT=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOINSECURE=""
GOMODCACHE="/home/dvogel/devel/golang/pkg/mod"
GONOPROXY=""
GONOSUMDB=""
GOOS="linux"
GOPATH="/home/dvogel/devel/golang"
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/usr/lib/go-1.19"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/usr/lib/go-1.19/pkg/tool/linux_amd64"
GOVCS=""
GOVERSION="go1.19.6"
GCCGO="gccgo"
GOAMD64="v1"
AR="ar"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD="/home/dvogel/devel/titd/go.mod"
GOWORK=""
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -Wl,--no-gc-sections -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build6079387=/tmp/go-build -gno-record-gcc-switches"

In ui/prompt.go I have:

$ nl -ba ui/prompt.go | head -n 72
     1  package ui
     2  
     3  import (
     4      "image"
     5      "image/color"
     6      "titd/world"
     7  
     8      "github.com/hajimehoshi/ebiten/v2"
     9      "github.com/yohamta/furex/v2"
    10  )
    11  
    12  type UiDrawFunc func(*ebiten.Image, image.Rectangle)
    13  
    14  func (self UiDrawFunc) HandleDraw(screen *ebiten.Image, frame image.Rectangle) {
    15      self(screen, frame)
    16  }
    17  
    18  type OfferEventReceiver interface {
    19      HandleOfferAcceptance(offerIdx int8)
    20      HandleOfferRejection(offerIdx int8)
    21  }
    22  
    23  type ItemButton struct {
    24      hover       bool
    25      drawHandler UiDrawFunc
    26      onClick     func()
    27  }
    28  
    29  var (
    30      _ furex.ButtonHandler          = (*ItemButton)(nil)
    31      _ furex.DrawHandler            = (*ItemButton)(nil)
    32      _ furex.MouseEnterLeaveHandler = (*ItemButton)(nil)
    33      _ furex.MouseLeftButtonHandler = (*ItemButton)(nil)
    34  )
    35  
    36  func (b *ItemButton) HandleDraw(screen *ebiten.Image, frame image.Rectangle) {
    37      if b.hover {
    38          screen.Fill(color.RGBA{255, 0, 0, 0})
    39      }
    40      b.drawHandler(screen, frame)
    41  }
    42  
    43  func (b *ItemButton) HandlePress(x, y int, touchId ebiten.TouchID) {
    44      return
    45  }
    46  
    47  func (b *ItemButton) HandleRelease(x, y int, isCancelled bool) {
    48      if !isCancelled {
    49          b.onClick()
    50      }
    51  }
    52  
    53  func (b *ItemButton) HandleJustPressedMouseButtonLeft(x, y int) bool {
    54      return true
    55  }
    56  
    57  func (b *ItemButton) HandleJustReleasedMouseButtonLeft(x, y int) {
    58      b.onClick()
    59      return
    60  }
    61  
    62  func (b *ItemButton) HandleMouseEnter(x, y int) bool {
    63      b.hover = true
    64      return true
    65  }
    66  
    67  func (b *ItemButton) HandleMouseLeave() {
    68      b.hover = false
    69  }
    70  
    71  func BuildItemView(drawHandler UiDrawFunc, onClick func()) *furex.View {
    72      b := ItemButton{
    73          drawHandler: drawHandler,
    74          onClick:     onClick,
    75      }

Delve knows about that source file:

$ go clean
$ dlv debug titd
$ Type 'help' for list of commands.
(dlv) sources ui/prompt.go
/home/dvogel/devel/titd/ui/prompt.go

Listing from that file works:

(dlv) list /home/dvogel/devel/titd/ui/prompt.go:58
Showing /home/dvogel/devel/titd/ui/prompt.go:58 (PC: 0x0)
    53: func (b *ItemButton) HandleJustPressedMouseButtonLeft(x, y int) bool {
    54:     return true
    55: }
    56: 
    57: func (b *ItemButton) HandleJustReleasedMouseButtonLeft(x, y int) {
    58:     b.onClick()
    59:     return
    60: }
    61: 
    62: func (b *ItemButton) HandleMouseEnter(x, y int) bool {
    63:     b.hover = true

So it can set breakpoints in this file but not on some non-statement lines (as expected):

(dlv) break /home/dvogel/devel/titd/ui/prompt.go:37
Breakpoint 1 set at 0x7eded6 for titd/ui.(*ItemButton).HandleDraw() ./ui/prompt.go:37
(dlv) break /home/dvogel/devel/titd/ui/prompt.go:38
Breakpoint 2 set at 0x7edee6 for titd/ui.(*ItemButton).HandleDraw() ./ui/prompt.go:38
(dlv) break /home/dvogel/devel/titd/ui/prompt.go:39
Command failed: could not find statement at /home/dvogel/devel/titd/ui/prompt.go:39, please use a line with a statement
(dlv) break /home/dvogel/devel/titd/ui/prompt.go:40
Breakpoint 3 set at 0x7edf2a for titd/ui.(*ItemButton).HandleDraw() ./ui/prompt.go:40
(dlv) break /home/dvogel/devel/titd/ui/prompt.go:41
Breakpoint 4 set at 0x7edf55 for titd/ui.(*ItemButton).HandleDraw() ./ui/prompt.go:41

The functions implementing the interfaces from the furex package seem to give it an issue. However non-interface functions after those are amenable to breakpoints so it's not a different view of the file:

(dlv) break /home/dvogel/devel/titd/ui/prompt.go:43
Command failed: could not find statement at /home/dvogel/devel/titd/ui/prompt.go:43, please use a line with a statement
(dlv) break /home/dvogel/devel/titd/ui/prompt.go:44
Command failed: could not find statement at /home/dvogel/devel/titd/ui/prompt.go:44, please use a line with a statement
(dlv) break /home/dvogel/devel/titd/ui/prompt.go:45
Command failed: could not find statement at /home/dvogel/devel/titd/ui/prompt.go:45, please use a line with a statement
(dlv) break /home/dvogel/devel/titd/ui/prompt.go:47
Command failed: could not find statement at /home/dvogel/devel/titd/ui/prompt.go:47, please use a line with a statement
(dlv) break /home/dvogel/devel/titd/ui/prompt.go:48
Command failed: could not find statement at /home/dvogel/devel/titd/ui/prompt.go:48, please use a line with a statement
(dlv) break /home/dvogel/devel/titd/ui/prompt.go:49
Command failed: could not find statement at /home/dvogel/devel/titd/ui/prompt.go:49, please use a line with a statement
(dlv) break /home/dvogel/devel/titd/ui/prompt.go:50
Command failed: could not find statement at /home/dvogel/devel/titd/ui/prompt.go:50, please use a line with a statement
(dlv) break /home/dvogel/devel/titd/ui/prompt.go:51
Command failed: could not find statement at /home/dvogel/devel/titd/ui/prompt.go:51, please use a line with a statement
(dlv) break /home/dvogel/devel/titd/ui/prompt.go:53
Command failed: could not find statement at /home/dvogel/devel/titd/ui/prompt.go:53, please use a line with a statement
(dlv) break /home/dvogel/devel/titd/ui/prompt.go:54
Command failed: could not find statement at /home/dvogel/devel/titd/ui/prompt.go:54, please use a line with a statement
(dlv) break /home/dvogel/devel/titd/ui/prompt.go:55
Command failed: could not find statement at /home/dvogel/devel/titd/ui/prompt.go:55, please use a line with a statement
(dlv) break /home/dvogel/devel/titd/ui/prompt.go:57
Command failed: could not find statement at /home/dvogel/devel/titd/ui/prompt.go:57, please use a line with a statement
(dlv) break /home/dvogel/devel/titd/ui/prompt.go:58
Command failed: could not find statement at /home/dvogel/devel/titd/ui/prompt.go:58, please use a line with a statement
(dlv) break /home/dvogel/devel/titd/ui/prompt.go:59
Command failed: could not find statement at /home/dvogel/devel/titd/ui/prompt.go:59, please use a line with a statement
(dlv) break /home/dvogel/devel/titd/ui/prompt.go:560
Command failed: could not find statement at /home/dvogel/devel/titd/ui/prompt.go:560, please use a line with a statement
(dlv) break /home/dvogel/devel/titd/ui/prompt.go:60
Command failed: could not find statement at /home/dvogel/devel/titd/ui/prompt.go:60, please use a line with a statement
(dlv) break /home/dvogel/devel/titd/ui/prompt.go:62
Command failed: could not find statement at /home/dvogel/devel/titd/ui/prompt.go:62, please use a line with a statement
(dlv) break /home/dvogel/devel/titd/ui/prompt.go:63
Command failed: could not find statement at /home/dvogel/devel/titd/ui/prompt.go:63, please use a line with a statement
(dlv) break /home/dvogel/devel/titd/ui/prompt.go:64
Command failed: could not find statement at /home/dvogel/devel/titd/ui/prompt.go:64, please use a line with a statement
(dlv) break /home/dvogel/devel/titd/ui/prompt.go:65
Command failed: could not find statement at /home/dvogel/devel/titd/ui/prompt.go:65, please use a line with a statement
(dlv) break /home/dvogel/devel/titd/ui/prompt.go:67
Command failed: could not find statement at /home/dvogel/devel/titd/ui/prompt.go:67, please use a line with a statement
(dlv) break /home/dvogel/devel/titd/ui/prompt.go:68
Command failed: could not find statement at /home/dvogel/devel/titd/ui/prompt.go:68, please use a line with a statement
(dlv) break /home/dvogel/devel/titd/ui/prompt.go:69
Command failed: could not find statement at /home/dvogel/devel/titd/ui/prompt.go:69, please use a line with a statement
(dlv) break /home/dvogel/devel/titd/ui/prompt.go:71
Breakpoint 5 set at 0x7edfca for titd/ui.BuildItemView() ./ui/prompt.go:71
(dlv) break /home/dvogel/devel/titd/ui/prompt.go:72
Breakpoint 6 set at 0x7edfeb for titd/ui.BuildItemView() ./ui/prompt.go:72

Similarly, specifying those functions by the package-based locspec shows the same split behavior:

(dlv) break ui.BuildItemView
Breakpoint 1 set at 0x7edfca for titd/ui.BuildItemView() ./ui/prompt.go:71
(dlv) break HandleJustReleasedMouseButtonLeft
Command failed: location "HandleJustReleasedMouseButtonLeft" not found
(dlv) break ui.(*ItemButton).HandleJustReleasedMouseButtonLeft
Command failed: location "ui.(*ItemButton).HandleJustReleasedMouseButtonLeft" not found
(dlv) break ui.ItemButton.HandleJustReleasedMouseButtonLeft
Command failed: location "ui.ItemButton.HandleJustReleasedMouseButtonLeft" not found

In the steps above I allowed delve to build the program but I get the same results if I manually build the program with go build -gcflags '-N -l' titd. I have been unable to reproduce this with a smaller, more generic case so I suspect this is somehow related to the particular nature of the use of these interfaces within furex. However, please note that if this is the case I'm not looking for delve to fix this, just to give better guidance on whether a user should expect this to work.

aarzilli commented 1 year ago

The simple explanation for this is that these functions are never called and therefore don't exist in the executable. You can verify if this is the case by using go tool objdump.

dvogel commented 1 year ago

You're right, I was able to verify that the function doesn't appear in the objdump output. Thanks for pointing me in that direction. It seems very counter-intuitive that a debug tool would omit functions. That may feel obvious if you have extensive experience with the go toolchain but if you come from a C background you're used to debug builds containing all defined code. It would be good if delve automated the listing of symbols and reporting that a function doesn't exist rather than claiming the line in the function is not a statement.