diff --git a/Documentation/api/dap/README.md b/Documentation/api/dap/README.md
index 74565d07bc..c75f2d99b6 100644
--- a/Documentation/api/dap/README.md
+++ b/Documentation/api/dap/README.md
@@ -33,6 +33,7 @@ In addition to the general [DAP spec](https://microsoft.github.io/debug-adapter-
stackTraceDepth
showGlobalVariables
showRegisters
+ showPprofLabels
hideSystemGoroutines
goroutineFilters
diff --git a/service/dap/command.go b/service/dap/command.go
index fb81a06978..0cff856ca0 100644
--- a/service/dap/command.go
+++ b/service/dap/command.go
@@ -62,7 +62,14 @@ Type "help" followed by the name of a command for more information about it.`
dlv config substitutePath -clear
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
+ dlv config showPprofLabels -clear
+ 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.
dlv sources []
@@ -138,7 +145,7 @@ func (s *Session) evaluateConfig(_, _ int, expr string) (string, error) {
Areas: []dap.InvalidatedAreas{"variables"},
},
})
- case "goroutineFilters", "hideSystemGoroutines":
+ case "goroutineFilters", "hideSystemGoroutines", "showPprofLabels":
// Thread related data has become invalidated.
s.send(&dap.InvalidatedEvent{
Event: *newEvent("invalidated"),
diff --git a/service/dap/config.go b/service/dap/config.go
index 7844589d4a..a162501eac 100644
--- a/service/dap/config.go
+++ b/service/dap/config.go
@@ -37,6 +37,15 @@ func configureSet(sargs *launchAttachArgs, args string) (bool, string, error) {
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)
if err != nil {
return false, "", err
@@ -85,3 +94,37 @@ func configureSetSubstitutePath(args *launchAttachArgs, rest string) error {
}
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
+}
diff --git a/service/dap/config_test.go b/service/dap/config_test.go
index a65b6ca42f..6701804a40 100644
--- a/service/dap/config_test.go
+++ b/service/dap/config_test.go
@@ -18,14 +18,14 @@ func TestListConfig(t *testing.T) {
args: args{
args: &launchAttachArgs{},
},
- want: formatConfig(0, false, false, "", false, [][2]string{}),
+ want: formatConfig(0, false, false, "", []string{}, false, [][2]string{}),
},
{
name: "default values",
args: args{
args: &defaultArgs,
},
- want: formatConfig(50, false, false, "", false, [][2]string{}),
+ want: formatConfig(50, false, false, "", []string{}, false, [][2]string{}),
},
{
name: "custom values",
@@ -33,11 +33,13 @@ func TestListConfig(t *testing.T) {
args: &launchAttachArgs{
StackTraceDepth: 35,
ShowGlobalVariables: true,
+ GoroutineFilters: "SomeFilter",
+ ShowPprofLabels: []string{"SomeLabel"},
substitutePathClientToServer: [][2]string{{"hello", "world"}},
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 {
diff --git a/service/dap/server.go b/service/dap/server.go
index 96e45c8de0..0495e44557 100644
--- a/service/dap/server.go
+++ b/service/dap/server.go
@@ -222,6 +222,11 @@ type launchAttachArgs struct {
ShowRegisters bool `cfgName:"showRegisters"`
// GoroutineFilters are the filters used when loading goroutines.
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
// responses.
HideSystemGoroutines bool `cfgName:"hideSystemGoroutines"`
@@ -241,6 +246,7 @@ var defaultArgs = launchAttachArgs{
HideSystemGoroutines: false,
ShowRegisters: false,
GoroutineFilters: "",
+ ShowPprofLabels: []string{},
substitutePathClientToServer: [][2]string{},
substitutePathServerToClient: [][2]string{},
}
@@ -280,11 +286,9 @@ const (
maxStringLenInCallRetVars = 1 << 10 // 1024
)
-var (
- // Max number of goroutines that we will return.
- // This is a var for testing
- maxGoroutines = 1 << 10
-)
+// Max number of goroutines that we will return.
+// This is a var for testing
+var maxGoroutines = 1 << 10
// NewServer creates a new DAP Server. It takes an opened Listener
// 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.HideSystemGoroutines = args.HideSystemGoroutines
s.args.GoroutineFilters = args.GoroutineFilters
+ s.args.ShowPprofLabels = args.ShowPprofLabels
if paths := args.SubstitutePath; len(paths) > 0 {
clientToServer := 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{
Output: msg + "\n",
Category: "console",
- }})
+ },
+ })
}
func (s *Session) onInitializeRequest(request *dap.InitializeRequest) {
@@ -889,7 +895,7 @@ func (s *Session) onLaunchRequest(request *dap.LaunchRequest) {
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 {
s.sendShowUserErrorResponse(request.Request,
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{
Output: fmt.Sprintf("Build Error: %s\n%s (%s)\n", cmd, strings.TrimSpace(string(out)), err.Error()),
Category: "stderr",
- }})
+ },
+ })
// Users are used to checking the Debug Console for build errors.
// No need to bother them with a visible pop-up.
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)
s.config.log.Debugf("launching binary '%s' with config: %s", debugbinary, prettyPrint(argsToLog))
- var redirected = false
+ redirected := false
switch args.OutputMode {
case "remote":
redirected = true
@@ -1062,7 +1069,8 @@ func (s *Session) onLaunchRequest(request *dap.LaunchRequest) {
Body: dap.OutputEventBody{
Output: outs,
Category: category,
- }})
+ },
+ })
}
if err != nil {
if err == io.EOF {
@@ -1687,7 +1695,8 @@ func (s *Session) onConfigurationDoneRequest(request *dap.ConfigurationDoneReque
func (s *Session) onContinueRequest(request *dap.ContinueRequest, allowNextStateChange chan struct{}) {
s.send(&dap.ContinueResponse{
Response: *newResponse(request.Request),
- Body: dap.ContinueResponseBody{AllThreadsContinued: true}})
+ Body: dap.ContinueResponseBody{AllThreadsContinued: true},
+ })
s.runUntilStopAndNotify(api.Continue, allowNextStateChange)
}
@@ -1762,7 +1771,8 @@ func (s *Session) onThreadsRequest(request *dap.ThreadsRequest) {
Body: dap.OutputEventBody{
Output: fmt.Sprintf("Unable to retrieve goroutines: %s\n", err.Error()),
Category: "stderr",
- }})
+ },
+ })
}
threads = []dap.Thread{{Id: 1, Name: "Dummy"}}
} else if len(gs) == 0 {
@@ -1812,10 +1822,41 @@ func (s *Session) onThreadsRequest(request *dap.ThreadsRequest) {
if g.Thread != nil && g.Thread.ThreadID() != 0 {
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`
// so no need to include them here.
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)
}
}
@@ -1836,7 +1877,7 @@ func (s *Session) onThreadsRequest(request *dap.ThreadsRequest) {
// - "remote" -- attaches client to a debugger already attached to a process.
// Required args: none (host/port are used externally to connect)
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 {
s.sendShowUserErrorResponse(request.Request, FailedToAttach, "Failed to attach", fmt.Sprintf("invalid debug configuration - %v", err))
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.
// 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
// 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.
@@ -3531,6 +3572,7 @@ func newEvent(event string) *dap.Event {
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)`
+
const BetterNextWhileNextingError = `Unable to step while the previous step is interrupted by a breakpoint.
Use 'Continue' to resume the original step command.`
diff --git a/service/dap/server_test.go b/service/dap/server_test.go
index b727bbfdc0..2e02032b15 100644
--- a/service/dap/server_test.go
+++ b/service/dap/server_test.go
@@ -31,12 +31,16 @@ import (
"github.com/google/go-dap"
)
-const stopOnEntry bool = true
-const hasChildren bool = true
-const noChildren bool = false
+const (
+ stopOnEntry bool = true
+ hasChildren bool = true
+ noChildren bool = false
+)
-const localsScope = 1000
-const globalsScope = 1001
+const (
+ localsScope = 1000
+ globalsScope = 1001
+)
var testBackend string
@@ -288,7 +292,8 @@ func TestSessionStop(t *testing.T) {
}
session := NewSession(conn, &Config{
Config: &service.Config{DisconnectChan: make(chan struct{})},
- StopTriggered: make(chan struct{})}, nil)
+ StopTriggered: make(chan struct{}),
+ }, nil)
serveDAPCodecDone := make(chan struct{})
go func() {
session.ServeDAPCodec()
@@ -812,7 +817,8 @@ func TestPreSetBreakpoint(t *testing.T) {
// wantFrames - number of frames returned (length of StackTraceResponse.Body.StackFrames array).
// wantTotalFrames - total number of stack frames available (StackTraceResponse.Body.TotalFrames).
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()
checkStackFramesNamed("", t, got, wantStartName, wantStartLine, wantStartID, wantFrames, wantTotalFrames, true)
}
@@ -883,7 +889,8 @@ func TestFilterGoroutines(t *testing.T) {
client.LaunchRequestWithArgs(map[string]interface{}{
"mode": "exec",
"program": fixture.Path,
- "stopOnEntry": !stopOnEntry})
+ "stopOnEntry": !stopOnEntry,
+ })
},
// Set breakpoints
fixture.Source, []int{30},
@@ -920,17 +927,19 @@ func TestFilterGoroutines(t *testing.T) {
},
disconnect: false,
}})
-
})
}
func checkStackFramesHasMore(t *testing.T, got *dap.StackTraceResponse,
- wantStartName string, wantStartLine, wantStartID, wantFrames, wantTotalFrames int) {
+ wantStartName string, wantStartLine, wantStartID, wantFrames, wantTotalFrames int,
+) {
t.Helper()
checkStackFramesNamed("", t, got, wantStartName, wantStartLine, wantStartID, wantFrames, wantTotalFrames, false)
}
+
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()
if totalExact && 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)
stResp = client.ExpectStackTraceResponse(t)
checkStackFramesExact(t, stResp, "main.main", 18, startHandle, 3, 3)
-
},
disconnect: false,
}})
@@ -1382,7 +1390,6 @@ func TestSelectedThreadsRequest(t *testing.T) {
oe := client.ExpectOutputEvent(t)
if !strings.HasPrefix(oe.Body.Output, "Too many goroutines") {
t.Errorf("got %#v, expected Output=\"Too many goroutines...\"\n", oe)
-
}
tr := client.ExpectThreadsResponse(t)
@@ -1403,10 +1410,71 @@ func TestSelectedThreadsRequest(t *testing.T) {
},
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) {
tests := []struct{ hideSystemGoroutines bool }{
{hideSystemGoroutines: true},
@@ -2288,7 +2356,7 @@ func TestVariablesLoading(t *testing.T) {
client.StackTraceRequest(1, 0, 0)
client.ExpectStackTraceResponse(t)
- var loadvars = func(frame int) {
+ loadvars := func(frame int) {
client.ScopesRequest(frame)
scopes := client.ExpectScopesResponse(t)
localsRef := 0
@@ -3311,7 +3379,6 @@ func TestSetFunctionBreakpoints(t *testing.T) {
t.Errorf("got %#v, want Reason=\"function breakpoint\", ThreadId=1", se)
}
checkStop(t, client, 1, "main.anotherFunction", 26)
-
},
disconnect: true,
}})
@@ -3613,7 +3680,6 @@ func TestSetBreakpointWhileRunning(t *testing.T) {
t.Errorf("\ngot %#v\nwant Reason='breakpoint' AllThreadsStopped=true ThreadId=1", se)
}
checkStop(t, client, 1, "main.sayhi", 9)
-
},
disconnect: true,
}})
@@ -3668,7 +3734,6 @@ func TestSetFunctionBreakpointWhileRunning(t *testing.T) {
t.Errorf("\ngot %#v\nwant Reason='breakpoint' AllThreadsStopped=true ThreadId=1", se)
}
checkStop(t, client, 1, "main.main", 16)
-
},
disconnect: true,
}})
@@ -3805,7 +3870,6 @@ func substitutePathTestHelper(t *testing.T, fixture protest.Fixture, client *dap
// Set breakpoints
filepath.Join(nonexistentDir, "loopprog.go"), []int{8},
[]onBreakpoint{{
-
execute: func() {
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
showGlobalVariables %v
showRegisters %v
goroutineFilters %q
+showPprofLabels %v
hideSystemGoroutines %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) {
@@ -4108,7 +4173,7 @@ Type 'dlv help' followed by a command for full documentation.
client.EvaluateRequest("dlv config -list", 1000, "repl")
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.
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")
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)
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.ExpectSetBreakpointsResponse(t)
- var parkedGoid = -1
+ parkedGoid := -1
for parkedGoid < 0 {
client.ContinueRequest(1)
client.ExpectContinueResponse(t)
@@ -4849,6 +4914,7 @@ func TestStepOutPreservesGoroutine(t *testing.T) {
}})
})
}
+
func checkStopOnNextWhileNextingError(t *testing.T, client *daptest.Client, threadID int) {
t.Helper()
oe := client.ExpectOutputEvent(t)
@@ -5046,7 +5112,6 @@ func TestPanicBreakpointOnContinue(t *testing.T) {
} else if frame.Source != nil && frame.Source.PresentationHint != "" {
t.Errorf("\ngot Body.StackFrames[%d]=%#v\nwant Source.PresentationHint=\"\"", i, frame)
}
-
}
},
disconnect: true,
@@ -5131,7 +5196,6 @@ func TestFatalThrowBreakpoint(t *testing.T) {
if eInfo.Body.ExceptionId != "fatal error" || !strings.HasPrefix(eInfo.Body.Description, errorPrefix) {
t.Errorf("\ngot %#v\nwant ExceptionId=\"runtime error\" Text=%s", eInfo, errorPrefix)
}
-
},
disconnect: true,
}})
@@ -5307,7 +5371,8 @@ func TestLaunchDebugRequest(t *testing.T) {
// only relying on the source to be built in response to LaunchRequest.
runDebugSession(t, client, "launch", func() {
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
@@ -5343,20 +5408,23 @@ func TestLaunchRequestDefaults(t *testing.T) {
runTest(t, "increment", func(client *daptest.Client, fixture protest.Fixture) {
runDebugSession(t, client, "launch", func() {
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) {
runDebugSession(t, client, "launch", func() {
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) {
runDebugSession(t, client, "launch", func() {
// Use the temporary output binary.
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() {
client.LaunchRequestWithArgs(map[string]interface{}{
"mode": "debug", "program": fixture.Source, "output": inrel,
- "cwd": filepath.Dir(wd)})
+ "cwd": filepath.Dir(wd),
+ })
},
// Set breakpoints
fixture.Source, []int{12},
@@ -5421,7 +5490,8 @@ func TestNoDebug_GoodExitStatus(t *testing.T) {
runTest(t, "increment", func(client *daptest.Client, fixture protest.Fixture) {
runNoDebugSession(t, client, func() {
client.LaunchRequestWithArgs(map[string]interface{}{
- "noDebug": true, "mode": "debug", "program": fixture.Source, "output": "__mybin"})
+ "noDebug": true, "mode": "debug", "program": fixture.Source, "output": "__mybin",
+ })
}, 0)
})
}
@@ -5430,7 +5500,8 @@ func TestNoDebug_BadExitStatus(t *testing.T) {
runTest(t, "issue1101", func(client *daptest.Client, fixture protest.Fixture) {
runNoDebugSession(t, client, func() {
client.LaunchRequestWithArgs(map[string]interface{}{
- "noDebug": true, "mode": "exec", "program": fixture.Path})
+ "noDebug": true, "mode": "exec", "program": fixture.Path,
+ })
}, 2)
})
}
@@ -5458,11 +5529,12 @@ func TestNoDebug_AcceptNoRequestsButDisconnect(t *testing.T) {
client.InitializeRequest()
client.ExpectInitializeResponseAndCapabilities(t)
client.LaunchRequestWithArgs(map[string]interface{}{
- "noDebug": true, "mode": "exec", "program": fixture.Path})
+ "noDebug": true, "mode": "exec", "program": fixture.Path,
+ })
client.ExpectLaunchResponse(t)
// Anything other than disconnect should get rejected
- var ExpectNoDebugError = func(cmd string) {
+ ExpectNoDebugError := func(cmd string) {
er := client.ExpectErrorResponse(t)
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)
@@ -5531,7 +5603,8 @@ func TestLaunchRequestWithRelativeBuildPath(t *testing.T) {
dlvwd, _ := os.Getwd()
runDebugSession(t, client, "launch", func() {
client.LaunchRequestWithArgs(map[string]interface{}{
- "mode": "debug", "program": program, "cwd": filepath.Dir(dlvwd)})
+ "mode": "debug", "program": program, "cwd": filepath.Dir(dlvwd),
+ })
})
<-serverStopped
}
@@ -5550,7 +5623,8 @@ func TestLaunchRequestWithRelativeExecPath(t *testing.T) {
defer os.Remove(symlink)
runDebugSession(t, client, "launch", func() {
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)
}
}
- }}})
+ },
+ }})
<-serverStopped
})
@@ -5656,7 +5731,8 @@ func TestLaunchRequestWithArgs(t *testing.T) {
runDebugSession(t, client, "launch", func() {
client.LaunchRequestWithArgs(map[string]interface{}{
"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.
client.LaunchRequestWithArgs(map[string]interface{}{
"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.
client.LaunchRequestWithArgs(map[string]interface{}{
"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,
},
} {
-
t.Run(tc.name, func(t *testing.T) {
for k, v := range tc.initEnv {
if v != nil {
@@ -5794,7 +5871,8 @@ func TestAttachRequest(t *testing.T) {
// Attach
func() {
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)
},
// Set breakpoints
@@ -7126,7 +7204,7 @@ func TestDisassemble(t *testing.T) {
}
// 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 {
t.Errorf("\ngot %#v\nwant len(instructions) = %d", dr, count)
}
diff --git a/service/dap/types.go b/service/dap/types.go
index 812dd1a4a4..1ab685f965 100644
--- a/service/dap/types.go
+++ b/service/dap/types.go
@@ -190,6 +190,12 @@ type LaunchAttachCommonConfig struct {
// https://github.com/go-delve/delve/blob/master/Documentation/cli/README.md#goroutines
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).
// This setting is useful when working in a file system with symbolic links,
// running remote debugging, or debugging an executable compiled externally.