delve/service/rpc1/server.go
Alessandro Arzilli ec5fcc07fe
proc,service/debugger: track how breakpoints were originally set (#3148)
Adds field to breakpoint struct to track how a breakpoint was
originally set, moves the logic for disabling and enabling a breakpoint
to proc.
This will allow creating suspended breakpoints that are automatically
enabled when a plugin is loaded. When follow exec mode is implemented
it will also be possible to automatically enable breakpoints (whether
or not they were suspended) on new child processes, as they are
spawned.

It also improves breakpoint restore after a restart, before this after
a restart breakpoints would be re-enabled using their file:line
position, for breakpoints set using a function name or a location
expression this could be the wrong location after a recompile.

Updates #1653
Updates #2551
2022-09-28 11:35:07 -07:00

331 lines
8.2 KiB
Go

package rpc1
import (
"errors"
"fmt"
"github.com/go-delve/delve/pkg/proc"
"github.com/go-delve/delve/service"
"github.com/go-delve/delve/service/api"
"github.com/go-delve/delve/service/debugger"
)
var defaultLoadConfig = proc.LoadConfig{
FollowPointers: true,
MaxVariableRecurse: 1,
MaxStringLen: 64,
MaxArrayValues: 64,
MaxStructFields: -1,
}
type RPCServer struct {
// config is all the information necessary to start the debugger and server.
config *service.Config
// debugger is a debugger service.
debugger *debugger.Debugger
}
func NewServer(config *service.Config, debugger *debugger.Debugger) *RPCServer {
return &RPCServer{config, debugger}
}
func (s *RPCServer) ProcessPid(arg1 interface{}, pid *int) error {
*pid = s.debugger.ProcessPid()
return nil
}
func (s *RPCServer) Detach(kill bool, ret *int) error {
return s.debugger.Detach(kill)
}
func (s *RPCServer) Restart(arg1 interface{}, arg2 *int) error {
if s.config.Debugger.AttachPid != 0 {
return errors.New("cannot restart process Delve did not create")
}
_, err := s.debugger.Restart(false, "", false, nil, [3]string{}, false)
return err
}
func (s *RPCServer) State(arg interface{}, state *api.DebuggerState) error {
st, err := s.debugger.State(false)
if err != nil {
return err
}
*state = *st
return nil
}
func (s *RPCServer) Command(command *api.DebuggerCommand, cb service.RPCCallback) {
st, err := s.debugger.Command(command, cb.SetupDoneChan())
cb.Return(st, err)
}
func (s *RPCServer) GetBreakpoint(id int, breakpoint *api.Breakpoint) error {
bp := s.debugger.FindBreakpoint(id)
if bp == nil {
return fmt.Errorf("no breakpoint with id %d", id)
}
*breakpoint = *bp
return nil
}
func (s *RPCServer) GetBreakpointByName(name string, breakpoint *api.Breakpoint) error {
bp := s.debugger.FindBreakpointByName(name)
if bp == nil {
return fmt.Errorf("no breakpoint with name %s", name)
}
*breakpoint = *bp
return nil
}
type StacktraceGoroutineArgs struct {
Id int
Depth int
Full bool
}
func (s *RPCServer) StacktraceGoroutine(args *StacktraceGoroutineArgs, locations *[]api.Stackframe) error {
var loadcfg *proc.LoadConfig = nil
if args.Full {
loadcfg = &defaultLoadConfig
}
locs, err := s.debugger.Stacktrace(int64(args.Id), args.Depth, 0)
if err != nil {
return err
}
*locations, err = s.debugger.ConvertStacktrace(locs, loadcfg)
return err
}
func (s *RPCServer) ListBreakpoints(arg interface{}, breakpoints *[]*api.Breakpoint) error {
*breakpoints = s.debugger.Breakpoints(false)
return nil
}
func (s *RPCServer) CreateBreakpoint(bp, newBreakpoint *api.Breakpoint) error {
if err := api.ValidBreakpointName(bp.Name); err != nil {
return err
}
createdbp, err := s.debugger.CreateBreakpoint(bp, "", nil)
if err != nil {
return err
}
*newBreakpoint = *createdbp
return nil
}
func (s *RPCServer) ClearBreakpoint(id int, breakpoint *api.Breakpoint) error {
bp := s.debugger.FindBreakpoint(id)
if bp == nil {
return fmt.Errorf("no breakpoint with id %d", id)
}
deleted, err := s.debugger.ClearBreakpoint(bp)
if err != nil {
return err
}
*breakpoint = *deleted
return nil
}
func (s *RPCServer) ClearBreakpointByName(name string, breakpoint *api.Breakpoint) error {
bp := s.debugger.FindBreakpointByName(name)
if bp == nil {
return fmt.Errorf("no breakpoint with name %s", name)
}
deleted, err := s.debugger.ClearBreakpoint(bp)
if err != nil {
return err
}
*breakpoint = *deleted
return nil
}
func (s *RPCServer) AmendBreakpoint(amend *api.Breakpoint, unused *int) error {
*unused = 0
if err := api.ValidBreakpointName(amend.Name); err != nil {
return err
}
return s.debugger.AmendBreakpoint(amend)
}
func (s *RPCServer) ListThreads(arg interface{}, threads *[]*api.Thread) (err error) {
pthreads, err := s.debugger.Threads()
if err != nil {
return err
}
s.debugger.LockTarget()
defer s.debugger.UnlockTarget()
*threads = api.ConvertThreads(pthreads, s.debugger.ConvertThreadBreakpoint)
return nil
}
func (s *RPCServer) GetThread(id int, thread *api.Thread) error {
t, err := s.debugger.FindThread(id)
if err != nil {
return err
}
if t == nil {
return fmt.Errorf("no thread with id %d", id)
}
s.debugger.LockTarget()
defer s.debugger.UnlockTarget()
*thread = *api.ConvertThread(t, s.debugger.ConvertThreadBreakpoint(t))
return nil
}
func (s *RPCServer) ListPackageVars(filter string, variables *[]api.Variable) error {
vars, err := s.debugger.PackageVariables(filter, defaultLoadConfig)
if err != nil {
return err
}
*variables = api.ConvertVars(vars)
return nil
}
type ThreadListArgs struct {
Id int
Filter string
}
func (s *RPCServer) ListThreadPackageVars(args *ThreadListArgs, variables *[]api.Variable) error {
vars, err := s.debugger.PackageVariables(args.Filter, defaultLoadConfig)
if err != nil {
return err
}
*variables = api.ConvertVars(vars)
return nil
}
func (s *RPCServer) ListRegisters(arg interface{}, registers *string) error {
state, err := s.debugger.State(false)
if err != nil {
return err
}
dregs, err := s.debugger.ThreadRegisters(state.CurrentThread.ID, false)
if err != nil {
return err
}
regs := api.Registers(api.ConvertRegisters(dregs, s.debugger.DwarfRegisterToString, false))
*registers = regs.String()
return nil
}
func (s *RPCServer) ListLocalVars(scope api.EvalScope, variables *[]api.Variable) error {
vars, err := s.debugger.LocalVariables(scope.GoroutineID, scope.Frame, scope.DeferredCall, defaultLoadConfig)
if err != nil {
return err
}
*variables = api.ConvertVars(vars)
return nil
}
func (s *RPCServer) ListFunctionArgs(scope api.EvalScope, variables *[]api.Variable) error {
vars, err := s.debugger.FunctionArguments(scope.GoroutineID, scope.Frame, scope.DeferredCall, defaultLoadConfig)
if err != nil {
return err
}
*variables = api.ConvertVars(vars)
return nil
}
type EvalSymbolArgs struct {
Scope api.EvalScope
Symbol string
}
func (s *RPCServer) EvalSymbol(args EvalSymbolArgs, variable *api.Variable) error {
v, err := s.debugger.EvalVariableInScope(args.Scope.GoroutineID, args.Scope.Frame, args.Scope.DeferredCall, args.Symbol, defaultLoadConfig)
if err != nil {
return err
}
*variable = *api.ConvertVar(v)
return nil
}
type SetSymbolArgs struct {
Scope api.EvalScope
Symbol string
Value string
}
func (s *RPCServer) SetSymbol(args SetSymbolArgs, unused *int) error {
*unused = 0
return s.debugger.SetVariableInScope(args.Scope.GoroutineID, args.Scope.Frame, args.Scope.DeferredCall, args.Symbol, args.Value)
}
func (s *RPCServer) ListSources(filter string, sources *[]string) error {
ss, err := s.debugger.Sources(filter)
if err != nil {
return err
}
*sources = ss
return nil
}
func (s *RPCServer) ListFunctions(filter string, funcs *[]string) error {
fns, err := s.debugger.Functions(filter)
if err != nil {
return err
}
*funcs = fns
return nil
}
func (s *RPCServer) ListTypes(filter string, types *[]string) error {
tps, err := s.debugger.Types(filter)
if err != nil {
return err
}
*types = tps
return nil
}
func (s *RPCServer) ListGoroutines(arg interface{}, goroutines *[]*api.Goroutine) error {
gs, _, err := s.debugger.Goroutines(0, 0)
if err != nil {
return err
}
s.debugger.LockTarget()
s.debugger.UnlockTarget()
*goroutines = api.ConvertGoroutines(s.debugger.Target(), gs)
return nil
}
func (c *RPCServer) AttachedToExistingProcess(arg interface{}, answer *bool) error {
if c.config.Debugger.AttachPid != 0 {
*answer = true
}
return nil
}
type FindLocationArgs struct {
Scope api.EvalScope
Loc string
}
func (c *RPCServer) FindLocation(args FindLocationArgs, answer *[]api.Location) error {
var err error
*answer, err = c.debugger.FindLocation(args.Scope.GoroutineID, args.Scope.Frame, args.Scope.DeferredCall, args.Loc, false, nil)
return err
}
type DisassembleRequest struct {
Scope api.EvalScope
StartPC, EndPC uint64
Flavour api.AssemblyFlavour
}
func (c *RPCServer) Disassemble(args DisassembleRequest, answer *api.AsmInstructions) error {
var err error
insts, err := c.debugger.Disassemble(args.Scope.GoroutineID, args.StartPC, args.EndPC)
if err != nil {
return err
}
*answer = make(api.AsmInstructions, len(insts))
for i := range insts {
(*answer)[i] = api.ConvertAsmInstruction(insts[i], c.debugger.AsmInstructionText(&insts[i], proc.AssemblyFlavour(args.Flavour)))
}
return nil
}