
* rebasing on master to implement --followcalls * in progress changes to enable --followcalls * rebase to master: modified function to add children to funcs array * modify main traversal loop * added tests to check different scenarios * added tests to check different scenarios * added tests to check different scenarios * add test to check for overlapping regular expression * modified type of strings array as a return only * changed depth to a simple integer instead of a global map * avoid calling traverse on recursive calls * Added tests for various call graphs to test trace followfuncs * Added tests for various call graphs to test trace followfuncs * Added tests for various call graphs to test trace followfuncs * made auxillary changes for build to go through for new option follow-calls * Add support to print depth of the function calls as well * Added two sample output files for checking * Bypass morestack_noctxt in output for verification testing * Corrected newline error by adding newlines only if the line does not match morestack_noctxt * Added more tests * Cleanup * Updated documentation * fixed error message in fmt.Errorf * Fixed result of Errorf not used error * Addressing review comments to fix depth reporting and other issues * dont invoke stacktrace if tracefollowcalls is enabled, compute depth from main regex root symbol than main.main * Addressing a part of review comments * Added changes to allow deferred functions to be picked up for tracing * Fix issue to avoid printing stack for a simple trace option * Moving most tests to integration2_test.go and keeping only one in dlv_test.go * Moving most tests to integration2_test.go and keeping only one in dlv_test.go * Adding panic-defer test case * Moved rest of the tests to integration2_test.go * addressing review comments: folding Functions and FunctionsDeep, reducing branches by using depth prefix, wrap using %w and other comments * Optimize traversal and parts of printing trace point function and modify trace output layout and adjust tests accordingly * Resolved error occurring due to staticcheck * Implemented traversal algorithm using breadth first search * Addressing review comments on the breadth first search implementation and other comments * Inline filterRuntimeFuncs and remove duplicate initialization
331 lines
8.3 KiB
Go
331 lines
8.3 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.DisconnectChan())
|
|
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, false)
|
|
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)
|
|
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, followCalls int, funcs *[]string) error {
|
|
fns, err := s.debugger.Functions(filter, followCalls)
|
|
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 (s *RPCServer) AttachedToExistingProcess(arg interface{}, answer *bool) error {
|
|
if s.config.Debugger.AttachPid != 0 {
|
|
*answer = true
|
|
}
|
|
return nil
|
|
}
|
|
|
|
type FindLocationArgs struct {
|
|
Scope api.EvalScope
|
|
Loc string
|
|
}
|
|
|
|
func (s *RPCServer) FindLocation(args FindLocationArgs, answer *[]api.Location) error {
|
|
var err error
|
|
*answer, _, err = s.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 (s *RPCServer) Disassemble(args DisassembleRequest, answer *api.AsmInstructions) error {
|
|
var err error
|
|
insts, err := s.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], s.debugger.AsmInstructionText(&insts[i], proc.AssemblyFlavour(args.Flavour)))
|
|
}
|
|
return nil
|
|
}
|