libgo: Implement and use runtime.Caller, runtime.Func.FileLine.
authorian <ian@138bc75d-0d04-0410-961f-82ee72b054a4>
Wed, 7 Mar 2012 01:16:20 +0000 (01:16 +0000)
committerian <ian@138bc75d-0d04-0410-961f-82ee72b054a4>
Wed, 7 Mar 2012 01:16:20 +0000 (01:16 +0000)
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@185025 138bc75d-0d04-0410-961f-82ee72b054a4

29 files changed:
libgo/Makefile.am
libgo/Makefile.in
libgo/go/debug/dwarf/const.go
libgo/go/debug/dwarf/entry.go
libgo/go/debug/dwarf/line.go [new file with mode: 0644]
libgo/go/debug/dwarf/line_test.go [new file with mode: 0644]
libgo/go/debug/dwarf/unit.go
libgo/go/debug/elf/elf_test.go
libgo/go/debug/elf/file.go
libgo/go/debug/elf/file_test.go
libgo/go/debug/elf/runtime.go [new file with mode: 0644]
libgo/go/debug/macho/file.go
libgo/go/debug/macho/file_test.go
libgo/go/encoding/binary/binary_test.go
libgo/go/encoding/binary/export_test.go [new file with mode: 0644]
libgo/go/encoding/binary/varint_test.go
libgo/go/log/log.go
libgo/go/log/log_test.go
libgo/go/net/http/pprof/pprof.go
libgo/go/net/ip_test.go
libgo/go/runtime/debug/stack.go
libgo/go/runtime/extern.go
libgo/go/runtime/pprof/pprof.go
libgo/go/testing/testing.go
libgo/runtime/go-caller.c
libgo/runtime/go-callers.c
libgo/runtime/mprof.goc
libgo/runtime/runtime.c
libgo/runtime/runtime.h

index d43f054..99294f1 100644 (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
index 418c787..b57d929 100644 (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 \
index 918b153..5301edc 100644 (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
+)
index 2885d8f..f9a4c1b 100644 (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) {
diff --git a/libgo/go/debug/dwarf/line.go b/libgo/go/debug/dwarf/line.go
new file mode 100644 (file)
index 0000000..091ebe0
--- /dev/null
@@ -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 (file)
index 0000000..2476a6f
--- /dev/null
@@ -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)
+               }
+       }
+}
index c10d75d..931468a 100644 (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) {
index 67b961b..b8cdbcc 100644 (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"
 )
index 184ca83..c2c03d2 100644 (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.
index 98f2723..105b697 100644 (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"
diff --git a/libgo/go/debug/elf/runtime.go b/libgo/go/debug/elf/runtime.go
new file mode 100644 (file)
index 0000000..23e79bf
--- /dev/null
@@ -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
+}
index fa73a31..6577803 100644 (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
index 640225b..ecc6f68 100644 (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"
 )
index ff361b7..dec47a1 100644 (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()
diff --git a/libgo/go/encoding/binary/export_test.go b/libgo/go/encoding/binary/export_test.go
new file mode 100644 (file)
index 0000000..9eae2a9
--- /dev/null
@@ -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
index 9476bd5..f67ca63 100644 (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) {
index a5d88fd..02a407e 100644 (file)
@@ -14,6 +14,7 @@ package log
 
 import (
        "bytes"
+       _ "debug/elf"
        "fmt"
        "io"
        "os"
index 72ebf39..158c3d9 100644 (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 {
index 06fcde1..b8874f3 100644 (file)
@@ -35,6 +35,7 @@ package pprof
 import (
        "bufio"
        "bytes"
+       _ "debug/elf"
        "fmt"
        "html/template"
        "io"
index df647ef..d0e987b 100644 (file)
@@ -6,6 +6,7 @@ package net
 
 import (
        "bytes"
+       _ "debug/elf"
        "reflect"
        "runtime"
        "testing"
index a533a5c..fc74e53 100644 (file)
@@ -8,6 +8,7 @@ package debug
 
 import (
        "bytes"
+       _ "debug/elf"
        "fmt"
        "io/ioutil"
        "os"
index 5fbfe54..2c097b0 100644 (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
 
index f67e8a8..87f17d2 100644 (file)
@@ -11,6 +11,7 @@ package pprof
 import (
        "bufio"
        "bytes"
+       _ "debug/elf"
        "fmt"
        "io"
        "runtime"
index 477d2ac..7072262 100644 (file)
@@ -79,6 +79,7 @@
 package testing
 
 import (
+       _ "debug/elf"
        "flag"
        "fmt"
        "os"
index b18759f..f2bebeb 100644 (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");
+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;
 }
index 0089c67..09556c3 100644 (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;
index e40dd61..c61c65c 100644 (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++;
index 78c865b..d1ce26d 100644 (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;
-}
index 40c59a8..e012e18 100644 (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