ogle/program/server: trace multiple frames. LGTM=r R=r https://golang.org/cl/87510043
diff --git a/arch/arch.go b/arch/arch.go index 92945ab..6a2d405 100644 --- a/arch/arch.go +++ b/arch/arch.go
@@ -19,7 +19,7 @@ IntSize int // PointerSize is the size of a pointer, in bytes. PointerSize int - // The byte order for I/O. + // ByteOrder is the byte order for ints and pointers. ByteOrder binary.ByteOrder BreakpointInstr [MaxBreakpointSize]byte } @@ -30,7 +30,7 @@ func (a *Architecture) Uint(buf []byte) uint64 { if len(buf) != a.IntSize { - panic("bad uint size") + panic("bad IntSize") } switch a.IntSize { case 4: @@ -38,7 +38,20 @@ case 8: return a.ByteOrder.Uint64(buf[:8]) } - panic("no uint size") + panic("no IntSize") +} + +func (a *Architecture) Uintptr(buf []byte) uint64 { + if len(buf) != a.PointerSize { + panic("bad PointerSize") + } + switch a.PointerSize { + case 4: + return uint64(a.ByteOrder.Uint32(buf[:4])) + case 8: + return a.ByteOrder.Uint64(buf[:8]) + } + panic("no PointerSize") } var AMD64 = Architecture{
diff --git a/gosym/pclntab.go b/gosym/pclntab.go index 6620aef..a9c2fc7 100644 --- a/gosym/pclntab.go +++ b/gosym/pclntab.go
@@ -355,9 +355,18 @@ // go12PCToLine maps program counter to line number for the Go 1.2 pcln table. func (t *LineTable) go12PCToLine(pc uint64) (line int) { + return t.go12PCToVal(pc, t.ptrsize+5*4) +} + +// go12PCToSPAdj maps program counter to Stack Pointer adjustment for the Go 1.2 pcln table. +func (t *LineTable) go12PCToSPAdj(pc uint64) (spadj int) { + return t.go12PCToVal(pc, t.ptrsize+3*4) +} + +func (t *LineTable) go12PCToVal(pc uint64, fOffset uint32) (val int) { defer func() { if recover() != nil { - line = -1 + val = -1 } }() @@ -366,7 +375,7 @@ return -1 } entry := t.uintptr(f) - linetab := t.binary.Uint32(f[t.ptrsize+5*4:]) + linetab := t.binary.Uint32(f[fOffset:]) return int(t.pcvalue(linetab, entry, pc)) }
diff --git a/gosym/symtab.go b/gosym/symtab.go index 3864e3c..8a3fc2a 100644 --- a/gosym/symtab.go +++ b/gosym/symtab.go
@@ -502,6 +502,17 @@ return } +// PCToSPAdj returns the stack pointer adjustment for a program counter. +func (t *Table) PCToSPAdj(pc uint64) (spadj int) { + if fn := t.PCToFunc(pc); fn == nil { + return 0 + } + if t.go12line != nil { + return t.go12line.go12PCToSPAdj(pc) + } + return 0 +} + // LineToPC looks up the first program counter on the given line in // the named file. It returns UnknownPathError or UnknownLineError if // there is an error looking up this line.
diff --git a/program/server/server.go b/program/server/server.go index 2835bdc..7ec321d 100644 --- a/program/server/server.go +++ b/program/server/server.go
@@ -7,6 +7,7 @@ package server import ( + "bytes" "fmt" "os" "regexp" @@ -137,14 +138,8 @@ if err != nil { return nil, err } - pcln := gosym.NewLineTable(pclndat, f.Section(".text").Addr) - tab, err := gosym.NewTable(symdat, pcln) - if err != nil { - return nil, err - } - - return tab, nil + return gosym.NewTable(symdat, pcln) } type file struct { @@ -466,73 +461,86 @@ s.mu.Lock() defer s.mu.Unlock() - if req.Count != 1 { - // TODO: implement. - return fmt.Errorf("Frames.Count != 1 is not implemented") - } - - // TODO: we're assuming we're at a function's entry point (LowPC). - regs := syscall.PtraceRegs{} err := s.ptraceGetRegs(s.stoppedPid, ®s) if err != nil { return err } - fp := regs.Rsp + uint64(s.arch.PointerSize) - - entry, err := s.entryForPC(regs.Rip) - if err != nil { - return err - } + pc, sp := regs.Rip, regs.Rsp var buf [8]byte - frame := program.Frame{} + b := new(bytes.Buffer) r := s.dwarfData.Reader() - r.Seek(entry.Offset) - for { - entry, err := r.Next() + + // 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) + + // TODO: the returned frame should be structured instead of a hacked up string. + b.Reset() + fmt.Fprintf(b, "PC=%#x, SP=%#x:", pc, sp) + + entry, err := s.entryForPC(pc) if err != nil { return err } - if entry.Tag == 0 { - break - } - if entry.Tag != dwarf.TagFormalParameter { - continue - } - if entry.Children { - // TODO: handle this?? - return fmt.Errorf("FormalParameter has children, expected none") - } - // TODO: the returned frame should be structured instead of a hacked up string. - location := uintptr(0) - for _, f := range entry.Field { - switch f.Attr { - case dwarf.AttrLocation: - offset := evalLocation(f.Val.([]uint8)) - location = uintptr(int64(fp) + offset) - frame.S += fmt.Sprintf("(%d(FP))", offset) - case dwarf.AttrName: - frame.S += " " + f.Val.(string) - case dwarf.AttrType: - t, err := s.dwarfData.Type(f.Val.(dwarf.Offset)) - if err == nil { - frame.S += fmt.Sprintf("[%v]", t) + r.Seek(entry.Offset) + for { + entry, err := r.Next() + if err != nil { + return err + } + if entry.Tag == 0 { + break + } + if entry.Tag != dwarf.TagFormalParameter { + continue + } + if entry.Children { + // TODO: handle this?? + return fmt.Errorf("FormalParameter has children, expected none") + } + + location := uintptr(0) + for _, f := range entry.Field { + switch f.Attr { + case dwarf.AttrLocation: + offset := evalLocation(f.Val.([]uint8)) + location = uintptr(int64(fp) + offset) + fmt.Fprintf(b, "(%d(FP))", offset) + case dwarf.AttrName: + fmt.Fprintf(b, " %s", f.Val.(string)) + case dwarf.AttrType: + t, err := s.dwarfData.Type(f.Val.(dwarf.Offset)) + if err == nil { + fmt.Fprintf(b, "[%v]", t) + } + if t.String() != "int" || t.Size() != int64(s.arch.IntSize) { + break + } + if location == 0 { + return fmt.Errorf("no location for FormalParameter") + } + err = s.ptracePeek(s.stoppedPid, location, buf[:s.arch.IntSize]) + if err != nil { + return err + } + fmt.Fprintf(b, "==%#x", s.arch.Int(buf[:s.arch.IntSize])) } - if t.String() != "int" || t.Size() != int64(s.arch.IntSize) { - break - } - if location == 0 { - return fmt.Errorf("no location for FormalParameter") - } - err = s.ptracePeek(s.stoppedPid, location, buf[:s.arch.IntSize]) - if err != nil { - return err - } - frame.S += fmt.Sprintf("==%#x", s.arch.Int(buf[:s.arch.IntSize])) } } + resp.Frames = append(resp.Frames, program.Frame{ + S: b.String(), + }) + + // Walk to the caller's PC and SP. + err = s.ptracePeek(s.stoppedPid, uintptr(fp-uint64(s.arch.PointerSize)), buf[:s.arch.PointerSize]) + if err != nil { + return fmt.Errorf("ptracePeek: %v", err) + } + pc, sp = s.arch.Uintptr(buf[:s.arch.PointerSize]), fp } - resp.Frames = append(resp.Frames, frame) return nil }