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

From-SVN: r185025
This commit is contained in:
Ian Lance Taylor 2012-03-07 01:16:20 +00:00
parent 1f3d0afc26
commit 0effc3f961
29 changed files with 862 additions and 69 deletions

View File

@ -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

View File

@ -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 \

View File

@ -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
)

View File

@ -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) {

View File

@ -0,0 +1,416 @@
// 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.
// DWARF line number information.
package dwarf
import (
"errors"
"path/filepath"
"sort"
"strconv"
)
// A Line holds all the available information about the source code
// corresponding to a specific program counter address.
type Line struct {
Filename string // source file name
OpIndex int // index of operation in VLIW instruction
Line int // line number
Column int // column number
ISA int // instruction set code
Discriminator int // block discriminator
Stmt bool // instruction starts statement
Block bool // instruction starts basic block
EndPrologue bool // instruction ends function prologue
BeginEpilogue bool // instruction begins function epilogue
}
// LineForPc returns the line number information for a program counter
// address, if any. When this returns multiple Line structures in a
// context where only one can be used, the last one is the best.
func (d *Data) LineForPC(pc uint64) ([]*Line, error) {
for i := range d.unit {
u := &d.unit[i]
if u.pc == nil {
if err := d.readUnitLine(i, u); err != nil {
return nil, err
}
}
for _, ar := range u.pc {
if pc >= ar.low && pc < ar.high {
return d.findLine(u, pc)
}
}
}
return nil, nil
}
// readUnitLine reads in the line number information for a compilation
// unit.
func (d *Data) readUnitLine(i int, u *unit) error {
r := d.unitReader(i)
setLineOff := false
for {
e, err := r.Next()
if err != nil {
return err
}
if e == nil {
break
}
if r.unit != i {
break
}
switch e.Tag {
case TagCompileUnit, TagSubprogram, TagEntryPoint, TagInlinedSubroutine:
low, lowok := e.Val(AttrLowpc).(uint64)
high, highok := e.Val(AttrHighpc).(uint64)
if lowok && highok {
u.pc = append(u.pc, addrRange{low, high})
} else if f, ok := e.Val(AttrRanges).(Offset); ok {
// TODO: Handle AttrRanges and .debug_ranges.
_ = f
}
if off, ok := e.Val(AttrStmtList).(int64); ok {
u.lineoff = Offset(off)
setLineOff = true
}
if dir, ok := e.Val(AttrCompDir).(string); ok {
u.dir = dir
}
}
}
if !setLineOff {
u.lineoff = Offset(0)
u.lineoff--
}
return nil
}
// findLine finds the line information for a PC value, given the unit
// containing the information.
func (d *Data) findLine(u *unit, pc uint64) ([]*Line, error) {
if u.lines == nil {
if err := d.parseLine(u); err != nil {
return nil, err
}
}
for _, ln := range u.lines {
if pc < ln.addrs[0].pc || pc > ln.addrs[len(ln.addrs)-1].pc {
continue
}
i := sort.Search(len(ln.addrs),
func(i int) bool { return ln.addrs[i].pc > pc })
i--
p := new(Line)
*p = ln.line
p.Line = ln.addrs[i].line
ret := []*Line{p}
for i++; i < len(ln.addrs) && ln.addrs[i].pc == pc; i++ {
p = new(Line)
*p = ln.line
p.Line = ln.addrs[i].line
ret = append(ret, p)
}
return ret, nil
}
return nil, nil
}
// FileLine returns the file name and line number for a program
// counter address, or "", 0 if unknown.
func (d *Data) FileLine(pc uint64) (string, int, error) {
r, err := d.LineForPC(pc)
if err != nil {
return "", 0, err
}
if r == nil {
return "", 0, nil
}
ln := r[len(r)-1]
return ln.Filename, ln.Line, nil
}
// A mapLineInfo holds the PC values and line numbers associated with
// a single Line structure. This representation is chosen to reduce
// memory usage based on typical debug info.
type mapLineInfo struct {
line Line // line.Line will be zero
addrs lineAddrs // sorted by PC
}
// A list of lines. This will be sorted by PC.
type lineAddrs []oneLineInfo
func (p lineAddrs) Len() int { return len(p) }
func (p lineAddrs) Less(i, j int) bool { return p[i].pc < p[j].pc }
func (p lineAddrs) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
// A oneLineInfo is a single PC and line number.
type oneLineInfo struct {
pc uint64
line int
}
// A lineHdr holds the relevant information from a line number
// program header.
type lineHdr struct {
version uint16 // version of line number encoding
minInsnLen uint8 // minimum instruction length
maxOpsPerInsn uint8 // maximum number of ops per instruction
defStmt bool // initial value of stmt register
lineBase int8 // line adjustment base
lineRange uint8 // line adjustment step
opBase uint8 // base of special opcode values
opLen []uint8 // lengths of standard opcodes
dirs []string // directories
files []string // file names
}
// parseLine parses the line number information for a compilation unit
func (d *Data) parseLine(u *unit) error {
if u.lineoff+1 == 0 {
return errors.New("unknown line offset")
}
b := makeBuf(d, "line", u.lineoff, d.line, u.addrsize)
len := uint64(b.uint32())
offSize := 4
if len == 0xffffffff {
len = b.uint64()
offSize = 8
}
end := b.off + Offset(len)
hdr := d.parseLineHdr(u, &b, offSize)
if b.err == nil {
d.parseLineProgram(u, &b, hdr, end)
}
return b.err
}
// parseLineHdr parses a line number program header.
func (d *Data) parseLineHdr(u *unit, b *buf, offSize int) (hdr lineHdr) {
hdr.version = b.uint16()
if hdr.version < 2 || hdr.version > 4 {
b.error("unsupported DWARF version " + strconv.Itoa(int(hdr.version)))
return
}
b.bytes(offSize) // header length
hdr.minInsnLen = b.uint8()
if hdr.version < 4 {
hdr.maxOpsPerInsn = 1
} else {
hdr.maxOpsPerInsn = b.uint8()
}
if b.uint8() == 0 {
hdr.defStmt = false
} else {
hdr.defStmt = true
}
hdr.lineBase = int8(b.uint8())
hdr.lineRange = b.uint8()
hdr.opBase = b.uint8()
hdr.opLen = b.bytes(int(hdr.opBase - 1))
for d := b.string(); len(d) > 0; d = b.string() {
hdr.dirs = append(hdr.dirs, d)
}
for f := b.string(); len(f) > 0; f = b.string() {
d := b.uint()
if !filepath.IsAbs(f) {
if d > 0 {
if d > uint64(len(hdr.dirs)) {
b.error("DWARF directory index out of range")
return
}
f = filepath.Join(hdr.dirs[d-1], f)
} else if u.dir != "" {
f = filepath.Join(u.dir, f)
}
}
b.uint() // file's last mtime
b.uint() // file length
hdr.files = append(hdr.files, f)
}
return
}
// parseLineProgram parses a line program, adding information to
// d.lineInfo as it goes.
func (d *Data) parseLineProgram(u *unit, b *buf, hdr lineHdr, end Offset) {
address := uint64(0)
line := 1
resetLineInfo := Line{
Filename: "",
OpIndex: 0,
Line: 0,
Column: 0,
ISA: 0,
Discriminator: 0,
Stmt: hdr.defStmt,
Block: false,
EndPrologue: false,
BeginEpilogue: false,
}
if len(hdr.files) > 0 {
resetLineInfo.Filename = hdr.files[0]
}
lineInfo := resetLineInfo
var lines []mapLineInfo
minInsnLen := uint64(hdr.minInsnLen)
maxOpsPerInsn := uint64(hdr.maxOpsPerInsn)
lineBase := int(hdr.lineBase)
lineRange := hdr.lineRange
newLineInfo := true
for b.off < end && b.err == nil {
op := b.uint8()
if op >= hdr.opBase {
// This is a special opcode.
op -= hdr.opBase
advance := uint64(op / hdr.lineRange)
opIndex := uint64(lineInfo.OpIndex)
address += minInsnLen * ((opIndex + advance) / maxOpsPerInsn)
newOpIndex := int((opIndex + advance) % maxOpsPerInsn)
line += lineBase + int(op%lineRange)
if newOpIndex != lineInfo.OpIndex {
lineInfo.OpIndex = newOpIndex
newLineInfo = true
}
lines, lineInfo, newLineInfo = d.addLine(lines, lineInfo, address, line, newLineInfo)
} else if op == LineExtendedOp {
c := b.uint()
op = b.uint8()
switch op {
case LineExtEndSequence:
u.lines = append(u.lines, lines...)
lineInfo = resetLineInfo
lines = nil
case LineExtSetAddress:
address = b.addr()
case LineExtDefineFile:
f := b.string()
d := b.uint()
b.uint() // mtime
b.uint() // length
if d > 0 && !filepath.IsAbs(f) {
if d >= uint64(len(hdr.dirs)) {
b.error("DWARF directory index out of range")
return
}
f = filepath.Join(hdr.dirs[d-1], f)
}
hdr.files = append(hdr.files, f)
case LineExtSetDiscriminator:
lineInfo.Discriminator = int(b.uint())
newLineInfo = true
default:
if c > 0 {
b.bytes(int(c) - 1)
}
}
} else {
switch op {
case LineCopy:
lines, lineInfo, newLineInfo = d.addLine(lines, lineInfo, address, line, newLineInfo)
case LineAdvancePC:
advance := b.uint()
opIndex := uint64(lineInfo.OpIndex)
address += minInsnLen * ((opIndex + advance) / maxOpsPerInsn)
newOpIndex := int((opIndex + advance) % maxOpsPerInsn)
if newOpIndex != lineInfo.OpIndex {
lineInfo.OpIndex = newOpIndex
newLineInfo = true
}
case LineAdvanceLine:
line += int(b.int())
case LineSetFile:
i := b.uint()
if i > uint64(len(hdr.files)) {
b.error("DWARF file number out of range")
return
}
lineInfo.Filename = hdr.files[i]
newLineInfo = true
case LineSetColumn:
lineInfo.Column = int(b.uint())
newLineInfo = true
case LineNegateStmt:
lineInfo.Stmt = !lineInfo.Stmt
newLineInfo = true
case LineSetBasicBlock:
lineInfo.Block = true
newLineInfo = true
case LineConstAddPC:
op = 255 - hdr.opBase
advance := uint64(op / hdr.lineRange)
opIndex := uint64(lineInfo.OpIndex)
address += minInsnLen * ((opIndex + advance) / maxOpsPerInsn)
newOpIndex := int((opIndex + advance) % maxOpsPerInsn)
if newOpIndex != lineInfo.OpIndex {
lineInfo.OpIndex = newOpIndex
newLineInfo = true
}
case LineFixedAdvancePC:
address += uint64(b.uint16())
if lineInfo.OpIndex != 0 {
lineInfo.OpIndex = 0
newLineInfo = true
}
case LineSetPrologueEnd:
lineInfo.EndPrologue = true
newLineInfo = true
case LineSetEpilogueBegin:
lineInfo.BeginEpilogue = true
newLineInfo = true
case LineSetISA:
lineInfo.ISA = int(b.uint())
newLineInfo = true
default:
if int(op) >= len(hdr.opLen) {
b.error("DWARF line opcode has unknown length")
return
}
for i := hdr.opLen[op-1]; i > 0; i-- {
b.int()
}
}
}
}
}
// addLine adds the current address and line to lines using lineInfo.
// If newLineInfo is true this is a new lineInfo. This returns the
// updated lines, lineInfo, and newLineInfo.
func (d *Data) addLine(lines []mapLineInfo, lineInfo Line, address uint64, line int, newLineInfo bool) ([]mapLineInfo, Line, bool) {
if newLineInfo {
if len(lines) > 0 {
sort.Sort(lines[len(lines)-1].addrs)
}
lines = append(lines, mapLineInfo{line: lineInfo})
}
p := &lines[len(lines)-1]
p.addrs = append(p.addrs, oneLineInfo{address, line})
if lineInfo.Block || lineInfo.EndPrologue || lineInfo.BeginEpilogue || lineInfo.Discriminator != 0 {
lineInfo.Block = false
lineInfo.EndPrologue = false
lineInfo.BeginEpilogue = false
lineInfo.Discriminator = 0
newLineInfo = true
} else {
newLineInfo = false
}
return lines, lineInfo, newLineInfo
}

View File

@ -0,0 +1,53 @@
// 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)
}
}
}

View File

@ -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) {

View File

@ -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"
)

View File

@ -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.

View File

@ -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"

View File

@ -0,0 +1,161 @@
// 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
}

View File

@ -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

View File

@ -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"
)

View File

@ -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()

View File

@ -0,0 +1,15 @@
// 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

View File

@ -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) {

View File

@ -14,6 +14,7 @@ package log
import (
"bytes"
_ "debug/elf"
"fmt"
"io"
"os"

View File

@ -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 {

View File

@ -35,6 +35,7 @@ package pprof
import (
"bufio"
"bytes"
_ "debug/elf"
"fmt"
"html/template"
"io"

View File

@ -6,6 +6,7 @@ package net
import (
"bytes"
_ "debug/elf"
"reflect"
"runtime"
"testing"

View File

@ -8,6 +8,7 @@ package debug
import (
"bytes"
_ "debug/elf"
"fmt"
"io/ioutil"
"os"

View File

@ -32,16 +32,8 @@ func Caller(skip int) (pc uintptr, file string, line int, ok bool)
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
name string
entry uintptr // entry pc
}
// 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

View File

@ -11,6 +11,7 @@ package pprof
import (
"bufio"
"bytes"
_ "debug/elf"
"fmt"
"io"
"runtime"

View File

@ -79,6 +79,7 @@
package testing
import (
_ "debug/elf"
"flag"
"fmt"
"os"

View File

@ -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)
{
return NULL;
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;
}

View File

@ -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;

View File

@ -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++;

View File

@ -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;
}

View File

@ -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