debug: add DeleteBreakpoints function

DeleteBreakpoints accepts a slice of addresses, and removes any breakpoints
set at those addresses.

Adds a test where some breakpoints are set and deleted in the program
being debugged, and we check the program stops at the expected place.

Change-Id: I4ea4718e21e51587e48bd2eb57033ca774004e1a
Reviewed-on: https://go-review.googlesource.com/10079
Reviewed-by: Rob Pike <r@golang.org>
diff --git a/ogle/demo/ogler/ogler_test.go b/ogle/demo/ogler/ogler_test.go
index 65483ee..7c5faa3 100644
--- a/ogle/demo/ogler/ogler_test.go
+++ b/ogle/demo/ogler/ogler_test.go
@@ -189,4 +189,38 @@
 			log.Fatalf("Didn't get %s = %s\n", v, e)
 		}
 	}
+
+	// Remove the breakpoint at main.foo, set a breakpoint at main.f1 and main.f2,
+	// then delete the breakpoint at main.f1.  Resume, then check we stopped at
+	// main.f2.
+	err = prog.DeleteBreakpoints(pcs)
+	if err != nil {
+		log.Fatalf("DeleteBreakpoints: %v", err)
+	}
+	pcs1, err := prog.Breakpoint("re:main.f1")
+	if err != nil {
+		log.Fatalf("Breakpoint: %v", err)
+	}
+	pcs2, err := prog.Breakpoint("re:main.f2")
+	if err != nil {
+		log.Fatalf("Breakpoint: %v", err)
+	}
+	err = prog.DeleteBreakpoints(pcs1)
+	if err != nil {
+		log.Fatalf("DeleteBreakpoints: %v", err)
+	}
+	status, err := prog.Resume()
+	if err != nil {
+		log.Fatalf("Resume: %v", err)
+	}
+	ok := false
+	for _, pc := range pcs2 {
+		if status.PC == pc {
+			ok = true
+			break
+		}
+	}
+	if !ok {
+		t.Errorf("Stopped at %X expected one of %X.", status.PC, pcs2)
+	}
 }
diff --git a/ogle/demo/tracee/main.go b/ogle/demo/tracee/main.go
index 9d4a36c..a9cf90d 100644
--- a/ogle/demo/tracee/main.go
+++ b/ogle/demo/tracee/main.go
@@ -82,6 +82,16 @@
 	fmt.Println(Z_slice, Z_slice_2, Z_slice_nil)
 	fmt.Println(Z_string, Z_struct)
 	fmt.Println(Z_unsafe_pointer, Z_unsafe_pointer_nil)
+	f1()
+	f2()
+}
+
+func f1() {
+	fmt.Println()
+}
+
+func f2() {
+	fmt.Println()
 }
 
 func bar() {
diff --git a/ogle/program/client/client.go b/ogle/program/client/client.go
index b0d547a..c84c49c 100644
--- a/ogle/program/client/client.go
+++ b/ogle/program/client/client.go
@@ -203,8 +203,10 @@
 	return resp.PCs, err
 }
 
-func (p *Program) DeleteBreakpoint(address string) error {
-	panic("unimplemented")
+func (p *Program) DeleteBreakpoints(pcs []uint64) error {
+	req := proxyrpc.DeleteBreakpointsRequest{PCs: pcs}
+	var resp proxyrpc.DeleteBreakpointsResponse
+	return p.client.Call("Server.DeleteBreakpoints", &req, &resp)
 }
 
 func (p *Program) Eval(expr string) ([]string, error) {
diff --git a/ogle/program/program.go b/ogle/program/program.go
index 4481413..b633e18 100644
--- a/ogle/program/program.go
+++ b/ogle/program/program.go
@@ -62,9 +62,9 @@
 	// expression to match a set of symbols.
 	Breakpoint(address string) (PCs []uint64, err error)
 
-	// DeleteBreakpoint removes the breakpoint at the specified
-	// address. TODO: Probably the wrong interface.
-	DeleteBreakpoint(address string) error
+	// DeleteBreakpoints removes the breakpoints at the specified addresses.
+	// Addresses where no breakpoint is set are ignored.
+	DeleteBreakpoints(pcs []uint64) error
 
 	// Eval evaluates the expression (typically an address) and returns
 	// its string representation(s). Multivalued expressions such as
diff --git a/ogle/program/proxyrpc/proxyrpc.go b/ogle/program/proxyrpc/proxyrpc.go
index fba3ec1..eef2864 100644
--- a/ogle/program/proxyrpc/proxyrpc.go
+++ b/ogle/program/proxyrpc/proxyrpc.go
@@ -73,6 +73,13 @@
 	PCs []uint64
 }
 
+type DeleteBreakpointsRequest struct {
+	PCs []uint64
+}
+
+type DeleteBreakpointsResponse struct {
+}
+
 type EvalRequest struct {
 	Expr string
 }
diff --git a/ogle/program/server/server.go b/ogle/program/server/server.go
index 1717094..296e20e 100644
--- a/ogle/program/server/server.go
+++ b/ogle/program/server/server.go
@@ -148,6 +148,8 @@
 	switch req := c.req.(type) {
 	case *proxyrpc.BreakpointRequest:
 		c.errc <- s.handleBreakpoint(req, c.resp.(*proxyrpc.BreakpointResponse))
+	case *proxyrpc.DeleteBreakpointsRequest:
+		c.errc <- s.handleDeleteBreakpoints(req, c.resp.(*proxyrpc.DeleteBreakpointsResponse))
 	case *proxyrpc.CloseRequest:
 		c.errc <- s.handleClose(req, c.resp.(*proxyrpc.CloseResponse))
 	case *proxyrpc.EvalRequest:
@@ -418,6 +420,17 @@
 	return nil
 }
 
+func (s *Server) DeleteBreakpoints(req *proxyrpc.DeleteBreakpointsRequest, resp *proxyrpc.DeleteBreakpointsResponse) error {
+	return s.call(s.breakpointc, req, resp)
+}
+
+func (s *Server) handleDeleteBreakpoints(req *proxyrpc.DeleteBreakpointsRequest, resp *proxyrpc.DeleteBreakpointsResponse) error {
+	for _, pc := range req.PCs {
+		delete(s.breakpoints, pc)
+	}
+	return nil
+}
+
 func (s *Server) setBreakpoints() error {
 	for pc := range s.breakpoints {
 		err := s.ptracePoke(s.stoppedPid, uintptr(pc), s.arch.BreakpointInstr[:s.arch.BreakpointSize])