ogle/program/server: stop Frames going past the top of the stack.

LGTM=r
R=r
https://golang.org/cl/89190043
diff --git a/program/server/dwarf.go b/program/server/dwarf.go
index 838f621..6733485 100644
--- a/program/server/dwarf.go
+++ b/program/server/dwarf.go
@@ -75,7 +75,7 @@
 }
 
 func (s *Server) lookupPC(pc uint64) (string, error) {
-	entry, err := s.entryForPC(pc)
+	entry, _, err := s.entryForPC(pc)
 	if err != nil {
 		return "", err
 	}
@@ -91,12 +91,13 @@
 	return name, nil
 }
 
-func (s *Server) entryForPC(pc uint64) (*dwarf.Entry, error) {
+func (s *Server) entryForPC(pc uint64) (entry *dwarf.Entry, lowpc uint64, err error) {
+	// TODO: do something better than a linear scan?
 	r := s.dwarfData.Reader()
 	for {
 		entry, err := r.Next()
 		if err != nil {
-			return nil, err
+			return nil, 0, err
 		}
 		if entry == nil {
 			// TODO: why don't we get an error here.
@@ -110,9 +111,9 @@
 		if !lok || !hok || pc < lowpc || highpc <= pc {
 			continue
 		}
-		return entry, nil
+		return entry, lowpc, nil
 	}
-	return nil, fmt.Errorf("PC %#x not found", pc)
+	return nil, 0, fmt.Errorf("PC %#x not found", pc)
 }
 
 func lookupAttr(e *dwarf.Entry, a dwarf.Attr) interface{} {
diff --git a/program/server/ptrace.go b/program/server/ptrace.go
index 0f25e80..9bd3357 100644
--- a/program/server/ptrace.go
+++ b/program/server/ptrace.go
@@ -7,7 +7,7 @@
 import (
 	"fmt"
 	"os"
-	"runtime"
+	rt "runtime"
 	"syscall"
 )
 
@@ -18,7 +18,7 @@
 	if cap(fc) != 0 || cap(ec) != 0 {
 		panic("ptraceRun was given unbuffered channels")
 	}
-	runtime.LockOSThread()
+	rt.LockOSThread()
 	for f := range fc {
 		ec <- f()
 	}
diff --git a/program/server/server.go b/program/server/server.go
index 7ec321d..57824a1 100644
--- a/program/server/server.go
+++ b/program/server/server.go
@@ -47,10 +47,26 @@
 	procIsUp    bool
 	stoppedPid  int
 	stoppedRegs syscall.PtraceRegs
+	runtime     runtime
 	breakpoints map[uint64]breakpoint
 	files       []*file // Index == file descriptor.
 }
 
+// runtime are the addresses, in the target program's address space, of Go
+// runtime functions such as runtime·lessstack.
+type runtime struct {
+	evaluated bool
+	evalErr   error
+
+	goexit                 uint64
+	mstart                 uint64
+	mcall                  uint64
+	morestack              uint64
+	lessstack              uint64
+	_rt0_go                uint64
+	externalthreadhandlerp uint64
+}
+
 // New parses the executable and builds local data structures for answering requests.
 // It returns a Server ready to serve requests about the executable.
 func New(executable string) (*Server, error) {
@@ -224,6 +240,8 @@
 		s.procIsUp = false
 		s.stoppedPid = 0
 		s.stoppedRegs = syscall.PtraceRegs{}
+		s.runtime.evaluated = false
+		s.runtime.evalErr = nil
 	}
 	p, err := s.startProcess(s.executable, nil, &os.ProcAttr{
 		Files: []*os.File{
@@ -461,6 +479,13 @@
 	s.mu.Lock()
 	defer s.mu.Unlock()
 
+	if !s.runtime.evaluated {
+		s.evaluateRuntime()
+	}
+	if s.runtime.evalErr != nil {
+		return s.runtime.evalErr
+	}
+
 	regs := syscall.PtraceRegs{}
 	err := s.ptraceGetRegs(s.stoppedPid, &regs)
 	if err != nil {
@@ -472,9 +497,7 @@
 	b := new(bytes.Buffer)
 	r := s.dwarfData.Reader()
 
-	// TODO: check that req.Count is sane.
 	// TODO: handle walking over a split stack.
-	// TODO: handle hitting the end of the stack.
 	for i := 0; i < req.Count; i++ {
 		fp := sp + uint64(int64(s.table.PCToSPAdj(pc))) + uint64(s.arch.PointerSize)
 
@@ -482,7 +505,7 @@
 		b.Reset()
 		fmt.Fprintf(b, "PC=%#x, SP=%#x:", pc, sp)
 
-		entry, err := s.entryForPC(pc)
+		entry, funcEntry, err := s.entryForPC(pc)
 		if err != nil {
 			return err
 		}
@@ -536,6 +559,9 @@
 		})
 
 		// Walk to the caller's PC and SP.
+		if s.topOfStack(funcEntry) {
+			break
+		}
 		err = s.ptracePeek(s.stoppedPid, uintptr(fp-uint64(s.arch.PointerSize)), buf[:s.arch.PointerSize])
 		if err != nil {
 			return fmt.Errorf("ptracePeek: %v", err)
@@ -544,3 +570,44 @@
 	}
 	return nil
 }
+
+func (s *Server) evaluateRuntime() {
+	s.runtime.evaluated = true
+	s.runtime.evalErr = nil
+
+	addrs := [...]struct {
+		name            string
+		p               *uint64
+		windowsSpecific bool
+	}{
+		{"runtime.goexit", &s.runtime.goexit, false},
+		{"runtime.mstart", &s.runtime.mstart, false},
+		{"runtime.mcall", &s.runtime.mcall, false},
+		{"runtime.morestack", &s.runtime.morestack, false},
+		{"runtime.lessstack", &s.runtime.lessstack, false},
+		{"_rt0_go", &s.runtime._rt0_go, false},
+		{"runtime.externalthreadhandlerp", &s.runtime.externalthreadhandlerp, true},
+	}
+	for _, a := range addrs {
+		if a.windowsSpecific {
+			// TODO: determine if the traced binary is for Windows.
+			*a.p = 0
+			continue
+		}
+		*a.p, s.runtime.evalErr = s.lookupSym(a.name)
+		if s.runtime.evalErr != nil {
+			return
+		}
+	}
+}
+
+// topOfStack is the out-of-process equivalent of runtime·topofstack.
+func (s *Server) topOfStack(funcEntry uint64) bool {
+	return funcEntry == s.runtime.goexit ||
+		funcEntry == s.runtime.mstart ||
+		funcEntry == s.runtime.mcall ||
+		funcEntry == s.runtime.morestack ||
+		funcEntry == s.runtime.lessstack ||
+		funcEntry == s.runtime._rt0_go ||
+		(s.runtime.externalthreadhandlerp != 0 && funcEntry == s.runtime.externalthreadhandlerp)
+}