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, &regs)
 	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
 }