ogle/program: add pc/sp/file/line info to the program.Frame structure.

LGTM=r
R=r
https://golang.org/cl/138470043
diff --git a/program/program.go b/program/program.go
index ac7325a..c81b9a2 100644
--- a/program/program.go
+++ b/program/program.go
@@ -104,5 +104,18 @@
 }
 
 type Frame struct {
+	// PC is the hardware program counter.
+	PC uint64
+	// SP is the hardware stack pointer.
+	SP uint64
+
+	// File and Line are the source code location of the PC.
+	File string
+	Line int
+
+	// TODO: add arguments and return values.
+
+	// S is an unstructured string value used for debugging the ogle
+	// library. It is not guaranteed to be in any particular format.
 	S string
 }
diff --git a/program/server/server.go b/program/server/server.go
index 9a1a4db..f181efc 100644
--- a/program/server/server.go
+++ b/program/server/server.go
@@ -555,16 +555,16 @@
 
 	// TODO: handle walking over a split stack.
 	for i := 0; i < req.Count; i++ {
+		b.Reset()
+		file, line, err := s.dwarfData.PCToLine(pc)
+		if err != nil {
+			return err
+		}
 		fpOffset, err := s.dwarfData.PCToSPOffset(pc)
 		if err != nil {
 			return err
 		}
 		fp := sp + uint64(fpOffset)
-
-		// 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, funcEntry, err := s.entryForPC(pc)
 		if err != nil {
 			return err
@@ -603,6 +603,11 @@
 			}
 		}
 		resp.Frames = append(resp.Frames, program.Frame{
+			PC:   pc,
+			SP:   sp,
+			File: file,
+			Line: line,
+			// TODO: delete the Frame.S field.
 			S: b.String(),
 		})