mirror of
https://github.com/golang/go
synced 2024-11-18 19:44:46 -07:00
runtime: convert symtab.c into symtab.go
Because symtab.c was partially converted before, the diffs are not terribly useful. The earlier conversion was trying to refactor or clean up the code in addition to doing the translation. It also made a mistake by redefining Func to be something users could overwrite. I undid those changes, making symtab.go a more literal line-for-line translation of symtab.c instead. LGTM=josharian R=golang-codereviews, dave, bradfitz, josharian CC=golang-codereviews, iant, khr, r https://golang.org/cl/140880043
This commit is contained in:
parent
4930a8d058
commit
97f8386af7
@ -85,7 +85,7 @@ type errorCString struct{ cstr unsafe.Pointer }
|
||||
func (e errorCString) RuntimeError() {}
|
||||
|
||||
func (e errorCString) Error() string {
|
||||
return "runtime error: " + cstringToGo(e.cstr)
|
||||
return "runtime error: " + gostringnocopy((*byte)(e.cstr))
|
||||
}
|
||||
|
||||
// For calling from C.
|
||||
|
@ -75,12 +75,6 @@ of the run-time system.
|
||||
*/
|
||||
package runtime
|
||||
|
||||
import "unsafe"
|
||||
|
||||
// sigpanic is the C function sigpanic.
|
||||
// That is, unsafe.Pointer(&sigpanic) is the C function pointer for sigpanic.
|
||||
var sigpanic struct{}
|
||||
|
||||
// Caller reports file and line number information about function invocations on
|
||||
// the calling goroutine's stack. The argument skip is the number of stack frames
|
||||
// to ascend, with 0 identifying the caller of Caller. (For historical reasons the
|
||||
@ -109,7 +103,7 @@ func Caller(skip int) (pc uintptr, file string, line int, ok bool) {
|
||||
// All architectures turn faults into apparent calls to sigpanic.
|
||||
// If we see a call to sigpanic, we do not back up the PC to find
|
||||
// the line number of the call instruction, because there is no call.
|
||||
if xpc > f.entry && (g == nil || g.entry != uintptr(unsafe.Pointer(&sigpanic))) {
|
||||
if xpc > f.entry && (g == nil || g.entry != funcPC(sigpanic)) {
|
||||
xpc--
|
||||
}
|
||||
line = int(funcline(f, xpc, &file))
|
||||
@ -117,11 +111,6 @@ func Caller(skip int) (pc uintptr, file string, line int, ok bool) {
|
||||
return
|
||||
}
|
||||
|
||||
func findfunc(uintptr) *_func
|
||||
|
||||
//go:noescape
|
||||
func funcline(*_func, uintptr, *string) int32
|
||||
|
||||
// Callers fills the slice pc with the program counters of function invocations
|
||||
// on the calling goroutine's stack. The argument skip is the number of stack frames
|
||||
// to skip before recording in pc, with 0 identifying the frame for Callers itself and
|
||||
|
@ -6,6 +6,8 @@
|
||||
// in Go binaries. It is included by both C and assembly, so it must
|
||||
// be written using #defines. It is included by the runtime package
|
||||
// as well as the compilers.
|
||||
//
|
||||
// symtab.go also contains a copy of these constants.
|
||||
|
||||
#define PCDATA_ArgSize 0 /* argument size at CALL instruction */
|
||||
#define PCDATA_StackMapIndex 1
|
||||
|
@ -120,6 +120,7 @@ runtime·goenvs_unix(void)
|
||||
syscall·envs.cap = n;
|
||||
}
|
||||
|
||||
#pragma textflag NOSPLIT
|
||||
Slice
|
||||
runtime·environ()
|
||||
{
|
||||
|
@ -145,19 +145,6 @@ type stringStruct struct {
|
||||
len int
|
||||
}
|
||||
|
||||
func cstringToGo(str unsafe.Pointer) (s string) {
|
||||
i := 0
|
||||
for ; ; i++ {
|
||||
if *(*byte)(unsafe.Pointer(uintptr(str) + uintptr(i))) == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
t := (*stringStruct)(unsafe.Pointer(&s))
|
||||
t.str = unsafe.Pointer(str)
|
||||
t.len = i
|
||||
return
|
||||
}
|
||||
|
||||
func intstring(v int64) string {
|
||||
s, b := rawstring(4)
|
||||
n := runetochar(b, rune(v))
|
||||
|
@ -248,19 +248,15 @@ func open(name *byte, mode, perm int32) int32
|
||||
//go:noescape
|
||||
func gotraceback(*bool) int32
|
||||
|
||||
func funcname(*_func) *byte
|
||||
|
||||
func gofuncname(f *_func) string {
|
||||
return gostringnocopy(funcname(f))
|
||||
}
|
||||
|
||||
const _NoArgs = ^uintptr(0)
|
||||
|
||||
var newproc, lessstack struct{} // C/assembly functions
|
||||
|
||||
func funcspdelta(*_func, uintptr) int32 // symtab.c
|
||||
func funcarglen(*_func, uintptr) int32 // symtab.c
|
||||
const _ArgsSizeUnknown = -0x80000000 // funcdata.h
|
||||
func newstack()
|
||||
func newproc()
|
||||
func lessstack()
|
||||
func morestack()
|
||||
func mstart()
|
||||
func rt0_go()
|
||||
func sigpanic()
|
||||
|
||||
// return0 is a stub used to return 0 from deferproc.
|
||||
// It is called at the very end of deferproc to signal
|
||||
|
@ -1,333 +0,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.
|
||||
|
||||
// Runtime symbol table parsing.
|
||||
// See http://golang.org/s/go12symtab for an overview.
|
||||
|
||||
#include "runtime.h"
|
||||
#include "defs_GOOS_GOARCH.h"
|
||||
#include "os_GOOS.h"
|
||||
#include "arch_GOARCH.h"
|
||||
#include "malloc.h"
|
||||
#include "funcdata.h"
|
||||
|
||||
typedef struct Ftab Ftab;
|
||||
struct Ftab
|
||||
{
|
||||
uintptr entry;
|
||||
uintptr funcoff;
|
||||
};
|
||||
|
||||
extern byte runtime·pclntab[];
|
||||
extern byte runtime·epclntab[];
|
||||
|
||||
static Ftab *ftab;
|
||||
static uintptr runtime·nftab;
|
||||
static uint32 *filetab;
|
||||
static uint32 runtime·nfiletab;
|
||||
|
||||
extern Slice runtime·pclntable;
|
||||
extern Slice runtime·ftabs;
|
||||
extern Slice runtime·filetab;
|
||||
extern uint32 runtime·pcquantum;
|
||||
|
||||
static String end = { (uint8*)"end", 3 };
|
||||
|
||||
void
|
||||
runtime·symtabinit(void)
|
||||
{
|
||||
int32 i, j;
|
||||
Func *f1, *f2;
|
||||
|
||||
// See golang.org/s/go12symtab for header: 0xfffffffb,
|
||||
// two zero bytes, a byte giving the PC quantum,
|
||||
// and a byte giving the pointer width in bytes.
|
||||
if(*(uint32*)runtime·pclntab != 0xfffffffb || runtime·pclntab[4] != 0 || runtime·pclntab[5] != 0 || runtime·pclntab[6] != PCQuantum || runtime·pclntab[7] != sizeof(void*)) {
|
||||
runtime·printf("runtime: function symbol table header: %x %x\n", *(uint32*)runtime·pclntab, *(uint32*)(runtime·pclntab+4));
|
||||
runtime·throw("invalid function symbol table\n");
|
||||
}
|
||||
|
||||
runtime·nftab = *(uintptr*)(runtime·pclntab+8);
|
||||
ftab = (Ftab*)(runtime·pclntab+8+sizeof(void*));
|
||||
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*)(runtime·pclntab + ftab[i].funcoff);
|
||||
f2 = (Func*)(runtime·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 == runtime·nftab ? "end" : runtime·funcname(f2));
|
||||
for(j=0; j<=i; j++)
|
||||
runtime·printf("\t%p %s\n", ftab[j].entry, runtime·funcname((Func*)(runtime·pclntab + ftab[j].funcoff)));
|
||||
runtime·throw("invalid runtime symbol table");
|
||||
}
|
||||
}
|
||||
|
||||
filetab = (uint32*)(runtime·pclntab + *(uint32*)&ftab[runtime·nftab].funcoff);
|
||||
runtime·nfiletab = filetab[0];
|
||||
|
||||
runtime·pcquantum = PCQuantum;
|
||||
|
||||
runtime·pclntable.array = (byte*)runtime·pclntab;
|
||||
runtime·pclntable.len = (byte*)runtime·epclntab - (byte*)runtime·pclntab;
|
||||
runtime·pclntable.cap = runtime·pclntable.len;
|
||||
|
||||
runtime·ftabs.array = (byte*)ftab;
|
||||
runtime·ftabs.len = runtime·nftab+1;
|
||||
runtime·ftabs.cap = runtime·ftabs.len;
|
||||
|
||||
runtime·filetab.array = (byte*)filetab;
|
||||
runtime·filetab.len = filetab[0];
|
||||
runtime·filetab.cap = runtime·filetab.len;
|
||||
}
|
||||
|
||||
static uint32
|
||||
readvarint(byte **pp)
|
||||
{
|
||||
byte *p;
|
||||
uint32 v;
|
||||
int32 shift;
|
||||
|
||||
v = 0;
|
||||
p = *pp;
|
||||
for(shift = 0;; shift += 7) {
|
||||
v |= (*p & 0x7F) << shift;
|
||||
if(!(*p++ & 0x80))
|
||||
break;
|
||||
}
|
||||
*pp = p;
|
||||
return v;
|
||||
}
|
||||
|
||||
void*
|
||||
runtime·funcdata(Func *f, int32 i)
|
||||
{
|
||||
byte *p;
|
||||
|
||||
if(i < 0 || i >= f->nfuncdata)
|
||||
return nil;
|
||||
p = (byte*)&f->nfuncdata + 4 + f->npcdata*4;
|
||||
if(sizeof(void*) == 8 && ((uintptr)p & 4)) {
|
||||
if(((uintptr)f & 4))
|
||||
runtime·printf("misaligned func %p\n", f);
|
||||
p += 4;
|
||||
}
|
||||
return ((void**)p)[i];
|
||||
}
|
||||
|
||||
static bool
|
||||
step(byte **pp, uintptr *pc, int32 *value, bool first)
|
||||
{
|
||||
uint32 uvdelta, pcdelta;
|
||||
int32 vdelta;
|
||||
|
||||
uvdelta = readvarint(pp);
|
||||
if(uvdelta == 0 && !first)
|
||||
return 0;
|
||||
if(uvdelta&1)
|
||||
uvdelta = ~(uvdelta>>1);
|
||||
else
|
||||
uvdelta >>= 1;
|
||||
vdelta = (int32)uvdelta;
|
||||
pcdelta = readvarint(pp) * PCQuantum;
|
||||
*value += vdelta;
|
||||
*pc += pcdelta;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Return associated data value for targetpc in func f.
|
||||
// (Source file is f->src.)
|
||||
static int32
|
||||
pcvalue(Func *f, int32 off, uintptr targetpc, bool strict)
|
||||
{
|
||||
byte *p;
|
||||
uintptr pc;
|
||||
int32 value;
|
||||
|
||||
enum {
|
||||
debug = 0
|
||||
};
|
||||
|
||||
// The table is a delta-encoded sequence of (value, pc) pairs.
|
||||
// Each pair states the given value is in effect up to pc.
|
||||
// The value deltas are signed, zig-zag encoded.
|
||||
// The pc deltas are unsigned.
|
||||
// The starting value is -1, the starting pc is the function entry.
|
||||
// The table ends at a value delta of 0 except in the first pair.
|
||||
if(off == 0)
|
||||
return -1;
|
||||
p = runtime·pclntab + off;
|
||||
pc = f->entry;
|
||||
value = -1;
|
||||
|
||||
if(debug && !runtime·panicking)
|
||||
runtime·printf("pcvalue start f=%s [%p] pc=%p targetpc=%p value=%d tab=%p\n",
|
||||
runtime·funcname(f), f, pc, targetpc, value, p);
|
||||
|
||||
while(step(&p, &pc, &value, pc == f->entry)) {
|
||||
if(debug)
|
||||
runtime·printf("\tvalue=%d until pc=%p\n", value, pc);
|
||||
if(targetpc < pc)
|
||||
return value;
|
||||
}
|
||||
|
||||
// If there was a table, it should have covered all program counters.
|
||||
// If not, something is wrong.
|
||||
if(runtime·panicking || !strict)
|
||||
return -1;
|
||||
runtime·printf("runtime: invalid pc-encoded table f=%s pc=%p targetpc=%p tab=%p\n",
|
||||
runtime·funcname(f), pc, targetpc, p);
|
||||
p = (byte*)f + off;
|
||||
pc = f->entry;
|
||||
value = -1;
|
||||
|
||||
while(step(&p, &pc, &value, pc == f->entry))
|
||||
runtime·printf("\tvalue=%d until pc=%p\n", value, pc);
|
||||
|
||||
runtime·throw("invalid runtime symbol table");
|
||||
return -1;
|
||||
}
|
||||
|
||||
static String unknown = { (uint8*)"?", 1 };
|
||||
|
||||
int8*
|
||||
runtime·funcname(Func *f)
|
||||
{
|
||||
if(f == nil || f->nameoff == 0)
|
||||
return nil;
|
||||
return (int8*)(runtime·pclntab + f->nameoff);
|
||||
}
|
||||
|
||||
static int32
|
||||
funcline(Func *f, uintptr targetpc, String *file, bool strict)
|
||||
{
|
||||
int32 line;
|
||||
int32 fileno;
|
||||
|
||||
*file = unknown;
|
||||
fileno = pcvalue(f, f->pcfile, targetpc, strict);
|
||||
line = pcvalue(f, f->pcln, targetpc, strict);
|
||||
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;
|
||||
}
|
||||
*file = runtime·gostringnocopy(runtime·pclntab + filetab[fileno]);
|
||||
return line;
|
||||
}
|
||||
|
||||
int32
|
||||
runtime·funcline(Func *f, uintptr targetpc, String *file)
|
||||
{
|
||||
return funcline(f, targetpc, file, true);
|
||||
}
|
||||
|
||||
int32
|
||||
runtime·funcspdelta(Func *f, uintptr targetpc)
|
||||
{
|
||||
int32 x;
|
||||
|
||||
x = pcvalue(f, f->pcsp, targetpc, true);
|
||||
if(x&(sizeof(void*)-1))
|
||||
runtime·printf("invalid spdelta %d %d\n", f->pcsp, x);
|
||||
return x;
|
||||
}
|
||||
|
||||
int32
|
||||
runtime·pcdatavalue(Func *f, int32 table, uintptr targetpc)
|
||||
{
|
||||
if(table < 0 || table >= f->npcdata)
|
||||
return -1;
|
||||
return pcvalue(f, (&f->nfuncdata)[1+table], targetpc, true);
|
||||
}
|
||||
|
||||
int32
|
||||
runtime·funcarglen(Func *f, uintptr targetpc)
|
||||
{
|
||||
if(targetpc == f->entry)
|
||||
return 0;
|
||||
return runtime·pcdatavalue(f, PCDATA_ArgSize, targetpc-PCQuantum);
|
||||
}
|
||||
|
||||
Func*
|
||||
runtime·findfunc(uintptr addr)
|
||||
{
|
||||
Ftab *f;
|
||||
int32 nf, n;
|
||||
|
||||
if(runtime·nftab == 0)
|
||||
return nil;
|
||||
if(addr < ftab[0].entry || addr >= ftab[runtime·nftab].entry)
|
||||
return nil;
|
||||
|
||||
// binary search to find func with entry <= addr.
|
||||
f = ftab;
|
||||
nf = runtime·nftab;
|
||||
while(nf > 0) {
|
||||
n = nf/2;
|
||||
if(f[n].entry <= addr && addr < f[n+1].entry)
|
||||
return (Func*)(runtime·pclntab + f[n].funcoff);
|
||||
else if(addr < f[n].entry)
|
||||
nf = n;
|
||||
else {
|
||||
f += n+1;
|
||||
nf -= n+1;
|
||||
}
|
||||
}
|
||||
|
||||
// can't get here -- we already checked above
|
||||
// that the address was in the table bounds.
|
||||
// this can only happen if the table isn't sorted
|
||||
// by address or if the binary search above is buggy.
|
||||
runtime·prints("findfunc unreachable\n");
|
||||
return nil;
|
||||
}
|
||||
|
||||
static bool
|
||||
hasprefix(String s, int8 *p)
|
||||
{
|
||||
int32 i;
|
||||
|
||||
for(i=0; i<s.len; i++) {
|
||||
if(p[i] == 0)
|
||||
return 1;
|
||||
if(p[i] != s.str[i])
|
||||
return 0;
|
||||
}
|
||||
return p[i] == 0;
|
||||
}
|
||||
|
||||
static bool
|
||||
contains(String s, int8 *p)
|
||||
{
|
||||
int32 i;
|
||||
|
||||
if(p[0] == 0)
|
||||
return 1;
|
||||
for(i=0; i<s.len; i++) {
|
||||
if(s.str[i] != p[0])
|
||||
continue;
|
||||
if(hasprefix((String){s.str + i, s.len - i}, p))
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool
|
||||
runtime·showframe(Func *f, G *gp)
|
||||
{
|
||||
static int32 traceback = -1;
|
||||
String name;
|
||||
|
||||
if(g->m->throwing > 0 && gp != nil && (gp == g->m->curg || gp == g->m->caughtsig))
|
||||
return 1;
|
||||
if(traceback < 0)
|
||||
traceback = runtime·gotraceback(nil);
|
||||
name = runtime·gostringnocopy((uint8*)runtime·funcname(f));
|
||||
|
||||
// Special case: always show runtime.panic frame, so that we can
|
||||
// see where a panic started in the middle of a stack trace.
|
||||
// See golang.org/issue/5832.
|
||||
if(name.len == 7+1+5 && hasprefix(name, "runtime.panic"))
|
||||
return 1;
|
||||
|
||||
return traceback > 1 || f != nil && contains(name, ".") && !hasprefix(name, "runtime.");
|
||||
}
|
@ -6,25 +6,140 @@ package runtime
|
||||
|
||||
import "unsafe"
|
||||
|
||||
// NOTE: Func does not expose the actual unexported fields, because we return *Func
|
||||
// values to users, and we want to keep them from being able to overwrite the data
|
||||
// with (say) *f = Func{}.
|
||||
// All code operating on a *Func must call raw to get the *_func instead.
|
||||
|
||||
// A Func represents a Go function in the running binary.
|
||||
type Func struct {
|
||||
opaque struct{} // unexported field to disallow conversions
|
||||
}
|
||||
|
||||
func (f *Func) raw() *_func {
|
||||
return (*_func)(unsafe.Pointer(f))
|
||||
}
|
||||
|
||||
// funcdata.h
|
||||
const (
|
||||
_PCDATA_ArgSize = 0
|
||||
_PCDATA_StackMapIndex = 1
|
||||
_FUNCDATA_ArgsPointerMaps = 0
|
||||
_FUNCDATA_LocalsPointerMaps = 1
|
||||
_FUNCDATA_DeadValueMaps = 2
|
||||
_ArgsSizeUnknown = -0x80000000
|
||||
)
|
||||
|
||||
var (
|
||||
pclntable []byte
|
||||
ftab []functab
|
||||
filetab []uint32
|
||||
|
||||
pclntab, epclntab struct{} // linker symbols
|
||||
)
|
||||
|
||||
type functab struct {
|
||||
entry uintptr
|
||||
funcoff uintptr
|
||||
}
|
||||
|
||||
func symtabinit() {
|
||||
// See golang.org/s/go12symtab for header: 0xfffffffb,
|
||||
// two zero bytes, a byte giving the PC quantum,
|
||||
// and a byte giving the pointer width in bytes.
|
||||
pcln := (*[8]byte)(unsafe.Pointer(&pclntab))
|
||||
pcln32 := (*[2]uint32)(unsafe.Pointer(&pclntab))
|
||||
if pcln32[0] != 0xfffffffb || pcln[4] != 0 || pcln[5] != 0 || pcln[6] != _PCQuantum || pcln[7] != ptrSize {
|
||||
println("runtime: function symbol table header:", hex(pcln32[0]), hex(pcln[4]), hex(pcln[5]), hex(pcln[6]), hex(pcln[7]))
|
||||
gothrow("invalid function symbol table\n")
|
||||
}
|
||||
|
||||
// pclntable is all bytes of pclntab symbol.
|
||||
sp := (*sliceStruct)(unsafe.Pointer(&pclntable))
|
||||
sp.array = unsafe.Pointer(&pclntab)
|
||||
sp.len = int(uintptr(unsafe.Pointer(&epclntab)) - uintptr(unsafe.Pointer(&pclntab)))
|
||||
sp.cap = sp.len
|
||||
|
||||
// ftab is lookup table for function by program counter.
|
||||
nftab := int(*(*uintptr)(add(unsafe.Pointer(pcln), 8)))
|
||||
p := add(unsafe.Pointer(pcln), 8+ptrSize)
|
||||
sp = (*sliceStruct)(unsafe.Pointer(&ftab))
|
||||
sp.array = p
|
||||
sp.len = nftab + 1
|
||||
sp.cap = sp.len
|
||||
for i := 0; i < nftab; i++ {
|
||||
// NOTE: ftab[nftab].entry is legal; it is the address beyond the final function.
|
||||
if ftab[i].entry > ftab[i+1].entry {
|
||||
f1 := (*_func)(unsafe.Pointer(&pclntable[ftab[i].funcoff]))
|
||||
f2 := (*_func)(unsafe.Pointer(&pclntable[ftab[i+1].funcoff]))
|
||||
f2name := "end"
|
||||
if i+1 < nftab {
|
||||
f2name = gofuncname(f2)
|
||||
}
|
||||
println("function symbol table not sorted by program counter:", hex(ftab[i].entry), gofuncname(f1), ">", hex(ftab[i+1].entry), f2name)
|
||||
for j := 0; j <= i; j++ {
|
||||
print("\t", hex(ftab[j].entry), " ", gofuncname((*_func)(unsafe.Pointer(&pclntable[ftab[j].funcoff]))))
|
||||
}
|
||||
gothrow("invalid runtime symbol table")
|
||||
}
|
||||
}
|
||||
|
||||
// file table follows ftab.
|
||||
sp = (*sliceStruct)(unsafe.Pointer(&filetab))
|
||||
p = unsafe.Pointer(add(unsafe.Pointer(pcln), ftab[nftab].funcoff))
|
||||
sp.array = unsafe.Pointer(add(unsafe.Pointer(pcln), ftab[nftab].funcoff))
|
||||
// length is in first element of array.
|
||||
// set len to 1 so we can get first element.
|
||||
sp.len = 1
|
||||
sp.cap = 1
|
||||
sp.len = int(filetab[0])
|
||||
sp.cap = sp.len
|
||||
}
|
||||
|
||||
// FuncForPC returns a *Func describing the function that contains the
|
||||
// given program counter address, or else nil.
|
||||
func FuncForPC(pc uintptr) *Func {
|
||||
if len(ftabs) == 0 {
|
||||
return (*Func)(unsafe.Pointer(findfunc(pc)))
|
||||
}
|
||||
|
||||
// Name returns the name of the function.
|
||||
func (f *Func) Name() string {
|
||||
return gofuncname(f.raw())
|
||||
}
|
||||
|
||||
// Entry returns the entry address of the function.
|
||||
func (f *Func) Entry() uintptr {
|
||||
return f.raw().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) {
|
||||
// Pass strict=false here, because anyone can call this function,
|
||||
// and they might just be wrong about targetpc belonging to f.
|
||||
line = int(funcline1(f.raw(), pc, &file, false))
|
||||
return file, line
|
||||
}
|
||||
|
||||
func findfunc(pc uintptr) *_func {
|
||||
if len(ftab) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if pc < ftabs[0].entry || pc >= ftabs[len(ftabs)-1].entry {
|
||||
if pc < ftab[0].entry || pc >= ftab[len(ftab)-1].entry {
|
||||
return nil
|
||||
}
|
||||
|
||||
// binary search to find func with entry <= pc.
|
||||
lo := 0
|
||||
nf := len(ftabs) - 1 // last entry is sentinel
|
||||
nf := len(ftab) - 1 // last entry is sentinel
|
||||
for nf > 0 {
|
||||
n := nf / 2
|
||||
f := &ftabs[lo+n]
|
||||
if f.entry <= pc && pc < ftabs[lo+n+1].entry {
|
||||
return (*Func)(unsafe.Pointer(&pclntable[f.funcoff]))
|
||||
f := &ftab[lo+n]
|
||||
if f.entry <= pc && pc < ftab[lo+n+1].entry {
|
||||
return (*_func)(unsafe.Pointer(&pclntable[f.funcoff]))
|
||||
} else if pc < f.entry {
|
||||
nf = n
|
||||
} else {
|
||||
@ -33,39 +148,11 @@ func FuncForPC(pc uintptr) *Func {
|
||||
}
|
||||
}
|
||||
|
||||
gothrow("FuncForPC: binary search failed")
|
||||
gothrow("findfunc: binary search failed")
|
||||
return nil
|
||||
}
|
||||
|
||||
// Name returns the name of the function.
|
||||
func (f *Func) Name() string {
|
||||
return cstringToGo(unsafe.Pointer(&pclntable[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 := int(f.pcvalue(f.pcfile, pc))
|
||||
if fileno == -1 || fileno >= len(filetab) {
|
||||
return "?", 0
|
||||
}
|
||||
line = int(f.pcvalue(f.pcln, pc))
|
||||
if line == -1 {
|
||||
return "?", 0
|
||||
}
|
||||
file = cstringToGo(unsafe.Pointer(&pclntable[filetab[fileno]]))
|
||||
return file, line
|
||||
}
|
||||
|
||||
// Return associated data value for targetpc in func f.
|
||||
func (f *Func) pcvalue(off int32, targetpc uintptr) int32 {
|
||||
func pcvalue(f *_func, off int32, targetpc uintptr, strict bool) int32 {
|
||||
if off == 0 {
|
||||
return -1
|
||||
}
|
||||
@ -82,9 +169,95 @@ func (f *Func) pcvalue(off int32, targetpc uintptr) int32 {
|
||||
return val
|
||||
}
|
||||
}
|
||||
|
||||
// If there was a table, it should have covered all program counters.
|
||||
// If not, something is wrong.
|
||||
if panicking != 0 || !strict {
|
||||
return -1
|
||||
}
|
||||
|
||||
print("runtime: invalid pc-encoded table f=", gofuncname(f), " pc=", hex(pc), " targetpc=", hex(targetpc), " tab=", p, "\n")
|
||||
|
||||
p = pclntable[off:]
|
||||
pc = f.entry
|
||||
val = -1
|
||||
for {
|
||||
var ok bool
|
||||
p, ok = step(p, &pc, &val, pc == f.entry)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
print("\tvalue=", val, " until pc=", hex(pc), "\n")
|
||||
}
|
||||
|
||||
gothrow("invalid runtime symbol table")
|
||||
return -1
|
||||
}
|
||||
|
||||
func funcname(f *_func) *byte {
|
||||
if f == nil || f.nameoff == 0 {
|
||||
return nil
|
||||
}
|
||||
return (*byte)(unsafe.Pointer(&pclntable[f.nameoff]))
|
||||
}
|
||||
|
||||
func gofuncname(f *_func) string {
|
||||
return gostringnocopy(funcname(f))
|
||||
}
|
||||
|
||||
func funcline1(f *_func, targetpc uintptr, file *string, strict bool) int32 {
|
||||
*file = "?"
|
||||
fileno := int(pcvalue(f, f.pcfile, targetpc, strict))
|
||||
line := pcvalue(f, f.pcln, targetpc, strict)
|
||||
if fileno == -1 || line == -1 || fileno >= len(filetab) {
|
||||
// print("looking for ", hex(targetpc), " in ", gofuncname(f), " got file=", fileno, " line=", lineno, "\n")
|
||||
return 0
|
||||
}
|
||||
*file = gostringnocopy(&pclntable[filetab[fileno]])
|
||||
return line
|
||||
}
|
||||
|
||||
func funcline(f *_func, targetpc uintptr, file *string) int32 {
|
||||
return funcline1(f, targetpc, file, true)
|
||||
}
|
||||
|
||||
func funcspdelta(f *_func, targetpc uintptr) int32 {
|
||||
x := pcvalue(f, f.pcsp, targetpc, true)
|
||||
if x&(ptrSize-1) != 0 {
|
||||
print("invalid spdelta ", f.pcsp, " ", x, "\n")
|
||||
}
|
||||
return x
|
||||
}
|
||||
|
||||
func pcdatavalue(f *_func, table int32, targetpc uintptr) int32 {
|
||||
if table < 0 || table >= f.npcdata {
|
||||
return -1
|
||||
}
|
||||
off := *(*int32)(add(unsafe.Pointer(&f.nfuncdata), unsafe.Sizeof(f.nfuncdata)+uintptr(table)*4))
|
||||
return pcvalue(f, off, targetpc, true)
|
||||
}
|
||||
|
||||
func funcarglen(f *_func, targetpc uintptr) int32 {
|
||||
if targetpc == f.entry {
|
||||
return 0
|
||||
}
|
||||
return pcdatavalue(f, _PCDATA_ArgSize, targetpc-_PCQuantum)
|
||||
}
|
||||
|
||||
func funcdata(f *_func, i int32) unsafe.Pointer {
|
||||
if i < 0 || i >= f.nfuncdata {
|
||||
return nil
|
||||
}
|
||||
p := add(unsafe.Pointer(&f.nfuncdata), unsafe.Sizeof(f.nfuncdata)+uintptr(f.npcdata)*4)
|
||||
if ptrSize == 8 && uintptr(p)&4 != 0 {
|
||||
if uintptr(unsafe.Pointer(f))&4 != 0 {
|
||||
println("runtime: misaligned func", f)
|
||||
}
|
||||
p = add(p, 4)
|
||||
}
|
||||
return *(*unsafe.Pointer)(add(p, uintptr(i)*ptrSize))
|
||||
}
|
||||
|
||||
// step advances to the next pc, value pair in the encoded table.
|
||||
func step(p []byte, pc *uintptr, val *int32, first bool) (newp []byte, ok bool) {
|
||||
p, uvdelta := readvarint(p)
|
||||
@ -98,7 +271,7 @@ func step(p []byte, pc *uintptr, val *int32, first bool) (newp []byte, ok bool)
|
||||
}
|
||||
vdelta := int32(uvdelta)
|
||||
p, pcdelta := readvarint(p)
|
||||
*pc += uintptr(pcdelta * pcquantum)
|
||||
*pc += uintptr(pcdelta * _PCQuantum)
|
||||
*val += vdelta
|
||||
return p, true
|
||||
}
|
||||
@ -117,30 +290,3 @@ func readvarint(p []byte) (newp []byte, val uint32) {
|
||||
}
|
||||
return p, v
|
||||
}
|
||||
|
||||
// Populated by runtime·symtabinit during bootstrapping. Treat as immutable.
|
||||
var (
|
||||
pclntable []byte
|
||||
ftabs []ftab
|
||||
filetab []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
|
||||
}
|
||||
|
@ -30,10 +30,22 @@ import "unsafe"
|
||||
|
||||
const usesLR = GOARCH != "amd64" && GOARCH != "amd64p32" && GOARCH != "386"
|
||||
|
||||
// jmpdeferPC is the PC at the beginning of the jmpdefer assembly function.
|
||||
// The traceback needs to recognize it on link register architectures.
|
||||
var jmpdeferPC = funcPC(jmpdefer)
|
||||
var deferprocPC = funcPC(deferproc)
|
||||
var (
|
||||
deferprocPC = funcPC(deferproc)
|
||||
goexitPC = funcPC(goexit)
|
||||
jmpdeferPC = funcPC(jmpdefer)
|
||||
lessstackPC = funcPC(lessstack)
|
||||
mcallPC = funcPC(mcall)
|
||||
morestackPC = funcPC(morestack)
|
||||
mstartPC = funcPC(mstart)
|
||||
newprocPC = funcPC(newproc)
|
||||
newstackPC = funcPC(newstack)
|
||||
onMPC = funcPC(onM)
|
||||
rt0_goPC = funcPC(rt0_go)
|
||||
sigpanicPC = funcPC(sigpanic)
|
||||
|
||||
externalthreadhandlerp uintptr // initialized elsewhere
|
||||
)
|
||||
|
||||
// System-specific hook. See traceback_windows.go
|
||||
var systraceback func(*_func, *stkframe, *g, bool, func(*stkframe, unsafe.Pointer) bool, unsafe.Pointer) (changed, aborted bool)
|
||||
@ -112,7 +124,7 @@ func gentraceback(pc0 uintptr, sp0 uintptr, lr0 uintptr, gp *g, skip int, pcbuf
|
||||
// fp is the frame pointer (caller's stack pointer) at that program counter, or nil if unknown.
|
||||
// stk is the stack containing sp.
|
||||
// The caller's program counter is lr, unless lr is zero, in which case it is *(uintptr*)sp.
|
||||
if frame.pc == uintptr(unsafe.Pointer(&lessstack)) {
|
||||
if frame.pc == lessstackPC {
|
||||
// Hit top of stack segment. Unwind to next segment.
|
||||
frame.pc = stk.gobuf.pc
|
||||
frame.sp = stk.gobuf.sp
|
||||
@ -213,7 +225,7 @@ func gentraceback(pc0 uintptr, sp0 uintptr, lr0 uintptr, gp *g, skip int, pcbuf
|
||||
frame.arglen = uintptr(f.args)
|
||||
} else if flr == nil {
|
||||
frame.arglen = 0
|
||||
} else if frame.lr == uintptr(unsafe.Pointer(&lessstack)) {
|
||||
} else if frame.lr == lessstackPC {
|
||||
frame.arglen = uintptr(stk.argsize)
|
||||
} else {
|
||||
i := funcarglen(flr, frame.lr)
|
||||
@ -342,8 +354,8 @@ func gentraceback(pc0 uintptr, sp0 uintptr, lr0 uintptr, gp *g, skip int, pcbuf
|
||||
n++
|
||||
|
||||
skipped:
|
||||
waspanic = f.entry == uintptr(unsafe.Pointer(&sigpanic))
|
||||
wasnewproc = f.entry == uintptr(unsafe.Pointer(&newproc)) || f.entry == deferprocPC
|
||||
waspanic = f.entry == sigpanicPC
|
||||
wasnewproc = f.entry == newprocPC || f.entry == deferprocPC
|
||||
|
||||
// Do not unwind past the bottom of the stack.
|
||||
if flr == nil {
|
||||
@ -448,8 +460,6 @@ func gentraceback(pc0 uintptr, sp0 uintptr, lr0 uintptr, gp *g, skip int, pcbuf
|
||||
return n
|
||||
}
|
||||
|
||||
func showframe(*_func, *g) bool
|
||||
|
||||
func printcreatedby(gp *g) {
|
||||
// Show what created goroutine, except main goroutine (goid 1).
|
||||
pc := gp.gopc
|
||||
@ -499,6 +509,40 @@ func gcallers(gp *g, skip int, pcbuf *uintptr, m int) int {
|
||||
return gentraceback(^uintptr(0), ^uintptr(0), 0, gp, skip, pcbuf, m, nil, nil, false)
|
||||
}
|
||||
|
||||
func showframe(f *_func, gp *g) bool {
|
||||
g := getg()
|
||||
if g.m.throwing > 0 && gp != nil && (gp == g.m.curg || gp == g.m.caughtsig) {
|
||||
return true
|
||||
}
|
||||
traceback := gotraceback(nil)
|
||||
name := gostringnocopy(funcname(f))
|
||||
|
||||
// Special case: always show runtime.panic frame, so that we can
|
||||
// see where a panic started in the middle of a stack trace.
|
||||
// See golang.org/issue/5832.
|
||||
if name == "runtime.panic" {
|
||||
return true
|
||||
}
|
||||
|
||||
return traceback > 1 || f != nil && contains(name, ".") && !hasprefix(name, "runtime.")
|
||||
}
|
||||
|
||||
func contains(s, t string) bool {
|
||||
if len(t) == 0 {
|
||||
return true
|
||||
}
|
||||
for i := 0; i < len(s); i++ {
|
||||
if s[i] == t[0] && hasprefix(s[i:], t) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func hasprefix(s, t string) bool {
|
||||
return len(s) >= len(t) && s[:len(t)] == t
|
||||
}
|
||||
|
||||
var gStatusStrings = [...]string{
|
||||
_Gidle: "idle",
|
||||
_Grunnable: "runnable",
|
||||
@ -583,22 +627,6 @@ func tracebackothers(me *g) {
|
||||
unlock(&allglock)
|
||||
}
|
||||
|
||||
func mstart()
|
||||
func morestack()
|
||||
func rt0_go()
|
||||
|
||||
var (
|
||||
goexitPC = funcPC(goexit)
|
||||
mstartPC = funcPC(mstart)
|
||||
mcallPC = funcPC(mcall)
|
||||
onMPC = funcPC(onM)
|
||||
morestackPC = funcPC(morestack)
|
||||
lessstackPC = funcPC(lessstack)
|
||||
rt0_goPC = funcPC(rt0_go)
|
||||
|
||||
externalthreadhandlerp uintptr // initialized elsewhere
|
||||
)
|
||||
|
||||
// Does f mark the top of a goroutine stack?
|
||||
func topofstack(f *_func) bool {
|
||||
pc := f.entry
|
||||
|
Loading…
Reference in New Issue
Block a user