1
0
mirror of https://github.com/golang/go synced 2024-10-04 20:21:22 -06:00
go/src/pkg/runtime/traceback_arm.c
Russ Cox b88148b9a0 undo CL 19810043 / 352f3b7c9664
The CL causes misc/cgo/test to fail randomly.
I suspect that the problem is the use of a division instruction
in usleep, which can be called while trying to acquire an m
and therefore cannot store the denominator in m.
The solution to that would be to rewrite the code to use a
magic multiply instead of a divide, but now we're getting
pretty far off the original code.

Go back to the original in preparation for a different,
less efficient but simpler fix.

««« original CL description
cmd/5l, runtime: make ARM integer division profiler-friendly

The implementation of division constructed non-standard
stack frames that could not be handled by the traceback
routines.

CL 13239052 left the frames non-standard but fixed them
for the specific case of a divide-by-zero panic.
A profiling signal can arrive at any time, so that fix
is not sufficient.

Change the division to store the extra argument in the M struct
instead of in a new stack slot. That keeps the frames bog standard
at all times.

Also fix a related bug in the traceback code: when starting
a traceback, the LR register should be ignored if the current
function has already allocated its stack frame and saved the
original LR on the stack. The stack copy should be used, as the
LR register may have been modified.

Combined, these make the torture test from issue 6681 pass.

Fixes #6681.

R=golang-dev, r, josharian
CC=golang-dev
https://golang.org/cl/19810043
»»»

TBR=r
CC=golang-dev
https://golang.org/cl/20350043
2013-10-31 17:18:57 +00:00

247 lines
6.6 KiB
C

// 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.
#include "runtime.h"
#include "arch_GOARCH.h"
#include "malloc.h"
#include "funcdata.h"
void runtime·sigpanic(void);
int32
runtime·gentraceback(uintptr pc0, uintptr sp0, uintptr lr0, G *gp, int32 skip, uintptr *pcbuf, int32 max, void (*callback)(Stkframe*, void*), void *v, bool printall)
{
int32 i, n, nprint, line;
uintptr x, tracepc;
bool waspanic, printing;
Func *f, *flr;
Stkframe frame;
Stktop *stk;
String file;
nprint = 0;
runtime·memclr((byte*)&frame, sizeof frame);
frame.pc = pc0;
frame.lr = lr0;
frame.sp = sp0;
waspanic = false;
printing = pcbuf==nil && callback==nil;
// If the PC is zero, it's likely a nil function call.
// Start in the caller's frame.
if(frame.pc == 0) {
frame.pc = frame.lr;
frame.lr = 0;
}
f = runtime·findfunc(frame.pc);
if(f == nil) {
if(callback != nil) {
runtime·printf("runtime: unknown pc %p\n", frame.pc);
runtime·throw("unknown pc");
}
return 0;
}
frame.fn = f;
n = 0;
stk = (Stktop*)gp->stackbase;
while(n < max) {
// Typically:
// pc is the PC of the running function.
// sp is the stack pointer at that program counter.
// 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)runtime·lessstack) {
// Hit top of stack segment. Unwind to next segment.
frame.pc = stk->gobuf.pc;
frame.sp = stk->gobuf.sp;
frame.lr = 0;
frame.fp = 0;
if(printing && runtime·showframe(nil, gp))
runtime·printf("----- stack segment boundary -----\n");
stk = (Stktop*)stk->stackbase;
f = runtime·findfunc(frame.pc);
if(f == nil) {
runtime·printf("runtime: unknown pc %p after stack split\n", frame.pc);
if(callback != nil)
runtime·throw("unknown pc");
}
frame.fn = f;
continue;
}
f = frame.fn;
// Found an actual function.
// Derive frame pointer and link register.
if(frame.fp == 0)
frame.fp = frame.sp + runtime·funcspdelta(f, frame.pc);
if(runtime·topofstack(f)) {
frame.lr = 0;
flr = nil;
} else {
if(frame.lr == 0)
frame.lr = *(uintptr*)frame.sp;
flr = runtime·findfunc(frame.lr);
if(flr == nil) {
runtime·printf("runtime: unexpected return pc for %s called from %p\n", runtime·funcname(f), frame.lr);
if(callback != nil)
runtime·throw("unknown caller pc");
}
}
frame.varp = (byte*)frame.fp;
// Derive size of arguments.
// Most functions have a fixed-size argument block,
// so we can use metadata about the function f.
// Not all, though: there are some variadic functions
// in package runtime and reflect, and for those we use call-specific
// metadata recorded by f's caller.
if(callback != nil || printing) {
frame.argp = (byte*)frame.fp + sizeof(uintptr);
if(f->args != ArgsSizeUnknown)
frame.arglen = f->args;
else if(flr == nil)
frame.arglen = 0;
else if(frame.lr == (uintptr)runtime·lessstack)
frame.arglen = stk->argsize;
else if((i = runtime·funcarglen(flr, frame.lr)) >= 0)
frame.arglen = i;
else {
runtime·printf("runtime: unknown argument frame size for %s called from %p [%s]\n",
runtime·funcname(f), frame.lr, flr ? runtime·funcname(flr) : "?");
if(callback != nil)
runtime·throw("invalid stack");
frame.arglen = 0;
}
}
if(skip > 0) {
skip--;
goto skipped;
}
if(pcbuf != nil)
pcbuf[n] = frame.pc;
if(callback != nil)
callback(&frame, v);
if(printing) {
if(printall || runtime·showframe(f, gp)) {
// Print during crash.
// main(0x1, 0x2, 0x3)
// /home/rsc/go/src/runtime/x.go:23 +0xf
tracepc = frame.pc; // back up to CALL instruction for funcline.
if(n > 0 && frame.pc > f->entry && !waspanic)
tracepc -= sizeof(uintptr);
runtime·printf("%s(", runtime·funcname(f));
for(i = 0; i < frame.arglen/sizeof(uintptr); i++) {
if(i >= 5) {
runtime·prints(", ...");
break;
}
if(i != 0)
runtime·prints(", ");
runtime·printhex(((uintptr*)frame.argp)[i]);
}
runtime·prints(")\n");
line = runtime·funcline(f, tracepc, &file);
runtime·printf("\t%S:%d", file, line);
if(frame.pc > f->entry)
runtime·printf(" +%p", (uintptr)(frame.pc - f->entry));
if(m->throwing > 0 && gp == m->curg)
runtime·printf(" fp=%p", frame.fp);
runtime·printf("\n");
nprint++;
}
}
n++;
skipped:
waspanic = f->entry == (uintptr)runtime·sigpanic;
// Do not unwind past the bottom of the stack.
if(flr == nil)
break;
// Unwind to next frame.
frame.pc = frame.lr;
frame.fn = flr;
frame.lr = 0;
frame.sp = frame.fp;
frame.fp = 0;
// sighandler saves the lr on stack before faking a call to sigpanic
if(waspanic) {
x = *(uintptr*)frame.sp;
frame.sp += 4;
frame.fn = f = runtime·findfunc(frame.pc);
if(f == nil)
frame.pc = x;
else if(f->frame == 0)
frame.lr = x;
}
}
if(pcbuf == nil && callback == nil)
n = nprint;
return n;
}
void
runtime·printcreatedby(G *gp)
{
int32 line;
uintptr pc, tracepc;
Func *f;
String file;
// Show what created goroutine, except main goroutine (goid 1).
if((pc = gp->gopc) != 0 && (f = runtime·findfunc(pc)) != nil &&
runtime·showframe(f, gp) && gp->goid != 1) {
runtime·printf("created by %s\n", runtime·funcname(f));
tracepc = pc; // back up to CALL instruction for funcline.
if(pc > f->entry)
tracepc -= PCQuantum;
line = runtime·funcline(f, tracepc, &file);
runtime·printf("\t%S:%d", file, line);
if(pc > f->entry)
runtime·printf(" +%p", (uintptr)(pc - f->entry));
runtime·printf("\n");
}
}
void
runtime·traceback(uintptr pc, uintptr sp, uintptr lr, G *gp)
{
if(gp->status == Gsyscall) {
// Override signal registers if blocked in system call.
pc = gp->syscallpc;
sp = gp->syscallsp;
lr = 0;
}
// Print traceback. By default, omits runtime frames.
// If that means we print nothing at all, repeat forcing all frames printed.
if(runtime·gentraceback(pc, sp, lr, gp, 0, nil, 100, nil, nil, false) == 0)
runtime·gentraceback(pc, sp, lr, gp, 0, nil, 100, nil, nil, true);
runtime·printcreatedby(gp);
}
// func caller(n int) (pc uintptr, file string, line int, ok bool)
int32
runtime·callers(int32 skip, uintptr *pcbuf, int32 m)
{
uintptr pc, sp;
sp = runtime·getcallersp(&skip);
pc = (uintptr)runtime·getcallerpc(&skip);
return runtime·gentraceback(pc, sp, 0, g, skip, pcbuf, m, nil, nil, false);
}