debug: add an implementation of program.Program for running and debugging programs locally, instead of through a proxy. Change-Id: Ib9e1392409e09e62a647122870813c3526b0fc8c Reviewed-on: https://go-review.googlesource.com/10565 Reviewed-by: Rob Pike <r@golang.org>
diff --git a/ogle/demo/ogler/ogler_test.go b/ogle/demo/ogler/ogler_test.go index fef5aff..8bceaff 100644 --- a/ogle/demo/ogler/ogler_test.go +++ b/ogle/demo/ogler/ogler_test.go
@@ -16,6 +16,7 @@ "golang.org/x/debug/ogle/program" "golang.org/x/debug/ogle/program/client" + "golang.org/x/debug/ogle/program/local" ) var expectedVarValues = map[string]interface{}{ @@ -119,14 +120,11 @@ return j == len(s) } -func run(t *testing.T, name string, args ...string) { +func run(name string, args ...string) error { cmd := exec.Command(name, args...) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr - err := cmd.Run() - if err != nil { - t.Fatal(err) - } + return cmd.Run() } const ( @@ -136,24 +134,43 @@ traceeBinary = "./tracee" ) -func TestBreakAndEval(t *testing.T) { - run(t, "go", "build", "-o", proxyBinary, proxySrc) - defer os.Remove(proxyBinary) +func TestMain(m *testing.M) { + os.Exit(buildAndRunTests(m)) +} - run(t, "go", "build", "-o", traceeBinary, traceeSrc) - defer os.Remove(traceeBinary) - - client.OgleproxyCmd = proxyBinary - var ( - prog program.Program - err error - ) - prog, err = client.New("localhost", traceeBinary) - if err != nil { - log.Fatalf("New: %v", err) +func buildAndRunTests(m *testing.M) int { + if err := run("go", "build", "-o", proxyBinary, proxySrc); err != nil { + fmt.Println(err) + return 1 } + client.OgleproxyCmd = proxyBinary + defer os.Remove(proxyBinary) + if err := run("go", "build", "-o", traceeBinary, traceeSrc); err != nil { + fmt.Println(err) + return 1 + } + defer os.Remove(traceeBinary) + return m.Run() +} - _, err = prog.Run("some", "arguments") +func TestLocalProgram(t *testing.T) { + prog, err := local.New(traceeBinary) + if err != nil { + t.Fatal("local.New:", err) + } + testProgram(t, prog) +} + +func TestRemoteProgram(t *testing.T) { + prog, err := client.New("localhost", traceeBinary) + if err != nil { + t.Fatal("client.New:", err) + } + testProgram(t, prog) +} + +func testProgram(t *testing.T, prog program.Program) { + _, err := prog.Run("some", "arguments") if err != nil { log.Fatalf("Run: %v", err) }
diff --git a/ogle/program/local/local.go b/ogle/program/local/local.go new file mode 100644 index 0000000..45868c4 --- /dev/null +++ b/ogle/program/local/local.go
@@ -0,0 +1,151 @@ +// 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 local provides access to a local program. +package local // import "golang.org/x/debug/ogle/program/local" + +import ( + "golang.org/x/debug/ogle/program" + "golang.org/x/debug/ogle/program/proxyrpc" + "golang.org/x/debug/ogle/program/server" +) + +var _ program.Program = (*Local)(nil) +var _ program.File = (*File)(nil) + +// Local implements the interface program.Program. +// Through that interface it provides access to a program being debugged. +type Local struct { + s *server.Server +} + +// New creates a new program from the specified file. +// The program can then be started by the Run method. +func New(textFile string) (*Local, error) { + s, err := server.New(textFile) + return &Local{s: s}, err +} + +func (l *Local) Open(name string, mode string) (program.File, error) { + req := proxyrpc.OpenRequest{ + Name: name, + Mode: mode, + } + var resp proxyrpc.OpenResponse + err := l.s.Open(&req, &resp) + if err != nil { + return nil, err + } + f := &File{ + prog: l, + fd: resp.FD, + } + return f, nil +} + +func (l *Local) Run(args ...string) (program.Status, error) { + req := proxyrpc.RunRequest{args} + var resp proxyrpc.RunResponse + err := l.s.Run(&req, &resp) + if err != nil { + return program.Status{}, err + } + return resp.Status, nil +} + +func (l *Local) Stop() (program.Status, error) { + panic("unimplemented") +} + +func (l *Local) Resume() (program.Status, error) { + req := proxyrpc.ResumeRequest{} + var resp proxyrpc.ResumeResponse + err := l.s.Resume(&req, &resp) + if err != nil { + return program.Status{}, err + } + return resp.Status, nil +} + +func (l *Local) Kill() (program.Status, error) { + panic("unimplemented") +} + +func (l *Local) Breakpoint(address string) ([]uint64, error) { + req := proxyrpc.BreakpointRequest{ + Address: address, + } + var resp proxyrpc.BreakpointResponse + err := l.s.Breakpoint(&req, &resp) + return resp.PCs, err +} + +func (l *Local) DeleteBreakpoints(pcs []uint64) error { + req := proxyrpc.DeleteBreakpointsRequest{PCs: pcs} + var resp proxyrpc.DeleteBreakpointsResponse + return l.s.DeleteBreakpoints(&req, &resp) +} + +func (l *Local) Eval(expr string) ([]string, error) { + req := proxyrpc.EvalRequest{ + Expr: expr, + } + var resp proxyrpc.EvalResponse + err := l.s.Eval(&req, &resp) + return resp.Result, err +} + +func (l *Local) Frames(count int) ([]program.Frame, error) { + req := proxyrpc.FramesRequest{ + Count: count, + } + var resp proxyrpc.FramesResponse + err := l.s.Frames(&req, &resp) + return resp.Frames, err +} + +func (l *Local) VarByName(name string) (program.Var, error) { + req := proxyrpc.VarByNameRequest{Name: name} + var resp proxyrpc.VarByNameResponse + err := l.s.VarByName(&req, &resp) + return resp.Var, err +} + +func (l *Local) Value(v program.Var) (program.Value, error) { + req := proxyrpc.ValueRequest{Var: v} + var resp proxyrpc.ValueResponse + err := l.s.Value(&req, &resp) + return resp.Value, err +} + +// File implements the program.File interface, providing access +// to file-like resources associated with the target program. +type File struct { + prog *Local // The Program associated with the file. + fd int // File descriptor. +} + +func (f *File) ReadAt(p []byte, offset int64) (int, error) { + req := proxyrpc.ReadAtRequest{ + FD: f.fd, + Len: len(p), + Offset: offset, + } + var resp proxyrpc.ReadAtResponse + err := f.prog.s.ReadAt(&req, &resp) + return copy(p, resp.Data), err +} + +func (f *File) WriteAt(p []byte, offset int64) (int, error) { + panic("unimplemented") +} + +func (f *File) Close() error { + req := proxyrpc.CloseRequest{ + FD: f.fd, + } + var resp proxyrpc.CloseResponse + err := f.prog.s.Close(&req, &resp) + return err +}