ogle: allow multi-valued expressions such as regular expressions
Allows us to set a group of breakpoints in a single call.
Clean up expression evaluation a bit (it's still rudimentary).
Fix a bug in breakpoint: need to back up the PC after the trap.
Also fix a silly bug in formatting the startup error in ogleproxy.

Can now trace a simple program using breakpoints.

LGTM=nigeltao
R=nigeltao
https://golang.org/cl/77140045
diff --git a/cmd/ogleproxy/main.go b/cmd/ogleproxy/main.go
index 6e1b6ea..72967bc 100644
--- a/cmd/ogleproxy/main.go
+++ b/cmd/ogleproxy/main.go
@@ -25,18 +25,18 @@
 	log.SetPrefix("ogleproxy: ")
 	flag.Parse()
 	if *textFlag == "" {
-		fmt.Println("OGLE BAD\nUsage")
+		fmt.Printf("OGLE BAD\nUsage")
 		flag.Usage()
 		os.Exit(2)
 	}
 	s, err := server.New(*textFlag)
 	if err != nil {
-		fmt.Println("OGLE BAD\n%s\n", err)
+		fmt.Printf("OGLE BAD\n%s\n", err)
 		os.Exit(2)
 	}
 	err = rpc.Register(s)
 	if err != nil {
-		fmt.Println("OGLE BAD\n%s\n", err)
+		fmt.Printf("OGLE BAD\n%s\n", err)
 		os.Exit(2)
 	}
 	fmt.Println("OGLE OK")
diff --git a/program/client/client.go b/program/client/client.go
index c376ae9..c779774 100644
--- a/program/client/client.go
+++ b/program/client/client.go
@@ -200,7 +200,7 @@
 	panic("unimplemented")
 }
 
-func (p *Program) Eval(expr string) (string, error) {
+func (p *Program) Eval(expr string) ([]string, error) {
 	req := proxyrpc.EvalRequest{
 		Expr: expr,
 	}
diff --git a/program/program.go b/program/program.go
index c91fc47..55657d6 100644
--- a/program/program.go
+++ b/program/program.go
@@ -59,20 +59,28 @@
 	// When the target binary is re-run, breakpoints are
 	// automatically re-established in the new process by
 	// re-evaluating the address.
-	// Address syntax:
-	//	"main.main"  Start of function
-	//	"main.go:23" Line number
-	//	(more to follow; may want an expression grammar)
-	// It is OK if two breakpoints evaluate to the same PC. (TODO: verify.)
+	// The address is the same mini-language accepted by Eval,
+	// which permits setting multiple breakpoints using a regular
+	// expression to match a set of symbols.
 	Breakpoint(address string) error
 
-	// DeleteBreakpoint removes the breakpoint at the specified
-	// address.
+	// DeleteBreakpoint removes the breakpoint at to the specified
+	// address. TODO: Probably the wrong interface.
 	DeleteBreakpoint(address string) error
 
 	// Eval evaluates the expression (typically an address) and returns
-	// its string representation.
-	Eval(expr string) (string, error)
+	// its string representation(s). Multivalued expressions such as
+	// matches for regular expressions return multiple values.
+	// Syntax:
+	//	re:regexp
+	//		Returns a list of symbol names that match the expression
+	//	sym:symbol
+	//		Returns a one-element list holding the hexadecimal
+	//		("0x1234") value of the address of the symbol
+	//	0x1234, 01234, 467
+	//		Returns a one-element list holding the name of the
+	//		symbol ("main.foo") at that address (hex, octal, decimal).
+	Eval(expr string) ([]string, error)
 }
 
 // The File interface provides access to file-like resources in the program.
diff --git a/program/proxyrpc/proxyrpc.go b/program/proxyrpc/proxyrpc.go
index 320bba6..16fb9f1 100644
--- a/program/proxyrpc/proxyrpc.go
+++ b/program/proxyrpc/proxyrpc.go
@@ -78,5 +78,5 @@
 }
 
 type EvalResponse struct {
-	Result string
+	Result []string
 }
diff --git a/program/server/ptrace.go b/program/server/ptrace.go
index 6b6ad39..0072e7f 100644
--- a/program/server/ptrace.go
+++ b/program/server/ptrace.go
@@ -50,6 +50,13 @@
 	return <-s.ec
 }
 
+func (s *Server) ptraceSetRegs(pid int, regs *syscall.PtraceRegs) (err error) {
+	s.fc <- func() error {
+		return syscall.PtraceSetRegs(pid, regs)
+	}
+	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)
diff --git a/program/server/server.go b/program/server/server.go
index eed4c11..d08ed47 100644
--- a/program/server/server.go
+++ b/program/server/server.go
@@ -9,7 +9,9 @@
 import (
 	"fmt"
 	"os"
+	"regexp"
 	"strconv"
+	"strings"
 	"sync"
 	"syscall"
 
@@ -254,7 +256,7 @@
 	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.
+	// back up the PC, 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 {
@@ -264,6 +266,12 @@
 			return fmt.Errorf("ptracePoke: %v", err)
 		}
 
+		regs.Rip-- // TODO: depends on length of trap.
+		err = s.ptraceSetRegs(s.proc.Pid, &regs)
+		if err != nil {
+			return fmt.Errorf("ptraceSetRegs: %v", err)
+		}
+
 		err = s.ptraceSingleStep(s.proc.Pid)
 		if err != nil {
 			return fmt.Errorf("ptraceSingleStep: %v", err)
@@ -284,27 +292,32 @@
 	s.mu.Lock()
 	defer s.mu.Unlock()
 
-	addr, err := s.eval(req.Address)
+	addrs, err := s.eval(req.Address)
 	if err != nil {
-		return fmt.Errorf("could not parse %q: %v", req.Address, err)
+		return err
 	}
-	pc, err := strconv.ParseUint(addr, 0, 0)
-	if err != nil {
-		return fmt.Errorf("ParseUint: %v", err)
-	}
+	for _, addr := range addrs {
+		pc, err := s.evalAddress(addr)
+		if err != nil {
+			return err
+		}
+		if _, alreadySet := s.breakpoints[pc]; alreadySet {
+			return fmt.Errorf("breakpoint already set at %#x (TODO)", pc)
+		}
 
-	buf := make([]byte, 1)
-	err = s.ptracePeek(s.proc.Pid, uintptr(pc), buf)
-	if err != nil {
-		return fmt.Errorf("ptracePoke: %v", err)
-	}
+		var buf [1]byte
+		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]}
+		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)
+		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
@@ -318,22 +331,65 @@
 	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?
+// eval evaluates an expression.
+// TODO: very weak.
+func (s *Server) eval(expr string) ([]string, error) {
+	switch {
+	case strings.HasPrefix(expr, "re:"):
+		// Regular expression. Return list of symbols.
+		expr = expr[3:]
+		re, err := regexp.Compile(expr)
+		if err != nil {
+			return nil, err
+		}
+		strs := make([]string, 0, 100)
+		for _, f := range s.symbols.Funcs {
+			if re.MatchString(f.Sym.Name) {
+				strs = append(strs, f.Sym.Name)
+			}
+		}
+		return strs, nil
+
+	case strings.HasPrefix(expr, "sym:"):
+		// Symbol lookup. Return address.
+		expr = expr[4:]
+		sym := s.symbols.LookupFunc(expr)
+		if sym == nil {
+			return nil, fmt.Errorf("symbol %q not found", expr)
+		}
+		return []string{fmt.Sprintf("%#x", sym.Value)}, nil
+
+	case len(expr) > 0 && '0' <= expr[0] && expr[0] <= '9':
+		// Numerical address. Return symbol.
+		addr, err := strconv.ParseUint(expr, 0, 0)
+		if err != nil {
+			return nil, err
+		}
+		fun := s.symbols.PCToFunc(addr)
+		if fun == nil {
+			return nil, fmt.Errorf("address %q has no func", expr)
+		}
+		return []string{fun.Sym.Name}, nil
+	}
+
+	return nil, fmt.Errorf("bad expression syntax: %q", expr)
+}
+
+// evalAddress takes a simple expression, either a symbol or hex value,
+// and evaluates it as an address.
+func (s *Server) evalAddress(expr string) (uint64, error) {
+	// Might be a symbol.
 	sym := s.symbols.LookupFunc(expr)
 	if sym != nil {
-		return fmt.Sprintf("%#x", sym.Value), nil
+		return sym.Value, nil
 	}
 
+	// Must be a number.
 	addr, err := strconv.ParseUint(expr, 0, 0)
 	if err != nil {
-		return "", fmt.Errorf("address %q not found: %v", expr, err)
+		return 0, fmt.Errorf("eval: %q is neither symbol nor number", expr)
 	}
 
-	fun := s.symbols.PCToFunc(addr)
-	if fun == nil {
-		return "", fmt.Errorf("address %q has no func", expr)
-	}
-	return fun.Sym.Name, nil
+	return addr, nil
+
 }