gitlab.com/Raven-IO/raven-delve@v1.22.4/service/rpc1/client.go (about) 1 package rpc1 2 3 import ( 4 "errors" 5 "fmt" 6 "log" 7 "net/rpc" 8 "net/rpc/jsonrpc" 9 10 "sync" 11 12 "gitlab.com/Raven-IO/raven-delve/service/api" 13 ) 14 15 // RPCClient is a RPC service.Client. 16 type RPCClient struct { 17 addr string 18 client *rpc.Client 19 haltMu sync.Mutex 20 haltReq bool 21 } 22 23 var errAPIUnsupported = errors.New("unsupported") 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 &RPCClient{ 32 addr: addr, 33 client: client, 34 } 35 } 36 37 func (c *RPCClient) ProcessPid() int { 38 var pid int 39 c.call("ProcessPid", nil, &pid) 40 return pid 41 } 42 43 func (c *RPCClient) Detach(kill bool) error { 44 return c.call("Detach", kill, nil) 45 } 46 47 func (c *RPCClient) Restart() error { 48 return c.call("Restart", nil, nil) 49 } 50 51 func (c *RPCClient) GetState() (*api.DebuggerState, error) { 52 state := new(api.DebuggerState) 53 err := c.call("State", nil, state) 54 return state, err 55 } 56 57 func (c *RPCClient) Continue() <-chan *api.DebuggerState { 58 ch := make(chan *api.DebuggerState) 59 c.haltMu.Lock() 60 c.haltReq = false 61 c.haltMu.Unlock() 62 go func() { 63 for { 64 c.haltMu.Lock() 65 if c.haltReq { 66 c.haltMu.Unlock() 67 close(ch) 68 return 69 } 70 c.haltMu.Unlock() 71 state := new(api.DebuggerState) 72 err := c.call("Command", &api.DebuggerCommand{Name: api.Continue}, state) 73 if err != nil { 74 state.Err = err 75 } 76 if state.Exited { 77 // Error types apparently cannot be marshalled by Go correctly. Must reset error here. 78 //lint:ignore ST1005 backwards compatibility 79 state.Err = fmt.Errorf("Process %d has exited with status %d", c.ProcessPid(), state.ExitStatus) 80 } 81 ch <- state 82 if err != nil || state.Exited { 83 close(ch) 84 return 85 } 86 87 isbreakpoint := false 88 istracepoint := true 89 for i := range state.Threads { 90 if state.Threads[i].Breakpoint != nil { 91 isbreakpoint = true 92 istracepoint = istracepoint && state.Threads[i].Breakpoint.Tracepoint 93 } 94 } 95 96 if !isbreakpoint || !istracepoint { 97 close(ch) 98 return 99 } 100 } 101 }() 102 return ch 103 } 104 105 func (c *RPCClient) Next() (*api.DebuggerState, error) { 106 state := new(api.DebuggerState) 107 err := c.call("Command", &api.DebuggerCommand{Name: api.Next}, state) 108 return state, err 109 } 110 111 func (c *RPCClient) Step() (*api.DebuggerState, error) { 112 state := new(api.DebuggerState) 113 err := c.call("Command", &api.DebuggerCommand{Name: api.Step}, state) 114 return state, err 115 } 116 117 func (c *RPCClient) Call(expr string) (*api.DebuggerState, error) { 118 state := new(api.DebuggerState) 119 err := c.call("Command", &api.DebuggerCommand{Name: api.Call, Expr: expr}, state) 120 return state, err 121 } 122 123 func (c *RPCClient) StepInstruction() (*api.DebuggerState, error) { 124 state := new(api.DebuggerState) 125 err := c.call("Command", &api.DebuggerCommand{Name: api.StepInstruction}, state) 126 return state, err 127 } 128 129 func (c *RPCClient) ReverseStepInstruction() (*api.DebuggerState, error) { 130 state := new(api.DebuggerState) 131 err := c.call("Command", &api.DebuggerCommand{Name: api.ReverseStepInstruction}, state) 132 return state, err 133 } 134 135 func (c *RPCClient) SwitchThread(threadID int) (*api.DebuggerState, error) { 136 state := new(api.DebuggerState) 137 cmd := &api.DebuggerCommand{ 138 Name: api.SwitchThread, 139 ThreadID: threadID, 140 } 141 err := c.call("Command", cmd, state) 142 return state, err 143 } 144 145 func (c *RPCClient) SwitchGoroutine(goroutineID int) (*api.DebuggerState, error) { 146 state := new(api.DebuggerState) 147 cmd := &api.DebuggerCommand{ 148 Name: api.SwitchGoroutine, 149 GoroutineID: int64(goroutineID), 150 } 151 err := c.call("Command", cmd, state) 152 return state, err 153 } 154 155 func (c *RPCClient) Halt() (*api.DebuggerState, error) { 156 state := new(api.DebuggerState) 157 c.haltMu.Lock() 158 c.haltReq = true 159 c.haltMu.Unlock() 160 err := c.call("Command", &api.DebuggerCommand{Name: api.Halt}, state) 161 return state, err 162 } 163 164 func (c *RPCClient) GetBreakpoint(id int) (*api.Breakpoint, error) { 165 breakpoint := new(api.Breakpoint) 166 err := c.call("GetBreakpoint", id, breakpoint) 167 return breakpoint, err 168 } 169 170 func (c *RPCClient) GetBreakpointByName(name string) (*api.Breakpoint, error) { 171 breakpoint := new(api.Breakpoint) 172 err := c.call("GetBreakpointByName", name, breakpoint) 173 return breakpoint, err 174 } 175 176 func (c *RPCClient) CreateBreakpoint(breakPoint *api.Breakpoint) (*api.Breakpoint, error) { 177 newBreakpoint := new(api.Breakpoint) 178 err := c.call("CreateBreakpoint", breakPoint, &newBreakpoint) 179 return newBreakpoint, err 180 } 181 182 func (c *RPCClient) ListBreakpoints() ([]*api.Breakpoint, error) { 183 var breakpoints []*api.Breakpoint 184 err := c.call("ListBreakpoints", nil, &breakpoints) 185 return breakpoints, err 186 } 187 188 func (c *RPCClient) ClearBreakpoint(id int) (*api.Breakpoint, error) { 189 bp := new(api.Breakpoint) 190 err := c.call("ClearBreakpoint", id, bp) 191 return bp, err 192 } 193 194 func (c *RPCClient) ClearBreakpointByName(name string) (*api.Breakpoint, error) { 195 bp := new(api.Breakpoint) 196 err := c.call("ClearBreakpointByName", name, bp) 197 return bp, err 198 } 199 200 func (c *RPCClient) AmendBreakpoint(bp *api.Breakpoint) error { 201 err := c.call("AmendBreakpoint", bp, nil) 202 return err 203 } 204 205 func (c *RPCClient) CancelNext() error { 206 return errAPIUnsupported 207 } 208 209 func (c *RPCClient) ListThreads() ([]*api.Thread, error) { 210 var threads []*api.Thread 211 err := c.call("ListThreads", nil, &threads) 212 return threads, err 213 } 214 215 func (c *RPCClient) GetThread(id int) (*api.Thread, error) { 216 thread := new(api.Thread) 217 err := c.call("GetThread", id, &thread) 218 return thread, err 219 } 220 221 func (c *RPCClient) EvalVariable(scope api.EvalScope, symbol string) (*api.Variable, error) { 222 v := new(api.Variable) 223 err := c.call("EvalSymbol", EvalSymbolArgs{scope, symbol}, v) 224 return v, err 225 } 226 227 func (c *RPCClient) SetVariable(scope api.EvalScope, symbol, value string) error { 228 var unused int 229 return c.call("SetSymbol", SetSymbolArgs{scope, symbol, value}, &unused) 230 } 231 232 func (c *RPCClient) ListSources(filter string) ([]string, error) { 233 var sources []string 234 err := c.call("ListSources", filter, &sources) 235 return sources, err 236 } 237 238 func (c *RPCClient) ListFunctions(filter string) ([]string, error) { 239 var funcs []string 240 err := c.call("ListFunctions", filter, &funcs) 241 return funcs, err 242 } 243 244 func (c *RPCClient) ListTypes(filter string) ([]string, error) { 245 var types []string 246 err := c.call("ListTypes", filter, &types) 247 return types, err 248 } 249 250 func (c *RPCClient) ListPackageVariables(filter string) ([]api.Variable, error) { 251 var vars []api.Variable 252 err := c.call("ListPackageVars", filter, &vars) 253 return vars, err 254 } 255 256 func (c *RPCClient) ListPackageVariablesFor(threadID int, filter string) ([]api.Variable, error) { 257 var vars []api.Variable 258 err := c.call("ListThreadPackageVars", &ThreadListArgs{Id: threadID, Filter: filter}, &vars) 259 return vars, err 260 } 261 262 func (c *RPCClient) ListLocalVariables(scope api.EvalScope) ([]api.Variable, error) { 263 var vars []api.Variable 264 err := c.call("ListLocalVars", scope, &vars) 265 return vars, err 266 } 267 268 func (c *RPCClient) ListRegisters() (string, error) { 269 var regs string 270 err := c.call("ListRegisters", nil, ®s) 271 return regs, err 272 } 273 274 func (c *RPCClient) ListFunctionArgs(scope api.EvalScope) ([]api.Variable, error) { 275 var vars []api.Variable 276 err := c.call("ListFunctionArgs", scope, &vars) 277 return vars, err 278 } 279 280 func (c *RPCClient) ListGoroutines() ([]*api.Goroutine, error) { 281 var goroutines []*api.Goroutine 282 err := c.call("ListGoroutines", nil, &goroutines) 283 return goroutines, err 284 } 285 286 func (c *RPCClient) Stacktrace(goroutineId, depth int, full bool) ([]api.Stackframe, error) { 287 var locations []api.Stackframe 288 err := c.call("StacktraceGoroutine", &StacktraceGoroutineArgs{Id: goroutineId, Depth: depth, Full: full}, &locations) 289 return locations, err 290 } 291 292 func (c *RPCClient) AttachedToExistingProcess() bool { 293 var answer bool 294 c.call("AttachedToExistingProcess", nil, &answer) 295 return answer 296 } 297 298 func (c *RPCClient) FindLocation(scope api.EvalScope, loc string) ([]api.Location, error) { 299 var answer []api.Location 300 err := c.call("FindLocation", FindLocationArgs{scope, loc}, &answer) 301 return answer, err 302 } 303 304 // DisassembleRange disassembles code between startPC and endPC 305 func (c *RPCClient) DisassembleRange(scope api.EvalScope, startPC, endPC uint64, flavour api.AssemblyFlavour) (api.AsmInstructions, error) { 306 var r api.AsmInstructions 307 err := c.call("Disassemble", DisassembleRequest{scope, startPC, endPC, flavour}, &r) 308 return r, err 309 } 310 311 // DisassemblePC disassembles function containing pc 312 func (c *RPCClient) DisassemblePC(scope api.EvalScope, pc uint64, flavour api.AssemblyFlavour) (api.AsmInstructions, error) { 313 var r api.AsmInstructions 314 err := c.call("Disassemble", DisassembleRequest{scope, pc, 0, flavour}, &r) 315 return r, err 316 } 317 318 func (c *RPCClient) call(method string, args, reply interface{}) error { 319 return c.client.Call("RPCServer."+method, args, reply) 320 }