1// Copyright 2019 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//      http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15// +build linux
16
17// Package local provides access to a local program.
18package local
19
20import (
21	"cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug"
22	"cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/server"
23	"cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/server/protocol"
24)
25
26var _ debug.Program = (*Program)(nil)
27var _ debug.File = (*File)(nil)
28
29// Program implements the debug.Program interface.
30// Through that interface it provides access to a program being debugged.
31type Program struct {
32	s *server.Server
33}
34
35// New creates a new program from the specified file.
36// The program can then be started by the Run method.
37func New(textFile string) (*Program, error) {
38	s, err := server.New(textFile)
39	return &Program{s: s}, err
40}
41
42func (p *Program) Open(name string, mode string) (debug.File, error) {
43	req := protocol.OpenRequest{
44		Name: name,
45		Mode: mode,
46	}
47	var resp protocol.OpenResponse
48	err := p.s.Open(&req, &resp)
49	if err != nil {
50		return nil, err
51	}
52	f := &File{
53		prog: p,
54		fd:   resp.FD,
55	}
56	return f, nil
57}
58
59func (p *Program) Run(args ...string) (debug.Status, error) {
60	req := protocol.RunRequest{args}
61	var resp protocol.RunResponse
62	err := p.s.Run(&req, &resp)
63	if err != nil {
64		return debug.Status{}, err
65	}
66	return resp.Status, nil
67}
68
69func (p *Program) Stop() (debug.Status, error) {
70	panic("unimplemented")
71}
72
73func (p *Program) Resume() (debug.Status, error) {
74	req := protocol.ResumeRequest{}
75	var resp protocol.ResumeResponse
76	err := p.s.Resume(&req, &resp)
77	if err != nil {
78		return debug.Status{}, err
79	}
80	return resp.Status, nil
81}
82
83func (p *Program) Kill() (debug.Status, error) {
84	panic("unimplemented")
85}
86
87func (p *Program) Breakpoint(address uint64) ([]uint64, error) {
88	req := protocol.BreakpointRequest{
89		Address: address,
90	}
91	var resp protocol.BreakpointResponse
92	err := p.s.Breakpoint(&req, &resp)
93	return resp.PCs, err
94}
95
96func (p *Program) BreakpointAtFunction(name string) ([]uint64, error) {
97	req := protocol.BreakpointAtFunctionRequest{
98		Function: name,
99	}
100	var resp protocol.BreakpointResponse
101	err := p.s.BreakpointAtFunction(&req, &resp)
102	return resp.PCs, err
103}
104
105func (p *Program) BreakpointAtLine(file string, line uint64) ([]uint64, error) {
106	req := protocol.BreakpointAtLineRequest{
107		File: file,
108		Line: line,
109	}
110	var resp protocol.BreakpointResponse
111	err := p.s.BreakpointAtLine(&req, &resp)
112	return resp.PCs, err
113}
114
115func (p *Program) DeleteBreakpoints(pcs []uint64) error {
116	req := protocol.DeleteBreakpointsRequest{PCs: pcs}
117	var resp protocol.DeleteBreakpointsResponse
118	return p.s.DeleteBreakpoints(&req, &resp)
119}
120
121func (p *Program) Eval(expr string) ([]string, error) {
122	req := protocol.EvalRequest{
123		Expr: expr,
124	}
125	var resp protocol.EvalResponse
126	err := p.s.Eval(&req, &resp)
127	return resp.Result, err
128}
129
130func (p *Program) Evaluate(e string) (debug.Value, error) {
131	req := protocol.EvaluateRequest{
132		Expression: e,
133	}
134	var resp protocol.EvaluateResponse
135	err := p.s.Evaluate(&req, &resp)
136	return resp.Result, err
137}
138
139func (p *Program) Frames(count int) ([]debug.Frame, error) {
140	req := protocol.FramesRequest{
141		Count: count,
142	}
143	var resp protocol.FramesResponse
144	err := p.s.Frames(&req, &resp)
145	return resp.Frames, err
146}
147
148func (p *Program) Goroutines() ([]*debug.Goroutine, error) {
149	req := protocol.GoroutinesRequest{}
150	var resp protocol.GoroutinesResponse
151	err := p.s.Goroutines(&req, &resp)
152	return resp.Goroutines, err
153}
154
155func (p *Program) VarByName(name string) (debug.Var, error) {
156	req := protocol.VarByNameRequest{Name: name}
157	var resp protocol.VarByNameResponse
158	err := p.s.VarByName(&req, &resp)
159	return resp.Var, err
160}
161
162func (p *Program) Value(v debug.Var) (debug.Value, error) {
163	req := protocol.ValueRequest{Var: v}
164	var resp protocol.ValueResponse
165	err := p.s.Value(&req, &resp)
166	return resp.Value, err
167}
168
169func (p *Program) MapElement(m debug.Map, index uint64) (debug.Var, debug.Var, error) {
170	req := protocol.MapElementRequest{Map: m, Index: index}
171	var resp protocol.MapElementResponse
172	err := p.s.MapElement(&req, &resp)
173	return resp.Key, resp.Value, err
174}
175
176// File implements the debug.File interface, providing access
177// to file-like resources associated with the target program.
178type File struct {
179	prog *Program // The Program associated with the file.
180	fd   int      // File descriptor.
181}
182
183func (f *File) ReadAt(p []byte, offset int64) (int, error) {
184	req := protocol.ReadAtRequest{
185		FD:     f.fd,
186		Len:    len(p),
187		Offset: offset,
188	}
189	var resp protocol.ReadAtResponse
190	err := f.prog.s.ReadAt(&req, &resp)
191	return copy(p, resp.Data), err
192}
193
194func (f *File) WriteAt(p []byte, offset int64) (int, error) {
195	panic("unimplemented")
196}
197
198func (f *File) Close() error {
199	req := protocol.CloseRequest{
200		FD: f.fd,
201	}
202	var resp protocol.CloseResponse
203	err := f.prog.s.Close(&req, &resp)
204	return err
205}
206