Commit 74e6f14a by Ian Lance Taylor

runtime: get missing function name from symbol table

    
    If we trace back through code that has no debug info, as when calling
    through C code compiled with -g0, we won't have a function name.
    Try to fetch the function name using the symbol table.
    
    Adding the test case revealed that gotest failed to use the gccgo tag
    when matching files, so add that.
    
    Reviewed-on: https://go-review.googlesource.com/92756

From-SVN: r257495
parent 080eaf7a
2aa95f1499cf931ef8e95c7958463829276a0f2c 7e94bac5676afc8188677c98ecb263c78c1a7f8d
The first line of this file holds the git revision number of the last The first line of this file holds the git revision number of the last
merge done from the gofrontend repository. merge done from the gofrontend repository.
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build cgo,gccgo
package runtime_test
import (
"bytes"
"fmt"
"internal/testenv"
"os"
"os/exec"
"strings"
"testing"
)
func TestGccgoCrashTraceback(t *testing.T) {
t.Parallel()
got := runTestProg(t, "testprogcgo", "CrashTracebackGccgo")
ok := true
for i := 1; i <= 3; i++ {
if !strings.Contains(got, fmt.Sprintf("CFunction%d", i)) {
t.Errorf("missing C function CFunction%d", i)
ok = false
}
}
if !ok {
t.Log(got)
}
}
func TestGccgoCrashTracebackNodebug(t *testing.T) {
testenv.MustHaveGoBuild(t)
if os.Getenv("CC") == "" {
t.Skip("no compiler in environment")
}
cc := strings.Fields(os.Getenv("CC"))
cc = append(cc, "-x", "c++", "-")
out, _ := exec.Command(cc[0], cc[1:]...).CombinedOutput()
if bytes.Contains(out, []byte("error trying to exec 'cc1plus'")) {
t.Skip("no C++ compiler")
}
os.Setenv("CXX", os.Getenv("CC"))
got := runTestProg(t, "testprogcxx", "CrashTracebackNodebug")
ok := true
for i := 1; i <= 3; i++ {
if !strings.Contains(got, fmt.Sprintf("cxxFunction%d", i)) {
t.Errorf("missing C++ function cxxFunction%d", i)
ok = false
}
}
if !ok {
t.Log(got)
}
}
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build gccgo
package main
// This program will crash.
// We want the stack trace to include the C functions.
/*
#cgo CFLAGS: -g -O0
#include <stdint.h>
char *p;
static int CFunction3(void) {
*p = 0;
return 0;
}
static int CFunction2(void) {
return CFunction3();
}
static int CFunction1(void) {
return CFunction2();
}
*/
import "C"
func init() {
register("CrashTracebackGccgo", CrashTracebackGccgo)
}
func CrashTracebackGccgo() {
C.CFunction1()
}
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import "os"
var cmds = map[string]func(){}
func register(name string, f func()) {
if cmds[name] != nil {
panic("duplicate registration: " + name)
}
cmds[name] = f
}
func registerInit(name string, f func()) {
if len(os.Args) >= 2 && os.Args[1] == name {
f()
}
}
func main() {
if len(os.Args) < 2 {
println("usage: " + os.Args[0] + " name-of-test")
return
}
f := cmds[os.Args[1]]
if f == nil {
println("unknown function: " + os.Args[1])
return
}
f()
}
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
char *p;
static int cxxFunction3() {
*p = 0;
return 0;
}
static int cxxFunction2() {
return cxxFunction3();
}
extern "C"
int cxxFunction1() {
return cxxFunction2();
}
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
// This program will crash.
// We want the stack trace to include the C++ functions,
// even though we compile with -g0.
/*
#cgo CXXFLAGS: -g0 -O0
extern int cxxFunction1(void);
*/
import "C"
func init() {
register("CrashTracebackNodebug", CrashTracebackNodebug)
}
func CrashTracebackNodebug() {
C.cxxFunction1()
}
...@@ -143,6 +143,20 @@ callback (void *data, uintptr_t pc, const char *filename, int lineno, ...@@ -143,6 +143,20 @@ callback (void *data, uintptr_t pc, const char *filename, int lineno,
return arg->index >= arg->max; return arg->index >= arg->max;
} }
/* Syminfo callback. */
static void
syminfo_fnname_callback (void *data, uintptr_t pc __attribute__ ((unused)),
const char *symname,
uintptr_t address __attribute__ ((unused)),
uintptr_t size __attribute__ ((unused)))
{
Location* locptr = (Location*) data;
if (symname != NULL)
locptr->function = runtime_gostringnocopy ((const byte *) symname);
}
/* Error callback. */ /* Error callback. */
static void static void
...@@ -179,15 +193,17 @@ int32 ...@@ -179,15 +193,17 @@ int32
runtime_callers (int32 skip, Location *locbuf, int32 m, bool keep_thunks) runtime_callers (int32 skip, Location *locbuf, int32 m, bool keep_thunks)
{ {
struct callers_data data; struct callers_data data;
struct backtrace_state* state;
int32 i;
data.locbuf = locbuf; data.locbuf = locbuf;
data.skip = skip + 1; data.skip = skip + 1;
data.index = 0; data.index = 0;
data.max = m; data.max = m;
data.keep_thunks = keep_thunks; data.keep_thunks = keep_thunks;
state = __go_get_backtrace_state ();
runtime_xadd (&runtime_in_callers, 1); runtime_xadd (&runtime_in_callers, 1);
backtrace_full (__go_get_backtrace_state (), 0, callback, error_callback, backtrace_full (state, 0, callback, error_callback, &data);
&data);
runtime_xadd (&runtime_in_callers, -1); runtime_xadd (&runtime_in_callers, -1);
/* For some reason GCC sometimes loses the name of a thunk function /* For some reason GCC sometimes loses the name of a thunk function
...@@ -204,6 +220,18 @@ runtime_callers (int32 skip, Location *locbuf, int32 m, bool keep_thunks) ...@@ -204,6 +220,18 @@ runtime_callers (int32 skip, Location *locbuf, int32 m, bool keep_thunks)
--data.index; --data.index;
} }
/* Try to use backtrace_syminfo to fill in any missing function
names. This can happen when tracing through an object which has
no debug info; backtrace_syminfo will look at the symbol table to
get the name. This should only happen when tracing through code
that is not written in Go and is not part of libgo. */
for (i = 0; i < data.index; ++i)
{
if (locbuf[i].function.len == 0 && locbuf[i].pc != 0)
backtrace_syminfo (state, locbuf[i].pc, syminfo_fnname_callback,
error_callback, &locbuf[i]);
}
return data.index; return data.index;
} }
......
...@@ -348,18 +348,18 @@ x) ...@@ -348,18 +348,18 @@ x)
fi fi
match=false match=false
;; ;;
$goos | $goarch | cgo | go1.[0-9]) $goos | $goarch | cgo | gccgo | go1.[0-9])
match=true match=true
;; ;;
"!"$goos | "!"$goarch | "!cgo" | "!"go1.[0-9]) "!"$goos | "!"$goarch | "!cgo" | "!gccgo" | "!"go1.[0-9])
;; ;;
*,*) *,*)
cmatch=true cmatch=true
for ctag in `echo $tag | sed -e 's/,/ /g'`; do for ctag in `echo $tag | sed -e 's/,/ /g'`; do
case $ctag in case $ctag in
$goos | $goarch | cgo | go1.[0-9]) $goos | $goarch | cgo | gccgo | go1.[0-9])
;; ;;
"!"$goos | "!"$goarch | "!cgo" | "!"go1.[0-9]) "!"$goos | "!"$goarch | "!cgo" | "!gccgo" | "!"go1.[0-9])
cmatch=false cmatch=false
;; ;;
"!"*) "!"*)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment