package rpc2 import ( "errors" "fmt" "regexp" "sort" "time" "github.com/go-delve/delve/pkg/dwarf/op" "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" ) 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} } type ProcessPidIn struct { } type ProcessPidOut struct { Pid int } // ProcessPid returns the pid of the process we are debugging. func (s *RPCServer) ProcessPid(arg ProcessPidIn, out *ProcessPidOut) error { out.Pid = s.debugger.ProcessPid() return nil } type LastModifiedIn struct { } type LastModifiedOut struct { Time time.Time } func (s *RPCServer) LastModified(arg LastModifiedIn, out *LastModifiedOut) error { out.Time = s.debugger.LastModified() return nil } type DetachIn struct { Kill bool } type DetachOut struct { } // Detach detaches the debugger, optionally killing the process. func (s *RPCServer) Detach(arg DetachIn, out *DetachOut) error { return s.debugger.Detach(arg.Kill) } type RestartIn struct { // Position to restart from, if it starts with 'c' it's a checkpoint ID, // otherwise it's an event number. Only valid for recorded targets. Position string // ResetArgs tell whether NewArgs and NewRedirects should take effect. ResetArgs bool // NewArgs are arguments to launch a new process. They replace only the // argv[1] and later. Argv[0] cannot be changed. NewArgs []string // When Rerecord is set the target will be rerecorded Rerecord bool // When Rebuild is set the process will be build again Rebuild bool NewRedirects [3]string } type RestartOut struct { DiscardedBreakpoints []api.DiscardedBreakpoint } // Restart restarts program. func (s *RPCServer) Restart(arg RestartIn, cb service.RPCCallback) { close(cb.SetupDoneChan()) if s.config.Debugger.AttachPid != 0 { cb.Return(nil, errors.New("cannot restart process Delve did not create")) return } var out RestartOut var err error out.DiscardedBreakpoints, err = s.debugger.Restart(arg.Rerecord, arg.Position, arg.ResetArgs, arg.NewArgs, arg.NewRedirects, arg.Rebuild) cb.Return(out, err) } type StateIn struct { // If NonBlocking is true State will return immediately even if the target process is running. NonBlocking bool } type StateOut struct { State *api.DebuggerState } // State returns the current debugger state. func (s *RPCServer) State(arg StateIn, cb service.RPCCallback) { close(cb.SetupDoneChan()) var out StateOut st, err := s.debugger.State(arg.NonBlocking) if err != nil { cb.Return(nil, err) return } out.State = st cb.Return(out, nil) } type CommandOut struct { State api.DebuggerState } // Command interrupts, continues and steps through the program. func (s *RPCServer) Command(command api.DebuggerCommand, cb service.RPCCallback) { st, err := s.debugger.Command(&command, cb.SetupDoneChan(), cb.DisconnectChan()) if err != nil { cb.Return(nil, err) return } var out CommandOut out.State = *st cb.Return(out, nil) } type GetBufferedTracepointsIn struct { } type GetBufferedTracepointsOut struct { TracepointResults []api.TracepointResult } func (s *RPCServer) GetBufferedTracepoints(arg GetBufferedTracepointsIn, out *GetBufferedTracepointsOut) error { out.TracepointResults = s.debugger.GetBufferedTracepoints() return nil } type GetBreakpointIn struct { Id int Name string } type GetBreakpointOut struct { Breakpoint api.Breakpoint } // GetBreakpoint gets a breakpoint by Name (if Name is not an empty string) or by ID. func (s *RPCServer) GetBreakpoint(arg GetBreakpointIn, out *GetBreakpointOut) error { var bp *api.Breakpoint if arg.Name != "" { bp = s.debugger.FindBreakpointByName(arg.Name) if bp == nil { return fmt.Errorf("no breakpoint with name %s", arg.Name) } } else { bp = s.debugger.FindBreakpoint(arg.Id) if bp == nil { return fmt.Errorf("no breakpoint with id %d", arg.Id) } } out.Breakpoint = *bp return nil } type StacktraceIn struct { Id int64 Depth int Full bool Defers bool // read deferred functions (equivalent to passing StacktraceReadDefers in Opts) Opts api.StacktraceOptions Cfg *api.LoadConfig } type StacktraceOut struct { Locations []api.Stackframe } // Stacktrace returns stacktrace of goroutine Id up to the specified Depth. // // If Full is set it will also the variable of all local variables // and function arguments of all stack frames. func (s *RPCServer) Stacktrace(arg StacktraceIn, out *StacktraceOut) error { cfg := arg.Cfg if cfg == nil && arg.Full { cfg = &api.LoadConfig{FollowPointers: true, MaxVariableRecurse: 1, MaxStringLen: 64, MaxArrayValues: 64, MaxStructFields: -1} } if arg.Defers { arg.Opts |= api.StacktraceReadDefers } var err error rawlocs, err := s.debugger.Stacktrace(arg.Id, arg.Depth, arg.Opts) if err != nil { return err } out.Locations, err = s.debugger.ConvertStacktrace(rawlocs, api.LoadConfigToProc(cfg)) return err } type AncestorsIn struct { GoroutineID int64 NumAncestors int Depth int } type AncestorsOut struct { Ancestors []api.Ancestor } // Ancestors returns the stacktraces for the ancestors of a goroutine. func (s *RPCServer) Ancestors(arg AncestorsIn, out *AncestorsOut) error { var err error out.Ancestors, err = s.debugger.Ancestors(arg.GoroutineID, arg.NumAncestors, arg.Depth) return err } type ListBreakpointsIn struct { All bool } type ListBreakpointsOut struct { Breakpoints []*api.Breakpoint } // ListBreakpoints gets all breakpoints. func (s *RPCServer) ListBreakpoints(arg ListBreakpointsIn, out *ListBreakpointsOut) error { out.Breakpoints = s.debugger.Breakpoints(arg.All) return nil } type CreateBreakpointIn struct { Breakpoint api.Breakpoint LocExpr string SubstitutePathRules [][2]string Suspended bool } type CreateBreakpointOut struct { Breakpoint api.Breakpoint } // CreateBreakpoint creates a new breakpoint. The client is expected to populate `CreateBreakpointIn` // with an `api.Breakpoint` struct describing where to set the breakpoint. For more information on // how to properly request a breakpoint via the `api.Breakpoint` struct see the documentation for // `debugger.CreateBreakpoint` here: https://pkg.go.dev/github.com/go-delve/delve/service/debugger#Debugger.CreateBreakpoint. func (s *RPCServer) CreateBreakpoint(arg CreateBreakpointIn, out *CreateBreakpointOut) error { if err := api.ValidBreakpointName(arg.Breakpoint.Name); err != nil { return err } createdbp, err := s.debugger.CreateBreakpoint(&arg.Breakpoint, arg.LocExpr, arg.SubstitutePathRules, arg.Suspended) if err != nil { return err } out.Breakpoint = *createdbp return nil } type CreateEBPFTracepointIn struct { FunctionName string } type CreateEBPFTracepointOut struct { Breakpoint api.Breakpoint } func (s *RPCServer) CreateEBPFTracepoint(arg CreateEBPFTracepointIn, out *CreateEBPFTracepointOut) error { return s.debugger.CreateEBPFTracepoint(arg.FunctionName) } type ClearBreakpointIn struct { Id int Name string } type ClearBreakpointOut struct { Breakpoint *api.Breakpoint } // ClearBreakpoint deletes a breakpoint by Name (if Name is not an // empty string) or by ID. func (s *RPCServer) ClearBreakpoint(arg ClearBreakpointIn, out *ClearBreakpointOut) error { var bp *api.Breakpoint if arg.Name != "" { bp = s.debugger.FindBreakpointByName(arg.Name) if bp == nil { return fmt.Errorf("no breakpoint with name %s", arg.Name) } } else { bp = s.debugger.FindBreakpoint(arg.Id) if bp == nil { return fmt.Errorf("no breakpoint with id %d", arg.Id) } } deleted, err := s.debugger.ClearBreakpoint(bp) if err != nil { return err } out.Breakpoint = deleted return nil } type ToggleBreakpointIn struct { Id int Name string } type ToggleBreakpointOut struct { Breakpoint *api.Breakpoint } // ToggleBreakpoint toggles on or off a breakpoint by Name (if Name is not an // empty string) or by ID. func (s *RPCServer) ToggleBreakpoint(arg ToggleBreakpointIn, out *ToggleBreakpointOut) error { var bp *api.Breakpoint if arg.Name != "" { bp = s.debugger.FindBreakpointByName(arg.Name) if bp == nil { return fmt.Errorf("no breakpoint with name %s", arg.Name) } } else { bp = s.debugger.FindBreakpoint(arg.Id) if bp == nil { return fmt.Errorf("no breakpoint with id %d", arg.Id) } } bp.Disabled = !bp.Disabled if err := api.ValidBreakpointName(bp.Name); err != nil { return err } if err := s.debugger.AmendBreakpoint(bp); err != nil { return err } out.Breakpoint = bp return nil } type AmendBreakpointIn struct { Breakpoint api.Breakpoint } type AmendBreakpointOut struct { } // AmendBreakpoint allows user to update an existing breakpoint // for example to change the information retrieved when the // breakpoint is hit or to change, add or remove the break condition. // // arg.Breakpoint.ID must be a valid breakpoint ID func (s *RPCServer) AmendBreakpoint(arg AmendBreakpointIn, out *AmendBreakpointOut) error { if err := api.ValidBreakpointName(arg.Breakpoint.Name); err != nil { return err } return s.debugger.AmendBreakpoint(&arg.Breakpoint) } type CancelNextIn struct { } type CancelNextOut struct { } func (s *RPCServer) CancelNext(arg CancelNextIn, out *CancelNextOut) error { return s.debugger.CancelNext() } type ListThreadsIn struct { } type ListThreadsOut struct { Threads []*api.Thread } // ListThreads lists all threads. func (s *RPCServer) ListThreads(arg ListThreadsIn, out *ListThreadsOut) (err error) { threads, err := s.debugger.Threads() if err != nil { return err } s.debugger.LockTarget() defer s.debugger.UnlockTarget() out.Threads = api.ConvertThreads(threads, s.debugger.ConvertThreadBreakpoint) return nil } type GetThreadIn struct { Id int } type GetThreadOut struct { Thread *api.Thread } // GetThread gets a thread by its ID. func (s *RPCServer) GetThread(arg GetThreadIn, out *GetThreadOut) error { t, err := s.debugger.FindThread(arg.Id) if err != nil { return err } if t == nil { return fmt.Errorf("no thread with id %d", arg.Id) } s.debugger.LockTarget() defer s.debugger.UnlockTarget() out.Thread = api.ConvertThread(t, s.debugger.ConvertThreadBreakpoint(t)) return nil } type ListPackageVarsIn struct { Filter string Cfg api.LoadConfig } type ListPackageVarsOut struct { Variables []api.Variable } // ListPackageVars lists all package variables in the context of the current thread. func (s *RPCServer) ListPackageVars(arg ListPackageVarsIn, out *ListPackageVarsOut) error { vars, err := s.debugger.PackageVariables(arg.Filter, *api.LoadConfigToProc(&arg.Cfg)) if err != nil { return err } out.Variables = api.ConvertVars(vars) return nil } type ListRegistersIn struct { ThreadID int IncludeFp bool Scope *api.EvalScope } type ListRegistersOut struct { Registers string Regs api.Registers } // ListRegisters lists registers and their values. // If ListRegistersIn.Scope is not nil the registers of that eval scope will // be returned, otherwise ListRegistersIn.ThreadID will be used. func (s *RPCServer) ListRegisters(arg ListRegistersIn, out *ListRegistersOut) error { if arg.ThreadID == 0 && arg.Scope == nil { state, err := s.debugger.State(false) if err != nil { return err } arg.ThreadID = state.CurrentThread.ID } var regs *op.DwarfRegisters var err error if arg.Scope != nil { regs, err = s.debugger.ScopeRegisters(arg.Scope.GoroutineID, arg.Scope.Frame, arg.Scope.DeferredCall) } else { regs, err = s.debugger.ThreadRegisters(arg.ThreadID) } if err != nil { return err } out.Regs = api.ConvertRegisters(regs, s.debugger.DwarfRegisterToString, arg.IncludeFp) out.Registers = out.Regs.String() return nil } type ListLocalVarsIn struct { Scope api.EvalScope Cfg api.LoadConfig } type ListLocalVarsOut struct { Variables []api.Variable } // ListLocalVars lists all local variables in scope. func (s *RPCServer) ListLocalVars(arg ListLocalVarsIn, out *ListLocalVarsOut) error { vars, err := s.debugger.LocalVariables(arg.Scope.GoroutineID, arg.Scope.Frame, arg.Scope.DeferredCall, *api.LoadConfigToProc(&arg.Cfg)) if err != nil { return err } out.Variables = api.ConvertVars(vars) return nil } type ListFunctionArgsIn struct { Scope api.EvalScope Cfg api.LoadConfig } type ListFunctionArgsOut struct { Args []api.Variable } // ListFunctionArgs lists all arguments to the current function func (s *RPCServer) ListFunctionArgs(arg ListFunctionArgsIn, out *ListFunctionArgsOut) error { vars, err := s.debugger.FunctionArguments(arg.Scope.GoroutineID, arg.Scope.Frame, arg.Scope.DeferredCall, *api.LoadConfigToProc(&arg.Cfg)) if err != nil { return err } out.Args = api.ConvertVars(vars) return nil } type EvalIn struct { Scope api.EvalScope Expr string Cfg *api.LoadConfig } type EvalOut struct { Variable *api.Variable } // Eval returns a variable in the specified context. // // See https://github.com/go-delve/delve/blob/master/Documentation/cli/expr.md // for a description of acceptable values of arg.Expr. func (s *RPCServer) Eval(arg EvalIn, out *EvalOut) error { cfg := arg.Cfg if cfg == nil { cfg = &api.LoadConfig{FollowPointers: true, MaxVariableRecurse: 1, MaxStringLen: 64, MaxArrayValues: 64, MaxStructFields: -1} } pcfg := *api.LoadConfigToProc(cfg) v, err := s.debugger.EvalVariableInScope(arg.Scope.GoroutineID, arg.Scope.Frame, arg.Scope.DeferredCall, arg.Expr, pcfg) if err != nil { return err } out.Variable = api.ConvertVar(v) return nil } type SetIn struct { Scope api.EvalScope Symbol string Value string } type SetOut struct { } // Set sets the value of a variable. Only numerical types and // pointers are currently supported. func (s *RPCServer) Set(arg SetIn, out *SetOut) error { return s.debugger.SetVariableInScope(arg.Scope.GoroutineID, arg.Scope.Frame, arg.Scope.DeferredCall, arg.Symbol, arg.Value) } type ListSourcesIn struct { Filter string } type ListSourcesOut struct { Sources []string } // ListSources lists all source files in the process matching filter. func (s *RPCServer) ListSources(arg ListSourcesIn, out *ListSourcesOut) error { ss, err := s.debugger.Sources(arg.Filter) if err != nil { return err } out.Sources = ss return nil } type ListFunctionsIn struct { Filter string } type ListFunctionsOut struct { Funcs []string } // ListFunctions lists all functions in the process matching filter. func (s *RPCServer) ListFunctions(arg ListFunctionsIn, out *ListFunctionsOut) error { fns, err := s.debugger.Functions(arg.Filter) if err != nil { return err } out.Funcs = fns return nil } type ListTypesIn struct { Filter string } type ListTypesOut struct { Types []string } // ListTypes lists all types in the process matching filter. func (s *RPCServer) ListTypes(arg ListTypesIn, out *ListTypesOut) error { tps, err := s.debugger.Types(arg.Filter) if err != nil { return err } out.Types = tps return nil } type ListGoroutinesIn struct { Start int Count int Filters []api.ListGoroutinesFilter api.GoroutineGroupingOptions EvalScope *api.EvalScope } type ListGoroutinesOut struct { Goroutines []*api.Goroutine Nextg int Groups []api.GoroutineGroup TooManyGroups bool } // ListGoroutines lists all goroutines. // If Count is specified ListGoroutines will return at the first Count // goroutines and an index in Nextg, that can be passed as the Start // parameter, to get more goroutines from ListGoroutines. // Passing a value of Start that wasn't returned by ListGoroutines will skip // an undefined number of goroutines. // // If arg.Filters are specified the list of returned goroutines is filtered // applying the specified filters. // For example: // // ListGoroutinesFilter{ Kind: ListGoroutinesFilterUserLoc, Negated: false, Arg: "afile.go" } // // will only return goroutines whose UserLoc contains "afile.go" as a substring. // More specifically a goroutine matches a location filter if the specified // location, formatted like this: // // filename:lineno in function // // contains Arg[0] as a substring. // // Filters can also be applied to goroutine labels: // // ListGoroutineFilter{ Kind: ListGoroutinesFilterLabel, Negated: false, Arg: "key=value" } // // this filter will only return goroutines that have a key=value label. // // If arg.GroupBy is not GoroutineFieldNone then the goroutines will // be grouped with the specified criterion. // If the value of arg.GroupBy is GoroutineLabel goroutines will // be grouped by the value of the label with key GroupByKey. // For each group a maximum of MaxGroupMembers example goroutines are // returned, as well as the total number of goroutines in the group. func (s *RPCServer) ListGoroutines(arg ListGoroutinesIn, out *ListGoroutinesOut) error { //TODO(aarzilli): if arg contains a running goroutines filter (not negated) // and start == 0 and count == 0 then we can optimize this by just looking // at threads directly. var gs []*proc.G var nextg int var err error var gsLoaded bool for _, filter := range arg.Filters { if filter.Kind == api.GoroutineWaitingOnChannel { if filter.Negated { return errors.New("channel filter can not be negated") } if arg.Count == 0 { return errors.New("count == 0 not allowed with a channel filter") } if arg.EvalScope == nil { return errors.New("channel filter without eval scope") } gs, err = s.debugger.ChanGoroutines(arg.EvalScope.GoroutineID, arg.EvalScope.Frame, arg.EvalScope.DeferredCall, filter.Arg, arg.Start, arg.Count) if len(gs) == arg.Count { nextg = arg.Start + len(gs) } else { nextg = -1 } gsLoaded = true break } } if !gsLoaded { gs, nextg, err = s.debugger.Goroutines(arg.Start, arg.Count) } if err != nil { return err } gs = s.debugger.FilterGoroutines(gs, arg.Filters) gs, out.Groups, out.TooManyGroups = s.debugger.GroupGoroutines(gs, &arg.GoroutineGroupingOptions) s.debugger.LockTarget() defer s.debugger.UnlockTarget() out.Goroutines = api.ConvertGoroutines(s.debugger.Target(), gs) out.Nextg = nextg return nil } type AttachedToExistingProcessIn struct { } type AttachedToExistingProcessOut struct { Answer bool } // AttachedToExistingProcess returns whether we attached to a running process or not func (s *RPCServer) AttachedToExistingProcess(arg AttachedToExistingProcessIn, out *AttachedToExistingProcessOut) error { if s.config.Debugger.AttachPid != 0 { out.Answer = true } return nil } type FindLocationIn struct { Scope api.EvalScope Loc string IncludeNonExecutableLines bool // SubstitutePathRules is a slice of source code path substitution rules, // the first entry of each pair is the path of a directory as it appears in // the executable file (i.e. the location of a source file when the program // was compiled), the second entry of each pair is the location of the same // directory on the client system. SubstitutePathRules [][2]string } type FindLocationOut struct { Locations []api.Location SubstituteLocExpr string // if this isn't an empty string it should be passed as the location expression for CreateBreakpoint instead of the original location expression } // FindLocation returns concrete location information described by a location expression. // // loc ::= : | [:] | // | (+|-) | | *
// * can be the full path of a file or just a suffix // * ::= .. | .(*). | . | . | (*). | // must be unambiguous // * // will return a location for each function matched by regex // * + returns a location for the line that is lines after the current line // * - returns a location for the line that is lines before the current line // * returns a location for a line in the current file // * *
returns the location corresponding to the specified address // // NOTE: this function does not actually set breakpoints. func (s *RPCServer) FindLocation(arg FindLocationIn, out *FindLocationOut) error { var err error out.Locations, out.SubstituteLocExpr, err = s.debugger.FindLocation(arg.Scope.GoroutineID, arg.Scope.Frame, arg.Scope.DeferredCall, arg.Loc, arg.IncludeNonExecutableLines, arg.SubstitutePathRules) return err } type DisassembleIn struct { Scope api.EvalScope StartPC, EndPC uint64 Flavour api.AssemblyFlavour } type DisassembleOut struct { Disassemble api.AsmInstructions } // Disassemble code. // // If both StartPC and EndPC are non-zero the specified range will be disassembled, otherwise the function containing StartPC will be disassembled. // // Scope is used to mark the instruction the specified goroutine is stopped at. // // Disassemble will also try to calculate the destination address of an absolute indirect CALL if it happens to be the instruction the selected goroutine is stopped at. func (s *RPCServer) Disassemble(arg DisassembleIn, out *DisassembleOut) error { var err error insts, err := s.debugger.Disassemble(arg.Scope.GoroutineID, arg.StartPC, arg.EndPC) if err != nil { return err } out.Disassemble = make(api.AsmInstructions, len(insts)) for i := range insts { out.Disassemble[i] = api.ConvertAsmInstruction(insts[i], s.debugger.AsmInstructionText(&insts[i], proc.AssemblyFlavour(arg.Flavour))) } return nil } type RecordedIn struct { } type RecordedOut struct { Recorded bool TraceDirectory string } func (s *RPCServer) Recorded(arg RecordedIn, out *RecordedOut) error { out.Recorded, out.TraceDirectory = s.debugger.Recorded() return nil } type CheckpointIn struct { Where string } type CheckpointOut struct { ID int } func (s *RPCServer) Checkpoint(arg CheckpointIn, out *CheckpointOut) error { var err error out.ID, err = s.debugger.Checkpoint(arg.Where) return err } type ListCheckpointsIn struct { } type ListCheckpointsOut struct { Checkpoints []api.Checkpoint } func (s *RPCServer) ListCheckpoints(arg ListCheckpointsIn, out *ListCheckpointsOut) error { var err error cps, err := s.debugger.Checkpoints() if err != nil { return err } out.Checkpoints = make([]api.Checkpoint, len(cps)) for i := range cps { out.Checkpoints[i] = api.Checkpoint(cps[i]) } return nil } type ClearCheckpointIn struct { ID int } type ClearCheckpointOut struct { } func (s *RPCServer) ClearCheckpoint(arg ClearCheckpointIn, out *ClearCheckpointOut) error { return s.debugger.ClearCheckpoint(arg.ID) } type IsMulticlientIn struct { } type IsMulticlientOut struct { // IsMulticlient returns true if the headless instance was started with --accept-multiclient IsMulticlient bool } func (s *RPCServer) IsMulticlient(arg IsMulticlientIn, out *IsMulticlientOut) error { *out = IsMulticlientOut{ IsMulticlient: s.config.AcceptMulti, } return nil } // FunctionReturnLocationsIn holds arguments for the // FunctionReturnLocationsRPC call. It holds the name of // the function for which all return locations should be // given. type FunctionReturnLocationsIn struct { // FnName is the name of the function for which all // return locations should be given. FnName string } // FunctionReturnLocationsOut holds the result of the FunctionReturnLocations // RPC call. It provides the list of addresses that the given function returns, // for example with a `RET` instruction or `CALL runtime.deferreturn`. type FunctionReturnLocationsOut struct { // Addrs is the list of all locations where the given function returns. Addrs []uint64 } // FunctionReturnLocations is the implements the client call of the same name. Look at client documentation for more information. func (s *RPCServer) FunctionReturnLocations(in FunctionReturnLocationsIn, out *FunctionReturnLocationsOut) error { addrs, err := s.debugger.FunctionReturnLocations(in.FnName) if err != nil { return err } *out = FunctionReturnLocationsOut{ Addrs: addrs, } return nil } // ListDynamicLibrariesIn holds the arguments of ListDynamicLibraries type ListDynamicLibrariesIn struct { } // ListDynamicLibrariesOut holds the return values of ListDynamicLibraries type ListDynamicLibrariesOut struct { List []api.Image } func (s *RPCServer) ListDynamicLibraries(in ListDynamicLibrariesIn, out *ListDynamicLibrariesOut) error { imgs := s.debugger.ListDynamicLibraries() out.List = make([]api.Image, 0, len(imgs)) for i := range imgs { out.List = append(out.List, api.ConvertImage(imgs[i])) } return nil } // ListPackagesBuildInfoIn holds the arguments of ListPackagesBuildInfo. type ListPackagesBuildInfoIn struct { IncludeFiles bool Filter string // if not empty, returns only packages matching the regexp. } // ListPackagesBuildInfoOut holds the return values of ListPackagesBuildInfo. type ListPackagesBuildInfoOut struct { List []api.PackageBuildInfo } // ListPackagesBuildInfo returns the list of packages used by the program along with // the directory where each package was compiled and optionally the list of // files constituting the package. // Note that the directory path is a best guess and may be wrong is a tool // other than cmd/go is used to perform the build. func (s *RPCServer) ListPackagesBuildInfo(in ListPackagesBuildInfoIn, out *ListPackagesBuildInfoOut) error { var pattern *regexp.Regexp if in.Filter != "" { p, err := regexp.Compile(in.Filter) if err != nil { return fmt.Errorf("invalid Filter pattern: %v", err) } pattern = p } pkgs := s.debugger.ListPackagesBuildInfo(in.IncludeFiles) out.List = make([]api.PackageBuildInfo, 0, len(pkgs)) for _, pkg := range pkgs { if pattern != nil && !pattern.MatchString(pkg.ImportPath) { continue } var files []string if len(pkg.Files) > 0 { files = make([]string, 0, len(pkg.Files)) for file := range pkg.Files { files = append(files, file) } } sort.Strings(files) out.List = append(out.List, api.PackageBuildInfo{ ImportPath: pkg.ImportPath, DirectoryPath: pkg.DirectoryPath, Files: files, }) } return nil } // ExamineMemoryIn holds the arguments of ExamineMemory type ExamineMemoryIn struct { Address uint64 Length int } // ExaminedMemoryOut holds the return values of ExamineMemory type ExaminedMemoryOut struct { Mem []byte IsLittleEndian bool } func (s *RPCServer) ExamineMemory(arg ExamineMemoryIn, out *ExaminedMemoryOut) error { if arg.Length > 1000 { return fmt.Errorf("len must be less than or equal to 1000") } Mem, err := s.debugger.ExamineMemory(arg.Address, arg.Length) if err != nil { return err } out.Mem = Mem out.IsLittleEndian = true //TODO: get byte order from debugger.target.BinInfo().Arch return nil } type StopRecordingIn struct { } type StopRecordingOut struct { } func (s *RPCServer) StopRecording(arg StopRecordingIn, cb service.RPCCallback) { close(cb.SetupDoneChan()) var out StopRecordingOut err := s.debugger.StopRecording() if err != nil { cb.Return(nil, err) return } cb.Return(out, nil) } type DumpStartIn struct { Destination string } type DumpStartOut struct { State api.DumpState } // DumpStart starts a core dump to arg.Destination. func (s *RPCServer) DumpStart(arg DumpStartIn, out *DumpStartOut) error { err := s.debugger.DumpStart(arg.Destination) if err != nil { return err } out.State = *api.ConvertDumpState(s.debugger.DumpWait(0)) return nil } type DumpWaitIn struct { Wait int } type DumpWaitOut struct { State api.DumpState } // DumpWait waits for the core dump to finish or for arg.Wait milliseconds. // Wait == 0 means return immediately. // Returns the core dump status func (s *RPCServer) DumpWait(arg DumpWaitIn, out *DumpWaitOut) error { out.State = *api.ConvertDumpState(s.debugger.DumpWait(time.Duration(arg.Wait) * time.Millisecond)) return nil } type DumpCancelIn struct { } type DumpCancelOut struct { } // DumpCancel cancels the core dump. func (s *RPCServer) DumpCancel(arg DumpCancelIn, out *DumpCancelOut) error { return s.debugger.DumpCancel() } type CreateWatchpointIn struct { Scope api.EvalScope Expr string Type api.WatchType } type CreateWatchpointOut struct { *api.Breakpoint } func (s *RPCServer) CreateWatchpoint(arg CreateWatchpointIn, out *CreateWatchpointOut) error { var err error out.Breakpoint, err = s.debugger.CreateWatchpoint(arg.Scope.GoroutineID, arg.Scope.Frame, arg.Scope.DeferredCall, arg.Expr, arg.Type) return err } type BuildIDIn struct { } type BuildIDOut struct { BuildID string } func (s *RPCServer) BuildID(arg BuildIDIn, out *BuildIDOut) error { out.BuildID = s.debugger.BuildID() return nil } type ListTargetsIn struct { } type ListTargetsOut struct { Targets []api.Target } // ListTargets returns the list of targets we are currently attached to. func (s *RPCServer) ListTargets(arg ListTargetsIn, out *ListTargetsOut) error { s.debugger.LockTarget() defer s.debugger.UnlockTarget() out.Targets = []api.Target{} for _, tgt := range s.debugger.TargetGroup().Targets() { if _, err := tgt.Valid(); err == nil { out.Targets = append(out.Targets, *api.ConvertTarget(tgt, s.debugger.ConvertThreadBreakpoint)) } } return nil } type FollowExecIn struct { Enable bool Regex string } type FollowExecOut struct { } // FollowExec enables or disables follow exec mode. func (s *RPCServer) FollowExec(arg FollowExecIn, out *FollowExecOut) error { return s.debugger.FollowExec(arg.Enable, arg.Regex) } type FollowExecEnabledIn struct { } type FollowExecEnabledOut struct { Enabled bool } // FollowExecEnabled returns true if follow exec mode is enabled. func (s *RPCServer) FollowExecEnabled(arg FollowExecEnabledIn, out *FollowExecEnabledOut) error { out.Enabled = s.debugger.FollowExecEnabled() return nil } type DebugInfoDirectoriesIn struct { Set bool List []string } type DebugInfoDirectoriesOut struct { List []string } func (s *RPCServer) DebugInfoDirectories(arg DebugInfoDirectoriesIn, out *DebugInfoDirectoriesOut) error { if arg.Set { s.debugger.SetDebugInfoDirectories(arg.List) } out.List = s.debugger.DebugInfoDirectories() return nil }