Commit 0effc3f9 by Ian Lance Taylor

libgo: Implement and use runtime.Caller, runtime.Func.FileLine.

From-SVN: r185025
parent 1f3d0afc
......@@ -1024,12 +1024,14 @@ go_debug_dwarf_files = \
go/debug/dwarf/buf.go \
go/debug/dwarf/const.go \
go/debug/dwarf/entry.go \
go/debug/dwarf/line.go \
go/debug/dwarf/open.go \
go/debug/dwarf/type.go \
go/debug/dwarf/unit.go
go_debug_elf_files = \
go/debug/elf/elf.go \
go/debug/elf/file.go
go/debug/elf/file.go \
go/debug/elf/runtime.go
go_debug_gosym_files = \
go/debug/gosym/pclntab.go \
go/debug/gosym/symtab.go
......
......@@ -1340,13 +1340,15 @@ go_debug_dwarf_files = \
go/debug/dwarf/buf.go \
go/debug/dwarf/const.go \
go/debug/dwarf/entry.go \
go/debug/dwarf/line.go \
go/debug/dwarf/open.go \
go/debug/dwarf/type.go \
go/debug/dwarf/unit.go
go_debug_elf_files = \
go/debug/elf/elf.go \
go/debug/elf/file.go
go/debug/elf/file.go \
go/debug/elf/runtime.go
go_debug_gosym_files = \
go/debug/gosym/pclntab.go \
......
......@@ -431,3 +431,30 @@ const (
encUnsignedChar = 0x08
encImaginaryFloat = 0x09
)
// Line number opcodes.
const (
LineExtendedOp = 0
LineCopy = 1
LineAdvancePC = 2
LineAdvanceLine = 3
LineSetFile = 4
LineSetColumn = 5
LineNegateStmt = 6
LineSetBasicBlock = 7
LineConstAddPC = 8
LineFixedAdvancePC = 9
// next 3 are DWARF 3
LineSetPrologueEnd = 10
LineSetEpilogueBegin = 11
LineSetISA = 12
)
// Line number extended opcodes.
const (
LineExtEndSequence = 1
LineExtSetAddress = 2
LineExtDefineFile = 3
// next 1 is DWARF 4
LineExtSetDiscriminator = 4
)
......@@ -246,6 +246,15 @@ func (d *Data) Reader() *Reader {
return r
}
// unitReader returns a new reader starting at a specific unit.
func (d *Data) unitReader(i int) *Reader {
r := &Reader{d: d}
r.unit = i
u := &d.unit[i]
r.b = makeBuf(d, "info", u.off, u.data, u.addrsize)
return r
}
// Seek positions the Reader at offset off in the encoded entry stream.
// Offset 0 can be used to denote the first entry.
func (r *Reader) Seek(off Offset) {
......
// Copyright 2012 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 dwarf_test
import (
. "debug/dwarf"
"path/filepath"
"testing"
)
type lineTest struct {
pc uint64
file string
line int
}
var elfLineTests = [...]lineTest{
{0x4004c4, "typedef.c", 83},
{0x4004c8, "typedef.c", 84},
{0x4004ca, "typedef.c", 84},
{0x4003e0, "", 0},
}
var machoLineTests = [...]lineTest{
{0x0, "typedef.c", 83},
}
func TestLineElf(t *testing.T) {
testLine(t, elfData(t, "testdata/typedef.elf"), elfLineTests[:], "elf")
}
func TestLineMachO(t *testing.T) {
testLine(t, machoData(t, "testdata/typedef.macho"), machoLineTests[:], "macho")
}
func testLine(t *testing.T, d *Data, tests []lineTest, kind string) {
for _, v := range tests {
file, line, err := d.FileLine(v.pc)
if err != nil {
t.Errorf("%s: %v", kind, err)
continue
}
if file != "" {
file = filepath.Base(file)
}
if file != v.file || line != v.line {
t.Errorf("%s: for %d have %q:%d want %q:%d",
kind, v.pc, file, line, v.file, v.line)
}
}
}
......@@ -12,9 +12,19 @@ import "strconv"
type unit struct {
base Offset // byte offset of header within the aggregate info
off Offset // byte offset of data within the aggregate info
lineoff Offset // byte offset of data within the line info
data []byte
atable abbrevTable
addrsize int
dir string
pc []addrRange // PC ranges in this compilation unit
lines []mapLineInfo // PC -> line mapping
}
// A range is an address range.
type addrRange struct {
low uint64
high uint64
}
func (d *Data) parseUnits() ([]unit, error) {
......
......@@ -2,9 +2,10 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package elf
package elf_test
import (
. "debug/elf"
"fmt"
"testing"
)
......
......@@ -563,7 +563,7 @@ func (f *File) DWARF() (*dwarf.Data, error) {
// There are many other DWARF sections, but these
// are the required ones, and the debug/dwarf package
// does not use the others, so don't bother loading them.
var names = [...]string{"abbrev", "info", "str"}
var names = [...]string{"abbrev", "info", "line", "str"}
var dat [len(names)][]byte
for i, name := range names {
name = ".debug_" + name
......@@ -592,8 +592,8 @@ func (f *File) DWARF() (*dwarf.Data, error) {
}
}
abbrev, info, str := dat[0], dat[1], dat[2]
return dwarf.New(abbrev, nil, nil, info, nil, nil, nil, str)
abbrev, info, line, str := dat[0], dat[1], dat[2], dat[3]
return dwarf.New(abbrev, nil, nil, info, line, nil, nil, str)
}
// Symbols returns the symbol table for f.
......
......@@ -2,10 +2,11 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package elf
package elf_test
import (
"debug/dwarf"
. "debug/elf"
"encoding/binary"
"net"
"os"
......
// Copyright 2012 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.
// This is gccgo-specific code that uses DWARF information to fetch
// file/line information for PC values. This package registers itself
// with the runtime package.
package elf
import (
"debug/dwarf"
"debug/macho"
"os"
"runtime"
"sort"
"sync"
)
func init() {
// Register our lookup functions with the runtime package.
runtime.RegisterDebugLookup(funcFileLine, symbolValue)
}
// The file struct holds information for a specific file that is part
// of the execution.
type file struct {
elf *File // If ELF
macho *macho.File // If Mach-O
dwarf *dwarf.Data // DWARF information
symsByName []sym // Sorted by name
symsByAddr []sym // Sorted by address
}
// Sort symbols by name.
type symsByName []sym
func (s symsByName) Len() int { return len(s) }
func (s symsByName) Less(i, j int) bool { return s[i].name < s[j].name }
func (s symsByName) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
// Sort symbols by address.
type symsByAddr []sym
func (s symsByAddr) Len() int { return len(s) }
func (s symsByAddr) Less(i, j int) bool { return s[i].addr < s[j].addr }
func (s symsByAddr) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
// The sym structure holds the information we care about for a symbol,
// namely name and address.
type sym struct {
name string
addr uintptr
}
// Open an input file.
func open(name string) (*file, error) {
efile, err := Open(name)
var mfile *macho.File
if err != nil {
var merr error
mfile, merr = macho.Open(name)
if merr != nil {
return nil, err
}
}
r := &file{elf: efile, macho: mfile}
if efile != nil {
r.dwarf, err = efile.DWARF()
} else {
r.dwarf, err = mfile.DWARF()
}
if err != nil {
return nil, err
}
var syms []sym
if efile != nil {
esyms, err := efile.Symbols()
if err != nil {
return nil, err
}
syms = make([]sym, 0, len(esyms))
for _, s := range esyms {
if ST_TYPE(s.Info) == STT_FUNC {
syms = append(syms, sym{s.Name, uintptr(s.Value)})
}
}
} else {
syms = make([]sym, 0, len(mfile.Symtab.Syms))
for _, s := range mfile.Symtab.Syms {
syms = append(syms, sym{s.Name, uintptr(s.Value)})
}
}
r.symsByName = make([]sym, len(syms))
copy(r.symsByName, syms)
sort.Sort(symsByName(r.symsByName))
r.symsByAddr = syms
sort.Sort(symsByAddr(r.symsByAddr))
return r, nil
}
// The main executable
var executable *file
// Only open the executable once.
var executableOnce sync.Once
func openExecutable() {
executableOnce.Do(func() {
f, err := open("/proc/self/exe")
if err != nil {
f, err = open(os.Args[0])
if err != nil {
return
}
}
executable = f
})
}
// The funcFileLine function looks up the function name, file name,
// and line number for a PC value.
func funcFileLine(pc uintptr, function *string, file *string, line *int) bool {
openExecutable()
if executable.dwarf == nil {
return false
}
f, ln, err := executable.dwarf.FileLine(uint64(pc))
if err != nil {
return false
}
*file = f
*line = ln
*function = ""
if len(executable.symsByAddr) > 0 && pc >= executable.symsByAddr[0].addr {
i := sort.Search(len(executable.symsByAddr),
func(i int) bool { return executable.symsByAddr[i].addr > pc })
*function = executable.symsByAddr[i-1].name
}
return true
}
// The symbolValue function fetches the value of a symbol.
func symbolValue(name string, val *uintptr) bool {
i := sort.Search(len(executable.symsByName),
func(i int) bool { return executable.symsByName[i].name >= name })
if i >= len(executable.symsByName) || executable.symsByName[i].name != name {
return false
}
*val = executable.symsByName[i].addr
return true
}
......@@ -467,7 +467,7 @@ func (f *File) DWARF() (*dwarf.Data, error) {
// There are many other DWARF sections, but these
// are the required ones, and the debug/dwarf package
// does not use the others, so don't bother loading them.
var names = [...]string{"abbrev", "info", "str"}
var names = [...]string{"abbrev", "info", "line", "str"}
var dat [len(names)][]byte
for i, name := range names {
name = "__debug_" + name
......@@ -482,8 +482,8 @@ func (f *File) DWARF() (*dwarf.Data, error) {
dat[i] = b
}
abbrev, info, str := dat[0], dat[1], dat[2]
return dwarf.New(abbrev, nil, nil, info, nil, nil, nil, str)
abbrev, info, line, str := dat[0], dat[1], dat[2], dat[3]
return dwarf.New(abbrev, nil, nil, info, line, nil, nil, str)
}
// ImportedSymbols returns the names of all symbols
......
......@@ -2,9 +2,10 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package macho
package macho_test
import (
. "debug/macho"
"reflect"
"testing"
)
......
......@@ -2,10 +2,11 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package binary
package binary_test
import (
"bytes"
. "encoding/binary"
"io"
"math"
"reflect"
......@@ -187,7 +188,7 @@ func BenchmarkReadStruct(b *testing.B) {
bsr := &byteSliceReader{}
var buf bytes.Buffer
Write(&buf, BigEndian, &s)
n := dataSize(reflect.ValueOf(s))
n := DataSize(reflect.ValueOf(s))
b.SetBytes(int64(n))
t := s
b.ResetTimer()
......
// Copyright 2012 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 binary
import "reflect"
// Export for testing.
func DataSize(v reflect.Value) int {
return dataSize(v)
}
var Overflow = overflow
......@@ -2,10 +2,11 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package binary
package binary_test
import (
"bytes"
. "encoding/binary"
"io"
"testing"
)
......@@ -134,8 +135,8 @@ func testOverflow(t *testing.T, buf []byte, n0 int, err0 error) {
}
func TestOverflow(t *testing.T) {
testOverflow(t, []byte{0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x2}, -10, overflow)
testOverflow(t, []byte{0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x1, 0, 0}, -13, overflow)
testOverflow(t, []byte{0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x2}, -10, Overflow)
testOverflow(t, []byte{0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x1, 0, 0}, -13, Overflow)
}
func TestNonCanonicalZero(t *testing.T) {
......
......@@ -14,6 +14,7 @@ package log
import (
"bytes"
_ "debug/elf"
"fmt"
"io"
"os"
......
......@@ -17,9 +17,9 @@ const (
Rdate = `[0-9][0-9][0-9][0-9]/[0-9][0-9]/[0-9][0-9]`
Rtime = `[0-9][0-9]:[0-9][0-9]:[0-9][0-9]`
Rmicroseconds = `\.[0-9][0-9][0-9][0-9][0-9][0-9]`
Rline = `[0-9]+:` // must update if the calls to l.Printf / l.Print below move
Rlongfile = `.*/[A-Za-z0-9_\-]+\.go:|\?\?\?:` + Rline
Rshortfile = `[A-Za-z0-9_\-]+\.go:|\?\?\?:` + Rline
Rline = `(54|56):` // must update if the calls to l.Printf / l.Print below move
Rlongfile = `.*/[A-Za-z0-9_\-]+\.go:` + Rline
Rshortfile = `[A-Za-z0-9_\-]+\.go:` + Rline
)
type tester struct {
......
......@@ -35,6 +35,7 @@ package pprof
import (
"bufio"
"bytes"
_ "debug/elf"
"fmt"
"html/template"
"io"
......
......@@ -6,6 +6,7 @@ package net
import (
"bytes"
_ "debug/elf"
"reflect"
"runtime"
"testing"
......
......@@ -8,6 +8,7 @@ package debug
import (
"bytes"
_ "debug/elf"
"fmt"
"io/ioutil"
"os"
......
......@@ -33,15 +33,7 @@ func Callers(skip int, pc []uintptr) int
type Func struct { // Keep in sync with runtime.h:struct Func
name string
typ string // go type string
src string // src file name
pcln []byte // pc/ln tab for this func
entry uintptr // entry pc
pc0 uintptr // starting pc, ln for table
ln0 int32
frame int32 // stack frame size
args int32 // number of 32-bit in/out args
locals int32 // number of 32-bit locals
}
// FuncForPC returns a *Func describing the function that contains the
......@@ -65,6 +57,10 @@ func (f *Func) FileLine(pc uintptr) (file string, line int) {
// implemented in symtab.c
func funcline_go(*Func, uintptr) (string, int)
// A gccgo specific hook to use debug info to get file/line info.
func RegisterDebugLookup(func(pc uintptr, function *string, file *string, line *int) bool,
func(sym string, val *uintptr) bool)
// mid returns the current os thread (m) id.
func mid() uint32
......
......@@ -11,6 +11,7 @@ package pprof
import (
"bufio"
"bytes"
_ "debug/elf"
"fmt"
"io"
"runtime"
......
......@@ -79,6 +79,7 @@
package testing
import (
_ "debug/elf"
"flag"
"fmt"
"os"
......
......@@ -8,8 +8,64 @@
#include <stdint.h>
#include "runtime.h"
#include "go-string.h"
/* Get the function name, file name, and line number for a PC value.
We use the DWARF debug information to get this. Rather than write
a whole new library in C, we use the existing Go library.
Unfortunately, the Go library is only available if the debug/elf
package is imported (we use debug/elf for both ELF and Mach-O, in
this case). We arrange for the debug/elf package to register
itself, and tweak the various packages that need this information
to import debug/elf where possible. */
/* The function that returns function/file/line information. */
typedef _Bool (*infofn_type) (uintptr_t, struct __go_string *,
struct __go_string *, int *);
static infofn_type infofn;
/* The function that returns the value of a symbol, used to get the
entry address of a function. */
typedef _Bool (*symvalfn_type) (struct __go_string, uintptr_t *);
static symvalfn_type symvalfn;
/* This is called by debug/elf to register the function that returns
function/file/line information. */
void RegisterDebugLookup (infofn_type, symvalfn_type)
__asm__ ("libgo_runtime.runtime.RegisterDebugLookup");
void
RegisterDebugLookup (infofn_type pi, symvalfn_type ps)
{
infofn = pi;
symvalfn = ps;
}
/* Return function/file/line information for PC. */
_Bool
__go_file_line (uintptr_t pc, struct __go_string *fn, struct __go_string *file,
int *line)
{
if (infofn == NULL)
return 0;
return infofn (pc, fn, file, line);
}
/* Return the value of a symbol. */
_Bool
__go_symbol_value (struct __go_string sym, uintptr_t *val)
{
if (symvalfn == NULL)
return 0;
return symvalfn (sym, val);
}
/* The values returned by runtime.Caller. */
struct caller_ret
......@@ -20,32 +76,71 @@ struct caller_ret
_Bool ok;
};
/* Implement runtime.Caller. */
struct caller_ret Caller (int n) asm ("libgo_runtime.runtime.Caller");
Func *FuncForPC (uintptr_t) asm ("libgo_runtime.runtime.FuncForPC");
/* Implement runtime.Caller. */
struct caller_ret
Caller (int n __attribute__ ((unused)))
Caller (int skip)
{
struct caller_ret ret;
uintptr pc;
int32 n;
struct __go_string fn;
/* A proper implementation needs to dig through the debugging
information. */
ret.pc = (uint64_t) (uintptr_t) __builtin_return_address (0);
ret.file.__data = NULL;
ret.file.__length = 0;
ret.line = 0;
ret.ok = 0;
runtime_memclr (&ret, sizeof ret);
n = runtime_callers (skip + 1, &pc, 1);
if (n < 1)
return ret;
ret.pc = pc;
ret.ok = __go_file_line (pc, &fn, &ret.file, &ret.line);
return ret;
}
/* Implement runtime.FuncForPC. */
void *FuncForPC (uintptr_t) asm ("libgo_runtime.runtime.FuncForPC");
void *
FuncForPC(uintptr_t pc __attribute__ ((unused)))
Func *
FuncForPC (uintptr_t pc)
{
Func *ret;
struct __go_string fn;
struct __go_string file;
int line;
uintptr_t val;
if (!__go_file_line (pc, &fn, &file, &line))
return NULL;
if (!__go_symbol_value (fn, &val))
return NULL;
ret = (Func *) runtime_malloc (sizeof (*ret));
ret->name = fn;
ret->entry = val;
return ret;
}
/* Look up the file and line information for a PC within a
function. */
struct funcline_go_return
{
struct __go_string retfile;
int retline;
};
struct funcline_go_return
runtime_funcline_go (Func *f, uintptr targetpc)
__asm__ ("libgo_runtime.runtime.funcline_go");
struct funcline_go_return
runtime_funcline_go (Func *f __attribute__((unused)), uintptr targetpc)
{
struct funcline_go_return ret;
struct __go_string fn;
if (!__go_file_line (targetpc, &fn, &ret.retfile, &ret.retline))
runtime_memclr (&ret, sizeof ret);
return ret;
}
......@@ -25,8 +25,13 @@ backtrace (struct _Unwind_Context *context, void *varg)
{
struct callers_data *arg = (struct callers_data *) varg;
uintptr pc;
int ip_before_insn = 0;
#ifdef HAVE_GETIPINFO
pc = _Unwind_GetIPInfo (context, &ip_before_insn);
#else
pc = _Unwind_GetIP (context);
#endif
/* FIXME: If PC is in the __morestack routine, we should ignore
it. */
......@@ -37,6 +42,11 @@ backtrace (struct _Unwind_Context *context, void *varg)
return _URC_END_OF_STACK;
else
{
/* Here PC will be the return address. We actually want the
address of the call instruction, so back up one byte and
count on the lookup routines handling that correctly. */
if (!ip_before_insn)
--pc;
arg->pcbuf[arg->index] = pc;
++arg->index;
}
......@@ -48,7 +58,7 @@ runtime_callers (int32 skip, uintptr *pcbuf, int32 m)
{
struct callers_data arg;
arg.skip = skip;
arg.skip = skip + 1;
arg.pcbuf = pcbuf;
arg.index = 0;
arg.max = m;
......
......@@ -225,11 +225,7 @@ runtime_MProf_Malloc(void *p, uintptr size)
return;
m->nomemprof++;
#if 0
nstk = runtime_callers(1, stk, 32);
#else
nstk = 0;
#endif
runtime_lock(&proflock);
b = stkbucket(stk, nstk, true);
b->recent_allocs++;
......
......@@ -211,22 +211,3 @@ runtime_cputicks(void)
return 0;
#endif
}
struct funcline_go_return
{
String retfile;
int32 retline;
};
struct funcline_go_return
runtime_funcline_go(void *f, uintptr targetpc)
__asm__("libgo_runtime.runtime.funcline_go");
struct funcline_go_return
runtime_funcline_go(void *f __attribute__((unused)),
uintptr targetpc __attribute__((unused)))
{
struct funcline_go_return ret;
runtime_memclr(&ret, sizeof ret);
return ret;
}
......@@ -48,6 +48,7 @@ typedef unsigned int uintptr __attribute__ ((mode (pointer)));
typedef uint8 bool;
typedef uint8 byte;
typedef struct Func Func;
typedef struct G G;
typedef union Lock Lock;
typedef struct M M;
......@@ -201,6 +202,14 @@ enum
#define NSIG 32
#endif
// NOTE(rsc): keep in sync with extern.go:/type.Func.
// Eventually, the loaded symbol table should be closer to this form.
struct Func
{
String name;
uintptr entry; // entry pc
};
/* Macros. */
#ifdef GOOS_windows
......
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