Repository: go-delve/delve
Pull request #2589 (2021-07-14 06:47:36)

cmd/dap: server - always headless, target - always foregrounded
updates #2552


Patch:
diff --git a/.teamcity/settings.kts b/.teamcity/settings.kts
index adf35a88..f19ae009 100644
--- a/.teamcity/settings.kts
+++ b/.teamcity/settings.kts
@@ -35,7 +35,6 @@ To debug in IntelliJ Idea, open the 'Maven Projects' tool window (View
 version = "2020.2"
 
 val targets = arrayOf(
-	"linux/amd64/1.14",
         "linux/amd64/1.15",
         "linux/amd64/1.16",
         "linux/amd64/1.17",
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0b4e6e73..2aa3dd05 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,49 @@
 All notable changes to this project will be documented in this file.
 This project adheres to Semantic Versioning.
 
+## [1.7.0] 2021-07-19
+
+### Added
+
+- Go 1.17 support (@aarzilli, @mknyszek)
+- Add new API and terminal command for setting watchpoints (@aarzilli)
+- Add filtering and grouping to goroutines command (@aarzilli)
+- Added support for hit count condition on breakpoints (@suzmue, @aarzilli)
+- DAP server: Handle SetVariable requests (@hyangah)
+- DAP server: Add clipboard support (@hyangah)
+
+### Fixed
+
+- DAP server: Several shutdown / disconnect fixes (@suzmue, @polinasok)
+- DAP server: Clean output executable name on Windows (@hyangah)
+- DAP server: Variables response must not have null variables array (@polinasok)
+- Fix runtimeTypeToDIE setup (necessary for Go 1.17) (@aarzilli)
+- Reenable CGO stacktrace test on arm64 (@derekparker)
+- Fix incorrect integer casts in freebsd C backend (@dwagin)
+- Ensure correct exit status reported on commands following process death (@derekparker)
+- Misc flakey test fixes / test refactoring (@polinasok)
+- Fix for frame parameter being ignored in ConvertEvalScope when no goroutine is found (@suzmue)
+- Ensure ContinueOnce returns StopExited if process exited, otherwise return StopUnknown (@polinasok)
+- Fix panic in RPC2.ListDynamicLibraries (@derekparker)
+- Fix typo in flag passed to check if debugserver supports unmask_signals (@staugust)
+
+### Changed
+
+- DAP server: Add sameuser security check (@hyangah)
+- DAP server: Changes to context-dependent load limits for string type (@hyangah, @polinasok)
+- DAP server: Add paging for arrays, slices and maps (@suzmue)
+- DAP server: Deemphasize internal runtime stack frames (@suzmue)
+- DAP server: Add throw reason to exception information upon panic (@suzmue)
+- DAP server: Set breakpoint hit ID (@suzmue)
+- DAP server: Add string value of byte/rune slice as child (@suzmue)
+- Documentation: Add viminspector to list of editor plugins (@aarzilli)
+- Support for ZMM registers in gdbserial backend (@aarzilli)
+- Remove support for stack barriers (@derekparker)
+- Improve support for DWARF5 (@derekparker)
+- Improve documentation (@derekparker, @aarzilli)
+- Print message and exit if Delve detects it is running under Rosetta on M1 macs (@aarzilli)
+- Drop official Go 1.14 support (@derekparker)
+
 ## [1.6.1] 2021-05-18
 
 ### Added
diff --git a/Documentation/usage/dlv.md b/Documentation/usage/dlv.md
index fdd3ca33..999ef679 100644
--- a/Documentation/usage/dlv.md
+++ b/Documentation/usage/dlv.md
@@ -41,7 +41,7 @@ Pass flags to the program you are debugging using `--`, for example:
 * [dlv attach](dlv_attach.md)	 - Attach to running process and begin debugging.
 * [dlv connect](dlv_connect.md)	 - Connect to a headless debug server.
 * [dlv core](dlv_core.md)	 - Examine a core dump.
-* [dlv dap](dlv_dap.md)	 - [EXPERIMENTAL] Starts a TCP server communicating via Debug Adaptor Protocol (DAP).
+* [dlv dap](dlv_dap.md)	 - [EXPERIMENTAL] Starts a headless TCP server communicating via Debug Adaptor Protocol (DAP).
 * [dlv debug](dlv_debug.md)	 - Compile and begin debugging main package in current directory, or the package specified.
 * [dlv exec](dlv_exec.md)	 - Execute a precompiled binary, and begin a debug session.
 * [dlv replay](dlv_replay.md)	 - Replays a rr trace.
diff --git a/Documentation/usage/dlv_dap.md b/Documentation/usage/dlv_dap.md
index 3a87a251..8fc171f3 100644
--- a/Documentation/usage/dlv_dap.md
+++ b/Documentation/usage/dlv_dap.md
@@ -1,13 +1,13 @@
 ## dlv dap
 
-[EXPERIMENTAL] Starts a TCP server communicating via Debug Adaptor Protocol (DAP).
+[EXPERIMENTAL] Starts a headless TCP server communicating via Debug Adaptor Protocol (DAP).
 
 ### Synopsis
 
 
-[EXPERIMENTAL] Starts a TCP server communicating via Debug Adaptor Protocol (DAP).
+[EXPERIMENTAL] Starts a headless TCP server communicating via Debug Adaptor Protocol (DAP).
 
-The server is headless and requires a DAP client like vscode to connect and request a binary
+The server is always headless and requires a DAP client like vscode to connect and request a binary
 to be launched or process to be attached to. The following modes are supported:
 - launch + exec (executes precompiled binary, like 'dlv exec')
 - launch + debug (builds and launches, like 'dlv debug')
diff --git a/cmd/dlv/cmds/commands.go b/cmd/dlv/cmds/commands.go
index e063c728..2f8ec18d 100644
--- a/cmd/dlv/cmds/commands.go
+++ b/cmd/dlv/cmds/commands.go
@@ -174,10 +174,10 @@ option to let the process continue or kill it.
 	// 'dap' subcommand.
 	dapCommand := &cobra.Command{
 		Use:   "dap",
-		Short: "[EXPERIMENTAL] Starts a TCP server communicating via Debug Adaptor Protocol (DAP).",
-		Long: `[EXPERIMENTAL] Starts a TCP server communicating via Debug Adaptor Protocol (DAP).
+		Short: "[EXPERIMENTAL] Starts a headless TCP server communicating via Debug Adaptor Protocol (DAP).",
+		Long: `[EXPERIMENTAL] Starts a headless TCP server communicating via Debug Adaptor Protocol (DAP).
 
-The server is headless and requires a DAP client like vscode to connect and request a binary
+The server is always headless and requires a DAP client like vscode to connect and request a binary
 to be launched or process to be attached to. The following modes are supported:
 - launch + exec (executes precompiled binary, like 'dlv exec')
 - launch + debug (builds and launches, like 'dlv debug')
@@ -188,6 +188,7 @@ While --continue is not supported, stopOnEntry launch/attach attribute can be us
 execution is resumed at the start of the debug session.`,
 		Run: dapCmd,
 	}
+	// TODO(polina): support --tty when dlv dap allows to launch a program from command-line
 	rootCommand.AddCommand(dapCommand)
 
 	// 'debug' subcommand.
@@ -409,7 +410,7 @@ func dapCmd(cmd *cobra.Command, args []string) {
 		}
 		defer logflags.Close()
 
-		if headless {
+		if cmd.Flag("headless").Changed {
 			fmt.Fprintf(os.Stderr, "Warning: dap mode is always headless\n")
 		}
 		if acceptMulti {
@@ -446,10 +447,9 @@ func dapCmd(cmd *cobra.Command, args []string) {
 			DisconnectChan: disconnectChan,
 			Debugger: debugger.Config{
 				Backend:              backend,
-				Foreground:           headless && tty == "",
+				Foreground:           true, // server always runs without terminal client
 				DebugInfoDirectories: conf.DebugInfoDirectories,
 				CheckGoVersion:       checkGoVersion,
-				TTY:                  tty,
 			},
 			CheckLocalConnUser: checkLocalConnUser,
 		})
diff --git a/pkg/goversion/compat.go b/pkg/goversion/compat.go
index 4e230294..b19528eb 100644
--- a/pkg/goversion/compat.go
+++ b/pkg/goversion/compat.go
@@ -6,7 +6,7 @@ import (
 
 var (
 	MinSupportedVersionOfGoMajor = 1
-	MinSupportedVersionOfGoMinor = 14
+	MinSupportedVersionOfGoMinor = 15
 	MaxSupportedVersionOfGoMajor = 1
 	MaxSupportedVersionOfGoMinor = 17
 	goTooOldErr                  = fmt.Errorf("Version of Go is too old for this version of Delve (minimum supported version %d.%d, suppress this error with --check-go-version=false)", MinSupportedVersionOfGoMajor, MinSupportedVersionOfGoMinor)
diff --git a/pkg/version/version.go b/pkg/version/version.go
index 114218c2..e353a651 100644
--- a/pkg/version/version.go
+++ b/pkg/version/version.go
@@ -14,7 +14,7 @@ type Version struct {
 var (
 	// DelveVersion is the current version of Delve.
 	DelveVersion = Version{
-		Major: "1", Minor: "6", Patch: "1", Metadata: "",
+		Major: "1", Minor: "7", Patch: "0", Metadata: "",
 		Build: "$Id$",
 	}
 )
diff --git a/service/dap/server.go b/service/dap/server.go
index cec104ad..31331d6e 100644
--- a/service/dap/server.go
+++ b/service/dap/server.go
@@ -972,6 +972,10 @@ func (s *Server) onDisconnectRequest(request *dap.DisconnectRequest) {
 	} else {
 		s.send(&dap.DisconnectResponse{Response: *newResponse(request.Request)})
 	}
+	// The debugging session has ended, so we send a terminated event.
+	s.send(&dap.TerminatedEvent{
+		Event: *newEvent("terminated"),
+	})
 }
 
 // stopDebugSession is called from Stop (main goroutine) and
@@ -1712,7 +1716,7 @@ func (s *Server) onVariablesRequest(request *dap.VariablesRequest) {
 		}
 	}
 
-	var children []dap.Variable
+	children := []dap.Variable{} // must return empty array, not null, if no children
 	if request.Arguments.Filter == "named" || request.Arguments.Filter == "" {
 		named, err := s.metadataToDAPVariables(v)
 		if err != nil {
@@ -1765,7 +1769,7 @@ func (s *Server) childrenToDAPVariables(v *fullyQualifiedVariable) ([]dap.Variab
 	// TODO(polina): consider convertVariableToString instead of convertVariable
 	// and avoid unnecessary creation of variable handles when this is called to
 	// compute evaluate names when this is called from onSetVariableRequest.
-	var children []dap.Variable
+	children := []dap.Variable{} // must return empty array, not null, if no children
 
 	switch v.Kind {
 	case reflect.Map:
@@ -1913,7 +1917,7 @@ func getNamedVariableCount(v *proc.Variable) int {
 // metadataToDAPVariables returns the DAP presentation of the referenced variable's metadata.
 // These are included as named variables
 func (s *Server) metadataToDAPVariables(v *fullyQualifiedVariable) ([]dap.Variable, error) {
-	var children []dap.Variable
+	children := []dap.Variable{} // must return empty array, not null, if no children
 
 	if isListOfBytesOrRunes(v.Variable) {
 		// Return the string value of []byte or []rune.
diff --git a/service/rpc2/server.go b/service/rpc2/server.go
index 056900ed..b51f4045 100644
--- a/service/rpc2/server.go
+++ b/service/rpc2/server.go
@@ -838,7 +838,7 @@ func (s *RPCServer) ListDynamicLibraries(in ListDynamicLibrariesIn, out *ListDyn
 	imgs := s.debugger.ListDynamicLibraries()
 	out.List = make([]api.Image, 0, len(imgs))
 	for i := range imgs {
-		out.List[i] = api.ConvertImage(imgs[i])
+		out.List = append(out.List, api.ConvertImage(imgs[i]))
 	}
 	return nil
 }


Test patch:
diff --git a/cmd/dlv/dlv_test.go b/cmd/dlv/dlv_test.go
index 2c5b23a7..479ac40e 100644
--- a/cmd/dlv/dlv_test.go
+++ b/cmd/dlv/dlv_test.go
@@ -637,6 +637,7 @@ func TestDap(t *testing.T) {
 	client := daptest.NewClient(listenAddr)
 	client.DisconnectRequest()
 	client.ExpectDisconnectResponse(t)
+	client.ExpectTerminatedEvent(t)
 	if _, err := client.ReadMessage(); err != io.EOF {
 		t.Errorf("got %q, want \"EOF\"\n", err)
 	}
diff --git a/service/dap/server_test.go b/service/dap/server_test.go
index 23084425..8d57a3c8 100644
--- a/service/dap/server_test.go
+++ b/service/dap/server_test.go
@@ -246,6 +246,7 @@ func TestLaunchStopOnEntry(t *testing.T) {
 		if dResp.Seq != 0 || dResp.RequestSeq != 13 {
 			t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=13", dResp)
 		}
+		client.ExpectTerminatedEvent(t)
 	})
 }
 
@@ -377,6 +378,7 @@ func TestAttachStopOnEntry(t *testing.T) {
 		client.CheckOutputEvent(t, msg)
 		msg = expectMessageFilterStopped(t, client)
 		client.CheckDisconnectResponse(t, msg)
+		client.ExpectTerminatedEvent(t)
 
 		// If this call to KeepAlive isn't here there's a chance that stdout will
 		// be garbage collected (since it is no longer alive long before this
@@ -452,6 +454,7 @@ func TestContinueOnEntry(t *testing.T) {
 		if dResp.Seq != 0 || dResp.RequestSeq != 8 {
 			t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=8", dResp)
 		}
+		client.ExpectTerminatedEvent(t)
 	})
 }
 
@@ -591,6 +594,7 @@ func TestPreSetBreakpoint(t *testing.T) {
 		client.ExpectOutputEventProcessExited(t, 0)
 		client.ExpectOutputEventDetaching(t)
 		client.ExpectDisconnectResponse(t)
+		client.ExpectTerminatedEvent(t)
 	})
 }
 
@@ -654,6 +658,9 @@ func checkScope(t *testing.T, got *dap.ScopesResponse, i int, name string, varRe
 //      numChildren - number of variables/fields/elements of this variable
 func checkChildren(t *testing.T, got *dap.VariablesResponse, parentName string, numChildren int) {
 	t.Helper()
+	if got.Body.Variables == nil {
+		t.Errorf("\ngot  %s children=%#v want []", parentName, got.Body.Variables)
+	}
 	if len(got.Body.Variables) != numChildren {
 		t.Errorf("\ngot  len(%s)=%d (children=%#v)\nwant len=%d", parentName, len(got.Body.Variables), got.Body.Variables, numChildren)
 	}
@@ -3854,6 +3861,7 @@ func runDebugSessionWithBPs(t *testing.T, client *daptest.Client, cmd string, cm
 				client.ExpectOutputEventDetachingKill(t)
 			}
 			client.ExpectDisconnectResponse(t)
+			client.ExpectTerminatedEvent(t)
 			return
 		}
 		client.ContinueRequest(1)
@@ -3872,6 +3880,7 @@ func runDebugSessionWithBPs(t *testing.T, client *daptest.Client, cmd string, cm
 		client.ExpectOutputEventDetachingKill(t)
 	}
 	client.ExpectDisconnectResponse(t)
+	client.ExpectTerminatedEvent(t)
 }
 
 // runDebugSession is a helper for executing the standard init and shutdown
@@ -3996,6 +4005,7 @@ func runNoDebugDebugSession(t *testing.T, client *daptest.Client, cmdRequest fun
 	client.ExpectTerminatedEvent(t)
 	client.DisconnectRequestWithKillOption(true)
 	client.ExpectDisconnectResponse(t)
+	client.ExpectTerminatedEvent(t)
 }
 
 func TestLaunchTestRequest(t *testing.T) {