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, ®s)
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)
+}