ogle/program: first cut of breakpoint support.

LGTM=r
R=r
https://golang.org/cl/78530044
diff --git a/program/client/client.go b/program/client/client.go
index 3b3a3df..c376ae9 100644
--- a/program/client/client.go
+++ b/program/client/client.go
@@ -159,7 +159,15 @@
 }
 
 func (p *Program) Run(start bool) (program.Status, error) {
-	panic("unimplemented")
+	req := proxyrpc.RunRequest{
+		Start: start,
+	}
+	var resp proxyrpc.RunResponse
+	err := p.client.Call("Server.Run", &req, &resp)
+	if err != nil {
+		return program.Status{}, err
+	}
+	return resp.Status, nil
 }
 
 func (p *Program) Stop() (program.Status, error) {
@@ -167,7 +175,13 @@
 }
 
 func (p *Program) Resume() (program.Status, error) {
-	panic("unimplemented")
+	req := proxyrpc.ResumeRequest{}
+	var resp proxyrpc.ResumeResponse
+	err := p.client.Call("Server.Resume", &req, &resp)
+	if err != nil {
+		return program.Status{}, err
+	}
+	return resp.Status, nil
 }
 
 func (p *Program) Kill() (program.Status, error) {
@@ -175,7 +189,11 @@
 }
 
 func (p *Program) Breakpoint(address string) error {
-	panic("unimplemented")
+	req := proxyrpc.BreakpointRequest{
+		Address: address,
+	}
+	var resp proxyrpc.BreakpointResponse
+	return p.client.Call("Server.Breakpoint", &req, &resp)
 }
 
 func (p *Program) DeleteBreakpoint(address string) error {
diff --git a/program/program.go b/program/program.go
index d27da54..c91fc47 100644
--- a/program/program.go
+++ b/program/program.go
@@ -87,5 +87,5 @@
 }
 
 type Status struct {
-	// TBD
+	PC, SP uint64
 }
diff --git a/program/proxyrpc/proxyrpc.go b/program/proxyrpc/proxyrpc.go
index b2a2977..320bba6 100644
--- a/program/proxyrpc/proxyrpc.go
+++ b/program/proxyrpc/proxyrpc.go
@@ -6,6 +6,8 @@
 // used to the ogleproxy.
 package proxyrpc
 
+import "code.google.com/p/ogle/program"
+
 // For regularity, each method has a unique Request and a Response type even
 // when not strictly necessary.
 
@@ -49,6 +51,28 @@
 	FD int
 }
 
+type RunRequest struct {
+	Start bool
+}
+
+type RunResponse struct {
+	Status program.Status
+}
+
+type ResumeRequest struct {
+}
+
+type ResumeResponse struct {
+	Status program.Status
+}
+
+type BreakpointRequest struct {
+	Address string
+}
+
+type BreakpointResponse struct {
+}
+
 type EvalRequest struct {
 	Expr string
 }
diff --git a/program/server/ptrace.go b/program/server/ptrace.go
new file mode 100644
index 0000000..6b6ad39
--- /dev/null
+++ b/program/server/ptrace.go
@@ -0,0 +1,96 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package server
+
+// TODO: syscall.PTRACE_O_TRACECLONE shenanigans to trace multi-threaded
+// programs.
+
+import (
+	"fmt"
+	"os"
+	"runtime"
+	"syscall"
+)
+
+// ptraceRun runs all the closures from fc on a dedicated OS thread. Errors
+// are returned on ec. Both channels must be unbuffered, to ensure that the
+// resultant error is sent back to the same goroutine that sent the closure.
+func ptraceRun(fc chan func() error, ec chan error) {
+	if cap(fc) != 0 || cap(ec) != 0 {
+		panic("ptraceRun was given unbuffered channels")
+	}
+	runtime.LockOSThread()
+	for f := range fc {
+		ec <- f()
+	}
+}
+
+func (s *Server) startProcess(name string, argv []string, attr *os.ProcAttr) (proc *os.Process, err error) {
+	s.fc <- func() error {
+		var err1 error
+		proc, err1 = os.StartProcess(name, argv, attr)
+		return err1
+	}
+	return proc, <-s.ec
+}
+
+func (s *Server) ptraceCont(pid int, signal int) (err error) {
+	s.fc <- func() error {
+		return syscall.PtraceCont(pid, signal)
+	}
+	return <-s.ec
+}
+
+func (s *Server) ptraceGetRegs(pid int, regsout *syscall.PtraceRegs) (err error) {
+	s.fc <- func() error {
+		return syscall.PtraceGetRegs(pid, regsout)
+	}
+	return <-s.ec
+}
+
+func (s *Server) ptracePeek(pid int, addr uintptr, out []byte) (err error) {
+	s.fc <- func() error {
+		n, err := syscall.PtracePeekText(pid, addr, out)
+		if err != nil {
+			return err
+		}
+		if n != len(out) {
+			return fmt.Errorf("ptracePeek: peeked %d bytes, want %d", n, len(out))
+		}
+		return nil
+	}
+	return <-s.ec
+}
+
+func (s *Server) ptracePoke(pid int, addr uintptr, data []byte) (err error) {
+	s.fc <- func() error {
+		n, err := syscall.PtracePokeText(pid, addr, data)
+		if err != nil {
+			return err
+		}
+		if n != len(data) {
+			return fmt.Errorf("ptracePoke: poked %d bytes, want %d", n, len(data))
+		}
+		return nil
+	}
+	return <-s.ec
+}
+
+func (s *Server) ptraceSingleStep(pid int) (err error) {
+	s.fc <- func() error {
+		return syscall.PtraceSingleStep(pid)
+	}
+	return <-s.ec
+}
+
+func (s *Server) wait() (err error) {
+	var status syscall.WaitStatus
+	s.fc <- func() error {
+		_, err1 := syscall.Wait4(-1, &status, 0, nil)
+		return err1
+	}
+	// TODO: do something with status.
+	return <-s.ec
+}
diff --git a/program/server/server.go b/program/server/server.go
index 6160966..eed4c11 100644
--- a/program/server/server.go
+++ b/program/server/server.go
@@ -9,6 +9,9 @@
 import (
 	"fmt"
 	"os"
+	"strconv"
+	"sync"
+	"syscall"
 
 	"debug/elf"
 	"debug/macho"
@@ -19,12 +22,24 @@
 	"code.google.com/p/ogle/program/proxyrpc"
 )
 
+type breakpoint struct {
+	pc        uint64
+	origInstr byte // TODO: don't be amd64-specific.
+}
+
 type Server struct {
 	executable string // Name of executable.
 	lines      *gosym.LineTable
 	symbols    *gosym.Table
-	// TODO: Needs mutex.
-	files []*file // Index == file descriptor.
+
+	mu sync.Mutex
+
+	fc chan func() error
+	ec chan error
+
+	proc        *os.Process
+	breakpoints map[uint64]breakpoint
+	files       []*file // Index == file descriptor.
 }
 
 // New parses the executable and builds local data structures for answering requests.
@@ -45,10 +60,14 @@
 		return nil, err
 	}
 	srv := &Server{
-		executable: executable,
-		lines:      lines,
-		symbols:    symbols,
+		executable:  executable,
+		lines:       lines,
+		symbols:     symbols,
+		fc:          make(chan func() error),
+		ec:          make(chan error),
+		breakpoints: make(map[uint64]breakpoint),
 	}
+	go ptraceRun(srv.fc, srv.ec)
 	return srv, nil
 }
 
@@ -117,6 +136,9 @@
 }
 
 func (s *Server) Open(req *proxyrpc.OpenRequest, resp *proxyrpc.OpenResponse) error {
+	s.mu.Lock()
+	defer s.mu.Unlock()
+
 	// TODO: Better simulation. For now we just open the named OS file.
 	var flag int
 	switch req.Mode {
@@ -151,6 +173,9 @@
 }
 
 func (s *Server) ReadAt(req *proxyrpc.ReadAtRequest, resp *proxyrpc.ReadAtResponse) error {
+	s.mu.Lock()
+	defer s.mu.Unlock()
+
 	fd := req.FD
 	if fd < 0 || len(s.files) <= fd || s.files[fd] == nil {
 		return fmt.Errorf("ReadAt: bad file descriptor %d", fd)
@@ -163,6 +188,9 @@
 }
 
 func (s *Server) Close(req *proxyrpc.CloseRequest, resp *proxyrpc.CloseResponse) error {
+	s.mu.Lock()
+	defer s.mu.Unlock()
+
 	fd := req.FD
 	if fd < 0 || fd >= len(s.files) || s.files[fd] == nil {
 		return fmt.Errorf("Close: bad file descriptor %d", fd)
@@ -173,14 +201,139 @@
 	return err
 }
 
-func (s *Server) Eval(req *proxyrpc.EvalRequest, resp *proxyrpc.EvalResponse) error {
-	expr := req.Expr
+func (s *Server) Run(req *proxyrpc.RunRequest, resp *proxyrpc.RunResponse) error {
+	s.mu.Lock()
+	defer s.mu.Unlock()
+
+	if s.proc != nil {
+		s.proc.Kill()
+		s.proc = nil
+	}
+	p, err := s.startProcess(s.executable, nil, &os.ProcAttr{
+		Files: []*os.File{
+			nil,       // TODO: be able to feed the target's stdin.
+			os.Stderr, // TODO: be able to capture the target's stdout.
+			os.Stderr,
+		},
+		Sys: &syscall.SysProcAttr{
+			Ptrace: !req.Start,
+		},
+	})
+	if err != nil {
+		return err
+	}
+	s.proc = p
+
+	if !req.Start {
+		// TODO: wait until /proc/{s.proc.Pid}/status says "State:	t (tracing stop)".
+	}
+	return nil
+}
+
+func (s *Server) Resume(req *proxyrpc.ResumeRequest, resp *proxyrpc.ResumeResponse) error {
+	s.mu.Lock()
+	defer s.mu.Unlock()
+
+	err := s.ptraceCont(s.proc.Pid, 0)
+	if err != nil {
+		return err
+	}
+
+	err = s.wait()
+	if err != nil {
+		return err
+	}
+
+	regs := syscall.PtraceRegs{}
+	err = s.ptraceGetRegs(s.proc.Pid, &regs)
+	if err != nil {
+		return err
+	}
+
+	resp.Status.PC = regs.Rip
+	resp.Status.SP = regs.Rsp
+
+	// If we're stopped on a breakpoint, restore the original code,
+	// step through a single instruction, and reset the breakpoint.
+	// TODO: should this happen here or just before the ptraceCont call?
+	bp, ok := s.breakpoints[regs.Rip-1] // TODO: -1 because on amd64, INT 3 is 1 byte (0xcc).
+	if ok {
+		pc := uintptr(regs.Rip - 1)
+		err := s.ptracePoke(s.proc.Pid, pc, []byte{bp.origInstr})
+		if err != nil {
+			return fmt.Errorf("ptracePoke: %v", err)
+		}
+
+		err = s.ptraceSingleStep(s.proc.Pid)
+		if err != nil {
+			return fmt.Errorf("ptraceSingleStep: %v", err)
+		}
+
+		buf := make([]byte, 1)
+		buf[0] = 0xcc // INT 3 instruction on x86.
+		err = s.ptracePoke(s.proc.Pid, pc, buf)
+		if err != nil {
+			return fmt.Errorf("ptracePoke: %v", err)
+		}
+	}
+
+	return nil
+}
+
+func (s *Server) Breakpoint(req *proxyrpc.BreakpointRequest, resp *proxyrpc.BreakpointResponse) (err error) {
+	s.mu.Lock()
+	defer s.mu.Unlock()
+
+	addr, err := s.eval(req.Address)
+	if err != nil {
+		return fmt.Errorf("could not parse %q: %v", req.Address, err)
+	}
+	pc, err := strconv.ParseUint(addr, 0, 0)
+	if err != nil {
+		return fmt.Errorf("ParseUint: %v", err)
+	}
+
+	buf := make([]byte, 1)
+	err = s.ptracePeek(s.proc.Pid, uintptr(pc), buf)
+	if err != nil {
+		return fmt.Errorf("ptracePoke: %v", err)
+	}
+
+	s.breakpoints[pc] = breakpoint{pc: pc, origInstr: buf[0]}
+
+	buf[0] = 0xcc // INT 3 instruction on x86.
+	err = s.ptracePoke(s.proc.Pid, uintptr(pc), buf)
+	if err != nil {
+		return fmt.Errorf("ptracePoke: %v", err)
+	}
+
+	return nil
+}
+
+func (s *Server) Eval(req *proxyrpc.EvalRequest, resp *proxyrpc.EvalResponse) (err error) {
+	s.mu.Lock()
+	defer s.mu.Unlock()
+
+	resp.Result, err = s.eval(req.Expr)
+	return err
+}
+
+func (s *Server) eval(expr string) (string, error) {
 	// Simple version: Turn symbol into address. Must be function.
 	// TODO: Why is Table.Syms always empty?
 	sym := s.symbols.LookupFunc(expr)
-	if sym == nil {
-		return fmt.Errorf("symbol %q not found")
+	if sym != nil {
+		return fmt.Sprintf("%#x", sym.Value), nil
 	}
-	resp.Result = fmt.Sprintf("%#x", sym.Value)
-	return nil
+
+	addr, err := strconv.ParseUint(expr, 0, 0)
+	if err != nil {
+		return "", fmt.Errorf("address %q not found: %v", expr, err)
+	}
+
+	fun := s.symbols.PCToFunc(addr)
+	if fun == nil {
+		return "", fmt.Errorf("address %q has no func", expr)
+	}
+	return fun.Sym.Name, nil
 }