golang / go

The Go programming language
https://go.dev
BSD 3-Clause "New" or "Revised" License
124.07k stars 17.68k forks source link

cmd/compile: can't break on inlined function #28603

Open aclements opened 6 years ago

aclements commented 6 years ago

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

$ go version
go version devel +e72595ee0f Mon Nov 5 19:10:33 2018 +0000 linux/amd64

(latest master)

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
GOARCH="amd64"
GOBIN=""
GOCACHE="/home/austin/.cache/go-build"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOOS="linux"
GOPATH="/home/austin/r/go"
GOPROXY=""
GORACE=""
GOROOT="/home/austin/go.dev"
GOTMPDIR=""
GOTOOLDIR="/home/austin/go.dev/pkg/tool/linux_amd64"
GCCGO="/usr/bin/gccgo"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD=""
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 -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build704555549=/tmp/go-build -gno-record-gcc-switches"

What did you do?

Attempt to set a breakpoint on an inlined function.

E.g., paste https://play.golang.org/p/rDXmoTUZKJb in to bp.go and run

go build bp.go
gdb ./bp
(gdb) br 'main.f'
Function "main.f" not defined.
(gdb) br 'main.main'
Breakpoint 1 at 0x455660: file /home/austin/go.dev/austin/bp.go, line 5.

What did you expect to see?

I expected to be able to set a breakpoint on main.f like I could on main.main. In C/C++, this would cause gdb to set a breakpoint on every location the function was inlined to.

What did you see instead?

main.f is completely missing from the debug info, so GDB is unable to set the breakpoint.

/cc @randall77 @thanm

thanm commented 6 years ago

From my experience working with C compilers, one sees the same thing there when optimization is enabled. For example with this program:

#include <stdlib.h>
static int f() {
  return 42;
}
int main(int argc, char **argv) {
  exit(f());
}

if I compile with tip clang, there is no trace of "f" left in the DWARF. GCC does slightly better in that it generates a subprogram DIE for "f", but since there are no longer any instructions from "f", you can't set a breakpoint on it.

At the moment the Go compiler is doing the same thing-- if there aren't any remaining instructions (defined in some loose sense) from the original inlined routine, it is not going to turn up in the DWARF.

randall77 commented 6 years ago

As far as I can tell, inlined functions that have instructions that end up in the binary, still don't work. For Go:

package main

var x int = 1

func main() {
    x = f(x)
    println(x)
}

func f(x int) int {
    return x << 8
}

and C

#include <stdlib.h>

static int f(int x) {
  return x << 8;
}

int g = 1;

int main(int argc, char **argv) {
  exit(f(g));
}

There's a shl $8 instruction in both binaries, but I can't break f in gdb on either of them.

thanm commented 6 years ago

I tried the shift example with tip GCC and GDB 7.x and it works for me:

$ /w/gcc-trunk/build-gcc/.../gcc -O -g inlshift8.c
$ /w/binutils-trunk/install-7.12/bin/gdb a.out
GNU gdb (GDB) 7.12.1.20170417-git
...
Reading symbols from a.out...done.
(gdb) b f
Breakpoint 1 at 0x400556: file inlshift8.c, line 10.
(gdb) run
Starting program: /usr/local/google/home/thanm/a.out 
Breakpoint 1, main (argc=1, argv=0x7fffffffdcd8) at inlshift8.c:10
10    exit(f(g));
(gdb) si
0x000000000040055c in f (x=<optimized out>) at inlshift8.c:10
10    exit(f(g));
(gdb) where
#0  0x000000000040055c in f (x=<optimized out>) at inlshift8.c:10
#1  main (argc=<optimized out>, argv=0x7fffffffdcd8) at inlshift8.c:10
(gdb) 

This is obviously not very helpful to you, however. With this sort of debug info generation (e.g. with other optimizations turned on) it's a very "best effort only" scenario. If things get optimized "too much" (whatever that is) then the DWARF disappears or gets degraded.

randall77 commented 6 years ago

Ah, I forgot the -g. The C example now works for me.

randall77 commented 5 years ago

Note that although you can't break at the name, you can break at the line number.

gopherbot commented 5 years ago

Change https://golang.org/cl/177697 mentions this issue: cmd/compile: mention issue 28603 when discussing where inline marks go

thanm commented 5 years ago

Duplicate of #26206