Skip to content

Take advantage of suspending breakpoint provided by delve #3713

@Lslightly

Description

@Lslightly

What version of Go, VS Code & VS Code Go extension are you using?

Version Information

Environment

GOBIN: undefined
toolsGopath:
gopath: /home/lqw/go
GOROOT: /home/lqw/go1.24.0
PATH: /home/lqw/.vscode-server/bin/6609ac3d66f4eade5cf376d1cb76f13985724bcb/bin/remote-cli:/home/lqw/.rvm/gems/ruby-3.0.0/bin:/home/lqw/.rvm/gems/ruby-3.0.0@global/bin:/home/lqw/.rvm/rubies/ruby-3.0.0/bin:/home/lqw/mygit/delve:/home/lqw/go/bin:/home/lqw/go1.24.0/bin:/home/lqw/scripts:/home/lqw/cmake/bin:/home/lqw/.local/bin:/home/lqw/miniconda3/bin:/home/lqw/miniconda3/condabin:/home/lqw/.opam/default/bin:/home/lqw/.elan/bin:/home/lqw/.cargo/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/usr/lib/wsl/lib:/home/lqw/.rvm/bin

Tools

go:	/home/lqw/go1.24.0/bin/go: go version go1.24.0 linux/amd64

gopls:	/home/lqw/go/bin/gopls	(version: v0.18.1 built with go: go1.24.1)
gotests:	not installed
gomodifytags:	not installed
impl:	not installed
goplay:	not installed
dlv:	/home/lqw/go/bin/dlv	(version: v0.0.0-20250311114909-570f5725d595+dirty built with go: go1.24.0)
staticcheck:	/home/lqw/go/bin/staticcheck	(version: v0.6.0 built with go: go1.24.0)

Go env

Workspace Folder (delve): /home/lqw/mygit/delve

AR='ar'
CC='gcc'
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_ENABLED='1'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
CXX='g++'
GCCGO='gccgo'
GO111MODULE='on'
GOAMD64='v1'
GOARCH='amd64'
GOAUTH='netrc'
GOBIN=''
GOCACHE='/home/lqw/.cache/go-build'
GOCACHEPROG=''
GODEBUG=''
GOENV='/home/lqw/.config/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFIPS140='off'
GOFLAGS=''
GOGCCFLAGS='-fPIC -m64 -pthread -Wl,--no-gc-sections -fmessage-length=0 -ffile-prefix-map=/tmp/go-build4247711127=/tmp/go-build -gno-record-gcc-switches'
GOHOSTARCH='amd64'
GOHOSTOS='linux'
GOINSECURE=''
GOMOD='/home/lqw/mygit/delve/go.mod'
GOMODCACHE='/home/lqw/go/pkg/mod'
GONOPROXY=''
GONOSUMDB=''
GOOS='linux'
GOPATH='/home/lqw/go'
GOPRIVATE=''
GOPROXY='https://goproxy.cn,direct'
GOROOT='/home/lqw/go1.24.0'
GOSUMDB='sum.golang.org'
GOTELEMETRY='local'
GOTELEMETRYDIR='/home/lqw/.config/go/telemetry'
GOTMPDIR=''
GOTOOLCHAIN='auto'
GOTOOLDIR='/home/lqw/go1.24.0/pkg/tool/linux_amd64'
GOVCS=''
GOVERSION='go1.24.0'
GOWORK=''
PKG_CONFIG='pkg-config'

Share the Go related settings you have added/edited

Run Preferences: Open Settings (JSON) command to open your settings.json file.
Share all the settings with the go. or ["go"] or gopls prefixes.

"go.alternateTools": {
    "dlv": "<modified dlv mentioned above>"
}

Describe the bug

A clear and concise description of what the bug.
A clear and concise description of what you expected to happen.

Bug: The suspended argument is always false when CreateBreakpoint is invoked in delve server through RPC call by vscode-go debug adapter. The consequence is that when the location of a breakpoint is not found in the delve debugger, the breakpoint will be deleted in service/debugger/debugger.go#L792-L797 as shown in the following code, which makes vscode frontend fail to set the breakpoint. Current interface not fully utilizes the ability of suspending breakpoints in delve, which can limit vscode-go debug adapter's ability in cases like multiple process debugging even if FollowExec is enabled.

if suspended {
	logflags.DebuggerLogger().Debugf("could not enable new breakpoint: %v (breakpoint will be suspended)", err)
} else {
	delete(d.target.LogicalBreakpoints, lbp.LogicalID)
	return nil, err
}

What I expected to see: suspended is always sent true so that when the location of a breakpoint is not found, the breakpoint can still be suspended. In the future, the breakpoint will be valid.

The interfaces in both sides are:

Current interface in delve is:

Code of delve
// Breakpoint addresses a set of locations at which process execution may be
// suspended.
type Breakpoint struct {
	// ID is a unique identifier for the breakpoint.
	ID int `json:"id"`
	// User defined name of the breakpoint.
	Name string `json:"name"`
	// Addr is deprecated, use Addrs.
	Addr uint64 `json:"addr"`
	// Addrs is the list of addresses for this breakpoint.
	Addrs []uint64 `json:"addrs"`
	// AddrPid[i] is the PID associated with by Addrs[i], when debugging a
	// single target process this is optional, otherwise it is mandatory.
	AddrPid []int `json:"addrpid"`
	// File is the source file for the breakpoint.
	File string `json:"file"`
	// Line is a line in File for the breakpoint.
	Line int `json:"line"`
	// FunctionName is the name of the function at the current breakpoint, and
	// may not always be available.
	FunctionName string `json:"functionName,omitempty"`
	// ExprString is the string that will be used to set a suspended breakpoint.
	ExprString string

	// Breakpoint condition
	Cond string
	// Breakpoint hit count condition.
	// Supported hit count conditions are "NUMBER" and "OP NUMBER".
	HitCond string
	// HitCondPerG use per goroutine hitcount as HitCond operand, instead of total hitcount
	HitCondPerG bool

	// Tracepoint flag, signifying this is a tracepoint.
	Tracepoint bool `json:"continue"`
	// TraceReturn flag signifying this is a breakpoint set at a return
	// statement in a traced function.
	TraceReturn bool `json:"traceReturn"`
	// retrieve goroutine information
	Goroutine bool `json:"goroutine"`
	// number of stack frames to retrieve
	Stacktrace int `json:"stacktrace"`
	// expressions to evaluate
	Variables []string `json:"variables,omitempty"`
	// LoadArgs requests loading function arguments when the breakpoint is hit
	LoadArgs *LoadConfig
	// LoadLocals requests loading function locals when the breakpoint is hit
	LoadLocals *LoadConfig

	// WatchExpr is the expression used to create this watchpoint
	WatchExpr string
	WatchType WatchType

	VerboseDescr []string `json:"VerboseDescr,omitempty"`

	// number of times a breakpoint has been reached in a certain goroutine
	HitCount map[string]uint64 `json:"hitCount"`
	// number of times a breakpoint has been reached
	TotalHitCount uint64 `json:"totalHitCount"`
	// Disabled flag, signifying the state of the breakpoint
	Disabled bool `json:"disabled"`

	UserData interface{} `json:"-"`

	// RootFuncName is the Root function from where tracing needs to be done
	RootFuncName string
	// TraceFollowCalls indicates the Depth of tracing
	TraceFollowCalls int
}

type CreateBreakpointIn struct {
	Breakpoint api.Breakpoint

	LocExpr             string
	SubstitutePathRules [][2]string
	Suspended           bool
}

Describe the solution you'd like
A clear and concise description of what you want to happen.

add suspended in RPC pack. vscode-go always send suspended=true to delve.

Describe alternatives you've considered
A clear and concise description of any alternative solutions or features you've considered.

nothing yet.

Additional context
Add any other context or screenshots about the feature request here.

This issue is blocking #3712. #3712 is a promising enhancement.

Related PR: go-delve/delve#3900

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions