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) +}