-
Notifications
You must be signed in to change notification settings - Fork 2.1k
/
server.go
1148 lines (972 loc) · 29.9 KB
/
server.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
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
FollowCalls int
}
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, arg.FollowCalls)
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 ::= <filename>:<line> | <function>[:<line>] | /<regex>/ | (+|-)<offset> | <line> | *<address>
// * <filename> can be the full path of a file or just a suffix
// * <function> ::= <package>.<receiver type>.<name> | <package>.(*<receiver type>).<name> | <receiver type>.<name> | <package>.<name> | (*<receiver type>).<name> | <name>
// <function> must be unambiguous
// * /<regex>/ will return a location for each function matched by regex
// * +<offset> returns a location for the line that is <offset> lines after the current line
// * -<offset> returns a location for the line that is <offset> lines before the current line
// * <line> returns a location for a line in the current file
// * *<address> 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
}
const ExamineMemoryLengthLimit = 1 << 16
func (s *RPCServer) ExamineMemory(arg ExamineMemoryIn, out *ExaminedMemoryOut) error {
if arg.Length > ExamineMemoryLengthLimit {
return errors.New("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 {