Show pprof labels in thread names (#3501)
* Add pprofLabelForThreadNames config The config is a string value that indicates the key of a pprof label whose value should be shown as a goroutine name in the threads view.
This commit is contained in:
parent
f558ca4f32
commit
f8c8b33da3
@ -33,6 +33,7 @@ In addition to the general [DAP spec](https://microsoft.github.io/debug-adapter-
|
|||||||
stackTraceDepth<br>
|
stackTraceDepth<br>
|
||||||
showGlobalVariables<br>
|
showGlobalVariables<br>
|
||||||
showRegisters<br>
|
showRegisters<br>
|
||||||
|
showPprofLabels<br>
|
||||||
hideSystemGoroutines<br>
|
hideSystemGoroutines<br>
|
||||||
goroutineFilters
|
goroutineFilters
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -62,7 +62,14 @@ Type "help" followed by the name of a command for more information about it.`
|
|||||||
dlv config substitutePath -clear
|
dlv config substitutePath -clear
|
||||||
|
|
||||||
Adds or removes a path substitution rule. If -clear is used all substitutePath rules are removed.
|
Adds or removes a path substitution rule. If -clear is used all substitutePath rules are removed.
|
||||||
See also Documentation/cli/substitutepath.md.`
|
See also Documentation/cli/substitutepath.md.
|
||||||
|
|
||||||
|
dlv config showPprofLabels <label>
|
||||||
|
dlv config showPprofLabels -clear <label>
|
||||||
|
dlv config showPprofLabels -clear
|
||||||
|
|
||||||
|
Adds or removes a label key to show in the callstack view. If -clear is used without an argument,
|
||||||
|
all labels are removed.`
|
||||||
msgSources = `Print list of source files.
|
msgSources = `Print list of source files.
|
||||||
|
|
||||||
dlv sources [<regex>]
|
dlv sources [<regex>]
|
||||||
@ -138,7 +145,7 @@ func (s *Session) evaluateConfig(_, _ int, expr string) (string, error) {
|
|||||||
Areas: []dap.InvalidatedAreas{"variables"},
|
Areas: []dap.InvalidatedAreas{"variables"},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
case "goroutineFilters", "hideSystemGoroutines":
|
case "goroutineFilters", "hideSystemGoroutines", "showPprofLabels":
|
||||||
// Thread related data has become invalidated.
|
// Thread related data has become invalidated.
|
||||||
s.send(&dap.InvalidatedEvent{
|
s.send(&dap.InvalidatedEvent{
|
||||||
Event: *newEvent("invalidated"),
|
Event: *newEvent("invalidated"),
|
||||||
|
@ -37,6 +37,15 @@ func configureSet(sargs *launchAttachArgs, args string) (bool, string, error) {
|
|||||||
return true, config.ConfigureListByName(sargs, cfgname, "cfgName"), nil
|
return true, config.ConfigureListByName(sargs, cfgname, "cfgName"), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if cfgname == "showPprofLabels" {
|
||||||
|
err := configureSetShowPprofLabels(sargs, rest)
|
||||||
|
if err != nil {
|
||||||
|
return false, "", err
|
||||||
|
}
|
||||||
|
// Print the updated labels
|
||||||
|
return true, config.ConfigureListByName(sargs, cfgname, "cfgName"), nil
|
||||||
|
}
|
||||||
|
|
||||||
err := config.ConfigureSetSimple(rest, cfgname, field)
|
err := config.ConfigureSetSimple(rest, cfgname, field)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, "", err
|
return false, "", err
|
||||||
@ -85,3 +94,37 @@ func configureSetSubstitutePath(args *launchAttachArgs, rest string) error {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func configureSetShowPprofLabels(args *launchAttachArgs, rest string) error {
|
||||||
|
if strings.TrimSpace(rest) == "-clear" {
|
||||||
|
args.ShowPprofLabels = args.ShowPprofLabels[:0]
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
delete := false
|
||||||
|
argv := config.SplitQuotedFields(rest, '"')
|
||||||
|
if len(argv) == 2 && argv[0] == "-clear" {
|
||||||
|
argv = argv[1:]
|
||||||
|
delete = true
|
||||||
|
}
|
||||||
|
switch len(argv) {
|
||||||
|
case 0:
|
||||||
|
// do nothing, let caller show the current list of labels
|
||||||
|
return nil
|
||||||
|
case 1:
|
||||||
|
if delete {
|
||||||
|
for i := range args.ShowPprofLabels {
|
||||||
|
if args.ShowPprofLabels[i] == argv[0] {
|
||||||
|
copy(args.ShowPprofLabels[i:], args.ShowPprofLabels[i+1:])
|
||||||
|
args.ShowPprofLabels = args.ShowPprofLabels[:len(args.ShowPprofLabels)-1]
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fmt.Errorf("could not find label %q", argv[0])
|
||||||
|
} else {
|
||||||
|
args.ShowPprofLabels = append(args.ShowPprofLabels, argv[0])
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("too many arguments to \"config showPprofLabels\"")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -18,14 +18,14 @@ func TestListConfig(t *testing.T) {
|
|||||||
args: args{
|
args: args{
|
||||||
args: &launchAttachArgs{},
|
args: &launchAttachArgs{},
|
||||||
},
|
},
|
||||||
want: formatConfig(0, false, false, "", false, [][2]string{}),
|
want: formatConfig(0, false, false, "", []string{}, false, [][2]string{}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "default values",
|
name: "default values",
|
||||||
args: args{
|
args: args{
|
||||||
args: &defaultArgs,
|
args: &defaultArgs,
|
||||||
},
|
},
|
||||||
want: formatConfig(50, false, false, "", false, [][2]string{}),
|
want: formatConfig(50, false, false, "", []string{}, false, [][2]string{}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "custom values",
|
name: "custom values",
|
||||||
@ -33,11 +33,13 @@ func TestListConfig(t *testing.T) {
|
|||||||
args: &launchAttachArgs{
|
args: &launchAttachArgs{
|
||||||
StackTraceDepth: 35,
|
StackTraceDepth: 35,
|
||||||
ShowGlobalVariables: true,
|
ShowGlobalVariables: true,
|
||||||
|
GoroutineFilters: "SomeFilter",
|
||||||
|
ShowPprofLabels: []string{"SomeLabel"},
|
||||||
substitutePathClientToServer: [][2]string{{"hello", "world"}},
|
substitutePathClientToServer: [][2]string{{"hello", "world"}},
|
||||||
substitutePathServerToClient: [][2]string{{"world", "hello"}},
|
substitutePathServerToClient: [][2]string{{"world", "hello"}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
want: formatConfig(35, true, false, "", false, [][2]string{{"hello", "world"}}),
|
want: formatConfig(35, true, false, "SomeFilter", []string{"SomeLabel"}, false, [][2]string{{"hello", "world"}}),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
|
@ -222,6 +222,11 @@ type launchAttachArgs struct {
|
|||||||
ShowRegisters bool `cfgName:"showRegisters"`
|
ShowRegisters bool `cfgName:"showRegisters"`
|
||||||
// GoroutineFilters are the filters used when loading goroutines.
|
// GoroutineFilters are the filters used when loading goroutines.
|
||||||
GoroutineFilters string `cfgName:"goroutineFilters"`
|
GoroutineFilters string `cfgName:"goroutineFilters"`
|
||||||
|
// ShowPprofLabels is an array of keys of pprof labels to show as a
|
||||||
|
// goroutine name in the threads view. If the array has one element, only
|
||||||
|
// that label's value will be shown; otherwise, each of the labels will be
|
||||||
|
// shown as "key:value". To show all labels, specify the single element "*".
|
||||||
|
ShowPprofLabels []string `cfgName:"showPprofLabels"`
|
||||||
// HideSystemGoroutines indicates if system goroutines should be removed from threads
|
// HideSystemGoroutines indicates if system goroutines should be removed from threads
|
||||||
// responses.
|
// responses.
|
||||||
HideSystemGoroutines bool `cfgName:"hideSystemGoroutines"`
|
HideSystemGoroutines bool `cfgName:"hideSystemGoroutines"`
|
||||||
@ -241,6 +246,7 @@ var defaultArgs = launchAttachArgs{
|
|||||||
HideSystemGoroutines: false,
|
HideSystemGoroutines: false,
|
||||||
ShowRegisters: false,
|
ShowRegisters: false,
|
||||||
GoroutineFilters: "",
|
GoroutineFilters: "",
|
||||||
|
ShowPprofLabels: []string{},
|
||||||
substitutePathClientToServer: [][2]string{},
|
substitutePathClientToServer: [][2]string{},
|
||||||
substitutePathServerToClient: [][2]string{},
|
substitutePathServerToClient: [][2]string{},
|
||||||
}
|
}
|
||||||
@ -280,11 +286,9 @@ const (
|
|||||||
maxStringLenInCallRetVars = 1 << 10 // 1024
|
maxStringLenInCallRetVars = 1 << 10 // 1024
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
// Max number of goroutines that we will return.
|
||||||
// Max number of goroutines that we will return.
|
// This is a var for testing
|
||||||
// This is a var for testing
|
var maxGoroutines = 1 << 10
|
||||||
maxGoroutines = 1 << 10
|
|
||||||
)
|
|
||||||
|
|
||||||
// NewServer creates a new DAP Server. It takes an opened Listener
|
// NewServer creates a new DAP Server. It takes an opened Listener
|
||||||
// via config and assumes its ownership. config.DisconnectChan has to be set;
|
// via config and assumes its ownership. config.DisconnectChan has to be set;
|
||||||
@ -355,6 +359,7 @@ func (s *Session) setLaunchAttachArgs(args LaunchAttachCommonConfig) {
|
|||||||
s.args.ShowRegisters = args.ShowRegisters
|
s.args.ShowRegisters = args.ShowRegisters
|
||||||
s.args.HideSystemGoroutines = args.HideSystemGoroutines
|
s.args.HideSystemGoroutines = args.HideSystemGoroutines
|
||||||
s.args.GoroutineFilters = args.GoroutineFilters
|
s.args.GoroutineFilters = args.GoroutineFilters
|
||||||
|
s.args.ShowPprofLabels = args.ShowPprofLabels
|
||||||
if paths := args.SubstitutePath; len(paths) > 0 {
|
if paths := args.SubstitutePath; len(paths) > 0 {
|
||||||
clientToServer := make([][2]string, 0, len(paths))
|
clientToServer := make([][2]string, 0, len(paths))
|
||||||
serverToClient := make([][2]string, 0, len(paths))
|
serverToClient := make([][2]string, 0, len(paths))
|
||||||
@ -817,7 +822,8 @@ func (s *Session) logToConsole(msg string) {
|
|||||||
Body: dap.OutputEventBody{
|
Body: dap.OutputEventBody{
|
||||||
Output: msg + "\n",
|
Output: msg + "\n",
|
||||||
Category: "console",
|
Category: "console",
|
||||||
}})
|
},
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Session) onInitializeRequest(request *dap.InitializeRequest) {
|
func (s *Session) onInitializeRequest(request *dap.InitializeRequest) {
|
||||||
@ -889,7 +895,7 @@ func (s *Session) onLaunchRequest(request *dap.LaunchRequest) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var args = defaultLaunchConfig // narrow copy for initializing non-zero default values
|
args := defaultLaunchConfig // narrow copy for initializing non-zero default values
|
||||||
if err := unmarshalLaunchAttachArgs(request.Arguments, &args); err != nil {
|
if err := unmarshalLaunchAttachArgs(request.Arguments, &args); err != nil {
|
||||||
s.sendShowUserErrorResponse(request.Request,
|
s.sendShowUserErrorResponse(request.Request,
|
||||||
FailedToLaunch, "Failed to launch", fmt.Sprintf("invalid debug configuration - %v", err))
|
FailedToLaunch, "Failed to launch", fmt.Sprintf("invalid debug configuration - %v", err))
|
||||||
@ -1002,7 +1008,8 @@ func (s *Session) onLaunchRequest(request *dap.LaunchRequest) {
|
|||||||
Body: dap.OutputEventBody{
|
Body: dap.OutputEventBody{
|
||||||
Output: fmt.Sprintf("Build Error: %s\n%s (%s)\n", cmd, strings.TrimSpace(string(out)), err.Error()),
|
Output: fmt.Sprintf("Build Error: %s\n%s (%s)\n", cmd, strings.TrimSpace(string(out)), err.Error()),
|
||||||
Category: "stderr",
|
Category: "stderr",
|
||||||
}})
|
},
|
||||||
|
})
|
||||||
// Users are used to checking the Debug Console for build errors.
|
// Users are used to checking the Debug Console for build errors.
|
||||||
// No need to bother them with a visible pop-up.
|
// No need to bother them with a visible pop-up.
|
||||||
s.sendErrorResponse(request.Request, FailedToLaunch, "Failed to launch",
|
s.sendErrorResponse(request.Request, FailedToLaunch, "Failed to launch",
|
||||||
@ -1035,7 +1042,7 @@ func (s *Session) onLaunchRequest(request *dap.LaunchRequest) {
|
|||||||
argsToLog.Cwd, _ = filepath.Abs(args.Cwd)
|
argsToLog.Cwd, _ = filepath.Abs(args.Cwd)
|
||||||
s.config.log.Debugf("launching binary '%s' with config: %s", debugbinary, prettyPrint(argsToLog))
|
s.config.log.Debugf("launching binary '%s' with config: %s", debugbinary, prettyPrint(argsToLog))
|
||||||
|
|
||||||
var redirected = false
|
redirected := false
|
||||||
switch args.OutputMode {
|
switch args.OutputMode {
|
||||||
case "remote":
|
case "remote":
|
||||||
redirected = true
|
redirected = true
|
||||||
@ -1062,7 +1069,8 @@ func (s *Session) onLaunchRequest(request *dap.LaunchRequest) {
|
|||||||
Body: dap.OutputEventBody{
|
Body: dap.OutputEventBody{
|
||||||
Output: outs,
|
Output: outs,
|
||||||
Category: category,
|
Category: category,
|
||||||
}})
|
},
|
||||||
|
})
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
@ -1687,7 +1695,8 @@ func (s *Session) onConfigurationDoneRequest(request *dap.ConfigurationDoneReque
|
|||||||
func (s *Session) onContinueRequest(request *dap.ContinueRequest, allowNextStateChange *syncflag) {
|
func (s *Session) onContinueRequest(request *dap.ContinueRequest, allowNextStateChange *syncflag) {
|
||||||
s.send(&dap.ContinueResponse{
|
s.send(&dap.ContinueResponse{
|
||||||
Response: *newResponse(request.Request),
|
Response: *newResponse(request.Request),
|
||||||
Body: dap.ContinueResponseBody{AllThreadsContinued: true}})
|
Body: dap.ContinueResponseBody{AllThreadsContinued: true},
|
||||||
|
})
|
||||||
s.runUntilStopAndNotify(api.Continue, allowNextStateChange)
|
s.runUntilStopAndNotify(api.Continue, allowNextStateChange)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1762,7 +1771,8 @@ func (s *Session) onThreadsRequest(request *dap.ThreadsRequest) {
|
|||||||
Body: dap.OutputEventBody{
|
Body: dap.OutputEventBody{
|
||||||
Output: fmt.Sprintf("Unable to retrieve goroutines: %s\n", err.Error()),
|
Output: fmt.Sprintf("Unable to retrieve goroutines: %s\n", err.Error()),
|
||||||
Category: "stderr",
|
Category: "stderr",
|
||||||
}})
|
},
|
||||||
|
})
|
||||||
}
|
}
|
||||||
threads = []dap.Thread{{Id: 1, Name: "Dummy"}}
|
threads = []dap.Thread{{Id: 1, Name: "Dummy"}}
|
||||||
} else if len(gs) == 0 {
|
} else if len(gs) == 0 {
|
||||||
@ -1812,10 +1822,41 @@ func (s *Session) onThreadsRequest(request *dap.ThreadsRequest) {
|
|||||||
if g.Thread != nil && g.Thread.ThreadID() != 0 {
|
if g.Thread != nil && g.Thread.ThreadID() != 0 {
|
||||||
thread = fmt.Sprintf(" (Thread %d)", g.Thread.ThreadID())
|
thread = fmt.Sprintf(" (Thread %d)", g.Thread.ThreadID())
|
||||||
}
|
}
|
||||||
|
var labels strings.Builder
|
||||||
|
writeLabelsForKeys := func(keys []string) {
|
||||||
|
for _, k := range keys {
|
||||||
|
labelValue := g.Labels()[k]
|
||||||
|
if labelValue != "" {
|
||||||
|
labels.WriteByte(' ')
|
||||||
|
labels.WriteString(k)
|
||||||
|
labels.WriteByte(':')
|
||||||
|
labels.WriteString(labelValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(s.args.ShowPprofLabels) == 1 {
|
||||||
|
labelKey := s.args.ShowPprofLabels[0]
|
||||||
|
if labelKey == "*" {
|
||||||
|
keys := make([]string, 0, len(g.Labels()))
|
||||||
|
for k := range g.Labels() {
|
||||||
|
keys = append(keys, k)
|
||||||
|
}
|
||||||
|
sort.Strings(keys)
|
||||||
|
writeLabelsForKeys(keys)
|
||||||
|
} else {
|
||||||
|
labelValue := g.Labels()[labelKey]
|
||||||
|
if labelValue != "" {
|
||||||
|
labels.WriteByte(' ')
|
||||||
|
labels.WriteString(labelValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
writeLabelsForKeys(s.args.ShowPprofLabels)
|
||||||
|
}
|
||||||
// File name and line number are communicated via `stackTrace`
|
// File name and line number are communicated via `stackTrace`
|
||||||
// so no need to include them here.
|
// so no need to include them here.
|
||||||
loc := g.UserCurrent()
|
loc := g.UserCurrent()
|
||||||
threads[i].Name = fmt.Sprintf("%s[Go %d] %s%s", selected, g.ID, fnName(&loc), thread)
|
threads[i].Name = fmt.Sprintf("%s[Go %d%s] %s%s", selected, g.ID, labels.String(), fnName(&loc), thread)
|
||||||
threads[i].Id = int(g.ID)
|
threads[i].Id = int(g.ID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1836,7 +1877,7 @@ func (s *Session) onThreadsRequest(request *dap.ThreadsRequest) {
|
|||||||
// - "remote" -- attaches client to a debugger already attached to a process.
|
// - "remote" -- attaches client to a debugger already attached to a process.
|
||||||
// Required args: none (host/port are used externally to connect)
|
// Required args: none (host/port are used externally to connect)
|
||||||
func (s *Session) onAttachRequest(request *dap.AttachRequest) {
|
func (s *Session) onAttachRequest(request *dap.AttachRequest) {
|
||||||
var args = defaultAttachConfig // narrow copy for initializing non-zero default values
|
args := defaultAttachConfig // narrow copy for initializing non-zero default values
|
||||||
if err := unmarshalLaunchAttachArgs(request.Arguments, &args); err != nil {
|
if err := unmarshalLaunchAttachArgs(request.Arguments, &args); err != nil {
|
||||||
s.sendShowUserErrorResponse(request.Request, FailedToAttach, "Failed to attach", fmt.Sprintf("invalid debug configuration - %v", err))
|
s.sendShowUserErrorResponse(request.Request, FailedToAttach, "Failed to attach", fmt.Sprintf("invalid debug configuration - %v", err))
|
||||||
return
|
return
|
||||||
@ -2597,7 +2638,7 @@ func (s *Session) convertVariableWithOpts(v *proc.Variable, qualifiedNameOrExpr
|
|||||||
|
|
||||||
// Some of the types might be fully or partially not loaded based on LoadConfig.
|
// Some of the types might be fully or partially not loaded based on LoadConfig.
|
||||||
// Those that are fully missing (e.g. due to hitting MaxVariableRecurse), can be reloaded in place.
|
// Those that are fully missing (e.g. due to hitting MaxVariableRecurse), can be reloaded in place.
|
||||||
var reloadVariable = func(v *proc.Variable, qualifiedNameOrExpr string) (value string) {
|
reloadVariable := func(v *proc.Variable, qualifiedNameOrExpr string) (value string) {
|
||||||
// We might be loading variables from the frame that's not topmost, so use
|
// We might be loading variables from the frame that's not topmost, so use
|
||||||
// frame-independent address-based expression, not fully-qualified name as per
|
// frame-independent address-based expression, not fully-qualified name as per
|
||||||
// https://github.com/go-delve/delve/blob/master/Documentation/api/ClientHowto.md#looking-into-variables.
|
// https://github.com/go-delve/delve/blob/master/Documentation/api/ClientHowto.md#looking-into-variables.
|
||||||
@ -3531,6 +3572,7 @@ func newEvent(event string) *dap.Event {
|
|||||||
|
|
||||||
const BetterBadAccessError = `invalid memory address or nil pointer dereference [signal SIGSEGV: segmentation violation]
|
const BetterBadAccessError = `invalid memory address or nil pointer dereference [signal SIGSEGV: segmentation violation]
|
||||||
Unable to propagate EXC_BAD_ACCESS signal to target process and panic (see https://github.com/go-delve/delve/issues/852)`
|
Unable to propagate EXC_BAD_ACCESS signal to target process and panic (see https://github.com/go-delve/delve/issues/852)`
|
||||||
|
|
||||||
const BetterNextWhileNextingError = `Unable to step while the previous step is interrupted by a breakpoint.
|
const BetterNextWhileNextingError = `Unable to step while the previous step is interrupted by a breakpoint.
|
||||||
Use 'Continue' to resume the original step command.`
|
Use 'Continue' to resume the original step command.`
|
||||||
|
|
||||||
|
@ -31,12 +31,16 @@ import (
|
|||||||
"github.com/google/go-dap"
|
"github.com/google/go-dap"
|
||||||
)
|
)
|
||||||
|
|
||||||
const stopOnEntry bool = true
|
const (
|
||||||
const hasChildren bool = true
|
stopOnEntry bool = true
|
||||||
const noChildren bool = false
|
hasChildren bool = true
|
||||||
|
noChildren bool = false
|
||||||
|
)
|
||||||
|
|
||||||
const localsScope = 1000
|
const (
|
||||||
const globalsScope = 1001
|
localsScope = 1000
|
||||||
|
globalsScope = 1001
|
||||||
|
)
|
||||||
|
|
||||||
var testBackend string
|
var testBackend string
|
||||||
|
|
||||||
@ -288,7 +292,8 @@ func TestSessionStop(t *testing.T) {
|
|||||||
}
|
}
|
||||||
session := NewSession(conn, &Config{
|
session := NewSession(conn, &Config{
|
||||||
Config: &service.Config{DisconnectChan: make(chan struct{})},
|
Config: &service.Config{DisconnectChan: make(chan struct{})},
|
||||||
StopTriggered: make(chan struct{})}, nil)
|
StopTriggered: make(chan struct{}),
|
||||||
|
}, nil)
|
||||||
serveDAPCodecDone := make(chan struct{})
|
serveDAPCodecDone := make(chan struct{})
|
||||||
go func() {
|
go func() {
|
||||||
session.ServeDAPCodec()
|
session.ServeDAPCodec()
|
||||||
@ -812,7 +817,8 @@ func TestPreSetBreakpoint(t *testing.T) {
|
|||||||
// wantFrames - number of frames returned (length of StackTraceResponse.Body.StackFrames array).
|
// wantFrames - number of frames returned (length of StackTraceResponse.Body.StackFrames array).
|
||||||
// wantTotalFrames - total number of stack frames available (StackTraceResponse.Body.TotalFrames).
|
// wantTotalFrames - total number of stack frames available (StackTraceResponse.Body.TotalFrames).
|
||||||
func checkStackFramesExact(t *testing.T, got *dap.StackTraceResponse,
|
func checkStackFramesExact(t *testing.T, got *dap.StackTraceResponse,
|
||||||
wantStartName string, wantStartLine interface{}, wantStartID, wantFrames, wantTotalFrames int) {
|
wantStartName string, wantStartLine interface{}, wantStartID, wantFrames, wantTotalFrames int,
|
||||||
|
) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
checkStackFramesNamed("", t, got, wantStartName, wantStartLine, wantStartID, wantFrames, wantTotalFrames, true)
|
checkStackFramesNamed("", t, got, wantStartName, wantStartLine, wantStartID, wantFrames, wantTotalFrames, true)
|
||||||
}
|
}
|
||||||
@ -883,7 +889,8 @@ func TestFilterGoroutines(t *testing.T) {
|
|||||||
client.LaunchRequestWithArgs(map[string]interface{}{
|
client.LaunchRequestWithArgs(map[string]interface{}{
|
||||||
"mode": "exec",
|
"mode": "exec",
|
||||||
"program": fixture.Path,
|
"program": fixture.Path,
|
||||||
"stopOnEntry": !stopOnEntry})
|
"stopOnEntry": !stopOnEntry,
|
||||||
|
})
|
||||||
},
|
},
|
||||||
// Set breakpoints
|
// Set breakpoints
|
||||||
fixture.Source, []int{30},
|
fixture.Source, []int{30},
|
||||||
@ -920,17 +927,19 @@ func TestFilterGoroutines(t *testing.T) {
|
|||||||
},
|
},
|
||||||
disconnect: false,
|
disconnect: false,
|
||||||
}})
|
}})
|
||||||
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkStackFramesHasMore(t *testing.T, got *dap.StackTraceResponse,
|
func checkStackFramesHasMore(t *testing.T, got *dap.StackTraceResponse,
|
||||||
wantStartName string, wantStartLine, wantStartID, wantFrames, wantTotalFrames int) {
|
wantStartName string, wantStartLine, wantStartID, wantFrames, wantTotalFrames int,
|
||||||
|
) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
checkStackFramesNamed("", t, got, wantStartName, wantStartLine, wantStartID, wantFrames, wantTotalFrames, false)
|
checkStackFramesNamed("", t, got, wantStartName, wantStartLine, wantStartID, wantFrames, wantTotalFrames, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkStackFramesNamed(testName string, t *testing.T, got *dap.StackTraceResponse,
|
func checkStackFramesNamed(testName string, t *testing.T, got *dap.StackTraceResponse,
|
||||||
wantStartName string, wantStartLine interface{}, wantStartID, wantFrames, wantTotalFrames int, totalExact bool) {
|
wantStartName string, wantStartLine interface{}, wantStartID, wantFrames, wantTotalFrames int, totalExact bool,
|
||||||
|
) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
if totalExact && got.Body.TotalFrames != wantTotalFrames {
|
if totalExact && got.Body.TotalFrames != wantTotalFrames {
|
||||||
t.Errorf("%s\ngot %#v\nwant TotalFrames=%d", testName, got.Body.TotalFrames, wantTotalFrames)
|
t.Errorf("%s\ngot %#v\nwant TotalFrames=%d", testName, got.Body.TotalFrames, wantTotalFrames)
|
||||||
@ -1202,7 +1211,6 @@ func TestStackTraceRequest(t *testing.T) {
|
|||||||
client.StackTraceRequest(1, 0, 0)
|
client.StackTraceRequest(1, 0, 0)
|
||||||
stResp = client.ExpectStackTraceResponse(t)
|
stResp = client.ExpectStackTraceResponse(t)
|
||||||
checkStackFramesExact(t, stResp, "main.main", 18, startHandle, 3, 3)
|
checkStackFramesExact(t, stResp, "main.main", 18, startHandle, 3, 3)
|
||||||
|
|
||||||
},
|
},
|
||||||
disconnect: false,
|
disconnect: false,
|
||||||
}})
|
}})
|
||||||
@ -1382,7 +1390,6 @@ func TestSelectedThreadsRequest(t *testing.T) {
|
|||||||
oe := client.ExpectOutputEvent(t)
|
oe := client.ExpectOutputEvent(t)
|
||||||
if !strings.HasPrefix(oe.Body.Output, "Too many goroutines") {
|
if !strings.HasPrefix(oe.Body.Output, "Too many goroutines") {
|
||||||
t.Errorf("got %#v, expected Output=\"Too many goroutines...\"\n", oe)
|
t.Errorf("got %#v, expected Output=\"Too many goroutines...\"\n", oe)
|
||||||
|
|
||||||
}
|
}
|
||||||
tr := client.ExpectThreadsResponse(t)
|
tr := client.ExpectThreadsResponse(t)
|
||||||
|
|
||||||
@ -1403,10 +1410,71 @@ func TestSelectedThreadsRequest(t *testing.T) {
|
|||||||
},
|
},
|
||||||
disconnect: true,
|
disconnect: true,
|
||||||
}})
|
}})
|
||||||
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGoroutineLabels(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
showPprofLabelsConfig []string
|
||||||
|
expectedPrefixWithLabel string
|
||||||
|
}{
|
||||||
|
{[]string{}, "* [Go 1]"},
|
||||||
|
{[]string{"k1"}, "* [Go 1 v1]"},
|
||||||
|
{[]string{"k2"}, "* [Go 1 v2]"},
|
||||||
|
{[]string{"k2", "k1"}, "* [Go 1 k2:v2 k1:v1]"}, // When passing keys explicitly, we show them in the given order
|
||||||
|
{[]string{"unknown"}, "* [Go 1]"},
|
||||||
|
{[]string{"unknown", "k1"}, "* [Go 1 k1:v1]"},
|
||||||
|
{[]string{"*"}, "* [Go 1 k1:v1 k2:v2]"}, // Special case for showing all labels; labels are shown sorted by key
|
||||||
|
}
|
||||||
|
for _, tc := range tests {
|
||||||
|
runTest(t, "goroutineLabels", func(client *daptest.Client, fixture protest.Fixture) {
|
||||||
|
runDebugSessionWithBPs(t, client, "launch",
|
||||||
|
// Launch
|
||||||
|
func() {
|
||||||
|
client.LaunchRequestWithArgs(map[string]interface{}{
|
||||||
|
"mode": "exec",
|
||||||
|
"program": fixture.Path,
|
||||||
|
"hideSystemGoroutines": true,
|
||||||
|
"showPprofLabels": tc.showPprofLabelsConfig,
|
||||||
|
"stopOnEntry": !stopOnEntry,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
// Breakpoints are set within the program
|
||||||
|
"", []int{},
|
||||||
|
[]onBreakpoint{{
|
||||||
|
execute: func() {
|
||||||
|
client.ThreadsRequest()
|
||||||
|
tr := client.ExpectThreadsResponse(t)
|
||||||
|
if len(tr.Body.Threads) != 1 {
|
||||||
|
t.Errorf("got %d threads, expected 1\n", len(tr.Body.Threads))
|
||||||
|
}
|
||||||
|
// The first breakpoint is before the call to pprof.Do; no labels yet:
|
||||||
|
expectedPrefix := "* [Go 1]"
|
||||||
|
if !strings.HasPrefix(tr.Body.Threads[0].Name, expectedPrefix) {
|
||||||
|
t.Errorf("got %s, expected %s\n", tr.Body.Threads[0].Name, expectedPrefix)
|
||||||
|
}
|
||||||
|
|
||||||
|
client.ContinueRequest(1)
|
||||||
|
client.ExpectContinueResponse(t)
|
||||||
|
client.ExpectStoppedEvent(t)
|
||||||
|
checkStop(t, client, 1, "main.f", 21)
|
||||||
|
client.ThreadsRequest()
|
||||||
|
tr = client.ExpectThreadsResponse(t)
|
||||||
|
if len(tr.Body.Threads) != 1 {
|
||||||
|
t.Errorf("got %d threads, expected 1\n", len(tr.Body.Threads))
|
||||||
|
}
|
||||||
|
// The second breakpoint is inside pprof.Do, so there are labels:
|
||||||
|
if !strings.HasPrefix(tr.Body.Threads[0].Name, tc.expectedPrefixWithLabel) {
|
||||||
|
t.Errorf("got %s, expected %s\n", tr.Body.Threads[0].Name, tc.expectedPrefixWithLabel)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
disconnect: true,
|
||||||
|
}},
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestHideSystemGoroutinesRequest(t *testing.T) {
|
func TestHideSystemGoroutinesRequest(t *testing.T) {
|
||||||
tests := []struct{ hideSystemGoroutines bool }{
|
tests := []struct{ hideSystemGoroutines bool }{
|
||||||
{hideSystemGoroutines: true},
|
{hideSystemGoroutines: true},
|
||||||
@ -2288,7 +2356,7 @@ func TestVariablesLoading(t *testing.T) {
|
|||||||
client.StackTraceRequest(1, 0, 0)
|
client.StackTraceRequest(1, 0, 0)
|
||||||
client.ExpectStackTraceResponse(t)
|
client.ExpectStackTraceResponse(t)
|
||||||
|
|
||||||
var loadvars = func(frame int) {
|
loadvars := func(frame int) {
|
||||||
client.ScopesRequest(frame)
|
client.ScopesRequest(frame)
|
||||||
scopes := client.ExpectScopesResponse(t)
|
scopes := client.ExpectScopesResponse(t)
|
||||||
localsRef := 0
|
localsRef := 0
|
||||||
@ -3311,7 +3379,6 @@ func TestSetFunctionBreakpoints(t *testing.T) {
|
|||||||
t.Errorf("got %#v, want Reason=\"function breakpoint\", ThreadId=1", se)
|
t.Errorf("got %#v, want Reason=\"function breakpoint\", ThreadId=1", se)
|
||||||
}
|
}
|
||||||
checkStop(t, client, 1, "main.anotherFunction", 26)
|
checkStop(t, client, 1, "main.anotherFunction", 26)
|
||||||
|
|
||||||
},
|
},
|
||||||
disconnect: true,
|
disconnect: true,
|
||||||
}})
|
}})
|
||||||
@ -3613,7 +3680,6 @@ func TestSetBreakpointWhileRunning(t *testing.T) {
|
|||||||
t.Errorf("\ngot %#v\nwant Reason='breakpoint' AllThreadsStopped=true ThreadId=1", se)
|
t.Errorf("\ngot %#v\nwant Reason='breakpoint' AllThreadsStopped=true ThreadId=1", se)
|
||||||
}
|
}
|
||||||
checkStop(t, client, 1, "main.sayhi", 9)
|
checkStop(t, client, 1, "main.sayhi", 9)
|
||||||
|
|
||||||
},
|
},
|
||||||
disconnect: true,
|
disconnect: true,
|
||||||
}})
|
}})
|
||||||
@ -3668,7 +3734,6 @@ func TestSetFunctionBreakpointWhileRunning(t *testing.T) {
|
|||||||
t.Errorf("\ngot %#v\nwant Reason='breakpoint' AllThreadsStopped=true ThreadId=1", se)
|
t.Errorf("\ngot %#v\nwant Reason='breakpoint' AllThreadsStopped=true ThreadId=1", se)
|
||||||
}
|
}
|
||||||
checkStop(t, client, 1, "main.main", 16)
|
checkStop(t, client, 1, "main.main", 16)
|
||||||
|
|
||||||
},
|
},
|
||||||
disconnect: true,
|
disconnect: true,
|
||||||
}})
|
}})
|
||||||
@ -3805,7 +3870,6 @@ func substitutePathTestHelper(t *testing.T, fixture protest.Fixture, client *dap
|
|||||||
// Set breakpoints
|
// Set breakpoints
|
||||||
filepath.Join(nonexistentDir, "loopprog.go"), []int{8},
|
filepath.Join(nonexistentDir, "loopprog.go"), []int{8},
|
||||||
[]onBreakpoint{{
|
[]onBreakpoint{{
|
||||||
|
|
||||||
execute: func() {
|
execute: func() {
|
||||||
checkStop(t, client, 1, "main.loop", 8)
|
checkStop(t, client, 1, "main.loop", 8)
|
||||||
},
|
},
|
||||||
@ -4063,15 +4127,16 @@ func TestEvaluateRequest(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func formatConfig(depth int, showGlobals, showRegisters bool, goroutineFilters string, hideSystemGoroutines bool, substitutePath [][2]string) string {
|
func formatConfig(depth int, showGlobals, showRegisters bool, goroutineFilters string, showPprofLabels []string, hideSystemGoroutines bool, substitutePath [][2]string) string {
|
||||||
formatStr := `stackTraceDepth %d
|
formatStr := `stackTraceDepth %d
|
||||||
showGlobalVariables %v
|
showGlobalVariables %v
|
||||||
showRegisters %v
|
showRegisters %v
|
||||||
goroutineFilters %q
|
goroutineFilters %q
|
||||||
|
showPprofLabels %v
|
||||||
hideSystemGoroutines %v
|
hideSystemGoroutines %v
|
||||||
substitutePath %v
|
substitutePath %v
|
||||||
`
|
`
|
||||||
return fmt.Sprintf(formatStr, depth, showGlobals, showRegisters, goroutineFilters, hideSystemGoroutines, substitutePath)
|
return fmt.Sprintf(formatStr, depth, showGlobals, showRegisters, goroutineFilters, showPprofLabels, hideSystemGoroutines, substitutePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEvaluateCommandRequest(t *testing.T) {
|
func TestEvaluateCommandRequest(t *testing.T) {
|
||||||
@ -4108,7 +4173,7 @@ Type 'dlv help' followed by a command for full documentation.
|
|||||||
|
|
||||||
client.EvaluateRequest("dlv config -list", 1000, "repl")
|
client.EvaluateRequest("dlv config -list", 1000, "repl")
|
||||||
got = client.ExpectEvaluateResponse(t)
|
got = client.ExpectEvaluateResponse(t)
|
||||||
checkEval(t, got, formatConfig(50, false, false, "", false, [][2]string{}), noChildren)
|
checkEval(t, got, formatConfig(50, false, false, "", []string{}, false, [][2]string{}), noChildren)
|
||||||
|
|
||||||
// Read and modify showGlobalVariables.
|
// Read and modify showGlobalVariables.
|
||||||
client.EvaluateRequest("dlv config -list showGlobalVariables", 1000, "repl")
|
client.EvaluateRequest("dlv config -list showGlobalVariables", 1000, "repl")
|
||||||
@ -4129,7 +4194,7 @@ Type 'dlv help' followed by a command for full documentation.
|
|||||||
|
|
||||||
client.EvaluateRequest("dlv config -list", 1000, "repl")
|
client.EvaluateRequest("dlv config -list", 1000, "repl")
|
||||||
got = client.ExpectEvaluateResponse(t)
|
got = client.ExpectEvaluateResponse(t)
|
||||||
checkEval(t, got, formatConfig(50, true, false, "", false, [][2]string{}), noChildren)
|
checkEval(t, got, formatConfig(50, true, false, "", []string{}, false, [][2]string{}), noChildren)
|
||||||
|
|
||||||
client.ScopesRequest(1000)
|
client.ScopesRequest(1000)
|
||||||
scopes = client.ExpectScopesResponse(t)
|
scopes = client.ExpectScopesResponse(t)
|
||||||
@ -4719,7 +4784,7 @@ func testNextParkedHelper(t *testing.T, client *daptest.Client, fixture protest.
|
|||||||
client.SetBreakpointsRequest(fixture.Source, []int{8})
|
client.SetBreakpointsRequest(fixture.Source, []int{8})
|
||||||
client.ExpectSetBreakpointsResponse(t)
|
client.ExpectSetBreakpointsResponse(t)
|
||||||
|
|
||||||
var parkedGoid = -1
|
parkedGoid := -1
|
||||||
for parkedGoid < 0 {
|
for parkedGoid < 0 {
|
||||||
client.ContinueRequest(1)
|
client.ContinueRequest(1)
|
||||||
client.ExpectContinueResponse(t)
|
client.ExpectContinueResponse(t)
|
||||||
@ -4849,6 +4914,7 @@ func TestStepOutPreservesGoroutine(t *testing.T) {
|
|||||||
}})
|
}})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkStopOnNextWhileNextingError(t *testing.T, client *daptest.Client, threadID int) {
|
func checkStopOnNextWhileNextingError(t *testing.T, client *daptest.Client, threadID int) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
oe := client.ExpectOutputEvent(t)
|
oe := client.ExpectOutputEvent(t)
|
||||||
@ -5046,7 +5112,6 @@ func TestPanicBreakpointOnContinue(t *testing.T) {
|
|||||||
} else if frame.Source != nil && frame.Source.PresentationHint != "" {
|
} else if frame.Source != nil && frame.Source.PresentationHint != "" {
|
||||||
t.Errorf("\ngot Body.StackFrames[%d]=%#v\nwant Source.PresentationHint=\"\"", i, frame)
|
t.Errorf("\ngot Body.StackFrames[%d]=%#v\nwant Source.PresentationHint=\"\"", i, frame)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
disconnect: true,
|
disconnect: true,
|
||||||
@ -5131,7 +5196,6 @@ func TestFatalThrowBreakpoint(t *testing.T) {
|
|||||||
if eInfo.Body.ExceptionId != "fatal error" || !strings.HasPrefix(eInfo.Body.Description, errorPrefix) {
|
if eInfo.Body.ExceptionId != "fatal error" || !strings.HasPrefix(eInfo.Body.Description, errorPrefix) {
|
||||||
t.Errorf("\ngot %#v\nwant ExceptionId=\"runtime error\" Text=%s", eInfo, errorPrefix)
|
t.Errorf("\ngot %#v\nwant ExceptionId=\"runtime error\" Text=%s", eInfo, errorPrefix)
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
disconnect: true,
|
disconnect: true,
|
||||||
}})
|
}})
|
||||||
@ -5307,7 +5371,8 @@ func TestLaunchDebugRequest(t *testing.T) {
|
|||||||
// only relying on the source to be built in response to LaunchRequest.
|
// only relying on the source to be built in response to LaunchRequest.
|
||||||
runDebugSession(t, client, "launch", func() {
|
runDebugSession(t, client, "launch", func() {
|
||||||
client.LaunchRequestWithArgs(map[string]interface{}{
|
client.LaunchRequestWithArgs(map[string]interface{}{
|
||||||
"mode": "debug", "program": fixture.Source, "output": tmpBin})
|
"mode": "debug", "program": fixture.Source, "output": tmpBin,
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
// Wait for the test to finish to capture all stderr
|
// Wait for the test to finish to capture all stderr
|
||||||
@ -5343,20 +5408,23 @@ func TestLaunchRequestDefaults(t *testing.T) {
|
|||||||
runTest(t, "increment", func(client *daptest.Client, fixture protest.Fixture) {
|
runTest(t, "increment", func(client *daptest.Client, fixture protest.Fixture) {
|
||||||
runDebugSession(t, client, "launch", func() {
|
runDebugSession(t, client, "launch", func() {
|
||||||
client.LaunchRequestWithArgs(map[string]interface{}{
|
client.LaunchRequestWithArgs(map[string]interface{}{
|
||||||
"mode": "" /*"debug" by default*/, "program": fixture.Source, "output": "__mybin"})
|
"mode": "" /*"debug" by default*/, "program": fixture.Source, "output": "__mybin",
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
runTest(t, "increment", func(client *daptest.Client, fixture protest.Fixture) {
|
runTest(t, "increment", func(client *daptest.Client, fixture protest.Fixture) {
|
||||||
runDebugSession(t, client, "launch", func() {
|
runDebugSession(t, client, "launch", func() {
|
||||||
client.LaunchRequestWithArgs(map[string]interface{}{
|
client.LaunchRequestWithArgs(map[string]interface{}{
|
||||||
/*"mode":"debug" by default*/ "program": fixture.Source, "output": "__mybin"})
|
/*"mode":"debug" by default*/ "program": fixture.Source, "output": "__mybin",
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
runTest(t, "increment", func(client *daptest.Client, fixture protest.Fixture) {
|
runTest(t, "increment", func(client *daptest.Client, fixture protest.Fixture) {
|
||||||
runDebugSession(t, client, "launch", func() {
|
runDebugSession(t, client, "launch", func() {
|
||||||
// Use the temporary output binary.
|
// Use the temporary output binary.
|
||||||
client.LaunchRequestWithArgs(map[string]interface{}{
|
client.LaunchRequestWithArgs(map[string]interface{}{
|
||||||
"mode": "debug", "program": fixture.Source})
|
"mode": "debug", "program": fixture.Source,
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -5373,7 +5441,8 @@ func TestLaunchRequestOutputPath(t *testing.T) {
|
|||||||
func() {
|
func() {
|
||||||
client.LaunchRequestWithArgs(map[string]interface{}{
|
client.LaunchRequestWithArgs(map[string]interface{}{
|
||||||
"mode": "debug", "program": fixture.Source, "output": inrel,
|
"mode": "debug", "program": fixture.Source, "output": inrel,
|
||||||
"cwd": filepath.Dir(wd)})
|
"cwd": filepath.Dir(wd),
|
||||||
|
})
|
||||||
},
|
},
|
||||||
// Set breakpoints
|
// Set breakpoints
|
||||||
fixture.Source, []int{12},
|
fixture.Source, []int{12},
|
||||||
@ -5421,7 +5490,8 @@ func TestNoDebug_GoodExitStatus(t *testing.T) {
|
|||||||
runTest(t, "increment", func(client *daptest.Client, fixture protest.Fixture) {
|
runTest(t, "increment", func(client *daptest.Client, fixture protest.Fixture) {
|
||||||
runNoDebugSession(t, client, func() {
|
runNoDebugSession(t, client, func() {
|
||||||
client.LaunchRequestWithArgs(map[string]interface{}{
|
client.LaunchRequestWithArgs(map[string]interface{}{
|
||||||
"noDebug": true, "mode": "debug", "program": fixture.Source, "output": "__mybin"})
|
"noDebug": true, "mode": "debug", "program": fixture.Source, "output": "__mybin",
|
||||||
|
})
|
||||||
}, 0)
|
}, 0)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -5430,7 +5500,8 @@ func TestNoDebug_BadExitStatus(t *testing.T) {
|
|||||||
runTest(t, "issue1101", func(client *daptest.Client, fixture protest.Fixture) {
|
runTest(t, "issue1101", func(client *daptest.Client, fixture protest.Fixture) {
|
||||||
runNoDebugSession(t, client, func() {
|
runNoDebugSession(t, client, func() {
|
||||||
client.LaunchRequestWithArgs(map[string]interface{}{
|
client.LaunchRequestWithArgs(map[string]interface{}{
|
||||||
"noDebug": true, "mode": "exec", "program": fixture.Path})
|
"noDebug": true, "mode": "exec", "program": fixture.Path,
|
||||||
|
})
|
||||||
}, 2)
|
}, 2)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -5458,11 +5529,12 @@ func TestNoDebug_AcceptNoRequestsButDisconnect(t *testing.T) {
|
|||||||
client.InitializeRequest()
|
client.InitializeRequest()
|
||||||
client.ExpectInitializeResponseAndCapabilities(t)
|
client.ExpectInitializeResponseAndCapabilities(t)
|
||||||
client.LaunchRequestWithArgs(map[string]interface{}{
|
client.LaunchRequestWithArgs(map[string]interface{}{
|
||||||
"noDebug": true, "mode": "exec", "program": fixture.Path})
|
"noDebug": true, "mode": "exec", "program": fixture.Path,
|
||||||
|
})
|
||||||
client.ExpectLaunchResponse(t)
|
client.ExpectLaunchResponse(t)
|
||||||
|
|
||||||
// Anything other than disconnect should get rejected
|
// Anything other than disconnect should get rejected
|
||||||
var ExpectNoDebugError = func(cmd string) {
|
ExpectNoDebugError := func(cmd string) {
|
||||||
er := client.ExpectErrorResponse(t)
|
er := client.ExpectErrorResponse(t)
|
||||||
if !checkErrorMessageFormat(er.Body.Error, fmt.Sprintf("noDebug mode: unable to process '%s' request", cmd)) {
|
if !checkErrorMessageFormat(er.Body.Error, fmt.Sprintf("noDebug mode: unable to process '%s' request", cmd)) {
|
||||||
t.Errorf("\ngot %#v\nwant 'noDebug mode: unable to process '%s' request'", er, cmd)
|
t.Errorf("\ngot %#v\nwant 'noDebug mode: unable to process '%s' request'", er, cmd)
|
||||||
@ -5531,7 +5603,8 @@ func TestLaunchRequestWithRelativeBuildPath(t *testing.T) {
|
|||||||
dlvwd, _ := os.Getwd()
|
dlvwd, _ := os.Getwd()
|
||||||
runDebugSession(t, client, "launch", func() {
|
runDebugSession(t, client, "launch", func() {
|
||||||
client.LaunchRequestWithArgs(map[string]interface{}{
|
client.LaunchRequestWithArgs(map[string]interface{}{
|
||||||
"mode": "debug", "program": program, "cwd": filepath.Dir(dlvwd)})
|
"mode": "debug", "program": program, "cwd": filepath.Dir(dlvwd),
|
||||||
|
})
|
||||||
})
|
})
|
||||||
<-serverStopped
|
<-serverStopped
|
||||||
}
|
}
|
||||||
@ -5550,7 +5623,8 @@ func TestLaunchRequestWithRelativeExecPath(t *testing.T) {
|
|||||||
defer os.Remove(symlink)
|
defer os.Remove(symlink)
|
||||||
runDebugSession(t, client, "launch", func() {
|
runDebugSession(t, client, "launch", func() {
|
||||||
client.LaunchRequestWithArgs(map[string]interface{}{
|
client.LaunchRequestWithArgs(map[string]interface{}{
|
||||||
"mode": "exec", "program": symlink})
|
"mode": "exec", "program": symlink,
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -5640,7 +5714,8 @@ func TestLaunchTestRequest(t *testing.T) {
|
|||||||
checkVarExact(t, locals, i, "wd", "wd", fmt.Sprintf("%q", tc.wantWD), "string", noChildren)
|
checkVarExact(t, locals, i, "wd", "wd", fmt.Sprintf("%q", tc.wantWD), "string", noChildren)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}}})
|
},
|
||||||
|
}})
|
||||||
|
|
||||||
<-serverStopped
|
<-serverStopped
|
||||||
})
|
})
|
||||||
@ -5656,7 +5731,8 @@ func TestLaunchRequestWithArgs(t *testing.T) {
|
|||||||
runDebugSession(t, client, "launch", func() {
|
runDebugSession(t, client, "launch", func() {
|
||||||
client.LaunchRequestWithArgs(map[string]interface{}{
|
client.LaunchRequestWithArgs(map[string]interface{}{
|
||||||
"mode": "exec", "program": fixture.Path,
|
"mode": "exec", "program": fixture.Path,
|
||||||
"args": []string{"test", "pass flag"}})
|
"args": []string{"test", "pass flag"},
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -5672,7 +5748,8 @@ func TestLaunchRequestWithBuildFlags(t *testing.T) {
|
|||||||
// only relying on the source to be built in response to LaunchRequest.
|
// only relying on the source to be built in response to LaunchRequest.
|
||||||
client.LaunchRequestWithArgs(map[string]interface{}{
|
client.LaunchRequestWithArgs(map[string]interface{}{
|
||||||
"mode": "debug", "program": fixture.Source, "output": "__mybin",
|
"mode": "debug", "program": fixture.Source, "output": "__mybin",
|
||||||
"buildFlags": "-ldflags '-X main.Hello=World'"})
|
"buildFlags": "-ldflags '-X main.Hello=World'",
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -5684,7 +5761,8 @@ func TestLaunchRequestWithBuildFlags2(t *testing.T) {
|
|||||||
// only relying on the source to be built in response to LaunchRequest.
|
// only relying on the source to be built in response to LaunchRequest.
|
||||||
client.LaunchRequestWithArgs(map[string]interface{}{
|
client.LaunchRequestWithArgs(map[string]interface{}{
|
||||||
"mode": "debug", "program": fixture.Source, "output": "__mybin",
|
"mode": "debug", "program": fixture.Source, "output": "__mybin",
|
||||||
"buildFlags": []string{"-ldflags", "-X main.Hello=World"}})
|
"buildFlags": []string{"-ldflags", "-X main.Hello=World"},
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -5747,7 +5825,6 @@ func TestLaunchRequestWithEnv(t *testing.T) {
|
|||||||
wantY: true,
|
wantY: true,
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
|
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
for k, v := range tc.initEnv {
|
for k, v := range tc.initEnv {
|
||||||
if v != nil {
|
if v != nil {
|
||||||
@ -5794,7 +5871,8 @@ func TestAttachRequest(t *testing.T) {
|
|||||||
// Attach
|
// Attach
|
||||||
func() {
|
func() {
|
||||||
client.AttachRequest(map[string]interface{}{
|
client.AttachRequest(map[string]interface{}{
|
||||||
/*"mode": "local" by default*/ "processId": cmd.Process.Pid, "stopOnEntry": false})
|
/*"mode": "local" by default*/ "processId": cmd.Process.Pid, "stopOnEntry": false,
|
||||||
|
})
|
||||||
client.ExpectCapabilitiesEventSupportTerminateDebuggee(t)
|
client.ExpectCapabilitiesEventSupportTerminateDebuggee(t)
|
||||||
},
|
},
|
||||||
// Set breakpoints
|
// Set breakpoints
|
||||||
@ -7126,7 +7204,7 @@ func TestDisassemble(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Request invalid instructions.
|
// Request invalid instructions.
|
||||||
var checkInvalidInstruction = func(instructions []dap.DisassembledInstruction, count int, address uint64) {
|
checkInvalidInstruction := func(instructions []dap.DisassembledInstruction, count int, address uint64) {
|
||||||
if len(instructions) != count {
|
if len(instructions) != count {
|
||||||
t.Errorf("\ngot %#v\nwant len(instructions) = %d", dr, count)
|
t.Errorf("\ngot %#v\nwant len(instructions) = %d", dr, count)
|
||||||
}
|
}
|
||||||
|
@ -190,6 +190,12 @@ type LaunchAttachCommonConfig struct {
|
|||||||
// https://github.com/go-delve/delve/blob/master/Documentation/cli/README.md#goroutines
|
// https://github.com/go-delve/delve/blob/master/Documentation/cli/README.md#goroutines
|
||||||
GoroutineFilters string `json:"goroutineFilters,omitempty"`
|
GoroutineFilters string `json:"goroutineFilters,omitempty"`
|
||||||
|
|
||||||
|
// Array of string values indicating the keys of pprof labels to show as a
|
||||||
|
// goroutine name in the threads view. If the array has one element, only
|
||||||
|
// that label's value will be shown; otherwise, each of the labels will be
|
||||||
|
// shown as "key:value". To show all labels, specify the single element "*".
|
||||||
|
ShowPprofLabels []string `json:"showPprofLabels,omitempty"`
|
||||||
|
|
||||||
// An array of mappings from a local path (client) to the remote path (debugger).
|
// An array of mappings from a local path (client) to the remote path (debugger).
|
||||||
// This setting is useful when working in a file system with symbolic links,
|
// This setting is useful when working in a file system with symbolic links,
|
||||||
// running remote debugging, or debugging an executable compiled externally.
|
// running remote debugging, or debugging an executable compiled externally.
|
||||||
|
Loading…
Reference in New Issue
Block a user