github.com/neilgarb/delve@v1.9.2-nobreaks/service/rpc2/client.go (about) 1 package rpc2 2 3 import ( 4 "fmt" 5 "log" 6 "net" 7 "net/rpc" 8 "net/rpc/jsonrpc" 9 "time" 10 11 "github.com/go-delve/delve/service" 12 "github.com/go-delve/delve/service/api" 13 ) 14 15 // RPCClient is a RPC service.Client. 16 type RPCClient struct { 17 client *rpc.Client 18 19 retValLoadCfg *api.LoadConfig 20 } 21 22 // Ensure the implementation satisfies the interface. 23 var _ service.Client = &RPCClient{} 24 25 // NewClient creates a new RPCClient. 26 func NewClient(addr string) *RPCClient { 27 client, err := jsonrpc.Dial("tcp", addr) 28 if err != nil { 29 log.Fatal("dialing:", err) 30 } 31 return newFromRPCClient(client) 32 } 33 34 func newFromRPCClient(client *rpc.Client) *RPCClient { 35 c := &RPCClient{client: client} 36 c.call("SetApiVersion", api.SetAPIVersionIn{APIVersion: 2}, &api.SetAPIVersionOut{}) 37 return c 38 } 39 40 // NewClientFromConn creates a new RPCClient from the given connection. 41 func NewClientFromConn(conn net.Conn) *RPCClient { 42 return newFromRPCClient(jsonrpc.NewClient(conn)) 43 } 44 45 func (c *RPCClient) ProcessPid() int { 46 out := new(ProcessPidOut) 47 c.call("ProcessPid", ProcessPidIn{}, out) 48 return out.Pid 49 } 50 51 func (c *RPCClient) BuildID() string { 52 out := new(BuildIDOut) 53 c.call("BuildID", BuildIDIn{}, out) 54 return out.BuildID 55 } 56 57 func (c *RPCClient) LastModified() time.Time { 58 out := new(LastModifiedOut) 59 c.call("LastModified", LastModifiedIn{}, out) 60 return out.Time 61 } 62 63 func (c *RPCClient) Detach(kill bool) error { 64 defer c.client.Close() 65 out := new(DetachOut) 66 return c.call("Detach", DetachIn{kill}, out) 67 } 68 69 func (c *RPCClient) Restart(rebuild bool) ([]api.DiscardedBreakpoint, error) { 70 out := new(RestartOut) 71 err := c.call("Restart", RestartIn{"", false, nil, false, rebuild, [3]string{}}, out) 72 return out.DiscardedBreakpoints, err 73 } 74 75 func (c *RPCClient) RestartFrom(rerecord bool, pos string, resetArgs bool, newArgs []string, newRedirects [3]string, rebuild bool) ([]api.DiscardedBreakpoint, error) { 76 out := new(RestartOut) 77 err := c.call("Restart", RestartIn{pos, resetArgs, newArgs, rerecord, rebuild, newRedirects}, out) 78 return out.DiscardedBreakpoints, err 79 } 80 81 func (c *RPCClient) GetState() (*api.DebuggerState, error) { 82 var out StateOut 83 err := c.call("State", StateIn{NonBlocking: false}, &out) 84 return out.State, err 85 } 86 87 func (c *RPCClient) GetStateNonBlocking() (*api.DebuggerState, error) { 88 var out StateOut 89 err := c.call("State", StateIn{NonBlocking: true}, &out) 90 return out.State, err 91 } 92 93 func (c *RPCClient) Continue() <-chan *api.DebuggerState { 94 return c.continueDir(api.Continue) 95 } 96 97 func (c *RPCClient) Rewind() <-chan *api.DebuggerState { 98 return c.continueDir(api.Rewind) 99 } 100 101 func (c *RPCClient) DirectionCongruentContinue() <-chan *api.DebuggerState { 102 return c.continueDir(api.DirectionCongruentContinue) 103 } 104 105 func (c *RPCClient) continueDir(cmd string) <-chan *api.DebuggerState { 106 ch := make(chan *api.DebuggerState) 107 go func() { 108 for { 109 out := new(CommandOut) 110 err := c.call("Command", &api.DebuggerCommand{Name: cmd, ReturnInfoLoadConfig: c.retValLoadCfg}, &out) 111 state := out.State 112 if err != nil { 113 state.Err = err 114 } 115 if state.Exited { 116 // Error types apparently cannot be marshalled by Go correctly. Must reset error here. 117 //lint:ignore ST1005 backwards compatibility 118 state.Err = fmt.Errorf("Process %d has exited with status %d", c.ProcessPid(), state.ExitStatus) 119 } 120 ch <- &state 121 if err != nil || state.Exited { 122 close(ch) 123 return 124 } 125 126 isbreakpoint := false 127 istracepoint := true 128 for i := range state.Threads { 129 if state.Threads[i].Breakpoint != nil { 130 isbreakpoint = true 131 istracepoint = istracepoint && (state.Threads[i].Breakpoint.Tracepoint || state.Threads[i].Breakpoint.TraceReturn) 132 } 133 } 134 135 if !isbreakpoint || !istracepoint { 136 close(ch) 137 return 138 } 139 } 140 }() 141 return ch 142 } 143 144 func (c *RPCClient) Next() (*api.DebuggerState, error) { 145 var out CommandOut 146 err := c.call("Command", api.DebuggerCommand{Name: api.Next, ReturnInfoLoadConfig: c.retValLoadCfg}, &out) 147 return &out.State, err 148 } 149 150 func (c *RPCClient) ReverseNext() (*api.DebuggerState, error) { 151 var out CommandOut 152 err := c.call("Command", api.DebuggerCommand{Name: api.ReverseNext, ReturnInfoLoadConfig: c.retValLoadCfg}, &out) 153 return &out.State, err 154 } 155 156 func (c *RPCClient) Step() (*api.DebuggerState, error) { 157 var out CommandOut 158 err := c.call("Command", api.DebuggerCommand{Name: api.Step, ReturnInfoLoadConfig: c.retValLoadCfg}, &out) 159 return &out.State, err 160 } 161 162 func (c *RPCClient) ReverseStep() (*api.DebuggerState, error) { 163 var out CommandOut 164 err := c.call("Command", api.DebuggerCommand{Name: api.ReverseStep, ReturnInfoLoadConfig: c.retValLoadCfg}, &out) 165 return &out.State, err 166 } 167 168 func (c *RPCClient) StepOut() (*api.DebuggerState, error) { 169 var out CommandOut 170 err := c.call("Command", api.DebuggerCommand{Name: api.StepOut, ReturnInfoLoadConfig: c.retValLoadCfg}, &out) 171 return &out.State, err 172 } 173 174 func (c *RPCClient) ReverseStepOut() (*api.DebuggerState, error) { 175 var out CommandOut 176 err := c.call("Command", api.DebuggerCommand{Name: api.ReverseStepOut, ReturnInfoLoadConfig: c.retValLoadCfg}, &out) 177 return &out.State, err 178 } 179 180 func (c *RPCClient) Call(goroutineID int, expr string, unsafe bool) (*api.DebuggerState, error) { 181 var out CommandOut 182 err := c.call("Command", api.DebuggerCommand{Name: api.Call, ReturnInfoLoadConfig: c.retValLoadCfg, Expr: expr, UnsafeCall: unsafe, GoroutineID: goroutineID}, &out) 183 return &out.State, err 184 } 185 186 func (c *RPCClient) StepInstruction() (*api.DebuggerState, error) { 187 var out CommandOut 188 err := c.call("Command", api.DebuggerCommand{Name: api.StepInstruction}, &out) 189 return &out.State, err 190 } 191 192 func (c *RPCClient) ReverseStepInstruction() (*api.DebuggerState, error) { 193 var out CommandOut 194 err := c.call("Command", api.DebuggerCommand{Name: api.ReverseStepInstruction}, &out) 195 return &out.State, err 196 } 197 198 func (c *RPCClient) SwitchThread(threadID int) (*api.DebuggerState, error) { 199 var out CommandOut 200 cmd := api.DebuggerCommand{ 201 Name: api.SwitchThread, 202 ThreadID: threadID, 203 } 204 err := c.call("Command", cmd, &out) 205 return &out.State, err 206 } 207 208 func (c *RPCClient) SwitchGoroutine(goroutineID int) (*api.DebuggerState, error) { 209 var out CommandOut 210 cmd := api.DebuggerCommand{ 211 Name: api.SwitchGoroutine, 212 GoroutineID: goroutineID, 213 } 214 err := c.call("Command", cmd, &out) 215 return &out.State, err 216 } 217 218 func (c *RPCClient) Halt() (*api.DebuggerState, error) { 219 var out CommandOut 220 err := c.call("Command", api.DebuggerCommand{Name: api.Halt}, &out) 221 return &out.State, err 222 } 223 224 func (c *RPCClient) GetBufferedTracepoints() ([]api.TracepointResult, error) { 225 var out GetBufferedTracepointsOut 226 err := c.call("GetBufferedTracepoints", GetBufferedTracepointsIn{}, &out) 227 return out.TracepointResults, err 228 } 229 230 func (c *RPCClient) GetBreakpoint(id int) (*api.Breakpoint, error) { 231 var out GetBreakpointOut 232 err := c.call("GetBreakpoint", GetBreakpointIn{id, ""}, &out) 233 return &out.Breakpoint, err 234 } 235 236 func (c *RPCClient) GetBreakpointByName(name string) (*api.Breakpoint, error) { 237 var out GetBreakpointOut 238 err := c.call("GetBreakpoint", GetBreakpointIn{0, name}, &out) 239 return &out.Breakpoint, err 240 } 241 242 // CreateBreakpoint will send a request to the RPC server to create a breakpoint. 243 // Please refer to the documentation for `Debugger.CreateBreakpoint` for a description of how 244 // the requested breakpoint parameters are interpreted and used: 245 // https://pkg.go.dev/github.com/go-delve/delve/service/debugger#Debugger.CreateBreakpoint 246 func (c *RPCClient) CreateBreakpoint(breakPoint *api.Breakpoint) (*api.Breakpoint, error) { 247 var out CreateBreakpointOut 248 err := c.call("CreateBreakpoint", CreateBreakpointIn{*breakPoint}, &out) 249 return &out.Breakpoint, err 250 } 251 252 func (c *RPCClient) CreateEBPFTracepoint(fnName string) error { 253 var out CreateEBPFTracepointOut 254 return c.call("CreateEBPFTracepoint", CreateEBPFTracepointIn{FunctionName: fnName}, &out) 255 } 256 257 func (c *RPCClient) CreateWatchpoint(scope api.EvalScope, expr string, wtype api.WatchType) (*api.Breakpoint, error) { 258 var out CreateWatchpointOut 259 err := c.call("CreateWatchpoint", CreateWatchpointIn{scope, expr, wtype}, &out) 260 return out.Breakpoint, err 261 } 262 263 func (c *RPCClient) ListBreakpoints(all bool) ([]*api.Breakpoint, error) { 264 var out ListBreakpointsOut 265 err := c.call("ListBreakpoints", ListBreakpointsIn{all}, &out) 266 return out.Breakpoints, err 267 } 268 269 func (c *RPCClient) ClearBreakpoint(id int) (*api.Breakpoint, error) { 270 var out ClearBreakpointOut 271 err := c.call("ClearBreakpoint", ClearBreakpointIn{id, ""}, &out) 272 return out.Breakpoint, err 273 } 274 275 func (c *RPCClient) ClearBreakpointByName(name string) (*api.Breakpoint, error) { 276 var out ClearBreakpointOut 277 err := c.call("ClearBreakpoint", ClearBreakpointIn{0, name}, &out) 278 return out.Breakpoint, err 279 } 280 281 func (c *RPCClient) ToggleBreakpoint(id int) (*api.Breakpoint, error) { 282 var out ToggleBreakpointOut 283 err := c.call("ToggleBreakpoint", ToggleBreakpointIn{id, ""}, &out) 284 return out.Breakpoint, err 285 } 286 287 func (c *RPCClient) ToggleBreakpointByName(name string) (*api.Breakpoint, error) { 288 var out ToggleBreakpointOut 289 err := c.call("ToggleBreakpoint", ToggleBreakpointIn{0, name}, &out) 290 return out.Breakpoint, err 291 } 292 293 func (c *RPCClient) AmendBreakpoint(bp *api.Breakpoint) error { 294 out := new(AmendBreakpointOut) 295 err := c.call("AmendBreakpoint", AmendBreakpointIn{*bp}, out) 296 return err 297 } 298 299 func (c *RPCClient) CancelNext() error { 300 var out CancelNextOut 301 return c.call("CancelNext", CancelNextIn{}, &out) 302 } 303 304 func (c *RPCClient) ListThreads() ([]*api.Thread, error) { 305 var out ListThreadsOut 306 err := c.call("ListThreads", ListThreadsIn{}, &out) 307 return out.Threads, err 308 } 309 310 func (c *RPCClient) GetThread(id int) (*api.Thread, error) { 311 var out GetThreadOut 312 err := c.call("GetThread", GetThreadIn{id}, &out) 313 return out.Thread, err 314 } 315 316 func (c *RPCClient) EvalVariable(scope api.EvalScope, expr string, cfg api.LoadConfig) (*api.Variable, error) { 317 var out EvalOut 318 err := c.call("Eval", EvalIn{scope, expr, &cfg}, &out) 319 return out.Variable, err 320 } 321 322 func (c *RPCClient) SetVariable(scope api.EvalScope, symbol, value string) error { 323 out := new(SetOut) 324 return c.call("Set", SetIn{scope, symbol, value}, out) 325 } 326 327 func (c *RPCClient) ListSources(filter string) ([]string, error) { 328 sources := new(ListSourcesOut) 329 err := c.call("ListSources", ListSourcesIn{filter}, sources) 330 return sources.Sources, err 331 } 332 333 func (c *RPCClient) ListFunctions(filter string) ([]string, error) { 334 funcs := new(ListFunctionsOut) 335 err := c.call("ListFunctions", ListFunctionsIn{filter}, funcs) 336 return funcs.Funcs, err 337 } 338 339 func (c *RPCClient) ListTypes(filter string) ([]string, error) { 340 types := new(ListTypesOut) 341 err := c.call("ListTypes", ListTypesIn{filter}, types) 342 return types.Types, err 343 } 344 345 func (c *RPCClient) ListPackageVariables(filter string, cfg api.LoadConfig) ([]api.Variable, error) { 346 var out ListPackageVarsOut 347 err := c.call("ListPackageVars", ListPackageVarsIn{filter, cfg}, &out) 348 return out.Variables, err 349 } 350 351 func (c *RPCClient) ListLocalVariables(scope api.EvalScope, cfg api.LoadConfig) ([]api.Variable, error) { 352 var out ListLocalVarsOut 353 err := c.call("ListLocalVars", ListLocalVarsIn{scope, cfg}, &out) 354 return out.Variables, err 355 } 356 357 func (c *RPCClient) ListThreadRegisters(threadID int, includeFp bool) (api.Registers, error) { 358 out := new(ListRegistersOut) 359 err := c.call("ListRegisters", ListRegistersIn{ThreadID: threadID, IncludeFp: includeFp, Scope: nil}, out) 360 return out.Regs, err 361 } 362 363 func (c *RPCClient) ListScopeRegisters(scope api.EvalScope, includeFp bool) (api.Registers, error) { 364 out := new(ListRegistersOut) 365 err := c.call("ListRegisters", ListRegistersIn{ThreadID: 0, IncludeFp: includeFp, Scope: &scope}, out) 366 return out.Regs, err 367 } 368 369 func (c *RPCClient) ListFunctionArgs(scope api.EvalScope, cfg api.LoadConfig) ([]api.Variable, error) { 370 var out ListFunctionArgsOut 371 err := c.call("ListFunctionArgs", ListFunctionArgsIn{scope, cfg}, &out) 372 return out.Args, err 373 } 374 375 func (c *RPCClient) ListGoroutines(start, count int) ([]*api.Goroutine, int, error) { 376 var out ListGoroutinesOut 377 err := c.call("ListGoroutines", ListGoroutinesIn{start, count, nil, api.GoroutineGroupingOptions{}}, &out) 378 return out.Goroutines, out.Nextg, err 379 } 380 381 func (c *RPCClient) ListGoroutinesWithFilter(start, count int, filters []api.ListGoroutinesFilter, group *api.GoroutineGroupingOptions) ([]*api.Goroutine, []api.GoroutineGroup, int, bool, error) { 382 if group == nil { 383 group = &api.GoroutineGroupingOptions{} 384 } 385 var out ListGoroutinesOut 386 err := c.call("ListGoroutines", ListGoroutinesIn{start, count, filters, *group}, &out) 387 return out.Goroutines, out.Groups, out.Nextg, out.TooManyGroups, err 388 } 389 390 func (c *RPCClient) Stacktrace(goroutineId, depth int, opts api.StacktraceOptions, cfg *api.LoadConfig) ([]api.Stackframe, error) { 391 var out StacktraceOut 392 err := c.call("Stacktrace", StacktraceIn{goroutineId, depth, false, false, opts, cfg}, &out) 393 return out.Locations, err 394 } 395 396 func (c *RPCClient) Ancestors(goroutineID int, numAncestors int, depth int) ([]api.Ancestor, error) { 397 var out AncestorsOut 398 err := c.call("Ancestors", AncestorsIn{goroutineID, numAncestors, depth}, &out) 399 return out.Ancestors, err 400 } 401 402 func (c *RPCClient) AttachedToExistingProcess() bool { 403 out := new(AttachedToExistingProcessOut) 404 c.call("AttachedToExistingProcess", AttachedToExistingProcessIn{}, out) 405 return out.Answer 406 } 407 408 func (c *RPCClient) FindLocation(scope api.EvalScope, loc string, findInstructions bool, substitutePathRules [][2]string) ([]api.Location, error) { 409 var out FindLocationOut 410 err := c.call("FindLocation", FindLocationIn{scope, loc, !findInstructions, substitutePathRules}, &out) 411 return out.Locations, err 412 } 413 414 // DisassembleRange disassembles code between startPC and endPC 415 func (c *RPCClient) DisassembleRange(scope api.EvalScope, startPC, endPC uint64, flavour api.AssemblyFlavour) (api.AsmInstructions, error) { 416 var out DisassembleOut 417 err := c.call("Disassemble", DisassembleIn{scope, startPC, endPC, flavour}, &out) 418 return out.Disassemble, err 419 } 420 421 // DisassemblePC disassembles function containing pc 422 func (c *RPCClient) DisassemblePC(scope api.EvalScope, pc uint64, flavour api.AssemblyFlavour) (api.AsmInstructions, error) { 423 var out DisassembleOut 424 err := c.call("Disassemble", DisassembleIn{scope, pc, 0, flavour}, &out) 425 return out.Disassemble, err 426 } 427 428 // Recorded returns true if the debugger target is a recording. 429 func (c *RPCClient) Recorded() bool { 430 out := new(RecordedOut) 431 c.call("Recorded", RecordedIn{}, out) 432 return out.Recorded 433 } 434 435 // TraceDirectory returns the path to the trace directory for a recording. 436 func (c *RPCClient) TraceDirectory() (string, error) { 437 var out RecordedOut 438 err := c.call("Recorded", RecordedIn{}, &out) 439 return out.TraceDirectory, err 440 } 441 442 // Checkpoint sets a checkpoint at the current position. 443 func (c *RPCClient) Checkpoint(where string) (checkpointID int, err error) { 444 var out CheckpointOut 445 err = c.call("Checkpoint", CheckpointIn{where}, &out) 446 return out.ID, err 447 } 448 449 // ListCheckpoints gets all checkpoints. 450 func (c *RPCClient) ListCheckpoints() ([]api.Checkpoint, error) { 451 var out ListCheckpointsOut 452 err := c.call("ListCheckpoints", ListCheckpointsIn{}, &out) 453 return out.Checkpoints, err 454 } 455 456 // ClearCheckpoint removes a checkpoint 457 func (c *RPCClient) ClearCheckpoint(id int) error { 458 var out ClearCheckpointOut 459 err := c.call("ClearCheckpoint", ClearCheckpointIn{id}, &out) 460 return err 461 } 462 463 func (c *RPCClient) SetReturnValuesLoadConfig(cfg *api.LoadConfig) { 464 c.retValLoadCfg = cfg 465 } 466 467 func (c *RPCClient) FunctionReturnLocations(fnName string) ([]uint64, error) { 468 var out FunctionReturnLocationsOut 469 err := c.call("FunctionReturnLocations", FunctionReturnLocationsIn{fnName}, &out) 470 return out.Addrs, err 471 } 472 473 func (c *RPCClient) IsMulticlient() bool { 474 var out IsMulticlientOut 475 c.call("IsMulticlient", IsMulticlientIn{}, &out) 476 return out.IsMulticlient 477 } 478 479 func (c *RPCClient) Disconnect(cont bool) error { 480 if cont { 481 out := new(CommandOut) 482 c.client.Go("RPCServer.Command", &api.DebuggerCommand{Name: api.Continue, ReturnInfoLoadConfig: c.retValLoadCfg}, &out, nil) 483 } 484 return c.client.Close() 485 } 486 487 func (c *RPCClient) ListDynamicLibraries() ([]api.Image, error) { 488 var out ListDynamicLibrariesOut 489 c.call("ListDynamicLibraries", ListDynamicLibrariesIn{}, &out) 490 return out.List, nil 491 } 492 493 func (c *RPCClient) ExamineMemory(address uint64, count int) ([]byte, bool, error) { 494 out := &ExaminedMemoryOut{} 495 496 err := c.call("ExamineMemory", ExamineMemoryIn{Length: count, Address: address}, out) 497 if err != nil { 498 return nil, false, err 499 } 500 return out.Mem, out.IsLittleEndian, nil 501 } 502 503 func (c *RPCClient) StopRecording() error { 504 return c.call("StopRecording", StopRecordingIn{}, &StopRecordingOut{}) 505 } 506 507 func (c *RPCClient) CoreDumpStart(dest string) (api.DumpState, error) { 508 out := &DumpStartOut{} 509 err := c.call("DumpStart", DumpStartIn{Destination: dest}, out) 510 return out.State, err 511 } 512 513 func (c *RPCClient) CoreDumpWait(msec int) api.DumpState { 514 out := &DumpWaitOut{} 515 _ = c.call("DumpWait", DumpWaitIn{Wait: msec}, out) 516 return out.State 517 } 518 519 func (c *RPCClient) CoreDumpCancel() error { 520 out := &DumpCancelOut{} 521 return c.call("DumpCancel", DumpCancelIn{}, out) 522 } 523 524 func (c *RPCClient) call(method string, args, reply interface{}) error { 525 return c.client.Call("RPCServer."+method, args, reply) 526 } 527 528 func (c *RPCClient) CallAPI(method string, args, reply interface{}) error { 529 return c.call(method, args, reply) 530 }