summaryrefslogtreecommitdiff
path: root/libgo
diff options
context:
space:
mode:
Diffstat (limited to 'libgo')
-rw-r--r--libgo/Makefile.am4
-rw-r--r--libgo/Makefile.in4
-rw-r--r--libgo/go/debug/dwarf/const.go27
-rw-r--r--libgo/go/debug/dwarf/entry.go9
-rw-r--r--libgo/go/debug/dwarf/line.go416
-rw-r--r--libgo/go/debug/dwarf/line_test.go53
-rw-r--r--libgo/go/debug/dwarf/unit.go10
-rw-r--r--libgo/go/debug/elf/elf_test.go3
-rw-r--r--libgo/go/debug/elf/file.go6
-rw-r--r--libgo/go/debug/elf/file_test.go3
-rw-r--r--libgo/go/debug/elf/runtime.go161
-rw-r--r--libgo/go/debug/macho/file.go6
-rw-r--r--libgo/go/debug/macho/file_test.go3
-rw-r--r--libgo/go/encoding/binary/binary_test.go5
-rw-r--r--libgo/go/encoding/binary/export_test.go15
-rw-r--r--libgo/go/encoding/binary/varint_test.go7
-rw-r--r--libgo/go/log/log.go1
-rw-r--r--libgo/go/log/log_test.go6
-rw-r--r--libgo/go/net/http/pprof/pprof.go1
-rw-r--r--libgo/go/net/ip_test.go1
-rw-r--r--libgo/go/runtime/debug/stack.go1
-rw-r--r--libgo/go/runtime/extern.go16
-rw-r--r--libgo/go/runtime/pprof/pprof.go1
-rw-r--r--libgo/go/testing/testing.go1
-rw-r--r--libgo/runtime/go-caller.c125
-rw-r--r--libgo/runtime/go-callers.c12
-rw-r--r--libgo/runtime/mprof.goc4
-rw-r--r--libgo/runtime/runtime.c19
-rw-r--r--libgo/runtime/runtime.h9
29 files changed, 861 insertions, 68 deletions
diff --git a/libgo/Makefile.am b/libgo/Makefile.am
index d43f0542938..99294f12c12 100644
--- a/libgo/Makefile.am
+++ b/libgo/Makefile.am
@@ -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
diff --git a/libgo/Makefile.in b/libgo/Makefile.in
index 418c7876a31..b57d92919b5 100644
--- a/libgo/Makefile.in
+++ b/libgo/Makefile.in
@@ -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 \
diff --git a/libgo/go/debug/dwarf/const.go b/libgo/go/debug/dwarf/const.go
index 918b153d078..5301edc461a 100644
--- a/libgo/go/debug/dwarf/const.go
+++ b/libgo/go/debug/dwarf/const.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
+)
diff --git a/libgo/go/debug/dwarf/entry.go b/libgo/go/debug/dwarf/entry.go
index 2885d8fa26c..f9a4c1b4119 100644
--- a/libgo/go/debug/dwarf/entry.go
+++ b/libgo/go/debug/dwarf/entry.go
@@ -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) {
diff --git a/libgo/go/debug/dwarf/line.go b/libgo/go/debug/dwarf/line.go
new file mode 100644
index 00000000000..091ebe0d17d
--- /dev/null
+++ b/libgo/go/debug/dwarf/line.go
@@ -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
+}
diff --git a/libgo/go/debug/dwarf/line_test.go b/libgo/go/debug/dwarf/line_test.go
new file mode 100644
index 00000000000..2476a6faf53
--- /dev/null
+++ b/libgo/go/debug/dwarf/line_test.go
@@ -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)
+ }
+ }
+}
diff --git a/libgo/go/debug/dwarf/unit.go b/libgo/go/debug/dwarf/unit.go
index c10d75dbdc9..931468a3718 100644
--- a/libgo/go/debug/dwarf/unit.go
+++ b/libgo/go/debug/dwarf/unit.go
@@ -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) {
diff --git a/libgo/go/debug/elf/elf_test.go b/libgo/go/debug/elf/elf_test.go
index 67b961b5c6c..b8cdbcc7e51 100644
--- a/libgo/go/debug/elf/elf_test.go
+++ b/libgo/go/debug/elf/elf_test.go
@@ -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"
)
diff --git a/libgo/go/debug/elf/file.go b/libgo/go/debug/elf/file.go
index 184ca8375b5..c2c03d2c647 100644
--- a/libgo/go/debug/elf/file.go
+++ b/libgo/go/debug/elf/file.go
@@ -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.
diff --git a/libgo/go/debug/elf/file_test.go b/libgo/go/debug/elf/file_test.go
index 98f2723c86e..105b697a4fb 100644
--- a/libgo/go/debug/elf/file_test.go
+++ b/libgo/go/debug/elf/file_test.go
@@ -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"
diff --git a/libgo/go/debug/elf/runtime.go b/libgo/go/debug/elf/runtime.go
new file mode 100644
index 00000000000..23e79bff5db
--- /dev/null
+++ b/libgo/go/debug/elf/runtime.go
@@ -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
+}
diff --git a/libgo/go/debug/macho/file.go b/libgo/go/debug/macho/file.go
index fa73a315c77..6577803a075 100644
--- a/libgo/go/debug/macho/file.go
+++ b/libgo/go/debug/macho/file.go
@@ -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
diff --git a/libgo/go/debug/macho/file_test.go b/libgo/go/debug/macho/file_test.go
index 640225b3291..ecc6f68a947 100644
--- a/libgo/go/debug/macho/file_test.go
+++ b/libgo/go/debug/macho/file_test.go
@@ -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"
)
diff --git a/libgo/go/encoding/binary/binary_test.go b/libgo/go/encoding/binary/binary_test.go
index ff361b7e379..dec47a1894f 100644
--- a/libgo/go/encoding/binary/binary_test.go
+++ b/libgo/go/encoding/binary/binary_test.go
@@ -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()
diff --git a/libgo/go/encoding/binary/export_test.go b/libgo/go/encoding/binary/export_test.go
new file mode 100644
index 00000000000..9eae2a961fc
--- /dev/null
+++ b/libgo/go/encoding/binary/export_test.go
@@ -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
diff --git a/libgo/go/encoding/binary/varint_test.go b/libgo/go/encoding/binary/varint_test.go
index 9476bd5fb7a..f67ca6321bd 100644
--- a/libgo/go/encoding/binary/varint_test.go
+++ b/libgo/go/encoding/binary/varint_test.go
@@ -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) {
diff --git a/libgo/go/log/log.go b/libgo/go/log/log.go
index a5d88fd9b34..02a407ebc60 100644
--- a/libgo/go/log/log.go
+++ b/libgo/go/log/log.go
@@ -14,6 +14,7 @@ package log
import (
"bytes"
+ _ "debug/elf"
"fmt"
"io"
"os"
diff --git a/libgo/go/log/log_test.go b/libgo/go/log/log_test.go
index 72ebf398eda..158c3d93c7e 100644
--- a/libgo/go/log/log_test.go
+++ b/libgo/go/log/log_test.go
@@ -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 {
diff --git a/libgo/go/net/http/pprof/pprof.go b/libgo/go/net/http/pprof/pprof.go
index 06fcde1447f..b8874f35d2f 100644
--- a/libgo/go/net/http/pprof/pprof.go
+++ b/libgo/go/net/http/pprof/pprof.go
@@ -35,6 +35,7 @@ package pprof
import (
"bufio"
"bytes"
+ _ "debug/elf"
"fmt"
"html/template"
"io"
diff --git a/libgo/go/net/ip_test.go b/libgo/go/net/ip_test.go
index df647ef73c0..d0e987b85f7 100644
--- a/libgo/go/net/ip_test.go
+++ b/libgo/go/net/ip_test.go
@@ -6,6 +6,7 @@ package net
import (
"bytes"
+ _ "debug/elf"
"reflect"
"runtime"
"testing"
diff --git a/libgo/go/runtime/debug/stack.go b/libgo/go/runtime/debug/stack.go
index a533a5c3bf4..fc74e537b75 100644
--- a/libgo/go/runtime/debug/stack.go
+++ b/libgo/go/runtime/debug/stack.go
@@ -8,6 +8,7 @@ package debug
import (
"bytes"
+ _ "debug/elf"
"fmt"
"io/ioutil"
"os"
diff --git a/libgo/go/runtime/extern.go b/libgo/go/runtime/extern.go
index 5fbfe547e46..2c097b0c013 100644
--- a/libgo/go/runtime/extern.go
+++ b/libgo/go/runtime/extern.go
@@ -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
diff --git a/libgo/go/runtime/pprof/pprof.go b/libgo/go/runtime/pprof/pprof.go
index f67e8a8f9ae..87f17d2db12 100644
--- a/libgo/go/runtime/pprof/pprof.go
+++ b/libgo/go/runtime/pprof/pprof.go
@@ -11,6 +11,7 @@ package pprof
import (
"bufio"
"bytes"
+ _ "debug/elf"
"fmt"
"io"
"runtime"
diff --git a/libgo/go/testing/testing.go b/libgo/go/testing/testing.go
index 477d2ac23ae..7072262a91d 100644
--- a/libgo/go/testing/testing.go
+++ b/libgo/go/testing/testing.go
@@ -79,6 +79,7 @@
package testing
import (
+ _ "debug/elf"
"flag"
"fmt"
"os"
diff --git a/libgo/runtime/go-caller.c b/libgo/runtime/go-caller.c
index b18759f2f4b..f2bebeba519 100644
--- a/libgo/runtime/go-caller.c
+++ b/libgo/runtime/go-caller.c
@@ -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");
+Func *
+FuncForPC (uintptr_t pc)
+{
+ Func *ret;
+ struct __go_string fn;
+ struct __go_string file;
+ int line;
+ uintptr_t val;
-void *
-FuncForPC(uintptr_t pc __attribute__ ((unused)))
+ 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
{
- return NULL;
+ 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;
}
diff --git a/libgo/runtime/go-callers.c b/libgo/runtime/go-callers.c
index 0089c67e0cd..09556c33c7a 100644
--- a/libgo/runtime/go-callers.c
+++ b/libgo/runtime/go-callers.c
@@ -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;
diff --git a/libgo/runtime/mprof.goc b/libgo/runtime/mprof.goc
index e40dd61f784..c61c65ccee3 100644
--- a/libgo/runtime/mprof.goc
+++ b/libgo/runtime/mprof.goc
@@ -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++;
diff --git a/libgo/runtime/runtime.c b/libgo/runtime/runtime.c
index 78c865ba175..d1ce26db499 100644
--- a/libgo/runtime/runtime.c
+++ b/libgo/runtime/runtime.c
@@ -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;
-}
diff --git a/libgo/runtime/runtime.h b/libgo/runtime/runtime.h
index 40c59a82777..e012e1833bb 100644
--- a/libgo/runtime/runtime.h
+++ b/libgo/runtime/runtime.h
@@ -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