Commit 97f8386a authored by Russ Cox's avatar Russ Cox

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
parent 4930a8d0
...@@ -85,7 +85,7 @@ type errorCString struct{ cstr unsafe.Pointer } ...@@ -85,7 +85,7 @@ type errorCString struct{ cstr unsafe.Pointer }
func (e errorCString) RuntimeError() {} func (e errorCString) RuntimeError() {}
func (e errorCString) Error() string { func (e errorCString) Error() string {
return "runtime error: " + cstringToGo(e.cstr) return "runtime error: " + gostringnocopy((*byte)(e.cstr))
} }
// For calling from C. // For calling from C.
......
...@@ -75,12 +75,6 @@ of the run-time system. ...@@ -75,12 +75,6 @@ of the run-time system.
*/ */
package runtime 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 // 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 // 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 // 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) { ...@@ -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. // 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 // 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. // 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-- xpc--
} }
line = int(funcline(f, xpc, &file)) line = int(funcline(f, xpc, &file))
...@@ -117,11 +111,6 @@ func Caller(skip int) (pc uintptr, file string, line int, ok bool) { ...@@ -117,11 +111,6 @@ func Caller(skip int) (pc uintptr, file string, line int, ok bool) {
return 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 // 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 // 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 // to skip before recording in pc, with 0 identifying the frame for Callers itself and
......
...@@ -6,6 +6,8 @@ ...@@ -6,6 +6,8 @@
// in Go binaries. It is included by both C and assembly, so it must // 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 // be written using #defines. It is included by the runtime package
// as well as the compilers. // 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_ArgSize 0 /* argument size at CALL instruction */
#define PCDATA_StackMapIndex 1 #define PCDATA_StackMapIndex 1
......
...@@ -120,6 +120,7 @@ runtime·goenvs_unix(void) ...@@ -120,6 +120,7 @@ runtime·goenvs_unix(void)
syscall·envs.cap = n; syscall·envs.cap = n;
} }
#pragma textflag NOSPLIT
Slice Slice
runtime·environ() runtime·environ()
{ {
......
...@@ -145,19 +145,6 @@ type stringStruct struct { ...@@ -145,19 +145,6 @@ type stringStruct struct {
len int 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 { func intstring(v int64) string {
s, b := rawstring(4) s, b := rawstring(4)
n := runetochar(b, rune(v)) n := runetochar(b, rune(v))
......
...@@ -248,19 +248,15 @@ func open(name *byte, mode, perm int32) int32 ...@@ -248,19 +248,15 @@ func open(name *byte, mode, perm int32) int32
//go:noescape //go:noescape
func gotraceback(*bool) int32 func gotraceback(*bool) int32
func funcname(*_func) *byte
func gofuncname(f *_func) string {
return gostringnocopy(funcname(f))
}
const _NoArgs = ^uintptr(0) const _NoArgs = ^uintptr(0)
var newproc, lessstack struct{} // C/assembly functions func newstack()
func newproc()
func funcspdelta(*_func, uintptr) int32 // symtab.c func lessstack()
func funcarglen(*_func, uintptr) int32 // symtab.c func morestack()
const _ArgsSizeUnknown = -0x80000000 // funcdata.h func mstart()
func rt0_go()
func sigpanic()
// return0 is a stub used to return 0 from deferproc. // return0 is a stub used to return 0 from deferproc.
// It is called at the very end of deferproc to signal // It is called at the very end of deferproc to signal
......
// 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,45 +6,110 @@ package runtime ...@@ -6,45 +6,110 @@ package runtime
import "unsafe" import "unsafe"
// FuncForPC returns a *Func describing the function that contains the // NOTE: Func does not expose the actual unexported fields, because we return *Func
// given program counter address, or else nil. // values to users, and we want to keep them from being able to overwrite the data
func FuncForPC(pc uintptr) *Func { // with (say) *f = Func{}.
if len(ftabs) == 0 { // All code operating on a *Func must call raw to get the *_func instead.
return nil
}
if pc < ftabs[0].entry || pc >= ftabs[len(ftabs)-1].entry { // A Func represents a Go function in the running binary.
return nil 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")
} }
// binary search to find func with entry <= pc. // pclntable is all bytes of pclntab symbol.
lo := 0 sp := (*sliceStruct)(unsafe.Pointer(&pclntable))
nf := len(ftabs) - 1 // last entry is sentinel sp.array = unsafe.Pointer(&pclntab)
for nf > 0 { sp.len = int(uintptr(unsafe.Pointer(&epclntab)) - uintptr(unsafe.Pointer(&pclntab)))
n := nf / 2 sp.cap = sp.len
f := &ftabs[lo+n]
if f.entry <= pc && pc < ftabs[lo+n+1].entry { // ftab is lookup table for function by program counter.
return (*Func)(unsafe.Pointer(&pclntable[f.funcoff])) nftab := int(*(*uintptr)(add(unsafe.Pointer(pcln), 8)))
} else if pc < f.entry { p := add(unsafe.Pointer(pcln), 8+ptrSize)
nf = n sp = (*sliceStruct)(unsafe.Pointer(&ftab))
} else { sp.array = p
lo += n + 1 sp.len = nftab + 1
nf -= n + 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")
} }
} }
gothrow("FuncForPC: binary search failed") // file table follows ftab.
return nil 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 {
return (*Func)(unsafe.Pointer(findfunc(pc)))
} }
// Name returns the name of the function. // Name returns the name of the function.
func (f *Func) Name() string { func (f *Func) Name() string {
return cstringToGo(unsafe.Pointer(&pclntable[f.nameoff])) return gofuncname(f.raw())
} }
// Entry returns the entry address of the function. // Entry returns the entry address of the function.
func (f *Func) Entry() uintptr { func (f *Func) Entry() uintptr {
return f.entry return f.raw().entry
} }
// FileLine returns the file name and line number of the // FileLine returns the file name and line number of the
...@@ -52,20 +117,42 @@ func (f *Func) Entry() uintptr { ...@@ -52,20 +117,42 @@ func (f *Func) Entry() uintptr {
// The result will not be accurate if pc is not a program // The result will not be accurate if pc is not a program
// counter within f. // counter within f.
func (f *Func) FileLine(pc uintptr) (file string, line int) { func (f *Func) FileLine(pc uintptr) (file string, line int) {
fileno := int(f.pcvalue(f.pcfile, pc)) // Pass strict=false here, because anyone can call this function,
if fileno == -1 || fileno >= len(filetab) { // and they might just be wrong about targetpc belonging to f.
return "?", 0 line = int(funcline1(f.raw(), pc, &file, false))
return file, line
}
func findfunc(pc uintptr) *_func {
if len(ftab) == 0 {
return nil
} }
line = int(f.pcvalue(f.pcln, pc))
if line == -1 { if pc < ftab[0].entry || pc >= ftab[len(ftab)-1].entry {
return "?", 0 return nil
} }
file = cstringToGo(unsafe.Pointer(&pclntable[filetab[fileno]]))
return file, line // binary search to find func with entry <= pc.
lo := 0
nf := len(ftab) - 1 // last entry is sentinel
for nf > 0 {
n := nf / 2
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 {
lo += n + 1
nf -= n + 1
}
}
gothrow("findfunc: binary search failed")
return nil
} }
// Return associated data value for targetpc in func f. func pcvalue(f *_func, off int32, targetpc uintptr, strict bool) int32 {
func (f *Func) pcvalue(off int32, targetpc uintptr) int32 {
if off == 0 { if off == 0 {
return -1 return -1
} }
...@@ -82,7 +169,93 @@ func (f *Func) pcvalue(off int32, targetpc uintptr) int32 { ...@@ -82,7 +169,93 @@ func (f *Func) pcvalue(off int32, targetpc uintptr) int32 {
return val 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 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. // step advances to the next pc, value pair in the encoded table.
...@@ -98,7 +271,7 @@ func step(p []byte, pc *uintptr, val *int32, first bool) (newp []byte, ok bool) ...@@ -98,7 +271,7 @@ func step(p []byte, pc *uintptr, val *int32, first bool) (newp []byte, ok bool)
} }
vdelta := int32(uvdelta) vdelta := int32(uvdelta)
p, pcdelta := readvarint(p) p, pcdelta := readvarint(p)
*pc += uintptr(pcdelta * pcquantum) *pc += uintptr(pcdelta * _PCQuantum)
*val += vdelta *val += vdelta
return p, true return p, true
} }
...@@ -117,30 +290,3 @@ func readvarint(p []byte) (newp []byte, val uint32) { ...@@ -117,30 +290,3 @@ func readvarint(p []byte) (newp []byte, val uint32) {
} }
return p, v 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" ...@@ -30,10 +30,22 @@ import "unsafe"
const usesLR = GOARCH != "amd64" && GOARCH != "amd64p32" && GOARCH != "386" const usesLR = GOARCH != "amd64" && GOARCH != "amd64p32" && GOARCH != "386"
// jmpdeferPC is the PC at the beginning of the jmpdefer assembly function. var (
// The traceback needs to recognize it on link register architectures. deferprocPC = funcPC(deferproc)
var jmpdeferPC = funcPC(jmpdefer) goexitPC = funcPC(goexit)
var deferprocPC = funcPC(deferproc) 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 // System-specific hook. See traceback_windows.go
var systraceback func(*_func, *stkframe, *g, bool, func(*stkframe, unsafe.Pointer) bool, unsafe.Pointer) (changed, aborted bool) 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 ...@@ -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. // fp is the frame pointer (caller's stack pointer) at that program counter, or nil if unknown.
// stk is the stack containing sp. // stk is the stack containing sp.
// The caller's program counter is lr, unless lr is zero, in which case it is *(uintptr*)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. // Hit top of stack segment. Unwind to next segment.
frame.pc = stk.gobuf.pc frame.pc = stk.gobuf.pc
frame.sp = stk.gobuf.sp frame.sp = stk.gobuf.sp
...@@ -213,7 +225,7 @@ func gentraceback(pc0 uintptr, sp0 uintptr, lr0 uintptr, gp *g, skip int, pcbuf ...@@ -213,7 +225,7 @@ func gentraceback(pc0 uintptr, sp0 uintptr, lr0 uintptr, gp *g, skip int, pcbuf
frame.arglen = uintptr(f.args) frame.arglen = uintptr(f.args)
} else if flr == nil { } else if flr == nil {
frame.arglen = 0 frame.arglen = 0
} else if frame.lr == uintptr(unsafe.Pointer(&lessstack)) { } else if frame.lr == lessstackPC {
frame.arglen = uintptr(stk.argsize) frame.arglen = uintptr(stk.argsize)
} else { } else {
i := funcarglen(flr, frame.lr) i := funcarglen(flr, frame.lr)
...@@ -342,8 +354,8 @@ func gentraceback(pc0 uintptr, sp0 uintptr, lr0 uintptr, gp *g, skip int, pcbuf ...@@ -342,8 +354,8 @@ func gentraceback(pc0 uintptr, sp0 uintptr, lr0 uintptr, gp *g, skip int, pcbuf
n++ n++
skipped: skipped:
waspanic = f.entry == uintptr(unsafe.Pointer(&sigpanic)) waspanic = f.entry == sigpanicPC
wasnewproc = f.entry == uintptr(unsafe.Pointer(&newproc)) || f.entry == deferprocPC wasnewproc = f.entry == newprocPC || f.entry == deferprocPC
// Do not unwind past the bottom of the stack. // Do not unwind past the bottom of the stack.
if flr == nil { if flr == nil {
...@@ -448,8 +460,6 @@ func gentraceback(pc0 uintptr, sp0 uintptr, lr0 uintptr, gp *g, skip int, pcbuf ...@@ -448,8 +460,6 @@ func gentraceback(pc0 uintptr, sp0 uintptr, lr0 uintptr, gp *g, skip int, pcbuf
return n return n
} }
func showframe(*_func, *g) bool
func printcreatedby(gp *g) { func printcreatedby(gp *g) {
// Show what created goroutine, except main goroutine (goid 1). // Show what created goroutine, except main goroutine (goid 1).
pc := gp.gopc pc := gp.gopc
...@@ -499,6 +509,40 @@ func gcallers(gp *g, skip int, pcbuf *uintptr, m int) int { ...@@ -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) 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{ var gStatusStrings = [...]string{
_Gidle: "idle", _Gidle: "idle",
_Grunnable: "runnable", _Grunnable: "runnable",
...@@ -583,22 +627,6 @@ func tracebackothers(me *g) { ...@@ -583,22 +627,6 @@ func tracebackothers(me *g) {
unlock(&allglock) 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? // Does f mark the top of a goroutine stack?
func topofstack(f *_func) bool { func topofstack(f *_func) bool {
pc := f.entry pc := f.entry
......
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