runtime: check explicitly for short unwinding of stacks

Right now we find out implicitly if stack barriers are in place,
or defers. This change makes sure we find out about short
unwinds always.

Change-Id: Ibdde1ba9c79eb792660dcb7aa6f186e4e4d559b3
Reviewed-on: https://go-review.googlesource.com/13966
Reviewed-by: Austin Clements <austin@google.com>
diff --git a/src/runtime/proc1.go b/src/runtime/proc1.go
index a570816..35d9e86 100644
--- a/src/runtime/proc1.go
+++ b/src/runtime/proc1.go
@@ -988,6 +988,7 @@
 	gp.sched.g = guintptr(unsafe.Pointer(gp))
 	gp.syscallpc = gp.sched.pc
 	gp.syscallsp = gp.sched.sp
+	gp.stktopsp = gp.sched.sp
 	// malg returns status as Gidle, change to Gsyscall before adding to allg
 	// where GC will see it.
 	casgstatus(gp, _Gidle, _Gsyscall)
@@ -2267,6 +2268,7 @@
 
 	memclr(unsafe.Pointer(&newg.sched), unsafe.Sizeof(newg.sched))
 	newg.sched.sp = sp
+	newg.stktopsp = sp
 	newg.sched.pc = funcPC(goexit) + _PCQuantum // +PCQuantum so that previous instruction is in same function
 	newg.sched.g = guintptr(unsafe.Pointer(newg))
 	gostartcallfn(&newg.sched, fn)
diff --git a/src/runtime/runtime2.go b/src/runtime/runtime2.go
index fbd43d2..7d3c8f6 100644
--- a/src/runtime/runtime2.go
+++ b/src/runtime/runtime2.go
@@ -229,6 +229,7 @@
 	syscallpc      uintptr        // if status==Gsyscall, syscallpc = sched.pc to use during gc
 	stkbar         []stkbar       // stack barriers, from low to high
 	stkbarPos      uintptr        // index of lowest stack barrier not hit
+	stktopsp       uintptr        // expected sp at top of stack, to check in traceback
 	param          unsafe.Pointer // passed parameter on wakeup
 	atomicstatus   uint32
 	stackLock      uint32 // sigprof/scang lock; TODO: fold in to atomicstatus
diff --git a/src/runtime/stack1.go b/src/runtime/stack1.go
index 9873bd8..78d168b 100644
--- a/src/runtime/stack1.go
+++ b/src/runtime/stack1.go
@@ -639,6 +639,7 @@
 	oldsize := gp.stackAlloc
 	gp.stackAlloc = newsize
 	gp.stkbar = newstkbar
+	gp.stktopsp += adjinfo.delta
 
 	// free old stack
 	if stackPoisonCopy != 0 {
diff --git a/src/runtime/traceback.go b/src/runtime/traceback.go
index 48ef6e5..1025032 100644
--- a/src/runtime/traceback.go
+++ b/src/runtime/traceback.go
@@ -479,6 +479,12 @@
 		throw("traceback has leftover stack barriers")
 	}
 
+	if callback != nil && n < max && frame.sp != gp.stktopsp {
+		print("runtime: g", gp.goid, ": frame.sp=", hex(frame.sp), " top=", hex(gp.stktopsp), "\n")
+		print("\tstack=[", hex(gp.stack.lo), "-", hex(gp.stack.hi), "] n=", n, " max=", max, "\n")
+		throw("traceback did not unwind completely")
+	}
+
 	return n
 }