Commit f277ebfc authored by Russ Cox's avatar Russ Cox

import debug/gosym from usr/austin/sym

R=austin
DELTA=958  (956 added, 0 deleted, 2 changed)
OCL=34180
CL=34212
parent 3dfbfdab
......@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// This package implements translation between
// unsigned integer values and byte sequences.
package binary
import (
......@@ -12,7 +14,7 @@ import (
)
// A ByteOrder specifies how to convert byte sequences into
// 16-, 32-, or 64-bit integers.
// 16-, 32-, or 64-bit unsigned integers.
type ByteOrder interface {
Uint16(b []byte) uint16;
Uint32(b []byte) uint32;
......
......@@ -2,11 +2,12 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package elf implements access to ELF object files.
package elf
import (
"debug/binary";
"fmt";
"fmt";
"io";
"os";
)
......@@ -66,6 +67,13 @@ type Section struct {
sr *io.SectionReader;
}
// Data reads and returns the contents of the ELF section.
func (s *Section) Data() ([]byte, os.Error) {
dat := make([]byte, s.sr.Size());
n, err := s.sr.ReadAt(dat, 0);
return dat[0:n], err;
}
// Open returns a new ReadSeeker reading the ELF section.
func (s *Section) Open() io.ReadSeeker {
return io.NewSectionReader(s.sr, 0, 1<<63 - 1);
......
// Empty include file to generate z symbols
// EOF
This diff is collapsed.
// Copyright 2009 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.
/*
* Line tables
*/
package gosym
import (
"debug/binary";
"io";
)
type LineTable struct {
Data []byte;
PC uint64;
Line int;
}
// TODO(rsc): Need to pull in quantum from architecture definition.
const quantum = 1;
func (t *LineTable) parse(targetPC uint64, targetLine int) (b []byte, pc uint64, line int) {
// The PC/line table can be thought of as a sequence of
// <pc update>* <line update>
// batches. Each update batch results in a (pc, line) pair,
// where line applies to every PC from pc up to but not
// including the pc of the next pair.
//
// Here we process each update individually, which simplifies
// the code, but makes the corner cases more confusing.
b, pc, line = t.Data, t.PC, t.Line;
for pc <= targetPC && line != targetLine && len(b) > 0 {
code := b[0];
b = b[1:len(b)];
switch {
case code == 0:
if len(b) < 4 {
b = b[0:0];
break;
}
val := binary.BigEndian.Uint32(b);
b = b[4:len(b)];
line += int(val);
case code <= 64:
line += int(code);
case code <= 128:
line -= int(code - 64);
default:
pc += quantum*uint64(code - 128);
continue;
}
pc += quantum;
}
return b, pc, line;
}
func (t *LineTable) slice(pc uint64) *LineTable {
data, pc, line := t.parse(pc, -1);
return &LineTable{data, pc, line};
}
func (t *LineTable) PCToLine(pc uint64) int {
b, pc, line := t.parse(pc, -1);
return line;
}
func (t *LineTable) LineToPC(line int, maxpc uint64) uint64 {
b, pc, line1 := t.parse(maxpc, line);
if line1 != line {
return 0;
}
// Subtract quantum from PC to account for post-line increment
return pc - quantum;
}
// NewLineTable returns a new PC/line table
// corresponding to the encoded data.
// Text must be the start address of the
// corresponding text segment.
func NewLineTable(data []byte, text uint64) *LineTable {
return &LineTable{data, text, 0};
}
// Copyright 2009 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 gosym
import (
"debug/elf";
"exec";
"io";
"os";
"testing";
"syscall";
)
func dotest() bool {
// For now, only works on ELF platforms.
return syscall.OS == "linux" && os.Getenv("GOARCH") == "amd64"
}
func getTable(t *testing.T) *Table {
f, tab := crack(os.Args[0], t);
f.Close();
return tab;
}
func crack(file string, t *testing.T) (*elf.File, *Table) {
// Open self
f, err := elf.Open(file);
if err != nil {
t.Fatal(err);
}
return parse(file, f, t);
}
func parse(file string, f *elf.File, t *testing.T) (*elf.File, *Table) {
symdat, err := f.Section(".gosymtab").Data();
if err != nil {
f.Close();
t.Fatalf("reading %s gosymtab: %v", file, err);
}
pclndat, err := f.Section(".gopclntab").Data();
if err != nil {
f.Close();
t.Fatalf("reading %s gopclntab: %v", file, err);
}
pcln := NewLineTable(pclndat, f.Section(".text").Addr);
tab, err := NewTable(symdat, pcln);
if err != nil {
f.Close();
t.Fatalf("parsing %s gosymtab: %v", file, err);
}
return f, tab;
}
var goarch = os.Getenv("O")
func TestLineFromAline(t *testing.T) {
if !dotest() {
return;
}
tab := getTable(t);
// Find the sym package
pkg := tab.LookupFunc("gosym.TestLineFromAline").Obj;
if pkg == nil {
t.Fatalf("nil pkg");
}
// Walk every absolute line and ensure that we hit every
// source line monotonically
lastline := make(map[string] int);
final := -1;
for i := 0; i < 10000; i++ {
path, line := pkg.lineFromAline(i);
// Check for end of object
if path == "" {
if final == -1 {
final = i - 1;
}
continue;
} else if final != -1 {
t.Fatalf("reached end of package at absolute line %d, but absolute line %d mapped to %s:%d", final, i, path, line);
}
// It's okay to see files multiple times (e.g., sys.a)
if line == 1 {
lastline[path] = 1;
continue;
}
// Check that the is the next line in path
ll, ok := lastline[path];
if !ok {
t.Errorf("file %s starts on line %d", path, line);
} else if line != ll + 1 {
t.Errorf("expected next line of file %s to be %d, got %d", path, ll + 1, line);
}
lastline[path] = line;
}
if final == -1 {
t.Errorf("never reached end of object");
}
}
func TestLineAline(t *testing.T) {
if !dotest() {
return;
}
tab := getTable(t);
for _, o := range tab.Files {
// A source file can appear multiple times in a
// object. alineFromLine will always return alines in
// the first file, so track which lines we've seen.
found := make(map[string] int);
for i := 0; i < 1000; i++ {
path, line := o.lineFromAline(i);
if path == "" {
break;
}
// cgo files are full of 'Z' symbols, which we don't handle
if len(path) > 4 && path[len(path)-4:len(path)] == ".cgo" {
continue;
}
if minline, ok := found[path]; path != "" && ok {
if minline >= line {
// We've already covered this file
continue;
}
}
found[path] = line;
a, err := o.alineFromLine(path, line);
if err != nil {
t.Errorf("absolute line %d in object %s maps to %s:%d, but mapping that back gives error %s", i, o.Paths[0].Name, path, line, err);
} else if a != i {
t.Errorf("absolute line %d in object %s maps to %s:%d, which maps back to absolute line %d\n", i, o.Paths[0].Name, path, line, a);
}
}
}
}
// gotest: if [ "$(uname)-$(uname -m)" = Linux-x86_64 ]; then
// gotest: mkdir -p _test && $AS pclinetest.s && $LD -E main -l -o _test/pclinetest pclinetest.$O
// gotest: fi
func TestPCLine(t *testing.T) {
if !dotest() {
return;
}
f, tab := crack("_test/pclinetest", t);
text := f.Section(".text");
textdat, err := text.Data();
if err != nil {
t.Fatalf("reading .text: %v", err);
}
// Test PCToLine
sym := tab.LookupFunc("linefrompc");
wantLine := 0;
for pc := sym.Entry; pc < sym.End; pc++ {
file, line, fn := tab.PCToLine(pc);
off := pc - text.Addr; // TODO(rsc): should not need off; bug in 8g
wantLine += int(textdat[off]);
if fn == nil {
t.Errorf("failed to get line of PC %#x", pc);
} else if len(file) < 12 || file[len(file)-12:len(file)] != "pclinetest.s" || line != wantLine || fn != sym {
t.Errorf("expected %s:%d (%s) at PC %#x, got %s:%d (%s)", "pclinetest.s", wantLine, sym.Name, pc, file, line, fn.Name);
}
}
// Test LineToPC
sym = tab.LookupFunc("pcfromline");
lookupline := -1;
wantLine = 0;
off := uint64(0); // TODO(rsc): should not need off; bug in 8g
for pc := sym.Value; pc < sym.End; pc += 2 + uint64(textdat[off]) {
file, line, fn := tab.PCToLine(pc);
off = pc-text.Addr;
wantLine += int(textdat[off]);
if line != wantLine {
t.Errorf("expected line %d at PC %#x in pcfromline, got %d", wantLine, pc, line);
off = pc+1-text.Addr;
continue;
}
if lookupline == -1 {
lookupline = line;
}
for ; lookupline <= line; lookupline++ {
pc2, fn2, err := tab.LineToPC(file, lookupline);
if lookupline != line {
// Should be nothing on this line
if err == nil {
t.Errorf("expected no PC at line %d, got %#x (%s)", lookupline, pc2, fn2.Name);
}
} else if err != nil {
t.Errorf("failed to get PC of line %d: %s", lookupline, err);
} else if pc != pc2 {
t.Errorf("expected PC %#x (%s) at line %d, got PC %#x (%s)", pc, fn.Name, line, pc2, fn2.Name);
}
}
off = pc+1-text.Addr;
}
}
This diff is collapsed.
......@@ -321,3 +321,7 @@ func (s *SectionReader) ReadAt(p []byte, off int64) (n int, err os.Error) {
return s.r.ReadAt(p, off);
}
// Size returns the size of the section in bytes.
func (s *SectionReader) Size() int64 {
return s.limit - s.base
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment