mirror of
https://github.com/golang/go
synced 2024-11-19 03:04:42 -07:00
runtime: add Go symtab implementation
LGTM=khr R=khr, dvyukov, dave CC=golang-codereviews, rsc https://golang.org/cl/124300044
This commit is contained in:
parent
3d3d539083
commit
0be59730fd
@ -97,37 +97,6 @@ func Caller(skip int) (pc uintptr, file string, line int, ok bool)
|
||||
// It returns the number of entries written to pc.
|
||||
func Callers(skip int, pc []uintptr) int
|
||||
|
||||
type Func struct {
|
||||
opaque struct{} // unexported field to disallow conversions
|
||||
}
|
||||
|
||||
// FuncForPC returns a *Func describing the function that contains the
|
||||
// given program counter address, or else nil.
|
||||
func FuncForPC(pc uintptr) *Func
|
||||
|
||||
// Name returns the name of the function.
|
||||
func (f *Func) Name() string {
|
||||
return funcname_go(f)
|
||||
}
|
||||
|
||||
// Entry returns the entry address of the function.
|
||||
func (f *Func) Entry() uintptr {
|
||||
return funcentry_go(f)
|
||||
}
|
||||
|
||||
// FileLine returns the file name and line number of the
|
||||
// source code corresponding to the program counter pc.
|
||||
// The result will not be accurate if pc is not a program
|
||||
// counter within f.
|
||||
func (f *Func) FileLine(pc uintptr) (file string, line int) {
|
||||
return funcline_go(f, pc)
|
||||
}
|
||||
|
||||
// implemented in symtab.c
|
||||
func funcline_go(*Func, uintptr) (string, int)
|
||||
func funcname_go(*Func) string
|
||||
func funcentry_go(*Func) uintptr
|
||||
|
||||
func getgoroot() string
|
||||
|
||||
// GOROOT returns the root of the Go tree.
|
||||
|
@ -447,7 +447,7 @@ enum
|
||||
// Layout of in-memory per-function information prepared by linker
|
||||
// See http://golang.org/s/go12symtab.
|
||||
// Keep in sync with linker and with ../../libmach/sym.c
|
||||
// and with package debug/gosym.
|
||||
// and with package debug/gosym and with symtab.go in package runtime.
|
||||
struct Func
|
||||
{
|
||||
uintptr entry; // start pc
|
||||
|
@ -5,7 +5,6 @@
|
||||
// Runtime symbol table parsing.
|
||||
// See http://golang.org/s/go12symtab for an overview.
|
||||
|
||||
package runtime
|
||||
#include "runtime.h"
|
||||
#include "defs_GOOS_GOARCH.h"
|
||||
#include "os_GOOS.h"
|
||||
@ -23,9 +22,14 @@ struct Ftab
|
||||
extern byte pclntab[];
|
||||
|
||||
static Ftab *ftab;
|
||||
static uintptr nftab;
|
||||
extern uintptr runtime·nftab;
|
||||
static uint32 *filetab;
|
||||
static uint32 nfiletab;
|
||||
extern uint32 runtime·nfiletab;
|
||||
|
||||
extern uintptr runtime·pclntab;
|
||||
extern uintptr runtime·ftab0;
|
||||
extern uintptr runtime·filetab0;
|
||||
extern uint32 runtime·pcquantum;
|
||||
|
||||
static String end = { (uint8*)"end", 3 };
|
||||
|
||||
@ -43,22 +47,27 @@ runtime·symtabinit(void)
|
||||
runtime·throw("invalid function symbol table\n");
|
||||
}
|
||||
|
||||
nftab = *(uintptr*)(pclntab+8);
|
||||
runtime·nftab = *(uintptr*)(pclntab+8);
|
||||
ftab = (Ftab*)(pclntab+8+sizeof(void*));
|
||||
for(i=0; i<nftab; i++) {
|
||||
// NOTE: ftab[nftab].entry is legal; it is the address beyond the final function.
|
||||
for(i=0; i<runtime·nftab; i++) {
|
||||
// NOTE: ftab[runtime·nftab].entry is legal; it is the address beyond the final function.
|
||||
if(ftab[i].entry > ftab[i+1].entry) {
|
||||
f1 = (Func*)(pclntab + ftab[i].funcoff);
|
||||
f2 = (Func*)(pclntab + ftab[i+1].funcoff);
|
||||
runtime·printf("function symbol table not sorted by program counter: %p %s > %p %s", ftab[i].entry, runtime·funcname(f1), ftab[i+1].entry, i+1 == nftab ? "end" : runtime·funcname(f2));
|
||||
runtime·printf("function symbol table not sorted by program counter: %p %s > %p %s", ftab[i].entry, runtime·funcname(f1), ftab[i+1].entry, i+1 == runtime·nftab ? "end" : runtime·funcname(f2));
|
||||
for(j=0; j<=i; j++)
|
||||
runtime·printf("\t%p %s\n", ftab[j].entry, runtime·funcname((Func*)(pclntab + ftab[j].funcoff)));
|
||||
runtime·throw("invalid runtime symbol table");
|
||||
}
|
||||
}
|
||||
|
||||
filetab = (uint32*)(pclntab + *(uint32*)&ftab[nftab].funcoff);
|
||||
nfiletab = filetab[0];
|
||||
filetab = (uint32*)(pclntab + *(uint32*)&ftab[runtime·nftab].funcoff);
|
||||
runtime·nfiletab = filetab[0];
|
||||
|
||||
runtime·pcquantum = PCQuantum;
|
||||
runtime·pclntab = (uintptr)pclntab;
|
||||
runtime·ftab0 = (uintptr)ftab;
|
||||
runtime·filetab0 = (uintptr)filetab;
|
||||
}
|
||||
|
||||
static uint32
|
||||
@ -187,7 +196,7 @@ funcline(Func *f, uintptr targetpc, String *file, bool strict)
|
||||
*file = unknown;
|
||||
fileno = pcvalue(f, f->pcfile, targetpc, strict);
|
||||
line = pcvalue(f, f->pcln, targetpc, strict);
|
||||
if(fileno == -1 || line == -1 || fileno >= nfiletab) {
|
||||
if(fileno == -1 || line == -1 || fileno >= runtime·nfiletab) {
|
||||
// runtime·printf("looking for %p in %S got file=%d line=%d\n", targetpc, *f->name, fileno, line);
|
||||
return 0;
|
||||
}
|
||||
@ -228,34 +237,20 @@ runtime·funcarglen(Func *f, uintptr targetpc)
|
||||
return runtime·pcdatavalue(f, PCDATA_ArgSize, targetpc-PCQuantum);
|
||||
}
|
||||
|
||||
func funcline_go(f *Func, targetpc uintptr) (retfile String, retline int) {
|
||||
// Pass strict=false here, because anyone can call this function,
|
||||
// and they might just be wrong about targetpc belonging to f.
|
||||
retline = funcline(f, targetpc, &retfile, false);
|
||||
}
|
||||
|
||||
func funcname_go(f *Func) (ret String) {
|
||||
ret = runtime·gostringnocopy((uint8*)runtime·funcname(f));
|
||||
}
|
||||
|
||||
func funcentry_go(f *Func) (ret uintptr) {
|
||||
ret = f->entry;
|
||||
}
|
||||
|
||||
Func*
|
||||
runtime·findfunc(uintptr addr)
|
||||
{
|
||||
Ftab *f;
|
||||
int32 nf, n;
|
||||
|
||||
if(nftab == 0)
|
||||
if(runtime·nftab == 0)
|
||||
return nil;
|
||||
if(addr < ftab[0].entry || addr >= ftab[nftab].entry)
|
||||
if(addr < ftab[0].entry || addr >= ftab[runtime·nftab].entry)
|
||||
return nil;
|
||||
|
||||
// binary search to find func with entry <= addr.
|
||||
f = ftab;
|
||||
nf = nftab;
|
||||
nf = runtime·nftab;
|
||||
while(nf > 0) {
|
||||
n = nf/2;
|
||||
if(f[n].entry <= addr && addr < f[n+1].entry)
|
||||
@ -276,10 +271,6 @@ runtime·findfunc(uintptr addr)
|
||||
return nil;
|
||||
}
|
||||
|
||||
func FuncForPC(pc uintptr) (ret *Func) {
|
||||
ret = runtime·findfunc(pc);
|
||||
}
|
||||
|
||||
static bool
|
||||
hasprefix(String s, int8 *p)
|
||||
{
|
153
src/pkg/runtime/symtab.go
Normal file
153
src/pkg/runtime/symtab.go
Normal file
@ -0,0 +1,153 @@
|
||||
// Copyright 2014 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 runtime
|
||||
|
||||
import "unsafe"
|
||||
|
||||
// FuncForPC returns a *Func describing the function that contains the
|
||||
// given program counter address, or else nil.
|
||||
func FuncForPC(pc uintptr) *Func {
|
||||
if nftab == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if pc < ftabi(0).entry || pc >= ftabi(nftab).entry {
|
||||
return nil
|
||||
}
|
||||
|
||||
// binary search to find func with entry <= pc.
|
||||
lo := uintptr(0)
|
||||
nf := nftab
|
||||
for nf > 0 {
|
||||
n := nf / 2
|
||||
f := ftabi(lo + n)
|
||||
if f.entry <= pc && pc < ftabi(lo+n+1).entry {
|
||||
return (*Func)(unsafe.Pointer(pclntab + f.funcoff))
|
||||
} else if pc < f.entry {
|
||||
nf = n
|
||||
} else {
|
||||
lo += n + 1
|
||||
nf -= n + 1
|
||||
}
|
||||
}
|
||||
|
||||
gothrow("FuncForPC: binary search failed")
|
||||
return nil
|
||||
}
|
||||
|
||||
// Name returns the name of the function.
|
||||
func (f *Func) Name() string {
|
||||
return cstringToGo(pclntab + uintptr(f.nameoff))
|
||||
}
|
||||
|
||||
// Entry returns the entry address of the function.
|
||||
func (f *Func) Entry() uintptr {
|
||||
return f.entry
|
||||
}
|
||||
|
||||
// FileLine returns the file name and line number of the
|
||||
// source code corresponding to the program counter pc.
|
||||
// The result will not be accurate if pc is not a program
|
||||
// counter within f.
|
||||
func (f *Func) FileLine(pc uintptr) (file string, line int) {
|
||||
fileno := f.pcvalue(f.pcfile, pc)
|
||||
if fileno == -1 || fileno >= int32(nfiletab) {
|
||||
return "?", 0
|
||||
}
|
||||
line = int(f.pcvalue(f.pcln, pc))
|
||||
if line == -1 {
|
||||
return "?", 0
|
||||
}
|
||||
file = cstringToGo(pclntab + uintptr(filetabi(uintptr(fileno))))
|
||||
return file, line
|
||||
}
|
||||
|
||||
// Return associated data value for targetpc in func f.
|
||||
func (f *Func) pcvalue(off int32, targetpc uintptr) int32 {
|
||||
if off == 0 {
|
||||
return -1
|
||||
}
|
||||
p := pclntab + uintptr(off)
|
||||
pc := f.entry
|
||||
val := int32(-1)
|
||||
for step(&p, &pc, &val, pc == f.entry) {
|
||||
if targetpc < pc {
|
||||
return val
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// step advances to the next pc, value pair in the encoded table.
|
||||
func step(p *uintptr, pc *uintptr, val *int32, first bool) bool {
|
||||
uvdelta := readvarint(p)
|
||||
if uvdelta == 0 && !first {
|
||||
return false
|
||||
}
|
||||
if uvdelta&1 != 0 {
|
||||
uvdelta = ^(uvdelta >> 1)
|
||||
} else {
|
||||
uvdelta >>= 1
|
||||
}
|
||||
vdelta := int32(uvdelta)
|
||||
pcdelta := readvarint(p) * pcquantum
|
||||
*pc += uintptr(pcdelta)
|
||||
*val += vdelta
|
||||
return true
|
||||
}
|
||||
|
||||
// readvarint reads a varint from *p and advances *p.
|
||||
func readvarint(pp *uintptr) uint32 {
|
||||
var v, shift uint32
|
||||
p := *pp
|
||||
for {
|
||||
b := *(*byte)(unsafe.Pointer(p))
|
||||
p++
|
||||
v |= (uint32(b) & 0x7F) << shift
|
||||
if b&0x80 == 0 {
|
||||
break
|
||||
}
|
||||
shift += 7
|
||||
}
|
||||
*pp = p
|
||||
return v
|
||||
}
|
||||
|
||||
// Populated by runtime·symtabinit during bootstrapping. Treat as immutable.
|
||||
var (
|
||||
pclntab uintptr // address of pclntab
|
||||
ftab0 uintptr // address of first ftab entry
|
||||
nftab uintptr
|
||||
filetab0 uintptr // address of first filetab entry
|
||||
nfiletab uint32
|
||||
pcquantum uint32
|
||||
)
|
||||
|
||||
type Func struct {
|
||||
entry uintptr // start pc
|
||||
nameoff int32 // function name
|
||||
|
||||
args int32 // in/out args size
|
||||
frame int32 // legacy frame size; use pcsp if possible
|
||||
|
||||
pcsp int32
|
||||
pcfile int32
|
||||
pcln int32
|
||||
npcdata int32
|
||||
nfuncdata int32
|
||||
}
|
||||
|
||||
type ftab struct {
|
||||
entry uintptr
|
||||
funcoff uintptr
|
||||
}
|
||||
|
||||
func ftabi(i uintptr) (f ftab) {
|
||||
return *(*ftab)(unsafe.Pointer(ftab0 + i*unsafe.Sizeof(f)))
|
||||
}
|
||||
|
||||
func filetabi(i uintptr) (f uint32) {
|
||||
return *(*uint32)(unsafe.Pointer(filetab0 + i*unsafe.Sizeof(f)))
|
||||
}
|
Loading…
Reference in New Issue
Block a user