Go version

go version go1.22.4 darwin/arm64

Output of go env in your module/workspace:

GO111MODULE=''
GOARCH='arm64'
GOBIN=''
GOCACHE='/tmp/Library/Caches/go-build'
GOENV='/tmp/Library/Application Support/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFLAGS=''
GOHOSTARCH='arm64'
GOHOSTOS='darwin'
GOINSECURE=''
GOMODCACHE='/tmp/go/pkg/mod'
GONOPROXY='example.com/*'
GONOSUMDB='*'
GOOS='darwin'
GOPATH='/tmp/go'
GOPRIVATE='example.com/*'
GOPROXY='https://example.com'
GOROOT='/usr/local/go'
GOSUMDB='off'
GOTMPDIR=''
GOTOOLCHAIN='local'
GOTOOLDIR='/usr/local/go/pkg/tool/darwin_arm64'
GOVCS=''
GOVERSION='go1.22.4'
GCCGO='gccgo'
AR='ar'
CC='clang'
CXX='clang++'
CGO_ENABLED='1'
GOMOD='/tmp/gotools/go.mod'
GOWORK=''
CGO_CFLAGS='-I/opt/homebrew/include/zookeeper'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
PKG_CONFIG='pkg-config'
GOGCCFLAGS='-fPIC -arch arm64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -ffile-prefix-map=/var/folders/bk/ypqf1wr10w5gmzpct5zk8kgr0000gn/T/go-build1645728497=/tmp/go-build -gno-record-gcc-switches -fno-common'

What did you do?

Running deadcode -whylive=somefunc . on some code produced a a panic:

$ deadcode -whylive=example.com/some.Func .
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x2 addr=0x38 pc=0x100f9b770]

goroutine 1 [running]:
main.main()
        /tmp/gotools/cmd/deadcode/deadcode.go:263 +0x17f0

The fix is trivial:

--- a/cmd/deadcode/deadcode.go
+++ b/cmd/deadcode/deadcode.go
@@ -260,7 +260,7 @@ func main() {
                        edges = append(edges, jsonEdge{
                                Initial:  cond(len(edges) == 0, prettyName(edge.Caller.Func, true), ""),
                                Kind:     cond(isStaticCall(edge), "static", "dynamic"),
-                               Position: toJSONPosition(prog.Fset.Position(edge.Site.Pos())),
+                               Position: toJSONPosition(prog.Fset.Position(edge.Pos())),
                                Callee:   prettyName(edge.Callee.Func, true),
                        })
                }

as edge.Pos() accounts for edge.Site possibly being nil. Unfortunately I'm not able to come up with a simple reproducer. My code is along the lines of:

package main

func f1() {}

func f4(f func()) {
    f()
}

func main() {
    f2 := func() {
        f1()
    }

    f3 := func() func() {
        return func() {
            f2()
        }
    }

    f4(f3())
}

but this one passes successfully (no panics), so there is something else I'm missing from my actual code:

$ deadcode -filter= -whylive=example.com.f1 .
                   example.com.main
  static@L0020 --> example.com.f4
 dynamic@L0006 --> example.com.main$1
  static@L0011 --> example.com.f1

With the above patch applied, the tool runs successfully and the output ends with:

$ deadcode -whylive=example.com/some.Func .
...
 dynamic@L0000 --> example.com/some.otherFunc$1
  static@L0001 --> example.com/some.Func

(note line 0 for the dynamic call, which is where the tool previously panicked)

Unless somebody more knowledgeable beats me to it, I'll create a pull request when I'm able to come up with a simple test to reproduce the panic.

What did you see happen?

Tool panicked with nil pointer dereference.

What did you expect to see?

Tool running successfully, reporting the callstack to the given function.

Comment From: gabyhelp

Similar Issues

  • https://github.com/golang/go/issues/63862
  • https://github.com/golang/go/issues/28242
  • https://github.com/golang/go/issues/66670
  • https://github.com/golang/go/issues/51303
  • https://github.com/golang/go/issues/24059

(Emoji vote if this was helpful or unhelpful; more detailed feedback welcome in this discussion.)

Comment From: rsc

/cc @adonovan

Comment From: adonovan

Thanks for the bug report. A nil Site means a reflective call, such as in this test case:

xtools$ cat a.go
package main

import "reflect"

func main() {
    reflect.ValueOf(foo).Call(nil)
}

func foo() {
    println("hi")
}

xtools$ callgraph ./a.go | grep foo
...
(reflect.Value).Call    --static-0:0--> command-line-arguments.foo
runtime.send    --dynamic-327:9-->  command-line-arguments.foo
runtime.recv    --dynamic-707:9-->  command-line-arguments.foo

xtools$ deadcode -whylive=command-line-arguments.foo ./a.go 
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x2 addr=0x38 pc=0x1004a54bc]

goroutine 1 gp=0x140000021c0 m=11 mp=0x1400030e808 [running]:
panic({0x1005de180?, 0x100967f20?})
    /Users/adonovan/w/goroot/src/runtime/panic.go:778 +0x154 fp=0x14000035540 sp=0x14000035490 pc=0x1000ba4c4
runtime.panicmem(...)
    /Users/adonovan/w/goroot/src/runtime/panic.go:261
runtime.sigpanic()
    /Users/adonovan/w/goroot/src/runtime/signal_unix.go:900 +0x300 fp=0x140000355a0 sp=0x14000035540 pc=0x1000f36f0
main.main()
    /Users/adonovan/go/pkg/mod/golang.org/x/tools@v0.21.1-0.20240517163634-c184dd7db2fd/cmd/deadcode/deadcode.go:263 +0x17bc fp=0x14000035f40 sp=0x140000355b0 pc=0x1004a54bc
runtime.main()
    /Users/adonovan/w/goroot/src/runtime/proc.go:270 +0x288 fp=0x14000035fd0 sp=0x14000035f40 pc=0x1000be3b8
runtime.goexit({})
    /Users/adonovan/w/goroot/src/runtime/asm_arm64.s:1223 +0x4 fp=0x14000035fd0 sp=0x14000035fd0 pc=0x1000f7844

I'll prepare a fix.

Comment From: gopherbot

Change https://go.dev/cl/591615 mentions this issue: cmd/deadcode: fix nil panic in Edge.Site.Pos