delve icon indicating copy to clipboard operation
delve copied to clipboard

conditional breakpoint evaluation fails: could not find symbol value for test (local variable)

Open stapelberg opened this issue 6 months ago • 4 comments
trafficstars

Today, I wanted to debug a specific entry of a table-driven test with many hundreds of table entries.

In my environment, launching delve on a test is a single-click action, but figuring out how to pass the -test.run=… flag to delve is more complicated.

So, I figured I would just use a conditional breakpoint within delve to stop program execution at the test of interest.

Here’s what I did:

% go version
go version go1.24.3 linux/amd64

% go install github.com/go-delve/delve/cmd/dlv@latest
go: downloading github.com/go-delve/delve v1.24.2

% git clone https://github.com/protocolbuffers/protobuf-go
# git revision e5d44688be415e6cea6c21ebf5d832ab04378837

% cd protobuf-go/proto

% dlv test -- -test.run=TestEncode
Type 'help' for list of commands.
(dlv) b encode_test.go:31
Breakpoint 1 set at 0xa5b06c,0xa5b2d6 for (multiple functions)() ./encode_test.go:31
(dlv) c
> [Breakpoint 1] google.golang.org/protobuf/proto_test.TestEncode() ./encode_test.go:31 (hits goroutine(6):1 total:1) (PC: 0xa5b06c)
    26:	)
    27:	
    28:	func TestEncode(t *testing.T) {
    29:		for _, test := range testValidMessages {
    30:			for _, want := range test.decodeTo {
=>  31:				t.Run(fmt.Sprintf("%s (%T)", test.desc, want), func(t *testing.T) {
    32:					opts := proto.MarshalOptions{
    33:						AllowPartial: test.partial,
    34:					}
    35:					wire, err := opts.Marshal(want)
    36:					if err != nil {
(dlv) p test.desc
"basic scalar types"
(dlv) cond 1 test.desc == "oneof (message)"
(dlv) c
> [Breakpoint 1] google.golang.org/protobuf/proto_test.TestEncode.func1() ./encode_test.go:31 (hits goroutine(7):1 total:2) (PC: 0xa5b2d6)
Command failed: error evaluating expression: could not find symbol value for test

I am not sure why this breakpoint condition doesn’t work. As the transcript shows, printing test.desc works fine at the location of the breakpoint, but for some reason, the breakpoint evaluation doesn’t see this variable…?!

Thanks in advance for taking a look

stapelberg avatar May 08 '25 14:05 stapelberg

It's because that line belongs to two functions (TestEncode and its closure) and for the closure the test variable isn't visible at entry for some reason. The breakpoint gets set on both of them.

aarzilli avatar May 08 '25 16:05 aarzilli

It's because that line belongs to two functions (TestEncode and its closure) and for the closure the test variable isn't visible at entry for some reason. The breakpoint gets set on both of them.

Thanks for the details! Yes, setting the breakpoint on encode_test.go:32 (one line later) makes the conditional breakpoint work.

But now I wonder: Why is test not in scope for the closure? Should I take this to the Go compiler folks?

stapelberg avatar May 20 '25 09:05 stapelberg

I remember there being some kind of problem with the way the compiler tracked the declaration line of captured variables. It could be that or it could be something else. Can I see the DIE for the closure function? You can get it with objdump or with diexplorer, or whatever you prefer. Or you can upload the binary file compiled with go test -c -gcflags='all=-N -l'.

aarzilli avatar May 20 '25 12:05 aarzilli

It could be that or it could be something else.

FWIW, I asked the compiler folks and someone suggested it might just be a range bug of sorts because the variable is available in the next line of the same closure.

You can get it with objdump or with diexplorer, or whatever you prefer. Or you can upload the binary file compiled with go test -c -gcflags='all=-N -l'.

I don’t know which flags you’d like me to pass to objdump, or how to invoke diexplorer (no usage instructions in the README).

Hence, I built the test binary with go1.24.2 (instructions per the first comment of this issue) and have attached it here for your convenience: proto.test.zip

stapelberg avatar Jun 06 '25 14:06 stapelberg