ogle: add BreakpointAtFunction and BreakpointAtLine, for setting a breakpoint at the start of a function or at a particular source line.
Breakpoint now only takes an address, instead of a formatted string to be evaluated.
Change-Id: I148960dcc149a7467478590eb7c6696597168a03
Reviewed-on: https://go-review.googlesource.com/10624
Reviewed-by: Rob Pike <r@golang.org>
diff --git a/ogle/demo/ogler/ogler_test.go b/ogle/demo/ogler/ogler_test.go
index fc7c511..4c4039f 100644
--- a/ogle/demo/ogler/ogler_test.go
+++ b/ogle/demo/ogler/ogler_test.go
@@ -175,9 +175,9 @@
log.Fatalf("Run: %v", err)
}
- pcs, err := prog.Breakpoint("re:main.foo")
+ pcs, err := prog.BreakpointAtFunction("main.foo")
if err != nil {
- log.Fatalf("Breakpoint: %v", err)
+ log.Fatalf("BreakpointAtFunction: %v", err)
}
fmt.Printf("breakpoints set at %x\n", pcs)
@@ -267,13 +267,13 @@
if err != nil {
log.Fatalf("DeleteBreakpoints: %v", err)
}
- pcs1, err := prog.Breakpoint("re:main.f1")
+ pcs1, err := prog.BreakpointAtFunction("main.f1")
if err != nil {
- log.Fatalf("Breakpoint: %v", err)
+ log.Fatalf("BreakpointAtFunction: %v", err)
}
- pcs2, err := prog.Breakpoint("re:main.f2")
+ pcs2, err := prog.BreakpointAtFunction("main.f2")
if err != nil {
- log.Fatalf("Breakpoint: %v", err)
+ log.Fatalf("BreakpointAtFunction: %v", err)
}
err = prog.DeleteBreakpoints(pcs1)
if err != nil {
diff --git a/ogle/program/client/client.go b/ogle/program/client/client.go
index 1eade3a..2464a6d 100644
--- a/ogle/program/client/client.go
+++ b/ogle/program/client/client.go
@@ -180,7 +180,7 @@
panic("unimplemented")
}
-func (p *Program) Breakpoint(address string) ([]uint64, error) {
+func (p *Program) Breakpoint(address uint64) ([]uint64, error) {
req := proxyrpc.BreakpointRequest{
Address: address,
}
@@ -189,6 +189,25 @@
return resp.PCs, err
}
+func (p *Program) BreakpointAtFunction(name string) ([]uint64, error) {
+ req := proxyrpc.BreakpointAtFunctionRequest{
+ Function: name,
+ }
+ var resp proxyrpc.BreakpointResponse
+ err := p.client.Call("Server.BreakpointAtFunction", &req, &resp)
+ return resp.PCs, err
+}
+
+func (p *Program) BreakpointAtLine(file string, line uint64) ([]uint64, error) {
+ req := proxyrpc.BreakpointAtLineRequest{
+ File: file,
+ Line: line,
+ }
+ var resp proxyrpc.BreakpointResponse
+ err := p.client.Call("Server.BreakpointAtLine", &req, &resp)
+ return resp.PCs, err
+}
+
func (p *Program) DeleteBreakpoints(pcs []uint64) error {
req := proxyrpc.DeleteBreakpointsRequest{PCs: pcs}
var resp proxyrpc.DeleteBreakpointsResponse
diff --git a/ogle/program/local/local.go b/ogle/program/local/local.go
index 45868c4..02c7d0c 100644
--- a/ogle/program/local/local.go
+++ b/ogle/program/local/local.go
@@ -72,15 +72,27 @@
panic("unimplemented")
}
-func (l *Local) Breakpoint(address string) ([]uint64, error) {
- req := proxyrpc.BreakpointRequest{
- Address: address,
- }
+func (l *Local) Breakpoint(address uint64) ([]uint64, error) {
+ req := proxyrpc.BreakpointRequest{address}
var resp proxyrpc.BreakpointResponse
err := l.s.Breakpoint(&req, &resp)
return resp.PCs, err
}
+func (l *Local) BreakpointAtFunction(name string) ([]uint64, error) {
+ req := proxyrpc.BreakpointAtFunctionRequest{name}
+ var resp proxyrpc.BreakpointResponse
+ err := l.s.BreakpointAtFunction(&req, &resp)
+ return resp.PCs, err
+}
+
+func (l *Local) BreakpointAtLine(file string, line uint64) ([]uint64, error) {
+ req := proxyrpc.BreakpointAtLineRequest{file, line}
+ var resp proxyrpc.BreakpointResponse
+ err := l.s.BreakpointAtLine(&req, &resp)
+ return resp.PCs, err
+}
+
func (l *Local) DeleteBreakpoints(pcs []uint64) error {
req := proxyrpc.DeleteBreakpointsRequest{PCs: pcs}
var resp proxyrpc.DeleteBreakpointsResponse
diff --git a/ogle/program/program.go b/ogle/program/program.go
index 77d8b8a..374a019 100644
--- a/ogle/program/program.go
+++ b/ogle/program/program.go
@@ -48,13 +48,13 @@
Kill() (Status, error)
// Breakpoint sets a breakpoint at the specified address.
- // When the target binary is re-run, breakpoints are
- // automatically re-established in the new process by
- // re-evaluating the address.
- // 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) (PCs []uint64, err error)
+ Breakpoint(address uint64) (PCs []uint64, err error)
+
+ // BreakpointAtFunction sets a breakpoint at the start of the specified function.
+ BreakpointAtFunction(name string) (PCs []uint64, err error)
+
+ // BreakpointAtLine sets a breakpoint at the specified source line.
+ BreakpointAtLine(file string, line uint64) (PCs []uint64, err error)
// DeleteBreakpoints removes the breakpoints at the specified addresses.
// Addresses where no breakpoint is set are ignored.
diff --git a/ogle/program/proxyrpc/proxyrpc.go b/ogle/program/proxyrpc/proxyrpc.go
index 04b2240..91bb49e 100644
--- a/ogle/program/proxyrpc/proxyrpc.go
+++ b/ogle/program/proxyrpc/proxyrpc.go
@@ -78,7 +78,16 @@
}
type BreakpointRequest struct {
- Address string
+ Address uint64
+}
+
+type BreakpointAtFunctionRequest struct {
+ Function string
+}
+
+type BreakpointAtLineRequest struct {
+ File string
+ Line uint64
}
type BreakpointResponse struct {
diff --git a/ogle/program/server/server.go b/ogle/program/server/server.go
index 4afb911..9e5b486 100644
--- a/ogle/program/server/server.go
+++ b/ogle/program/server/server.go
@@ -148,6 +148,10 @@
switch req := c.req.(type) {
case *proxyrpc.BreakpointRequest:
c.errc <- s.handleBreakpoint(req, c.resp.(*proxyrpc.BreakpointResponse))
+ case *proxyrpc.BreakpointAtFunctionRequest:
+ c.errc <- s.handleBreakpointAtFunction(req, c.resp.(*proxyrpc.BreakpointResponse))
+ case *proxyrpc.BreakpointAtLineRequest:
+ c.errc <- s.handleBreakpointAtLine(req, c.resp.(*proxyrpc.BreakpointResponse))
case *proxyrpc.DeleteBreakpointsRequest:
c.errc <- s.handleDeleteBreakpoints(req, c.resp.(*proxyrpc.DeleteBreakpointsResponse))
case *proxyrpc.CloseRequest:
@@ -399,29 +403,49 @@
}
func (s *Server) handleBreakpoint(req *proxyrpc.BreakpointRequest, resp *proxyrpc.BreakpointResponse) error {
- addrs, err := s.eval(req.Address)
+ return s.addBreakpoints([]uint64{req.Address}, resp)
+}
+
+func (s *Server) BreakpointAtFunction(req *proxyrpc.BreakpointAtFunctionRequest, resp *proxyrpc.BreakpointResponse) error {
+ return s.call(s.breakpointc, req, resp)
+}
+
+func (s *Server) handleBreakpointAtFunction(req *proxyrpc.BreakpointAtFunctionRequest, resp *proxyrpc.BreakpointResponse) error {
+ pc, err := s.lookupFunction(req.Function)
if err != nil {
return err
}
- var bp breakpoint
- 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)
- }
+ return s.addBreakpoints([]uint64{pc}, resp)
+}
- err = s.ptracePeek(s.stoppedPid, uintptr(pc), bp.origInstr[:s.arch.BreakpointSize])
- if err != nil {
+func (s *Server) BreakpointAtLine(req *proxyrpc.BreakpointAtLineRequest, resp *proxyrpc.BreakpointResponse) error {
+ return s.call(s.breakpointc, req, resp)
+}
+
+func (s *Server) handleBreakpointAtLine(req *proxyrpc.BreakpointAtLineRequest, resp *proxyrpc.BreakpointResponse) error {
+ return fmt.Errorf("not implemented")
+}
+
+// addBreakpoints adds breakpoints at the addresses in pcs, then stores pcs in the response.
+func (s *Server) addBreakpoints(pcs []uint64, resp *proxyrpc.BreakpointResponse) error {
+ // Get the original code at each address with ptracePeek.
+ bps := make([]breakpoint, 0, len(pcs))
+ for _, pc := range pcs {
+ if _, alreadySet := s.breakpoints[pc]; alreadySet {
+ continue
+ }
+ var bp breakpoint
+ if err := s.ptracePeek(s.stoppedPid, uintptr(pc), bp.origInstr[:s.arch.BreakpointSize]); err != nil {
return fmt.Errorf("ptracePeek: %v", err)
}
bp.pc = pc
- s.breakpoints[pc] = bp
- resp.PCs = append(resp.PCs, pc)
+ bps = append(bps, bp)
}
-
+ // If all the peeks succeeded, update the list of breakpoints.
+ for _, bp := range bps {
+ s.breakpoints[bp.pc] = bp
+ }
+ resp.PCs = pcs
return nil
}