Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cmd/compile: missing debug information for return values of runtime functions #48573

Closed
michael-obermueller opened this issue Sep 23, 2021 · 7 comments

Comments

@michael-obermueller
Copy link

michael-obermueller commented Sep 23, 2021

What version of Go are you using (go version)?

$ go version
go version go1.17 windows/amd64

Does this issue reproduce with the latest release?

yes

What operating system and processor architecture are you using (go env)?

go env Output
$ go env
set GO111MODULE=
set GOARCH=amd64
set GOBIN=
set GOCACHE=C:\Users\Michael.Obermueller\AppData\Local\go-build
set GOENV=C:\Users\Michael.Obermueller\AppData\Roaming\go\env
set GOEXE=.exe
set GOEXPERIMENT=
set GOFLAGS=
set GOHOSTARCH=amd64
set GOHOSTOS=windows
set GOINSECURE=
set GOMODCACHE=C:\Users\Michael.Obermueller\go\pkg\mod
set GONOPROXY=...
set GONOSUMDB=...
set GOOS=windows
set GOPATH=C:\Users\Michael.Obermueller\go
set GOPRIVATE=...
set GOPROXY=https://proxy.golang.org,direct
set GOROOT=C:\workspaces\devtools\go\go-master
set GOSUMDB=sum.golang.org
set GOTMPDIR=
set GOTOOLDIR=C:\workspaces\devtools\go\go-master\pkg\tool\windows_amd64
set GOVCS=
set GOVERSION=go1.17
set GCCGO=gccgo
set AR=ar
set CC=gcc
set CXX=g++
set CGO_ENABLED=1
set GOMOD=C:\workspaces\repos\go-world\src\GoWorld\go.mod
set CGO_CFLAGS=-g -O2
set CGO_CPPFLAGS=
set CGO_CXXFLAGS=-g -O2
set CGO_FFLAGS=-g -O2
set CGO_LDFLAGS=-g -O2
set PKG_CONFIG=pkg-config
set GOGCCFLAGS=-m64 -mthreads -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=C:\Users\MICHAE~1.OBE\AppData\Local\Temp\go-build2406948064=/tmp/go-build -gno-record-gcc-switches
GOROOT/bin/go version: go version go1.17 windows/amd64
GOROOT/bin/go tool compile -V: compile version go1.17

What did you do?

Build an application with the Go 1.17 toolchain using command go build -gcflags=all="-N -l" HelloWorld.go:

package main

import "fmt"

func main() {
    fmt.Println("hello world")
}

The DWARF information for Go functions defined in the runtime package does not contain DW_TAG_formal_parameter entries for return values. I have checked multiple runtime functions, for all of them I see the same problem. As an example let's check runtime.mapaccess2 which has the following signature:

func mapaccess2(t *maptype, h *hmap, key unsafe.Pointer) (unsafe.Pointer, bool)

What did you expect to see?

DW_TAG_formal_parameter entries for the two return values of runtime.mapaccess2
If I build the program with Go 1.16.8 toolchain the return value entries exist:

DWARF Go 1.16.8

debug_1_16_8.txt:

 <1><be36>: Abbrev Number: 3 (DW_TAG_subprogram)
    <be37>   DW_AT_name        : runtime.mapaccess2
    <be4a>   DW_AT_low_pc      : 0x40f9c0
    <be52>   DW_AT_high_pc     : 0x40fd2a
    <be5a>   DW_AT_frame_base  : 1 byte block: 9c 	(DW_OP_call_frame_cfa)
    <be5c>   DW_AT_decl_file   : 0x26
    <be60>   DW_AT_external    : 1
 <2><be9a>: Abbrev Number: 16 (DW_TAG_formal_parameter)
    <be9b>   DW_AT_name        : t
    <be9d>   DW_AT_variable_parameter: 0
    <be9e>   DW_AT_decl_line   : 452
    <bea0>   DW_AT_type        : <0x39b60>
    <bea4>   DW_AT_location    : 0xa10c (location list)
 <2><bea8>: Abbrev Number: 16 (DW_TAG_formal_parameter)
    <bea9>   DW_AT_name        : h
    <beab>   DW_AT_variable_parameter: 0
    <beac>   DW_AT_decl_line   : 452
    <beae>   DW_AT_type        : <0x39b7f>
    <beb2>   DW_AT_location    : 0xa13f (location list)
 <2><beb6>: Abbrev Number: 16 (DW_TAG_formal_parameter)
    <beb7>   DW_AT_name        : key
    <bebb>   DW_AT_variable_parameter: 0
    <bebc>   DW_AT_decl_line   : 452
    <bebe>   DW_AT_type        : <0x34320>
    <bec2>   DW_AT_location    : 0xa173 (location list)
 <2><bec6>: Abbrev Number: 15 (DW_TAG_formal_parameter)
    <bec7>   DW_AT_name        : ~r3
    <becb>   DW_AT_variable_parameter: 1
    <becc>   DW_AT_decl_line   : 452
    <bece>   DW_AT_type        : <0x34320>
    <bed2>   DW_AT_location    : 0 byte block: 	()
 <2><bed3>: Abbrev Number: 15 (DW_TAG_formal_parameter)
    <bed4>   DW_AT_name        : ~r4
    <bed8>   DW_AT_variable_parameter: 1
    <bed9>   DW_AT_decl_line   : 452
    <bedb>   DW_AT_type        : <0x344ba>
    <bedf>   DW_AT_location    : 0 byte block: 	()
...

What did you see instead?

With Go 1.17 toolchain the return value entries are missing:

DWARF Go 1.17

debug.txt:

 <1><f8bc>: Abbrev Number: 3 (DW_TAG_subprogram)
    <f8bd>   DW_AT_name        : runtime.mapaccess2
    <f8d0>   DW_AT_low_pc      : 0x40dc60
    <f8d8>   DW_AT_high_pc     : 0x40df30
    <f8e0>   DW_AT_frame_base  : 1 byte block: 9c 	(DW_OP_call_frame_cfa)
    <f8e2>   DW_AT_decl_file   : 0x27
    <f8e6>   DW_AT_external    : 1
 <2><f8e7>: Abbrev Number: 16 (DW_TAG_formal_parameter)
    <f8e8>   DW_AT_name        : t
    <f8ea>   DW_AT_variable_parameter: 0
    <f8eb>   DW_AT_decl_line   : 452
    <f8ed>   DW_AT_type        : <0x45c77>
    <f8f1>   DW_AT_location    : 0xf86c (location list)
 <2><f8f5>: Abbrev Number: 16 (DW_TAG_formal_parameter)
    <f8f6>   DW_AT_name        : h
    <f8f8>   DW_AT_variable_parameter: 0
    <f8f9>   DW_AT_decl_line   : 452
    <f8fb>   DW_AT_type        : <0x45c96>
    <f8ff>   DW_AT_location    : 0xf8b2 (location list)
 <2><f903>: Abbrev Number: 16 (DW_TAG_formal_parameter)
    <f904>   DW_AT_name        : key
    <f908>   DW_AT_variable_parameter: 0
    <f909>   DW_AT_decl_line   : 452
    <f90b>   DW_AT_type        : <0x3ffa9>
    <f90f>   DW_AT_location    : 0xf920 (location list)
 ...
@PeterFeicht
Copy link
Contributor

This might be related to #46845.

@ianlancetaylor
Copy link
Contributor

CC @thanm

@thanm
Copy link
Contributor

thanm commented Sep 24, 2021

Thanks. As mentioned in #46845, when you build with go build -gcflags=all="-N -l", the Go command is intentionally not disabling optimization for the runtime, since there are runtime functions that have to be built with optimization to function correctly.

With that said, it seems odd that the return params are missing entirely (as opposed to being present, but not having accurate location expressions). I'll take a look and see if I can figure out what the issue is.

@thanm thanm self-assigned this Sep 24, 2021
@randall77 randall77 added this to the Go1.18 milestone Oct 2, 2021
@heschi
Copy link
Contributor

heschi commented Oct 6, 2021

Ping from the release team: any updates? This is marked as a release blocker.

@thanm
Copy link
Contributor

thanm commented Oct 6, 2021

Thanks for the ping. Hadn't realized that this had been marked as a release blocker (is there a way to be notified when this happens?). I'll see about taking a look later this week.

@thanm
Copy link
Contributor

thanm commented Nov 9, 2021

Some background material on this bug:

When optimization is turned on, the DWARF-gen code that emits variable and formal_parameter DIEs draws info from two places: the SSA name table, and the "Dcl" slice for the function (code here), which has entries for in and out parameters and locals for the function.

The general rule is that if a variable is SSA-able, it will get entered into the SSA name table, and will be incorporated into the DWARF by the locationlists code. If a var is not in the SSA name table, then dwarfgen will pick it up from the "Dcl" slice for the function.

Here's an example function:

  func foo(x int, s string, b GiantBlob, unusedParam [128]byte) uintptr {
    var unusedLocal [15]string
    ...
  }

For this function "x" and "s" are passed in register, "b" and "unusedParam" are passed on the stack.

At SSA construction time, the "Dcl" slice for function foo will contain entries for all of the vars above, e.g.

	Name		Class
	x		PARAM
	s		PARAM
	b		PARAM
	unusedParam	PARAM
	~r0		PARAMOUT
	unusedLocal	AUTO
	...

After we enter SSA and optimization starts, the back end will optimize things away, remove dead code, etc, until we arrive at the "stackframe" SSA pass. This pass sorts locals by size and assigns concrete offsets to things in the stack frame, and will prune away autos from the Dcl list that are no longer referenced (code here). The upshot is that if a variable gets pruned by "stackframe" because it is unused (and also doesn't appear in the SSA name table) then we don't emit a DWARF DIE for it (as with "unusedLocal" above).

During the register ABI work, a change was made in CL 302071 to "stackframe" to treat register-resident output parameter (PARAMOUT) variables that same as locals, which meant that if they were unused, we'd delete them from the "Dcl" slice.

The second thing that happened during the register ABI work is that we changed the code emitted by the back end for "return" statements changed. Whereas before a statement like

  return 42

would be lowered to a store into the PARAMOUT variable (e.g. "~r0"), with the register ABI turned on this code would be lowered into a "MakeResult" op, which doesn't refer to the PARAMOUT variable at all.

Bottom line is that we now skip PARAMOUT vars during DWARF variable DIE generation unless they are not register-resident, since they no longer appear either the SSA name table or the "Dcl" slice.

There are a couple of different ways we could fix this bug. One possibility would be to just cache away a copy of the pre-optimization "Dcl" slice and use that as a way to pick up PARAMOUT nodes. A second possibility would be to move away from using the "Dcl" slice entirely, and instead operate off the function signature type itself (since the optimizer is not going to be messing with that). The one pothole/wrinkle there is that the DWARF param/var DIE includes DeclLine and DeclFile attributes, and we can't get that sort of info just from the signature type.

@gopherbot
Copy link

Change https://golang.org/cl/362618 mentions this issue: cmd/compile: include register-resident output params in DWARF-gen

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

7 participants