You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

306 lines
7.8 KiB

  1. // Copyright 2018 Google Inc. All Rights Reserved.
  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. // Package remote provides remote access to a debugproxy server.
  15. package remote
  16. import (
  17. "fmt"
  18. "io"
  19. "net/rpc"
  20. "os"
  21. "os/exec"
  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/protocol"
  24. )
  25. var _ debug.Program = (*Program)(nil)
  26. var _ debug.File = (*File)(nil)
  27. // Program implements the debug.Program interface.
  28. // Through that interface it provides access to a program being
  29. // debugged on a possibly remote machine by communicating
  30. // with a debugproxy adjacent to the target program.
  31. type Program struct {
  32. client *rpc.Client
  33. }
  34. // DebugproxyCmd is the path to the debugproxy command. It is a variable in case
  35. // the default value, "debugproxy", is not in the $PATH.
  36. var DebugproxyCmd = "debugproxy"
  37. // New connects to the specified host using SSH, starts DebugproxyCmd
  38. // there, and creates a new program from the specified file.
  39. // The program can then be started by the Run method.
  40. func New(host string, textFile string) (*Program, error) {
  41. // TODO: add args.
  42. cmdStrs := []string{"/usr/bin/ssh", host, DebugproxyCmd, "-text", textFile}
  43. if host == "localhost" {
  44. cmdStrs = cmdStrs[2:]
  45. }
  46. cmd := exec.Command(cmdStrs[0], cmdStrs[1:]...)
  47. stdin, toStdin, err := os.Pipe()
  48. if err != nil {
  49. return nil, err
  50. }
  51. fromStdout, stdout, err := os.Pipe()
  52. if err != nil {
  53. return nil, err
  54. }
  55. cmd.Stdin = stdin
  56. cmd.Stdout = stdout
  57. cmd.Stderr = os.Stderr // Stderr from proxy appears on our stderr.
  58. err = cmd.Start()
  59. if err != nil {
  60. return nil, err
  61. }
  62. stdout.Close()
  63. if msg, err := readLine(fromStdout); err != nil {
  64. return nil, err
  65. } else if msg != "OK" {
  66. // Communication error.
  67. return nil, fmt.Errorf("unrecognized message %q", msg)
  68. }
  69. program := &Program{
  70. client: rpc.NewClient(&rwc{
  71. ssh: cmd,
  72. r: fromStdout,
  73. w: toStdin,
  74. }),
  75. }
  76. return program, nil
  77. }
  78. // readLine reads one line of text from the reader. It does no buffering.
  79. // The trailing newline is read but not returned.
  80. func readLine(r io.Reader) (string, error) {
  81. b := make([]byte, 0, 10)
  82. var c [1]byte
  83. for {
  84. _, err := io.ReadFull(r, c[:])
  85. if err != nil {
  86. return "", err
  87. }
  88. if c[0] == '\n' {
  89. break
  90. }
  91. b = append(b, c[0])
  92. }
  93. return string(b), nil
  94. }
  95. // rwc creates a single io.ReadWriteCloser from a read side and a write side.
  96. // It also holds the command object so we can wait for SSH to complete.
  97. // It allows us to do RPC over an SSH connection.
  98. type rwc struct {
  99. ssh *exec.Cmd
  100. r *os.File
  101. w *os.File
  102. }
  103. func (rwc *rwc) Read(p []byte) (int, error) {
  104. return rwc.r.Read(p)
  105. }
  106. func (rwc *rwc) Write(p []byte) (int, error) {
  107. return rwc.w.Write(p)
  108. }
  109. func (rwc *rwc) Close() error {
  110. rerr := rwc.r.Close()
  111. werr := rwc.w.Close()
  112. cerr := rwc.ssh.Wait()
  113. if cerr != nil {
  114. // Wait exit status is most important.
  115. return cerr
  116. }
  117. if rerr != nil {
  118. return rerr
  119. }
  120. return werr
  121. }
  122. func (p *Program) Open(name string, mode string) (debug.File, error) {
  123. req := protocol.OpenRequest{
  124. Name: name,
  125. Mode: mode,
  126. }
  127. var resp protocol.OpenResponse
  128. err := p.client.Call("Server.Open", &req, &resp)
  129. if err != nil {
  130. return nil, err
  131. }
  132. f := &File{
  133. prog: p,
  134. fd: resp.FD,
  135. }
  136. return f, nil
  137. }
  138. func (p *Program) Run(args ...string) (debug.Status, error) {
  139. req := protocol.RunRequest{args}
  140. var resp protocol.RunResponse
  141. err := p.client.Call("Server.Run", &req, &resp)
  142. if err != nil {
  143. return debug.Status{}, err
  144. }
  145. return resp.Status, nil
  146. }
  147. func (p *Program) Stop() (debug.Status, error) {
  148. panic("unimplemented")
  149. }
  150. func (p *Program) Resume() (debug.Status, error) {
  151. req := protocol.ResumeRequest{}
  152. var resp protocol.ResumeResponse
  153. err := p.client.Call("Server.Resume", &req, &resp)
  154. if err != nil {
  155. return debug.Status{}, err
  156. }
  157. return resp.Status, nil
  158. }
  159. func (p *Program) Kill() (debug.Status, error) {
  160. panic("unimplemented")
  161. }
  162. func (p *Program) Breakpoint(address uint64) ([]uint64, error) {
  163. req := protocol.BreakpointRequest{
  164. Address: address,
  165. }
  166. var resp protocol.BreakpointResponse
  167. err := p.client.Call("Server.Breakpoint", &req, &resp)
  168. return resp.PCs, err
  169. }
  170. func (p *Program) BreakpointAtFunction(name string) ([]uint64, error) {
  171. req := protocol.BreakpointAtFunctionRequest{
  172. Function: name,
  173. }
  174. var resp protocol.BreakpointResponse
  175. err := p.client.Call("Server.BreakpointAtFunction", &req, &resp)
  176. return resp.PCs, err
  177. }
  178. func (p *Program) BreakpointAtLine(file string, line uint64) ([]uint64, error) {
  179. req := protocol.BreakpointAtLineRequest{
  180. File: file,
  181. Line: line,
  182. }
  183. var resp protocol.BreakpointResponse
  184. err := p.client.Call("Server.BreakpointAtLine", &req, &resp)
  185. return resp.PCs, err
  186. }
  187. func (p *Program) DeleteBreakpoints(pcs []uint64) error {
  188. req := protocol.DeleteBreakpointsRequest{PCs: pcs}
  189. var resp protocol.DeleteBreakpointsResponse
  190. return p.client.Call("Server.DeleteBreakpoints", &req, &resp)
  191. }
  192. func (p *Program) Eval(expr string) ([]string, error) {
  193. req := protocol.EvalRequest{
  194. Expr: expr,
  195. }
  196. var resp protocol.EvalResponse
  197. err := p.client.Call("Server.Eval", &req, &resp)
  198. return resp.Result, err
  199. }
  200. func (p *Program) Evaluate(e string) (debug.Value, error) {
  201. req := protocol.EvaluateRequest{
  202. Expression: e,
  203. }
  204. var resp protocol.EvaluateResponse
  205. err := p.client.Call("Server.Evaluate", &req, &resp)
  206. return resp.Result, err
  207. }
  208. func (p *Program) Frames(count int) ([]debug.Frame, error) {
  209. req := protocol.FramesRequest{
  210. Count: count,
  211. }
  212. var resp protocol.FramesResponse
  213. err := p.client.Call("Server.Frames", &req, &resp)
  214. return resp.Frames, err
  215. }
  216. func (p *Program) Goroutines() ([]*debug.Goroutine, error) {
  217. req := protocol.GoroutinesRequest{}
  218. var resp protocol.GoroutinesResponse
  219. err := p.client.Call("Server.Goroutines", &req, &resp)
  220. return resp.Goroutines, err
  221. }
  222. func (p *Program) VarByName(name string) (debug.Var, error) {
  223. req := protocol.VarByNameRequest{Name: name}
  224. var resp protocol.VarByNameResponse
  225. err := p.client.Call("Server.VarByName", &req, &resp)
  226. return resp.Var, err
  227. }
  228. func (p *Program) Value(v debug.Var) (debug.Value, error) {
  229. req := protocol.ValueRequest{Var: v}
  230. var resp protocol.ValueResponse
  231. err := p.client.Call("Server.Value", &req, &resp)
  232. return resp.Value, err
  233. }
  234. func (p *Program) MapElement(m debug.Map, index uint64) (debug.Var, debug.Var, error) {
  235. req := protocol.MapElementRequest{Map: m, Index: index}
  236. var resp protocol.MapElementResponse
  237. err := p.client.Call("Server.MapElement", &req, &resp)
  238. return resp.Key, resp.Value, err
  239. }
  240. // File implements the debug.File interface, providing access
  241. // to file-like resources associated with the target program.
  242. type File struct {
  243. prog *Program // The Program associated with the file.
  244. fd int // File descriptor.
  245. }
  246. func (f *File) ReadAt(p []byte, offset int64) (int, error) {
  247. req := protocol.ReadAtRequest{
  248. FD: f.fd,
  249. Len: len(p),
  250. Offset: offset,
  251. }
  252. var resp protocol.ReadAtResponse
  253. err := f.prog.client.Call("Server.ReadAt", &req, &resp)
  254. return copy(p, resp.Data), err
  255. }
  256. func (f *File) WriteAt(p []byte, offset int64) (int, error) {
  257. req := protocol.WriteAtRequest{
  258. FD: f.fd,
  259. Data: p,
  260. Offset: offset,
  261. }
  262. var resp protocol.WriteAtResponse
  263. err := f.prog.client.Call("Server.WriteAt", &req, &resp)
  264. return resp.Len, err
  265. }
  266. func (f *File) Close() error {
  267. req := protocol.CloseRequest{
  268. FD: f.fd,
  269. }
  270. var resp protocol.CloseResponse
  271. err := f.prog.client.Call("Server.Close", &req, &resp)
  272. return err
  273. }