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