libgo: Implement and use runtime.Caller, runtime.Func.FileLine.
From-SVN: r185025
This commit is contained in:
parent
1f3d0afc26
commit
0effc3f961
@ -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) {
|
||||
|
416
libgo/go/debug/dwarf/line.go
Normal file
416
libgo/go/debug/dwarf/line.go
Normal 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
|
||||
}
|
53
libgo/go/debug/dwarf/line_test.go
Normal file
53
libgo/go/debug/dwarf/line_test.go
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
@ -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"
|
||||
|
161
libgo/go/debug/elf/runtime.go
Normal file
161
libgo/go/debug/elf/runtime.go
Normal 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
|
||||
}
|
@ -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()
|
||||
|
15
libgo/go/encoding/binary/export_test.go
Normal file
15
libgo/go/encoding/binary/export_test.go
Normal 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
|
@ -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"
|
||||
|
@ -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
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user