runtime: add Callers

cut copies of traceback from 6 to 1.

R=r
CC=golang-dev
https://golang.org/cl/703041
diff --git a/src/pkg/runtime/386/traceback.c b/src/pkg/runtime/386/traceback.c
deleted file mode 100644
index 6b6a7aa..0000000
--- a/src/pkg/runtime/386/traceback.c
+++ /dev/null
@@ -1,149 +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.
-
-#include "runtime.h"
-
-// TODO(rsc): Move this into portable code, with calls to a
-// machine-dependent isclosure() function.
-
-void
-traceback(byte *pc0, byte *sp, G *g)
-{
-	Stktop *stk;
-	uintptr pc, tracepc;
-	int32 i, n;
-	Func *f;
-	byte *p;
-
-	pc = (uintptr)pc0;
-
-	// If the PC is zero, it's likely a nil function call.
-	// Start in the caller's frame.
-	if(pc == 0) {
-		pc = *(uintptr*)sp;
-		sp += sizeof(uintptr);
-	}
-
-	stk = (Stktop*)g->stackbase;
-	for(n=0; n<100; n++) {
-		if(pc == (uint64)·lessstack) {
-			// printf("--\n");
-			// pop to earlier stack block
-			pc = (uintptr)stk->gobuf.pc;
-			sp = stk->gobuf.sp;
-			stk = (Stktop*)stk->stackbase;
-		}
-		p = (byte*)pc;
-		tracepc = pc;
-		if(n > 0 && pc != (uint64)goexit)
-			tracepc--;	// get to CALL instruction
-		f = findfunc(tracepc);
-		if(f == nil) {
-			// dangerous, but poke around to see if it is a closure
-			// ADDL $xxx, SP; RET
-			if(pc > 0x1000 && p[0] == 0x81 && p[1] == 0xc4 && p[6] == 0xc3) {
-				sp += *(uint32*)(p+2) + 8;
-				pc = *(uintptr*)(sp - 8);
-				if(pc <= 0x1000)
-					return;
-				continue;
-			}
-			printf("%p unknown pc\n", pc);
-			return;
-		}
-		if(f->frame < sizeof(uintptr))	// assembly funcs say 0 but lie
-			sp += sizeof(uintptr);
-		else
-			sp += f->frame;
-
-		// print this frame
-		//	main+0xf /home/rsc/go/src/runtime/x.go:23
-		//		main(0x1, 0x2, 0x3)
-		printf("%S", f->name);
-		if(pc > f->entry)
-			printf("+%p", (uintptr)(pc - f->entry));
-		printf(" %S:%d\n", f->src, funcline(f, tracepc));
-		printf("\t%S(", f->name);
-		for(i = 0; i < f->args; i++) {
-			if(i != 0)
-				prints(", ");
-			·printhex(((uint32*)sp)[i]);
-			if(i >= 4) {
-				prints(", ...");
-				break;
-			}
-		}
-		prints(")\n");
-
-		pc = *(uintptr*)(sp-sizeof(uintptr));
-		if(pc <= 0x1000)
-			return;
-	}
-	prints("...\n");
-}
-
-// func caller(n int) (pc uintptr, file string, line int, ok bool)
-void
-·Caller(int32 n, uintptr retpc, String retfile, int32 retline, bool retbool)
-{
-	uintptr pc;
-	byte *sp;
-	byte *p;
-	Stktop *stk;
-	Func *f;
-
-	// our caller's pc, sp.
-	sp = (byte*)&n;
-	pc = *((uintptr*)sp - 1);
-	if((f = findfunc(pc)) == nil) {
-	error:
-		retpc = 0;
-		retline = 0;
-		retfile = emptystring;
-		retbool = false;
-		FLUSH(&retpc);
-		FLUSH(&retfile);
-		FLUSH(&retline);
-		FLUSH(&retbool);
-		return;
-	}
-
-	// now unwind n levels
-	stk = (Stktop*)g->stackbase;
-	while(n-- > 0) {
-		while(pc == (uintptr)·lessstack) {
-			pc = (uintptr)stk->gobuf.pc;
-			sp = stk->gobuf.sp;
-			stk = (Stktop*)stk->stackbase;
-		}
-
-		if(f->frame < sizeof(uintptr))	// assembly functions lie
-			sp += sizeof(uintptr);
-		else
-			sp += f->frame;
-
-	loop:
-		pc = *((uintptr*)sp - 1);
-		if(pc <= 0x1000 || (f = findfunc(pc)) == nil) {
-			// dangerous, but let's try this.
-			// see if it is a closure.
-			p = (byte*)pc;
-			// ADDL $xxx, SP; RET
-			if(pc > 0x1000 && p[0] == 0x81 && p[1] == 0xc4 && p[6] == 0xc3) {
-				sp += *(uint32*)(p+2) + sizeof(uintptr);
-				goto loop;
-			}
-			goto error;
-		}
-	}
-
-	retpc = pc;
-	retfile = f->src;
-	retline = funcline(f, pc-1);
-	retbool = true;
-	FLUSH(&retpc);
-	FLUSH(&retfile);
-	FLUSH(&retline);
-	FLUSH(&retbool);
-}
diff --git a/src/pkg/runtime/Makefile b/src/pkg/runtime/Makefile
index 71ab072..8327709 100644
--- a/src/pkg/runtime/Makefile
+++ b/src/pkg/runtime/Makefile
@@ -149,3 +149,11 @@
 # for discovering offsets inside structs when debugging
 runtime.acid.$(GOARCH): runtime.h proc.c
 	$(QUOTED_GOBIN)/$(CC) $(CFLAGS) -a proc.c >$@
+
+# 386 traceback is really amd64 traceback
+ifeq ($(GOARCH),386)
+
+traceback.$O:	amd64/traceback.c
+	$(QUOTED_GOBIN)/$(CC) $(CFLAGS) $<
+
+endif
diff --git a/src/pkg/runtime/amd64/traceback.c b/src/pkg/runtime/amd64/traceback.c
index 8fe23f3..37c06d0 100644
--- a/src/pkg/runtime/amd64/traceback.c
+++ b/src/pkg/runtime/amd64/traceback.c
@@ -3,144 +3,115 @@
 // license that can be found in the LICENSE file.
 
 #include "runtime.h"
+#include "malloc.h"
 
-void
-traceback(byte *pc0, byte *sp, G *g)
+// This code is also used for the 386 tracebacks.
+// Use uintptr for an appropriate word-sized integer.
+
+// Generic traceback.  Handles runtime stack prints (pcbuf == nil)
+// as well as the runtime.Callers function (pcbuf != nil).
+// A little clunky to merge the two but avoids duplicating
+// the code and all its subtlety.
+static int32
+gentraceback(byte *pc0, byte *sp, G *g, int32 skip, uintptr *pcbuf, int32 m)
 {
-	Stktop *stk;
-	uint64 pc, tracepc;
-	int32 i, n;
-	Func *f;
 	byte *p;
-
-	pc = (uint64)pc0;
+	int32 i, n, iter;
+	uintptr pc, tracepc;
+	Stktop *stk;
+	Func *f;
+	
+	pc = (uintptr)pc0;
 
 	// If the PC is zero, it's likely a nil function call.
 	// Start in the caller's frame.
 	if(pc == 0) {
-		pc = *(uint64*)sp;
-		sp += 8;
+		pc = *(uintptr*)sp;
+		sp += sizeof(uintptr);
 	}
-
+	
+	n = 0;
 	stk = (Stktop*)g->stackbase;
-	for(n=0; n<100; n++) {
-		if(pc == (uint64)·lessstack) {
-			// pop to earlier stack block
-			// printf("-- stack jump %p => %p\n", sp, stk->gobuf.sp);
+	for(iter = 0; iter < 100 && n < m; iter++) {	// iter avoids looping forever
+		if(pc == (uintptr)·lessstack) {
+			// Hit top of stack segment.  Unwind to next segment.
 			pc = (uintptr)stk->gobuf.pc;
 			sp = stk->gobuf.sp;
 			stk = (Stktop*)stk->stackbase;
+			continue;
 		}
-		p = (byte*)pc;
-		tracepc = pc;	// used for line number, function
-		if(n > 0 && pc != (uint64)goexit)
-			tracepc--;	// get to CALL instruction
-		f = findfunc(tracepc);
-		if(f == nil) {
-			// dangerous, but poke around to see if it is a closure
-			// ADDQ $xxx, SP; RET
-			if(p[0] == 0x48 && p[1] == 0x81 && p[2] == 0xc4 && p[7] == 0xc3) {
-				sp += *(uint32*)(p+3) + 8;
-				pc = *(uint64*)(sp - 8);
-				if(pc <= 0x1000)
-					return;
+
+		if(pc <= 0x1000 || (f = findfunc(pc)) == nil) {
+			// Dangerous, but worthwhile: see if this is a closure:
+			//	ADDQ $wwxxyyzz, SP; RET
+			//	[48] 81 c4 zz yy xx ww c3
+			// The 0x48 byte is only on amd64.
+			p = (byte*)pc;
+			if(mheap.min < p && p+8 < mheap.max &&  // pointer in allocated memory
+			   (sizeof(uintptr) != 8 || *p++ == 0x48) &&  // skip 0x48 byte on amd64
+			   p[0] == 0x81 && p[1] == 0xc4 && p[6] == 0xc3) {
+				sp += *(uint32*)(p+2);
+				pc = *(uintptr*)sp;
+				sp += sizeof(uintptr);
 				continue;
 			}
-			printf("%p unknown pc\n", pc);
-			return;
+			// Unknown pc; stop.
+			break;
 		}
-		if(f->frame < sizeof(uintptr))	// assembly funcs say 0 but lie
-			sp += sizeof(uintptr);
-		else
-			sp += f->frame;
 
-		// print this frame
-		//	main+0xf /home/rsc/go/src/runtime/x.go:23
-		//		main(0x1, 0x2, 0x3)
-		printf("%S", f->name);
-		if(pc > f->entry)
-			printf("+%p", (uintptr)(pc - f->entry));
-		printf(" %S:%d\n", f->src, funcline(f, tracepc));
-		printf("\t%S(", f->name);
-		for(i = 0; i < f->args; i++) {
-			if(i != 0)
-				prints(", ");
-			·printhex(((uint32*)sp)[i]);
-			if(i >= 4) {
-				prints(", ...");
-				break;
+		// Found an actual function worth reporting.
+		if(skip > 0)
+			skip--;
+		else if(pcbuf != nil)
+			pcbuf[n++] = pc;
+		else {
+			// Print during crash.
+			//	main+0xf /home/rsc/go/src/runtime/x.go:23
+			//		main(0x1, 0x2, 0x3)
+			printf("%S", f->name);
+			if(pc > f->entry)
+				printf("+%p", (uintptr)(pc - f->entry));
+			tracepc = pc;	// back up to CALL instruction for funcline.
+			if(n > 0 && pc > f->entry)
+				tracepc--;
+			printf(" %S:%d\n", f->src, funcline(f, tracepc));
+			printf("\t%S(", f->name);
+			for(i = 0; i < f->args; i++) {
+				if(i != 0)
+					prints(", ");
+				·printhex(((uintptr*)sp)[i]);
+				if(i >= 4) {
+					prints(", ...");
+					break;
+				}
 			}
+			prints(")\n");
+			n++;
 		}
-		prints(")\n");
-
-		pc = *(uintptr*)(sp-sizeof(uintptr));
-		if(pc <= 0x1000)
-			return;
-	}
-	prints("...\n");
-}
-
-// func caller(n int) (pc uint64, file string, line int, ok bool)
-void
-·Caller(int32 n, uint64 retpc, String retfile, int32 retline, bool retbool)
-{
-	uint64 pc;
-	byte *sp;
-	byte *p;
-	Stktop *stk;
-	Func *f;
-
-	// our caller's pc, sp.
-	sp = (byte*)&n;
-	pc = *(uint64*)(sp-8);
-	if((f = findfunc(pc)) == nil) {
-	error:
-		retpc = 0;
-		retline = 0;
-		retfile = emptystring;
-		retbool = false;
-		FLUSH(&retpc);
-		FLUSH(&retfile);
-		FLUSH(&retline);
-		FLUSH(&retbool);
-		return;
-	}
-
-	// now unwind n levels
-	stk = (Stktop*)g->stackbase;
-	while(n-- > 0) {
-		while(pc == (uintptr)·lessstack) {
-			pc = (uintptr)stk->gobuf.pc;
-			sp = stk->gobuf.sp;
-			stk = (Stktop*)stk->stackbase;
-		}
-
+		
 		if(f->frame < sizeof(uintptr))	// assembly functions lie
 			sp += sizeof(uintptr);
 		else
 			sp += f->frame;
-
-	loop:
 		pc = *((uintptr*)sp - 1);
-		if(pc <= 0x1000 || (f = findfunc(pc)) == nil) {
-			// dangerous, but let's try this.
-			// see if it is a closure.
-			p = (byte*)pc;
-			// ADDQ $xxx, SP; RET
-			if(pc > 0x1000 && p[0] == 0x48 && p[1] == 0x81 && p[2] == 0xc4 && p[7] == 0xc3) {
-				sp += *(uint32*)(p+3) + 8;
-				goto loop;
-			}
-			goto error;
-		}
 	}
+	return n;
+}
 
-	retpc = pc;
-	retfile = f->src;
-	retline = funcline(f, pc-1);
-	retbool = true;
-	FLUSH(&retpc);
-	FLUSH(&retfile);
-	FLUSH(&retline);
-	FLUSH(&retbool);
+void
+traceback(byte *pc0, byte *sp, G *g)
+{
+	gentraceback(pc0, sp, g, 0, nil, 100);
+}
+
+int32
+callers(int32 skip, uintptr *pcbuf, int32 m)
+{
+	byte *pc, *sp;
+
+	// our caller's pc, sp.
+	sp = (byte*)&skip;
+	pc = *(byte**)(sp-sizeof(uintptr));
+
+	return gentraceback(pc, sp, g, skip, pcbuf, m);
 }
diff --git a/src/pkg/runtime/arm/traceback.c b/src/pkg/runtime/arm/traceback.c
index 5c68c15..edddafe 100644
--- a/src/pkg/runtime/arm/traceback.c
+++ b/src/pkg/runtime/arm/traceback.c
@@ -10,138 +10,11 @@
 void
 traceback(byte *pc0, byte *sp, G *g)
 {
-// 	Stktop *stk;
-// 	uintptr pc;
-// 	int32 i, n;
-// 	Func *f;
-// 	byte *p;
-
-// 	pc = (uintptr)pc0;
-
-// 	// If the PC is zero, it's likely a nil function call.
-// 	// Start in the caller's frame.
-// 	if(pc == 0) {
-// 		pc = *(uintptr*)sp;
-// 		sp += sizeof(uintptr);
-// 	}
-
-// 	stk = (Stktop*)g->stackbase;
-// 	for(n=0; n<100; n++) {
-// 		while(pc == (uintptr)retfromnewstack) {
-// 			// pop to earlier stack block
-// 			sp = stk->oldsp;
-// 			stk = (Stktop*)stk->oldbase;
-// 			pc = *(uintptr*)(sp+sizeof(uintptr));
-// 			sp += 2*sizeof(uintptr);	// two irrelevant calls on stack: morestack plus its call
-// 		}
-// 		f = findfunc(pc);
-// 		if(f == nil) {
-// 			// dangerous, but poke around to see if it is a closure
-// 			p = (byte*)pc;
-// 			// ADDL $xxx, SP; RET
-// 			if(p[0] == 0x81 && p[1] == 0xc4 && p[6] == 0xc3) {
-// 				sp += *(uint32*)(p+2) + 8;
-// 				pc = *(uintptr*)(sp - 8);
-// 				if(pc <= 0x1000)
-// 					return;
-// 				continue;
-// 			}
-// 			printf("%p unknown pc\n", pc);
-// 			return;
-// 		}
-// 		if(f->frame < sizeof(uintptr))	// assembly funcs say 0 but lie
-// 			sp += sizeof(uintptr);
-// 		else
-// 			sp += f->frame;
-
-// 		// print this frame
-// 		//	main+0xf /home/rsc/go/src/runtime/x.go:23
-// 		//		main(0x1, 0x2, 0x3)
-// 		printf("%S", f->name);
-// 		if(pc > f->entry)
-// 			printf("+%p", (uintptr)(pc - f->entry));
-// 		printf(" %S:%d\n", f->src, funcline(f, pc-1));	// -1 to get to CALL instr.
-// 		printf("\t%S(", f->name);
-// 		for(i = 0; i < f->args; i++) {
-// 			if(i != 0)
-// 				prints(", ");
-// 			·printhex(((uint32*)sp)[i]);
-// 			if(i >= 4) {
-// 				prints(", ...");
-// 				break;
-// 			}
-// 		}
-// 		prints(")\n");
-
-// 		pc = *(uintptr*)(sp-sizeof(uintptr));
-// 		if(pc <= 0x1000)
-// 			return;
-// 	}
-// 	prints("...\n");
 }
 
 // func caller(n int) (pc uintptr, file string, line int, ok bool)
-void
-·Caller(int32 n, uintptr retpc, String retfile, int32 retline, bool retbool)
+int32
+callers(int32 skip, uintptr *pcbuf, int32 m)
 {
-// 	uintptr pc;
-// 	byte *sp;
-// 	byte *p;
-// 	Stktop *stk;
-// 	Func *f;
-
-// 	// our caller's pc, sp.
-// 	sp = (byte*)&n;
-// 	pc = *((uintptr*)sp - 1);
-// 	if((f = findfunc(pc)) == nil) {
-// 	error:
-// 		retpc = 0;
-// 		retline = 0;
-// 		retfile = emptystring;
-// 		retbool = false;
-// 		FLUSH(&retpc);
-// 		FLUSH(&retfile);
-// 		FLUSH(&retline);
-// 		FLUSH(&retbool);
-// 		return;
-// 	}
-
-// 	// now unwind n levels
-// 	stk = (Stktop*)g->stackbase;
-// 	while(n-- > 0) {
-// 		while(pc == (uintptr)retfromnewstack) {
-// 			sp = stk->oldsp;
-// 			stk = (Stktop*)stk->oldbase;
-// 			pc = *((uintptr*)sp + 1);
-// 			sp += 2*sizeof(uintptr);
-// 		}
-
-// 		if(f->frame < sizeof(uintptr))	// assembly functions lie
-// 			sp += sizeof(uintptr);
-// 		else
-// 			sp += f->frame;
-
-// 	loop:
-// 		pc = *((uintptr*)sp - 1);
-// 		if(pc <= 0x1000 || (f = findfunc(pc)) == nil) {
-// 			// dangerous, but let's try this.
-// 			// see if it is a closure.
-// 			p = (byte*)pc;
-// 			// ADDL $xxx, SP; RET
-// 			if(p[0] == 0x81 && p[1] == 0xc4 && p[6] == 0xc3) {
-// 				sp += *(uint32*)(p+2) + sizeof(uintptr);
-// 				goto loop;
-// 			}
-// 			goto error;
-// 		}
-// 	}
-
-// 	retpc = pc;
-// 	retfile = f->src;
-// 	retline = funcline(f, pc-1);
-// 	retbool = true;
-// 	FLUSH(&retpc);
-// 	FLUSH(&retfile);
-// 	FLUSH(&retline);
-// 	FLUSH(&retbool);
+	return 0;
 }
diff --git a/src/pkg/runtime/extern.go b/src/pkg/runtime/extern.go
index b4d903c..2ee20cd 100644
--- a/src/pkg/runtime/extern.go
+++ b/src/pkg/runtime/extern.go
@@ -10,8 +10,6 @@
 */
 package runtime
 
-// These functions are implemented in the base runtime library, ../../runtime/.
-
 // Gosched yields the processor, allowing other goroutines to run.  It does not
 // suspend the current goroutine, so execution resumes automatically.
 func Gosched()
diff --git a/src/pkg/runtime/runtime.c b/src/pkg/runtime/runtime.c
index ed1bdca..f4882d8 100644
--- a/src/pkg/runtime/runtime.c
+++ b/src/pkg/runtime/runtime.c
@@ -481,3 +481,29 @@
 	gettime(&sec, &usec);
 	return sec*1000000000 + (int64)usec*1000;
 }
+
+void
+·Caller(int32 skip, uintptr retpc, String retfile, int32 retline, bool retbool)
+{
+	Func *f;
+
+	if(callers(skip, &retpc, 1) == 0 || (f = findfunc(retpc-1)) == nil) {
+		retfile = emptystring;
+		retline = 0;
+		retbool = false;
+	} else {
+		retfile = f->src;
+		retline = funcline(f, retpc-1);
+		retbool = true;
+	}
+	FLUSH(&retfile);
+	FLUSH(&retline);
+	FLUSH(&retbool);
+}
+
+void
+·Callers(int32 skip, Slice pc, int32 retn)
+{
+	retn = callers(skip, (uintptr*)pc.array, pc.len);
+	FLUSH(&retn);
+}
diff --git a/src/pkg/runtime/runtime.h b/src/pkg/runtime/runtime.h
index 194503e..622f680 100644
--- a/src/pkg/runtime/runtime.h
+++ b/src/pkg/runtime/runtime.h
@@ -407,6 +407,7 @@
 void	siginit(void);
 bool	sigsend(int32 sig);
 void	gettime(int64*, int32*);
+int32	callers(int32, uintptr*, int32);
 int64	nanotime(void);
 
 #pragma	varargck	argpos	printf	1