Repository: go-delve/delve
Pull request #2423 (2021-04-08 08:03:08)

service/dap: support running requests asynchronously
Currently the server processes one request at a time synchronously. That means that while continue (or next, or call, etc) are running, he server will not respond to any additional requests. This change updates the server to start a separate goroutine for running requests and resume the request loop to accept other requests. This will allow us to process requests for pause, setting breakpoints and disconnecting while the session is running, and return explicit errors for requests that cannot be processed while running (e.g. evaluating expressions).

Updates #1515
Updates https://github.com/golang/vscode-go/issues/1347


aarzilli: It seems to me that this approach would lead to a lot of repeated code. Wouldn't it be easier to handle as much as possible of this inside (*Server).handleRequest?

For example:


```
func (s *Server) handleRequest(request dap.Message) {
	defer func() {
		// In case a handler panics, we catch the panic and send an error response
		// back to the client.
		if ierr := recover(); ierr != nil {
			s.sendInternalErrorResponse(request.GetSeq(), fmt.Sprintf("%v", ierr))
		}
	}()

	jsonmsg, _ := json.Marshal(request)
	s.log.Debug("[<- from client]", string(jsonmsg))
	
	// Requests that can be handled regardless of whether the targret is running
	switch request := request.(type) {
	case *dap.PauseRequest:
		//... do Command(api.Halt)...
		return
	}
	
	s.runningMu.Lock()
	defer s.runningMu.Unlock()
	
	if s.running {
		// Requests that can be handled while the target is running, or return an error
		switch request := request.(type {
		case *dap.ThreadsRequest:
			response := &dap.ThreadsResponse{
				Response: *newResponse(request.Request),
				Body:     dap.ThreadsResponseBody{Threads: []dap.Thread{{Id: 1, Name: "Dummy"}}},
			}
			s.send(response)
		default:
			s.sendErrorResponse(request.Request, DebuggeeIsRunningError, "Unable to produce stack trace", "debuggee is running")
		}
		return
	}
	
	// Requests that can only be handled while stopped
	switch request := request.(type) {
	case *dap.ContinueRequest:
		go s.onContinueRequest(request, resumeRequestLoop)
		<-resumeRequestLoop
		s.running = true // will be set back to false at the end of doCommand
	//... other request handers, mostly unchanged...
	}
}
```

I think this is also less error prone. For example, it seems to me, that this PR will accept two consecutive ContinueRequests and the second one will block until the first one terminates and then start immediately. Is that the intended behavior?

If at some point you decide to actually serve requests that happen while the target is running, for example if you want to make SetBreakpointsRequest:

- stop the target
- set breakpoints
- resume the target

the stop and resume logic can be handled inside the `if s.running` branch, and be the same for every request.

polinasok: @hyangah 
polinasok: @suzmue 
polinasok: > My current recommendation is to maintain the existing handleRequest structure (before this PR), but have a single separate, dedicated goroutine that processes the remaining synchronous operations sequentially.

I looked into this and decided that it doesn't make sense. I added comments to HandleRequest, explaining why. Please let me know if I misunderstood something in your proposal.

> Btw I observed that onEvaluateRequest still runs in the Run goroutine after this PR, but if it's for call, this calls Debugger.Command. I expect the same for onSetVariableRequest too, and wonder if they are safe to run in the Run goroutine.

You are right. This one is a bit more nuanced than others, so I wanted to deal with it separately once we figure out the design with the simpler items. There is a related TODO under call that's also waiting for async support. But specifically about the issue you are asking about - yes the call will block processing any new requests. I think that's fine for assignments because they should be quick. But it is not fine if you call some long-running function because you won't be able to pause or even disconnect. Besides making it asynchronous, we would also need to issue a Continue event to let the editor know that we are executing, so it updates the UI accordingly. And in that case, I will need to add a proper stopped event when things stop. 
polinasok: The failure is related and I am looking into it.
polinasok: FYI I fixed the failing test, but do not fully understand how [loopprog](https://github.com/go-delve/delve/blob/master/_fixtures/loopprog.go) that has an infinite loop would terminate on halt. I was not able to reproduce this on either Mac or Linux machines that I have. Only saw this via TeamCity.
aarzilli: > FYI I fixed the failing test, but do not fully understand how [loopprog](https://github.com/go-delve/delve/blob/master/_fixtures/loopprog.go) that has an infinite loop would terminate on halt. I was not able to reproduce this on either Mac or Linux machines that I have. Only saw this via TeamCity.

Seems strange.
hyangah: @aarzilli @derekparker It would be great if this can be merged soon so VS Code Go extension can handle Delve DAP debug session teardown cleanly. Thanks for the review!

Patch:
diff --git a/Documentation/api/ClientHowto.md b/Documentation/api/ClientHowto.md
index dc58a8f3..34807c13 100644
--- a/Documentation/api/ClientHowto.md
+++ b/Documentation/api/ClientHowto.md
@@ -33,7 +33,7 @@ This will, however, cause problems if you actually spawn multiple instances of t
 
 It's probably better to let Delve pick a random unused port number on its own. To do this do not specify any `--listen` option and read one line of output from dlv's stdout. If the first line emitted by dlv starts with "API server listening at: " then dlv started correctly and the rest of the line specifies the address that Delve is listening at.
 
-The `--log-to-file` and `--log-to-fd` options can be used to redirect the "API server listening at:" message to a file or to a file descriptor. If neither is specified the message will be output to stdout.
+The `--log-dest` option can be used to redirect the "API server listening at:" message to a file or to a file descriptor. If the flag is not specified, the message will be output to stdout while other log messages are output to stderr.
 
 ## Controlling the backend
 
diff --git a/cmd/dlv/cmds/commands.go b/cmd/dlv/cmds/commands.go
index 2e94261f..3d097b26 100644
--- a/cmd/dlv/cmds/commands.go
+++ b/cmd/dlv/cmds/commands.go
@@ -253,7 +253,11 @@ or later, -gcflags="-N -l" on earlier versions of Go.`,
 The test command allows you to begin a new debug session in the context of your
 unit tests. By default Delve will debug the tests in the current directory.
 Alternatively you can specify a package name, and Delve will debug the tests in
-that package instead.`,
+that package instead. Double-dashes ` + "`--`" + ` can be used to pass arguments to the test program:
+
+dlv test [package] -- -test.v -other-argument
+
+See also: 'go help testflag'.`,
 		Run: testCmd,
 	}
 	testCommand.Flags().String("output", "debug.test", "Output path for the binary.")
diff --git a/go.mod b/go.mod
index fed3c715..ac6035a8 100644
--- a/go.mod
+++ b/go.mod
@@ -6,7 +6,7 @@ require (
 	github.com/cosiner/argv v0.1.0
 	github.com/cpuguy83/go-md2man v1.0.10 // indirect
 	github.com/creack/pty v1.1.9
-	github.com/google/go-dap v0.4.0
+	github.com/google/go-dap v0.5.0
 	github.com/hashicorp/golang-lru v0.5.4
 	github.com/inconshreveable/mousetrap v1.0.0 // indirect
 	github.com/mattn/go-colorable v0.0.0-20170327083344-ded68f7a9561
diff --git a/go.sum b/go.sum
index d24b4547..3d011c8f 100644
--- a/go.sum
+++ b/go.sum
@@ -2,8 +2,6 @@ github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWR
 github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8=
 github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
 github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
-github.com/cosiner/argv v0.0.0-20170225145430-13bacc38a0a5 h1:rIXlvz2IWiupMFlC45cZCXZFvKX/ExBcSLrDy2G0Lp8=
-github.com/cosiner/argv v0.0.0-20170225145430-13bacc38a0a5/go.mod h1:p/NrK5tF6ICIly4qwEDsf6VDirFiWWz0FenfYBwJaKQ=
 github.com/cosiner/argv v0.1.0 h1:BVDiEL32lwHukgJKP87btEPenzrrHUjajs/8yzaqcXg=
 github.com/cosiner/argv v0.1.0/go.mod h1:EusR6TucWKX+zFgtdUsKT2Cvg45K5rtpCcWz4hK06d8=
 github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk=
@@ -12,12 +10,8 @@ github.com/creack/pty v1.1.9 h1:uDmaGzcdjhF4i/plgjmEsriH11Y0o7RKapEf/LDaM3w=
 github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/google/go-dap v0.2.0 h1:whjIGQRumwbR40qRU7CEKuFLmePUUc2s4Nt9DoXXxWk=
-github.com/google/go-dap v0.2.0/go.mod h1:5q8aYQFnHOAZEMP+6vmq25HKYAEwE+LF5yh7JKrrhSQ=
-github.com/google/go-dap v0.3.0 h1:Dc4izN0u4VhZERYrz80f1PSEoDsVfdVmdM/W82CxzFk=
-github.com/google/go-dap v0.3.0/go.mod h1:5q8aYQFnHOAZEMP+6vmq25HKYAEwE+LF5yh7JKrrhSQ=
-github.com/google/go-dap v0.4.0 h1:bWSjcM9zp/jEFD4YbWERcHSed8vHbEdk0rmTvqgXDAs=
-github.com/google/go-dap v0.4.0/go.mod h1:5q8aYQFnHOAZEMP+6vmq25HKYAEwE+LF5yh7JKrrhSQ=
+github.com/google/go-dap v0.5.0 h1:RMHAVn5xeunBakYk65ggHXttk6qjZVdbmi+xhAoL2wY=
+github.com/google/go-dap v0.5.0/go.mod h1:5q8aYQFnHOAZEMP+6vmq25HKYAEwE+LF5yh7JKrrhSQ=
 github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
 github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
 github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
@@ -42,8 +36,6 @@ github.com/spf13/pflag v0.0.0-20170417173400-9e4c21054fa1 h1:7bozMfSdo41n2NOc0Gs
 github.com/spf13/pflag v0.0.0-20170417173400-9e4c21054fa1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
 github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
 github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
-go.starlark.net v0.0.0-20190702223751-32f345186213 h1:lkYv5AKwvvduv5XWP6szk/bvvgO6aDeUujhZQXIFTes=
-go.starlark.net v0.0.0-20190702223751-32f345186213/go.mod h1:c1/X6cHgvdXj6pUlmWKMkuqRnW4K8x2vwt6JAaaircg=
 go.starlark.net v0.0.0-20200821142938-949cc6f4b097 h1:YiRMXXgG+Pg26t1fjq+iAjaauKWMC9cmGFrtOEuwDDg=
 go.starlark.net v0.0.0-20200821142938-949cc6f4b097/go.mod h1:f0znQkUKRrkk36XxWbGjMqQM8wGv/xHBVE2qc3B5oFU=
 golang.org/x/arch v0.0.0-20190927153633-4e8777c89be4 h1:QlVATYS7JBoZMVaf+cNjb90WD/beKVHnIxFKT4QaHVI=
@@ -56,17 +48,12 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEha
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb h1:fgwFCsaw9buMuxNd6+DQfAuSFqbNiQZpcgJQAgJsK6k=
-golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae h1:Ih9Yo4hSPImZOpfGuA4bR/ORKTAbhZo2AbWNRCnevdo=
 golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
-golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20191127201027-ecd32218bd7f h1:3MlESg/jvTr87F4ttA/q4B+uhe/q6qleC9/DP+IwQmY=
 golang.org/x/tools v0.0.0-20191127201027-ecd32218bd7f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20200415034506-5d8e1897c761 h1:FVw4lelfGRNPqB3C8qX1m+QyeM2vzToIwlFhEZX42y8=
-golang.org/x/tools v0.0.0-20200415034506-5d8e1897c761/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 h1:9zdDQZ7Thm29KFXgAX/+yaf3eVbP7djjWp/dXAppNCc=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
diff --git a/pkg/dwarf/line/line_parser.go b/pkg/dwarf/line/line_parser.go
index a6babe16..09dd6de7 100644
--- a/pkg/dwarf/line/line_parser.go
+++ b/pkg/dwarf/line/line_parser.go
@@ -89,11 +89,19 @@ func Parse(compdir string, buf *bytes.Buffer, logfn func(string, ...interface{})
 
 	parseDebugLinePrologue(dbl, buf)
 	if dbl.Prologue.Version >= 5 {
-		parseIncludeDirs5(dbl, buf)
-		parseFileEntries5(dbl, buf)
+		if !parseIncludeDirs5(dbl, buf) {
+			return nil
+		}
+		if !parseFileEntries5(dbl, buf) {
+			return nil
+		}
 	} else {
-		parseIncludeDirs2(dbl, buf)
-		parseFileEntries2(dbl, buf)
+		if !parseIncludeDirs2(dbl, buf) {
+			return nil
+		}
+		if !parseFileEntries2(dbl, buf) {
+			return nil
+		}
 	}
 
 	// Instructions size calculation breakdown:
@@ -135,20 +143,30 @@ func parseDebugLinePrologue(dbl *DebugLineInfo, buf *bytes.Buffer) {
 }
 
 // parseIncludeDirs2 parses the directory table for DWARF version 2 through 4.
-func parseIncludeDirs2(info *DebugLineInfo, buf *bytes.Buffer) {
+func parseIncludeDirs2(info *DebugLineInfo, buf *bytes.Buffer) bool {
 	for {
-		str, _ := util.ParseString(buf)
+		str, err := util.ParseString(buf)
+		if err != nil {
+			if info.Logf != nil {
+				info.Logf("error reading string: %v", err)
+			}
+			return false
+		}
 		if str == "" {
 			break
 		}
 
 		info.IncludeDirs = append(info.IncludeDirs, str)
 	}
+	return true
 }
 
 // parseIncludeDirs5 parses the directory table for DWARF version 5.
-func parseIncludeDirs5(info *DebugLineInfo, buf *bytes.Buffer) {
+func parseIncludeDirs5(info *DebugLineInfo, buf *bytes.Buffer) bool {
 	dirEntryFormReader := readEntryFormat(buf, info.Logf)
+	if dirEntryFormReader == nil {
+		return false
+	}
 	dirCount, _ := util.DecodeULEB128(buf)
 	info.IncludeDirs = make([]string, 0, dirCount)
 	for i := uint64(0); i < dirCount; i++ {
@@ -168,13 +186,23 @@ func parseIncludeDirs5(info *DebugLineInfo, buf *bytes.Buffer) {
 			case _DW_LNCT_MD5:
 			}
 		}
+		if dirEntryFormReader.err != nil {
+			if info.Logf != nil {
+				info.Logf("error reading directory entries table: %v", dirEntryFormReader.err)
+			}
+			return false
+		}
 	}
+	return true
 }
 
 // parseFileEntries2 parses the file table for DWARF 2 through 4
-func parseFileEntries2(info *DebugLineInfo, buf *bytes.Buffer) {
+func parseFileEntries2(info *DebugLineInfo, buf *bytes.Buffer) bool {
 	for {
 		entry := readFileEntry(info, buf, true)
+		if entry == nil {
+			return false
+		}
 		if entry.Path == "" {
 			break
 		}
@@ -182,12 +210,20 @@ func parseFileEntries2(info *DebugLineInfo, buf *bytes.Buffer) {
 		info.FileNames = append(info.FileNames, entry)
 		info.Lookup[entry.Path] = entry
 	}
+	return true
 }
 
 func readFileEntry(info *DebugLineInfo, buf *bytes.Buffer, exitOnEmptyPath bool) *FileEntry {
 	entry := new(FileEntry)
 
-	entry.Path, _ = util.ParseString(buf)
+	var err error
+	entry.Path, err = util.ParseString(buf)
+	if err != nil {
+		if info.Logf != nil {
+			info.Logf("error reading file entry: %v", err)
+		}
+		return nil
+	}
 	if entry.Path == "" && exitOnEmptyPath {
 		return entry
 	}
@@ -225,8 +261,11 @@ func pathIsAbs(s string) bool {
 }
 
 // parseFileEntries5 parses the file table for DWARF 5
-func parseFileEntries5(info *DebugLineInfo, buf *bytes.Buffer) {
+func parseFileEntries5(info *DebugLineInfo, buf *bytes.Buffer) bool {
 	fileEntryFormReader := readEntryFormat(buf, info.Logf)
+	if fileEntryFormReader == nil {
+		return false
+	}
 	fileCount, _ := util.DecodeULEB128(buf)
 	info.FileNames = make([]*FileEntry, 0, fileCount)
 	for i := 0; i < int(fileCount); i++ {
@@ -265,5 +304,12 @@ func parseFileEntries5(info *DebugLineInfo, buf *bytes.Buffer) {
 			info.FileNames = append(info.FileNames, entry)
 			info.Lookup[entry.Path] = entry
 		}
+		if fileEntryFormReader.err != nil {
+			if info.Logf != nil {
+				info.Logf("error reading file entries table: %v", fileEntryFormReader.err)
+			}
+			return false
+		}
 	}
+	return true
 }
diff --git a/pkg/dwarf/line/parse_util.go b/pkg/dwarf/line/parse_util.go
index 10dd9633..6f96569e 100644
--- a/pkg/dwarf/line/parse_util.go
+++ b/pkg/dwarf/line/parse_util.go
@@ -3,6 +3,7 @@ package line
 import (
 	"bytes"
 	"encoding/binary"
+	"errors"
 
 	"github.com/go-delve/delve/pkg/dwarf/util"
 )
@@ -39,6 +40,8 @@ const (
 	_DW_LNCT_MD5
 )
 
+var ErrBufferUnderflow = errors.New("buffer underflow")
+
 type formReader struct {
 	logf         func(string, ...interface{})
 	contentTypes []uint64
@@ -51,11 +54,15 @@ type formReader struct {
 	u64   uint64
 	i64   int64
 	str   string
+	err   error
 
 	nexti int
 }
 
 func readEntryFormat(buf *bytes.Buffer, logf func(string, ...interface{})) *formReader {
+	if buf.Len() < 1 {
+		return nil
+	}
 	count := buf.Next(1)[0]
 	r := &formReader{
 		logf:         logf,
@@ -70,10 +77,14 @@ func readEntryFormat(buf *bytes.Buffer, logf func(string, ...interface{})) *form
 }
 
 func (rdr *formReader) reset() {
+	rdr.err = nil
 	rdr.nexti = 0
 }
 
 func (rdr *formReader) next(buf *bytes.Buffer) bool {
+	if rdr.err != nil {
+		return false
+	}
 	if rdr.nexti >= len(rdr.contentTypes) {
 		return false
 	}
@@ -87,24 +98,52 @@ func (rdr *formReader) next(buf *bytes.Buffer) bool {
 		rdr.readBlock(buf, n)
 
 	case _DW_FORM_block1:
+		if buf.Len() < 1 {
+			rdr.err = ErrBufferUnderflow
+			return false
+		}
 		rdr.readBlock(buf, uint64(buf.Next(1)[0]))
 
 	case _DW_FORM_block2:
+		if buf.Len() < 2 {
+			rdr.err = ErrBufferUnderflow
+			return false
+		}
 		rdr.readBlock(buf, uint64(binary.LittleEndian.Uint16(buf.Next(2))))
 
 	case _DW_FORM_block4:
+		if buf.Len() < 4 {
+			rdr.err = ErrBufferUnderflow
+			return false
+		}
 		rdr.readBlock(buf, uint64(binary.LittleEndian.Uint32(buf.Next(4))))
 
 	case _DW_FORM_data1, _DW_FORM_flag, _DW_FORM_strx1:
+		if buf.Len() < 1 {
+			rdr.err = ErrBufferUnderflow
+			return false
+		}
 		rdr.u64 = uint64(buf.Next(1)[0])
 
 	case _DW_FORM_data2, _DW_FORM_strx2:
+		if buf.Len() < 2 {
+			rdr.err = ErrBufferUnderflow
+			return false
+		}
 		rdr.u64 = uint64(binary.LittleEndian.Uint16(buf.Next(2)))
 
 	case _DW_FORM_data4, _DW_FORM_line_strp, _DW_FORM_sec_offset, _DW_FORM_strp, _DW_FORM_strx4:
+		if buf.Len() < 4 {
+			rdr.err = ErrBufferUnderflow
+			return false
+		}
 		rdr.u64 = uint64(binary.LittleEndian.Uint32(buf.Next(4)))
 
 	case _DW_FORM_data8:
+		if buf.Len() < 8 {
+			rdr.err = ErrBufferUnderflow
+			return false
+		}
 		rdr.u64 = binary.LittleEndian.Uint64(buf.Next(8))
 
 	case _DW_FORM_data16:
@@ -120,6 +159,10 @@ func (rdr *formReader) next(buf *bytes.Buffer) bool {
 		rdr.str, _ = util.ParseString(buf)
 
 	case _DW_FORM_strx3:
+		if buf.Len() < 3 {
+			rdr.err = ErrBufferUnderflow
+			return false
+		}
 		rdr.u64 = uint64(binary.LittleEndian.Uint32(append(buf.Next(3), 0x0)))
 
 	default:
@@ -136,6 +179,10 @@ func (rdr *formReader) next(buf *bytes.Buffer) bool {
 }
 
 func (rdr *formReader) readBlock(buf *bytes.Buffer, n uint64) {
+	if uint64(buf.Len()) < n {
+		rdr.err = ErrBufferUnderflow
+		return
+	}
 	if cap(rdr.block) < int(n) {
 		rdr.block = make([]byte, 0, n)
 	}
diff --git a/pkg/dwarf/line/state_machine.go b/pkg/dwarf/line/state_machine.go
index ddc83410..d7e11c26 100644
--- a/pkg/dwarf/line/state_machine.go
+++ b/pkg/dwarf/line/state_machine.go
@@ -552,8 +552,9 @@ func setdiscriminator(sm *StateMachine, buf *bytes.Buffer) {
 }
 
 func definefile(sm *StateMachine, buf *bytes.Buffer) {
-	entry := readFileEntry(sm.dbl, sm.buf, false)
-	sm.definedFiles = append(sm.definedFiles, entry)
+	if entry := readFileEntry(sm.dbl, sm.buf, false); entry != nil {
+		sm.definedFiles = append(sm.definedFiles, entry)
+	}
 }
 
 func prologueend(sm *StateMachine, buf *bytes.Buffer) {
diff --git a/pkg/dwarf/util/util.go b/pkg/dwarf/util/util.go
index 0fcf64f7..6ca75e1c 100644
--- a/pkg/dwarf/util/util.go
+++ b/pkg/dwarf/util/util.go
@@ -128,13 +128,13 @@ func EncodeSLEB128(out io.ByteWriter, x int64) {
 }
 
 // ParseString reads a null-terminated string from data.
-func ParseString(data *bytes.Buffer) (string, uint32) {
+func ParseString(data *bytes.Buffer) (string, error) {
 	str, err := data.ReadString(0x0)
 	if err != nil {
-		panic("Could not parse string")
+		return "", err
 	}
 
-	return str[:len(str)-1], uint32(len(str))
+	return str[:len(str)-1], nil
 }
 
 // ReadUintRaw reads an integer of ptrSize bytes, with the specified byte order, from reader.
diff --git a/pkg/proc/gdbserial/gdbserver.go b/pkg/proc/gdbserial/gdbserver.go
index 4a1cab30..d35da689 100644
--- a/pkg/proc/gdbserial/gdbserver.go
+++ b/pkg/proc/gdbserial/gdbserver.go
@@ -330,8 +330,11 @@ func (p *gdbProcess) Connect(conn net.Conn, path string, pid int, debugInfoDirs
 
 				// Workaround for darwin arm64. Apple's debugserver seems to have a problem
 				// with not selecting the correct thread in the 'g' command and the returned
-				// registers are empty / an E74 error is thrown.
-				if v[len("version:"):] == "1200" && p.bi.Arch.Name == "arm64" {
+				// registers are empty / an E74 error is thrown. This was reported to LLVM
+				// as https://bugs.llvm.org/show_bug.cgi?id=50169
+				version, err := strconv.ParseInt(v[len("version:"):], 10, 32)
+
+				if err == nil && version >= 1200 && version <= 1205 && p.bi.Arch.Name == "arm64" {
 					p.gcmdok = false
 				}
 			}
diff --git a/service/dap/error_ids.go b/service/dap/error_ids.go
index 03ec41e7..68a4d3de 100644
--- a/service/dap/error_ids.go
+++ b/service/dap/error_ids.go
@@ -20,6 +20,7 @@ const (
 	UnableToListGlobals        = 2007
 	UnableToLookupVariable     = 2008
 	UnableToEvaluateExpression = 2009
-	// Add more codes as we support more requests
-	DisconnectError = 5000
+
+	DebuggeeIsRunning = 4000
+	DisconnectError   = 5000
 )
diff --git a/service/dap/server.go b/service/dap/server.go
index 1eddf87e..06e66865 100644
--- a/service/dap/server.go
+++ b/service/dap/server.go
@@ -37,7 +37,7 @@ import (
 )
 
 // Server implements a DAP server that can accept a single client for
-// a single debug session (for now). It does yet not support restarting.
+// a single debug session (for now). It does not yet support restarting.
 // That means that in addition to explicit shutdown requests,
 // program termination and failed or closed client connection
 // would also result in stopping this single-use server.
@@ -53,21 +53,41 @@ import (
 //
 // (2) Run goroutine started from Run() that serves as both
 // a listener and a client goroutine. It accepts a client connection,
-// reads, decodes and processes each request, issuing commands to the
-// underlying debugger and sending back events and responses.
-// This gorouitne sends a stop-server signal via config.DisconnecChan
-// when encounering a client connection error or responding to
-// a DAP disconnect request.
+// reads, decodes and dispatches each request from the client.
+// For synchronous requests, it issues commands to the
+// underlying debugger and sends back events and responses.
+// These requests block while the debuggee is running, so,
+// where applicable, the handlers need to check if debugging
+// state is running, so there is a need for a halt request or
+// a dummy/error response to avoid blocking.
+//
+// This is the only goroutine that sends a stop-server signal
+// via config.DisconnecChan when encountering a client connection
+// error or responding to a (synchronous) DAP disconnect request.
+// Once stop is triggered, the goroutine exits.
 //
 // TODO(polina): add another layer of per-client goroutines to support multiple clients
-// TODO(polina): make it asynchronous (i.e. launch goroutine per request)
+//
+// (3) Per-request goroutine is started for each asynchronous request
+// that resumes execution. We check if target is running already, so
+// there should be no more than one pending asynchronous request at
+// a time. This goroutine issues commands to the underlying debugger
+// and sends back events and responses. It takes a setup-done channel
+// as an argument and temporarily blocks the request loop until setup
+// for asynchronous execution is complete and targe is running.
+// Once done, it unblocks processing of parallel requests unblocks
+// (e.g. disconnecting while the program is running).
+//
+// These per-request goroutines never send a stop-server signal.
+// They block on running debugger commands that are interrupted
+// when halt is issued while stopping. At that point these goroutines
+// wrap-up and exit.
 type Server struct {
 	// config is all the information necessary to start the debugger and server.
 	config *service.Config
 	// listener is used to accept the client connection.
 	listener net.Listener
-	// stopTriggered is closed when the server is Stop()-ed. This can be used to signal
-	// to goroutines run by the server that it's time to quit.
+	// stopTriggered is closed when the server is Stop()-ed.
 	stopTriggered chan struct{}
 	// reader is used to read requests from the connection.
 	reader *bufio.Reader
@@ -86,6 +106,7 @@ type Server struct {
 	// mu synchronizes access to objects set on start-up (from run goroutine)
 	// and stopped on teardown (from main goroutine)
 	mu sync.Mutex
+
 	// conn is the accepted client connection.
 	conn net.Conn
 	// debugger is the underlying debugger service.
@@ -94,6 +115,10 @@ type Server struct {
 	binaryToRemove string
 	// noDebugProcess is set for the noDebug launch process.
 	noDebugProcess *exec.Cmd
+
+	// sendingMu synchronizes writing to net.Conn
+	// to ensure that messages do not get interleaved
+	sendingMu sync.Mutex
 }
 
 // launchAttachArgs captures arguments from launch/attach request that
@@ -217,6 +242,7 @@ func (s *Server) Stop() {
 		// allowing the run goroutine to exit.
 		_ = s.conn.Close()
 	}
+
 	if s.debugger != nil {
 		killProcess := s.config.Debugger.AttachPid == 0
 		s.stopDebugSession(killProcess)
@@ -238,8 +264,6 @@ func (s *Server) Stop() {
 // The function safeguards agaist closing the channel more
 // than once and can be called multiple times. It is not thread-safe
 // and is currently only called from the run goroutine.
-// TODO(polina): lock this when we add more goroutines that could call
-// this when we support asynchronous request-response communication.
 func (s *Server) triggerServerStop() {
 	// Avoid accidentally closing the channel twice and causing a panic, when
 	// this function is called more than once. For example, we could have the
@@ -314,66 +338,137 @@ func (s *Server) serveDAPCodec() {
 	}
 }
 
+// In case a handler panics, we catch the panic to avoid crashing both
+// the server and the target. We send an error response back, but
+// in case its a dup and ignored by the client, we also log the error.
+func (s *Server) recoverPanic(request dap.Message) {
+	if ierr := recover(); ierr != nil {
+		s.log.Errorf("recovered panic: %s\n%s\n", ierr, debug.Stack())
+		s.sendInternalErrorResponse(request.GetSeq(), fmt.Sprintf("%v", ierr))
+	}
+}
+
 func (s *Server) handleRequest(request dap.Message) {
-	defer func() {
-		// In case a handler panics, we catch the panic and send an error response
-		// back to the client.
-		if ierr := recover(); ierr != nil {
-			s.log.Errorf("stacktrace from recovered panic:\n%s\n", debug.Stack())
-			s.sendInternalErrorResponse(request.GetSeq(), fmt.Sprintf("%v", ierr))
-		}
-	}()
+	defer s.recoverPanic(request)
 
 	jsonmsg, _ := json.Marshal(request)
 	s.log.Debug("[<- from client]", string(jsonmsg))
 
+	if _, ok := request.(dap.RequestMessage); !ok {
+		s.sendInternalErrorResponse(request.GetSeq(), fmt.Sprintf("Unable to process non-request %#v\n", request))
+		return
+	}
+
+	// These requests, can be handeled regardless of whether the targret is running
 	switch request := request.(type) {
-	case *dap.InitializeRequest:
-		// Required
-		s.onInitializeRequest(request)
-	case *dap.LaunchRequest:
-		// Required
-		s.onLaunchRequest(request)
-	case *dap.AttachRequest:
-		// Required
-		s.onAttachRequest(request)
 	case *dap.DisconnectRequest:
 		// Required
 		s.onDisconnectRequest(request)
+		return
+	case *dap.PauseRequest:
+		// Required
+		// TODO: implement this request in V0
+		s.onPauseRequest(request)
+		return
 	case *dap.TerminateRequest:
 		// Optional (capability ‘supportsTerminateRequest‘)
 		// TODO: implement this request in V1
 		s.onTerminateRequest(request)
+		return
 	case *dap.RestartRequest:
 		// Optional (capability ‘supportsRestartRequest’)
 		// TODO: implement this request in V1
 		s.onRestartRequest(request)
-	case *dap.SetBreakpointsRequest:
-		// Required
-		s.onSetBreakpointsRequest(request)
-	case *dap.SetFunctionBreakpointsRequest:
-		// Optional (capability ‘supportsFunctionBreakpoints’)
-		// TODO: implement this request in V1
-		s.onSetFunctionBreakpointsRequest(request)
-	case *dap.SetExceptionBreakpointsRequest:
-		// Optional (capability ‘exceptionBreakpointFilters’)
-		s.onSetExceptionBreakpointsRequest(request)
+		return
+	}
+
+	// Most requests cannot be processed while the debuggee is running.
+	// We have a couple of options for handling these without blocking
+	// the request loop indefinitely when we are in running state.
+	// --1-- Return a dummy response or an error right away.
+	// --2-- Halt execution, process the request, resume execution.
+	// TODO(polina): do this for setting breakpoints
+	// --3-- Handle such requests asynchronously and let them block until
+	// the process stops or terminates (e.g. using a channel and a single
+	// goroutine to preserve the order). This might not be appropriate
+	// for requests such as continue or step because they would skip
+	// the stop, resuming execution right away. Other requests
+	// might not be relevant anymore when the stop is finally reached, and
+	// state changed from the previous snapshot. The user might want to
+	// resume execution before the backlog of buffered requests is cleared,
+	// so we would have to either cancel them or delay processing until
+	// the next stop. In addition, the editor itself might block waiting
+	// for these requests to return. We are not aware of any requests
+	// that would benefit from this approach at this time.
+	if s.debugger != nil && s.debugger.IsRunning() {
+		switch request := request.(type) {
+		case *dap.ThreadsRequest:
+			// On start-up, the client requests the baseline of currently existing threads
+			// right away as there are a number of DAP requests that require a thread id
+			// (pause, continue, stacktrace, etc). This can happen after the program
+			// continues on entry, preventing the client from handling any pause requests
+			// from the user. We remedy this by sending back a placeholder thread id
+			// for the current goroutine.
+			response := &dap.ThreadsResponse{
+				Response: *newResponse(request.Request),
+				Body:     dap.ThreadsResponseBody{Threads: []dap.Thread{{Id: -1, Name: "Current"}}},
+			}
+			s.send(response)
+		default:
+			r := request.(dap.RequestMessage).GetRequest()
+			s.sendErrorResponse(*r, DebuggeeIsRunning, fmt.Sprintf("Unable to process `%s`", r.Command), "debuggee is running")
+		}
+		return
+	}
+
+	// Requests below can only be handled while target is stopped.
+	// Some of them are blocking and will be handled synchronously
+	// on this goroutine while non-blocking requests will be dispatched
+	// to another goroutine. Please note that because of the running
+	// check above, there should be no more than one pending asynchronous
+	// request at a time.
+
+	// Non-blocking request handlers will signal when they are ready
+	// setting up for async execution, so more requests can be processed.
+	resumeRequestLoop := make(chan struct{})
+
+	switch request := request.(type) {
+	//--- Asynchronous requests ---
 	case *dap.ConfigurationDoneRequest:
 		// Optional (capability ‘supportsConfigurationDoneRequest’)
-		// Supported by vscode-go
-		s.onConfigurationDoneRequest(request)
+		go func() {
+			defer s.recoverPanic(request)
+			s.onConfigurationDoneRequest(request, resumeRequestLoop)
+		}()
+		<-resumeRequestLoop
 	case *dap.ContinueRequest:
 		// Required
-		s.onContinueRequest(request)
+		go func() {
+			defer s.recoverPanic(request)
+			s.onContinueRequest(request, resumeRequestLoop)
+		}()
+		<-resumeRequestLoop
 	case *dap.NextRequest:
 		// Required
-		s.onNextRequest(request)
+		go func() {
+			defer s.recoverPanic(request)
+			s.onNextRequest(request, resumeRequestLoop)
+		}()
+		<-resumeRequestLoop
 	case *dap.StepInRequest:
 		// Required
-		s.onStepInRequest(request)
+		go func() {
+			defer s.recoverPanic(request)
+			s.onStepInRequest(request, resumeRequestLoop)
+		}()
+		<-resumeRequestLoop
 	case *dap.StepOutRequest:
 		// Required
-		s.onStepOutRequest(request)
+		go func() {
+			defer s.recoverPanic(request)
+			s.onStepOutRequest(request, resumeRequestLoop)
+		}()
+		<-resumeRequestLoop
 	case *dap.StepBackRequest:
 		// Optional (capability ‘supportsStepBack’)
 		// TODO: implement this request in V1
@@ -382,16 +477,29 @@ func (s *Server) handleRequest(request dap.Message) {
 		// Optional (capability ‘supportsStepBack’)
 		// TODO: implement this request in V1
 		s.onReverseContinueRequest(request)
-	case *dap.RestartFrameRequest:
-		// Optional (capability ’supportsRestartFrame’)
-		s.sendUnsupportedErrorResponse(request.Request)
-	case *dap.GotoRequest:
-		// Optional (capability ‘supportsGotoTargetsRequest’)
-		s.sendUnsupportedErrorResponse(request.Request)
-	case *dap.PauseRequest:
+	//--- Synchronous requests ---
+	case *dap.InitializeRequest:
 		// Required
-		// TODO: implement this request in V0
-		s.onPauseRequest(request)
+		s.onInitializeRequest(request)
+	case *dap.LaunchRequest:
+		// Required
+		s.onLaunchRequest(request)
+	case *dap.AttachRequest:
+		// Required
+		s.onAttachRequest(request)
+	case *dap.SetBreakpointsRequest:
+		// Required
+		s.onSetBreakpointsRequest(request)
+	case *dap.SetFunctionBreakpointsRequest:
+		// Optional (capability ‘supportsFunctionBreakpoints’)
+		// TODO: implement this request in V1
+		s.onSetFunctionBreakpointsRequest(request)
+	case *dap.SetExceptionBreakpointsRequest:
+		// Optional (capability ‘exceptionBreakpointFilters’)
+		s.onSetExceptionBreakpointsRequest(request)
+	case *dap.ThreadsRequest:
+		// Required
+		s.onThreadsRequest(request)
 	case *dap.StackTraceRequest:
 		// Required
 		s.onStackTraceRequest(request)
@@ -401,6 +509,9 @@ func (s *Server) handleRequest(request dap.Message) {
 	case *dap.VariablesRequest:
 		// Required
 		s.onVariablesRequest(request)
+	case *dap.EvaluateRequest:
+		// Required
+		s.onEvaluateRequest(request)
 	case *dap.SetVariableRequest:
 		// Optional (capability ‘supportsSetVariable’)
 		// Supported by vscode-go
@@ -410,20 +521,37 @@ func (s *Server) handleRequest(request dap.Message) {
 		// Optional (capability ‘supportsSetExpression’)
 		// TODO: implement this request in V1
 		s.onSetExpressionRequest(request)
+	case *dap.LoadedSourcesRequest:
+		// Optional (capability ‘supportsLoadedSourcesRequest’)
+		// TODO: implement this request in V1
+		s.onLoadedSourcesRequest(request)
+	case *dap.ReadMemoryRequest:
+		// Optional (capability ‘supportsReadMemoryRequest‘)
+		// TODO: implement this request in V1
+		s.onReadMemoryRequest(request)
+	case *dap.DisassembleRequest:
+		// Optional (capability ‘supportsDisassembleRequest’)
+		// TODO: implement this request in V1
+		s.onDisassembleRequest(request)
+	case *dap.CancelRequest:
+		// Optional (capability ‘supportsCancelRequest’)
+		// TODO: does this request make sense for delve?
+		s.onCancelRequest(request)
+	//--- Requests that we do not plan to support ---
+	case *dap.RestartFrameRequest:
+		// Optional (capability ’supportsRestartFrame’)
+		s.sendUnsupportedErrorResponse(request.Request)
+	case *dap.GotoRequest:
+		// Optional (capability ‘supportsGotoTargetsRequest’)
+		s.sendUnsupportedErrorResponse(request.Request)
 	case *dap.SourceRequest:
 		// Required
 		// This does not make sense in the context of Go as
 		// the source cannot be a string eval'ed at runtime.
 		s.sendUnsupportedErrorResponse(request.Request)
-	case *dap.ThreadsRequest:
-		// Required
-		s.onThreadsRequest(request)
 	case *dap.TerminateThreadsRequest:
 		// Optional (capability ‘supportsTerminateThreadsRequest’)
 		s.sendUnsupportedErrorResponse(request.Request)
-	case *dap.EvaluateRequest:
-		// Required
-		s.onEvaluateRequest(request)
 	case *dap.StepInTargetsRequest:
 		// Optional (capability ‘supportsStepInTargetsRequest’)
 		s.sendUnsupportedErrorResponse(request.Request)
@@ -437,28 +565,12 @@ func (s *Server) handleRequest(request dap.Message) {
 		// Optional (capability ‘supportsExceptionInfoRequest’)
 		// TODO: does this request make sense for delve?
 		s.sendUnsupportedErrorResponse(request.Request)
-	case *dap.LoadedSourcesRequest:
-		// Optional (capability ‘supportsLoadedSourcesRequest’)
-		// TODO: implement this request in V1
-		s.onLoadedSourcesRequest(request)
 	case *dap.DataBreakpointInfoRequest:
 		// Optional (capability ‘supportsDataBreakpoints’)
 		s.sendUnsupportedErrorResponse(request.Request)
 	case *dap.SetDataBreakpointsRequest:
 		// Optional (capability ‘supportsDataBreakpoints’)
 		s.sendUnsupportedErrorResponse(request.Request)
-	case *dap.ReadMemoryRequest:
-		// Optional (capability ‘supportsReadMemoryRequest‘)
-		// TODO: implement this request in V1
-		s.onReadMemoryRequest(request)
-	case *dap.DisassembleRequest:
-		// Optional (capability ‘supportsDisassembleRequest’)
-		// TODO: implement this request in V1
-		s.onDisassembleRequest(request)
-	case *dap.CancelRequest:
-		// Optional (capability ‘supportsCancelRequest’)
-		// TODO: does this request make sense for delve?
-		s.onCancelRequest(request)
 	case *dap.BreakpointLocationsRequest:
 		// Optional (capability ‘supportsBreakpointLocationsRequest’)
 		s.sendUnsupportedErrorResponse(request.Request)
@@ -477,6 +589,11 @@ func (s *Server) handleRequest(request dap.Message) {
 func (s *Server) send(message dap.Message) {
 	jsonmsg, _ := json.Marshal(message)
 	s.log.Debug("[-> to client]", string(jsonmsg))
+	// TODO(polina): consider using a channel for all the sends and to have a dedicated
+	// goroutine that reads from that channel and sends over the connection.
+	// This will avoid blocking on slow network sends.
+	s.sendingMu.Lock()
+	defer s.sendingMu.Unlock()
 	_ = dap.WriteProtocolMessage(s.conn, message)
 }
 
@@ -757,9 +874,15 @@ func (s *Server) stopDebugSession(killProcess bool) error {
 	}
 	var err error
 	var exited error
+	// Halting will stop any debugger command that's pending on another
+	// per-request goroutine, hence unblocking that goroutine to wrap-up and exit.
+	// TODO(polina): Per-request goroutine could still not be done when this one is.
+	// To avoid goroutine leaks, we can use a wait group or have the goroutine listen
+	// for a stop signal on a dedicated quit channel at suitable points (use context?).
+	// Additional clean-up might be especially critical when we support multiple clients.
 	state, err := s.debugger.Command(&api.DebuggerCommand{Name: api.Halt}, nil)
 	if err == proc.ErrProcessDetached {
-		s.log.Debug(err)
+		s.log.Debug("halt returned error:", err)
 		return nil
 	}
 	if err != nil {
@@ -767,10 +890,11 @@ func (s *Server) stopDebugSession(killProcess bool) error {
 		case proc.ErrProcessExited:
 			exited = err
 		default:
-			s.log.Error(err)
+			s.log.Error("halt returned error:", err)
 		}
 	} else if state.Exited {
 		exited = proc.ErrProcessExited{Pid: s.debugger.ProcessPid(), Status: state.ExitStatus}
+		s.log.Debug("halt returned state:", exited)
 	}
 	if exited != nil {
 		s.logToConsole(exited.Error())
@@ -795,12 +919,17 @@ func (s *Server) stopDebugSession(killProcess bool) error {
 	return err
 }
 
+func (s *Server) isNoDebug() bool {
+	s.mu.Lock()
+	defer s.mu.Unlock()
+	return s.noDebugProcess != nil
+}
+
 func (s *Server) onSetBreakpointsRequest(request *dap.SetBreakpointsRequest) {
-	if s.noDebugProcess != nil {
+	if s.isNoDebug() {
 		s.sendErrorResponse(request.Request, UnableToSetBreakpoints, "Unable to set or clear breakpoints", "running in noDebug mode")
 		return
 	}
-	// TODO(polina): handle this while running by halting first.
 
 	if request.Arguments.Source.Path == "" {
 		s.sendErrorResponse(request.Request, UnableToSetBreakpoints, "Unable to set or clear breakpoints", "empty file path")
@@ -865,8 +994,24 @@ func (s *Server) onSetExceptionBreakpointsRequest(request *dap.SetExceptionBreak
 	s.send(&dap.SetExceptionBreakpointsResponse{Response: *newResponse(request.Request)})
 }
 
-func (s *Server) onConfigurationDoneRequest(request *dap.ConfigurationDoneRequest) {
-	if s.debugger != nil && s.args.stopOnEntry {
+func (s *Server) asyncCommandDone(asyncSetupDone chan struct{}) {
+	if asyncSetupDone != nil {
+		select {
+		case <-asyncSetupDone:
+			// already closed
+		default:
+			close(asyncSetupDone)
+		}
+	}
+}
+
+// onConfigurationDoneRequest handles 'configurationDone' request.
+// This is an optional request enabled by capability ‘supportsConfigurationDoneRequest’.
+// It gets triggered after all the debug requests that followinitalized event,
+// so the s.debugger is guaranteed to be set.
+func (s *Server) onConfigurationDoneRequest(request *dap.ConfigurationDoneRequest, asyncSetupDone chan struct{}) {
+	defer s.asyncCommandDone(asyncSetupDone)
+	if s.args.stopOnEntry {
 		e := &dap.StoppedEvent{
 			Event: *newEvent("stopped"),
 			Body:  dap.StoppedEventBody{Reason: "entry", ThreadId: 1, AllThreadsStopped: true},
@@ -874,16 +1019,18 @@ func (s *Server) onConfigurationDoneRequest(request *dap.ConfigurationDoneReques
 		s.send(e)
 	}
 	s.send(&dap.ConfigurationDoneResponse{Response: *newResponse(request.Request)})
-	if s.debugger != nil && !s.args.stopOnEntry {
-		s.doCommand(api.Continue)
+	if !s.args.stopOnEntry {
+		s.doCommand(api.Continue, asyncSetupDone)
 	}
 }
 
-func (s *Server) onContinueRequest(request *dap.ContinueRequest) {
+// onContinueRequest handles 'continue' request.
+// This is a mandatory request to support.
+func (s *Server) onContinueRequest(request *dap.ContinueRequest, asyncSetupDone chan struct{}) {
 	s.send(&dap.ContinueResponse{
 		Response: *newResponse(request.Request),
 		Body:     dap.ContinueResponseBody{AllThreadsContinued: true}})
-	s.doCommand(api.Continue)
+	s.doCommand(api.Continue, asyncSetupDone)
 }
 
 func fnName(loc *proc.Location) string {
@@ -893,11 +1040,15 @@ func fnName(loc *proc.Location) string {
 	return loc.Fn.Name
 }
 
+// onThreadsRequest handles 'threads' request.
+// This is a mandatory request to support.
+// It is sent in response to configurationDone response and stopped events.
 func (s *Server) onThreadsRequest(request *dap.ThreadsRequest) {
 	if s.debugger == nil {
 		s.sendErrorResponse(request.Request, UnableToDisplayThreads, "Unable to display threads", "debugger is nil")
 		return
 	}
+
 	gs, _, err := s.debugger.Goroutines(0, 0)
 	if err != nil {
 		switch err.(type) {
@@ -997,23 +1148,23 @@ func (s *Server) onAttachRequest(request *dap.AttachRequest) {
 
 // onNextRequest handles 'next' request.
 // This is a mandatory request to support.
-func (s *Server) onNextRequest(request *dap.NextRequest) {
+func (s *Server) onNextRequest(request *dap.NextRequest, asyncSetupDone chan struct{}) {
 	s.send(&dap.NextResponse{Response: *newResponse(request.Request)})
-	s.doStepCommand(api.Next, request.Arguments.ThreadId)
+	s.doStepCommand(api.Next, request.Arguments.ThreadId, asyncSetupDone)
 }
 
 // onStepInRequest handles 'stepIn' request
 // This is a mandatory request to support.
-func (s *Server) onStepInRequest(request *dap.StepInRequest) {
+func (s *Server) onStepInRequest(request *dap.StepInRequest, asyncSetupDone chan struct{}) {
 	s.send(&dap.StepInResponse{Response: *newResponse(request.Request)})
-	s.doStepCommand(api.Step, request.Arguments.ThreadId)
+	s.doStepCommand(api.Step, request.Arguments.ThreadId, asyncSetupDone)
 }
 
 // onStepOutRequest handles 'stepOut' request
 // This is a mandatory request to support.
-func (s *Server) onStepOutRequest(request *dap.StepOutRequest) {
+func (s *Server) onStepOutRequest(request *dap.StepOutRequest, asyncSetupDone chan struct{}) {
 	s.send(&dap.StepOutResponse{Response: *newResponse(request.Request)})
-	s.doStepCommand(api.StepOut, request.Arguments.ThreadId)
+	s.doStepCommand(api.StepOut, request.Arguments.ThreadId, asyncSetupDone)
 }
 
 func stoppedGoroutineID(state *api.DebuggerState) (id int) {
@@ -1025,8 +1176,13 @@ func stoppedGoroutineID(state *api.DebuggerState) (id int) {
 	return id
 }
 
-func (s *Server) doStepCommand(command string, threadId int) {
-	// Use SwitchGoroutine to change the current goroutine.
+// doStepCommand is a wrapper around doCommand that
+// first switches selected goroutine. asyncSetupDone is
+// a channel that will be closed to signal that an
+// asynchornous command has completed setup or was interrupted
+// due to an error, so the server is ready to receive new requests.
+func (s *Server) doStepCommand(command string, threadId int, asyncSetupDone chan struct{}) {
+	defer s.asyncCommandDone(asyncSetupDone)
 	_, err := s.debugger.Command(&api.DebuggerCommand{Name: api.SwitchGoroutine, GoroutineID: threadId}, nil)
 	if err != nil {
 		s.log.Errorf("Error switching goroutines while stepping: %v", err)
@@ -1044,7 +1200,7 @@ func (s *Server) doStepCommand(command string, threadId int) {
 		s.send(stopped)
 		return
 	}
-	s.doCommand(command)
+	s.doCommand(command, asyncSetupDone)
 }
 
 // onPauseRequest sends a not-yet-implemented error response.
@@ -1062,6 +1218,8 @@ type stackFrame struct {
 
 // onStackTraceRequest handles ‘stackTrace’ requests.
 // This is a mandatory request to support.
+// As per DAP spec, this request only gets triggered as a follow-up
+// to a successful threads request as part of the "request waterfall".
 func (s *Server) onStackTraceRequest(request *dap.StackTraceRequest) {
 	goroutineID := request.Arguments.ThreadId
 	frames, err := s.debugger.Stacktrace(goroutineID, s.args.stackTraceDepth, 0)
@@ -1101,6 +1259,9 @@ func (s *Server) onStackTraceRequest(request *dap.StackTraceRequest) {
 
 // onScopesRequest handles 'scopes' requests.
 // This is a mandatory request to support.
+// It is automatically sent as part of the threads > stacktrace > scopes > variables
+// "waterfall" to highlight the topmost frame at stops, after an evaluate request
+// for the selected scope or when a user selects different scopes in the UI.
 func (s *Server) onScopesRequest(request *dap.ScopesRequest) {
 	sf, ok := s.stackFrameHandles.get(request.Arguments.FrameId)
 	if !ok {
@@ -1431,6 +1592,7 @@ func (s *Server) onEvaluateRequest(request *dap.EvaluateRequest) {
 		s.sendErrorResponseWithOpts(request.Request, UnableToEvaluateExpression, "Unable to evaluate expression", "debugger is nil", showErrorToUser)
 		return
 	}
+
 	// Default to the topmost stack frame of the current goroutine in case
 	// no frame is specified (e.g. when stopped on entry or no call stack frame is expanded)
 	goid, frame := -1, 0
@@ -1457,6 +1619,9 @@ func (s *Server) onEvaluateRequest(request *dap.EvaluateRequest) {
 			s.sendErrorResponseWithOpts(request.Request, UnableToEvaluateExpression, "Unable to evaluate expression", err.Error(), showErrorToUser)
 			return
 		}
+		// TODO(polina): since call will resume execution of all goroutines,
+		// we should do this asynchronously and send a continued event to the
+		// editor, followed by a stop event when the call completes.
 		state, err := s.debugger.Command(&api.DebuggerCommand{
 			Name:                 api.Call,
 			ReturnInfoLoadConfig: api.LoadConfigFromProc(&DefaultLoadConfig),
@@ -1677,16 +1842,18 @@ func (s *Server) resetHandlesForStoppedEvent() {
 
 // doCommand runs a debugger command until it stops on
 // termination, error, breakpoint, etc, when an appropriate
-// event needs to be sent to the client.
-func (s *Server) doCommand(command string) {
-	if s.debugger == nil {
-		return
-	}
-
-	state, err := s.debugger.Command(&api.DebuggerCommand{Name: command}, nil)
+// event needs to be sent to the client. asyncSetupDone is
+// a channel that will be closed to signal that an
+// asynchornous command has completed setup or was interrupted
+// due to an error, so the server is ready to receive new requests.
+func (s *Server) doCommand(command string, asyncSetupDone chan struct{}) {
+	// TODO(polina): it appears that debugger.Command doesn't close
+	// asyncSetupDone (e.g. when having an error next while nexting).
+	// So we should always close it ourselves just in case.
+	defer s.asyncCommandDone(asyncSetupDone)
+	state, err := s.debugger.Command(&api.DebuggerCommand{Name: command}, asyncSetupDone)
 	if _, isexited := err.(proc.ErrProcessExited); isexited || err == nil && state.Exited {
-		e := &dap.TerminatedEvent{Event: *newEvent("terminated")}
-		s.send(e)
+		s.send(&dap.TerminatedEvent{Event: *newEvent("terminated")})
 		return
 	}
 
@@ -1697,9 +1864,15 @@ func (s *Server) doCommand(command string) {
 	if err == nil {
 		stopped.Body.ThreadId = stoppedGoroutineID(state)
 
-		switch s.debugger.StopReason() {
+		sr := s.debugger.StopReason()
+		s.log.Debugf("%q command stopped - reason %q", command, sr)
+		switch sr {
 		case proc.StopNextFinished:
 			stopped.Body.Reason = "step"
+		case proc.StopManual: // triggered by halt
+			stopped.Body.Reason = "pause"
+		case proc.StopUnknown: // can happen while stopping
+			stopped.Body.Reason = "unknown"
 		default:
 			stopped.Body.Reason = "breakpoint"
 		}
@@ -1711,7 +1884,6 @@ func (s *Server) doCommand(command string) {
 				stopped.Body.Reason = "panic"
 			}
 		}
-		s.send(stopped)
 	} else {
 		s.log.Error("runtime error: ", err)
 		stopped.Body.Reason = "runtime error"
@@ -1724,7 +1896,6 @@ func (s *Server) doCommand(command string) {
 		if err == nil {
 			stopped.Body.ThreadId = stoppedGoroutineID(state)
 		}
-		s.send(stopped)
 
 		// TODO(polina): according to the spec, the extra 'text' is supposed to show up in the UI (e.g. on hover),
 		// but so far I am unable to get this to work in vscode - see https://github.com/microsoft/vscode/issues/104475.
@@ -1739,6 +1910,11 @@ func (s *Server) doCommand(command string) {
 				Category: "stderr",
 			}})
 	}
+
+	// NOTE: If we happen to be responding to another request with an is-running
+	// error while this one completes, it is possible that the error response
+	// will arrive after this stopped event.
+	s.send(stopped)
 }
 
 func (s *Server) toClientPath(path string) string {
diff --git a/service/debugger/debugger.go b/service/debugger/debugger.go
index 053b8787..14ecd46d 100644
--- a/service/debugger/debugger.go
+++ b/service/debugger/debugger.go
@@ -549,7 +549,7 @@ func (d *Debugger) Restart(rerecord bool, pos string, resetArgs bool, newArgs []
 
 // State returns the current state of the debugger.
 func (d *Debugger) State(nowait bool) (*api.DebuggerState, error) {
-	if d.isRunning() && nowait {
+	if d.IsRunning() && nowait {
 		return &api.DebuggerState{Running: true}, nil
 	}
 
@@ -973,7 +973,7 @@ func (d *Debugger) setRunning(running bool) {
 	d.runningMutex.Unlock()
 }
 
-func (d *Debugger) isRunning() bool {
+func (d *Debugger) IsRunning() bool {
 	d.runningMutex.Lock()
 	defer d.runningMutex.Unlock()
 	return d.running
@@ -1711,7 +1711,7 @@ func (d *Debugger) GetVersion(out *api.GetVersionOut) error {
 		}
 	}
 
-	if !d.isRecording() && !d.isRunning() {
+	if !d.isRecording() && !d.IsRunning() {
 		out.TargetGoVersion = d.target.BinInfo().Producer()
 	}
 
diff --git a/vendor/github.com/google/go-dap/codec.go b/vendor/github.com/google/go-dap/codec.go
index d0443b1d..f9cd799d 100644
--- a/vendor/github.com/google/go-dap/codec.go
+++ b/vendor/github.com/google/go-dap/codec.go
@@ -24,13 +24,14 @@ import (
 // DecodeProtocolMessageFieldError describes which JSON attribute
 // has an unsupported value that the decoding cannot handle.
 type DecodeProtocolMessageFieldError struct {
+	Seq        int
 	SubType    string
 	FieldName  string
 	FieldValue string
 }
 
 func (e *DecodeProtocolMessageFieldError) Error() string {
-	return fmt.Sprintf("%s %s '%s' is not supported", e.SubType, e.FieldName, e.FieldValue)
+	return fmt.Sprintf("%s %s '%s' is not supported (seq: %d)", e.SubType, e.FieldName, e.FieldValue, e.Seq)
 }
 
 // DecodeProtocolMessage parses the JSON-encoded data and returns the result of
@@ -50,7 +51,7 @@ func DecodeProtocolMessage(data []byte) (Message, error) {
 	case "event":
 		return decodeEvent(data)
 	default:
-		return nil, &DecodeProtocolMessageFieldError{"ProtocolMessage", "type", protomsg.Type}
+		return nil, &DecodeProtocolMessageFieldError{protomsg.GetSeq(), "ProtocolMessage", "type", protomsg.Type}
 	}
 }
 
@@ -69,15 +70,24 @@ func decodeRequest(data []byte) (Message, error) {
 		err := json.Unmarshal(data, requestPtr)
 		return requestPtr, err
 	}
-	return nil, &DecodeProtocolMessageFieldError{"Request", "command", r.Command}
+	return nil, &DecodeProtocolMessageFieldError{r.GetSeq(), "Request", "command", r.Command}
 }
 
 // Mapping of request commands and corresponding struct constructors that
 // can be passed to json.Unmarshal.
 var requestCtor = map[string]messageCtor{
-	"cancel":                  func() Message { return &CancelRequest{} },
-	"runInTerminal":           func() Message { return &RunInTerminalRequest{} },
-	"initialize":              func() Message { return &InitializeRequest{} },
+	"cancel":        func() Message { return &CancelRequest{} },
+	"runInTerminal": func() Message { return &RunInTerminalRequest{} },
+	"initialize": func() Message {
+		return &InitializeRequest{
+			Arguments: InitializeRequestArguments{
+				// Set the default values specified here: https://microsoft.github.io/debug-adapter-protocol/specification#Requests_Initialize.
+				LinesStartAt1:   true,
+				ColumnsStartAt1: true,
+				PathFormat:      "path",
+			},
+		}
+	},
 	"configurationDone":       func() Message { return &ConfigurationDoneRequest{} },
 	"launch":                  func() Message { return &LaunchRequest{} },
 	"attach":                  func() Message { return &AttachRequest{} },
@@ -136,7 +146,7 @@ func decodeResponse(data []byte) (Message, error) {
 		err := json.Unmarshal(data, responsePtr)
 		return responsePtr, err
 	}
-	return nil, &DecodeProtocolMessageFieldError{"Response", "command", r.Command}
+	return nil, &DecodeProtocolMessageFieldError{r.GetSeq(), "Response", "command", r.Command}
 }
 
 // Mapping of response commands and corresponding struct constructors that
@@ -198,7 +208,7 @@ func decodeEvent(data []byte) (Message, error) {
 		err := json.Unmarshal(data, eventPtr)
 		return eventPtr, err
 	}
-	return nil, &DecodeProtocolMessageFieldError{"Event", "event", e.Event}
+	return nil, &DecodeProtocolMessageFieldError{e.GetSeq(), "Event", "event", e.Event}
 }
 
 // Mapping of event ids and corresponding struct constructors that
diff --git a/vendor/github.com/google/go-dap/io.go b/vendor/github.com/google/go-dap/io.go
index 5b85851b..87125ef2 100644
--- a/vendor/github.com/google/go-dap/io.go
+++ b/vendor/github.com/google/go-dap/io.go
@@ -95,7 +95,7 @@ func ReadBaseMessage(r *bufio.Reader) ([]byte, error) {
 // and required:
 // 		Content-Length: [0-9]+\r\n\r\n
 // Extracts and returns the content length.
-func readContentLengthHeader(r *bufio.Reader) (contentLength int, err error) {
+func readContentLengthHeader(r *bufio.Reader) (contentLength int64, err error) {
 	// Look for <some header>\r\n\r\n
 	headerWithCr, err := r.ReadString('\r')
 	if err != nil {
@@ -115,7 +115,7 @@ func readContentLengthHeader(r *bufio.Reader) (contentLength int, err error) {
 	if len(headerAndLength) < 2 {
 		return 0, ErrHeaderNotContentLength
 	}
-	return strconv.Atoi(headerAndLength[1])
+	return strconv.ParseInt(headerAndLength[1], 10, 64)
 }
 
 // WriteProtocolMessage encodes message and writes it to w.
diff --git a/vendor/github.com/google/go-dap/schematypes.go b/vendor/github.com/google/go-dap/schematypes.go
index fcd6f61a..219159e9 100644
--- a/vendor/github.com/google/go-dap/schematypes.go
+++ b/vendor/github.com/google/go-dap/schematypes.go
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// DO NOT EDIT: This file is auto-generated.
+// Code generated by "cmd/gentypes/gentypes.go"; DO NOT EDIT.
 // DAP spec: https://microsoft.github.io/debug-adapter-protocol/specification
 // See cmd/gentypes/README.md for additional details.
 
@@ -166,6 +166,7 @@ type StoppedEventBody struct {
 	PreserveFocusHint bool   `json:"preserveFocusHint,omitempty"`
 	Text              string `json:"text,omitempty"`
 	AllThreadsStopped bool   `json:"allThreadsStopped,omitempty"`
+	HitBreakpointIds  []int  `json:"hitBreakpointIds,omitempty"`
 }
 
 func (e *StoppedEvent) GetEvent() *Event { return &e.Event }
@@ -374,6 +375,23 @@ type ProgressEndEventBody struct {
 
 func (e *ProgressEndEvent) GetEvent() *Event { return &e.Event }
 
+// InvalidatedEvent: This event signals that some state in the debug adapter has changed and requires that the client needs to re-render the data snapshot previously requested.
+// Debug adapters do not have to emit this event for runtime changes like stopped or thread events because in that case the client refetches the new state anyway. But the event can be used for example to refresh the UI after rendering formatting has changed in the debug adapter.
+// This event should only be sent if the debug adapter has received a value true for the 'supportsInvalidatedEvent' capability of the 'initialize' request.
+type InvalidatedEvent struct {
+	Event
+
+	Body InvalidatedEventBody `json:"body"`
+}
+
+type InvalidatedEventBody struct {
+	Areas        []InvalidatedAreas `json:"areas,omitempty"`
+	ThreadId     int                `json:"threadId,omitempty"`
+	StackFrameId int                `json:"stackFrameId,omitempty"`
+}
+
+func (e *InvalidatedEvent) GetEvent() *Event { return &e.Event }
+
 // RunInTerminalRequest: This optional request is sent from the debug adapter to the client to run a command in a terminal.
 // This is typically used to launch the debuggee in a terminal provided by the client.
 // This request should only be called if the client has passed the value true for the 'supportsRunInTerminalRequest' capability of the 'initialize' request.
@@ -427,14 +445,15 @@ type InitializeRequestArguments struct {
 	ClientName                   string `json:"clientName,omitempty"`
 	AdapterID                    string `json:"adapterID"`
 	Locale                       string `json:"locale,omitempty"`
-	LinesStartAt1                bool   `json:"linesStartAt1,omitempty"`
-	ColumnsStartAt1              bool   `json:"columnsStartAt1,omitempty"`
+	LinesStartAt1                bool   `json:"linesStartAt1"`
+	ColumnsStartAt1              bool   `json:"columnsStartAt1"`
 	PathFormat                   string `json:"pathFormat,omitempty"`
 	SupportsVariableType         bool   `json:"supportsVariableType,omitempty"`
 	SupportsVariablePaging       bool   `json:"supportsVariablePaging,omitempty"`
 	SupportsRunInTerminalRequest bool   `json:"supportsRunInTerminalRequest,omitempty"`
 	SupportsMemoryReferences     bool   `json:"supportsMemoryReferences,omitempty"`
 	SupportsProgressReporting    bool   `json:"supportsProgressReporting,omitempty"`
+	SupportsInvalidatedEvent     bool   `json:"supportsInvalidatedEvent,omitempty"`
 }
 
 // InitializeResponse: Response to 'initialize' request.
@@ -516,6 +535,7 @@ func (r *RestartRequest) GetRequest() *Request { return &r.Request }
 
 // RestartArguments: Arguments for 'restart' request.
 type RestartArguments struct {
+	Arguments interface{} `json:"arguments,omitempty"`
 }
 
 // RestartResponse: Response to 'restart' request. This is just an acknowledgement, so no body field is required.
@@ -542,6 +562,7 @@ func (r *DisconnectRequest) GetRequest() *Request { return &r.Request }
 type DisconnectArguments struct {
 	Restart           bool `json:"restart,omitempty"`
 	TerminateDebuggee bool `json:"terminateDebuggee,omitempty"`
+	SuspendDebuggee   bool `json:"suspendDebuggee,omitempty"`
 }
 
 // DisconnectResponse: Response to 'disconnect' request. This is just an acknowledgement, so no body field is required.
@@ -686,13 +707,23 @@ func (r *SetExceptionBreakpointsRequest) GetRequest() *Request { return &r.Reque
 
 // SetExceptionBreakpointsArguments: Arguments for 'setExceptionBreakpoints' request.
 type SetExceptionBreakpointsArguments struct {
-	Filters          []string           `json:"filters"`
-	ExceptionOptions []ExceptionOptions `json:"exceptionOptions,omitempty"`
+	Filters          []string                 `json:"filters"`
+	FilterOptions    []ExceptionFilterOptions `json:"filterOptions,omitempty"`
+	ExceptionOptions []ExceptionOptions       `json:"exceptionOptions,omitempty"`
 }
 
-// SetExceptionBreakpointsResponse: Response to 'setExceptionBreakpoints' request. This is just an acknowledgement, so no body field is required.
+// SetExceptionBreakpointsResponse: Response to 'setExceptionBreakpoints' request.
+// The response contains an array of Breakpoint objects with information about each exception breakpoint or filter. The Breakpoint objects are in the same order as the elements of the 'filters', 'filterOptions', 'exceptionOptions' arrays given as arguments. If both 'filters' and 'filterOptions' are given, the returned array must start with 'filters' information first, followed by 'filterOptions' information.
+// The mandatory 'verified' property of a Breakpoint object signals whether the exception breakpoint or filter could be successfully created and whether the optional condition or hit count expressions are valid. In case of an error the 'message' property explains the problem. An optional 'id' property can be used to introduce a unique ID for the exception breakpoint or filter so that it can be updated subsequently by sending breakpoint events.
+// For backward compatibility both the 'breakpoints' array and the enclosing 'body' are optional. If these elements are missing a client will not be able to show problems for individual exception breakpoints or filters.
 type SetExceptionBreakpointsResponse struct {
 	Response
+
+	Body SetExceptionBreakpointsResponseBody `json:"body,omitempty"`
+}
+
+type SetExceptionBreakpointsResponseBody struct {
+	Breakpoints []Breakpoint `json:"breakpoints,omitempty"`
 }
 
 func (r *SetExceptionBreakpointsResponse) GetResponse() *Response { return &r.Response }
@@ -1008,7 +1039,8 @@ type PauseResponse struct {
 
 func (r *PauseResponse) GetResponse() *Response { return &r.Response }
 
-// StackTraceRequest: The request returns a stacktrace from the current execution state.
+// StackTraceRequest: The request returns a stacktrace from the current execution state of a given thread.
+// A client can request all stack frames by omitting the startFrame and levels arguments. For performance conscious clients and if the debug adapter's 'supportsDelayedStackTraceLoading' capability is true, stack frames can be retrieved in a piecemeal way with the startFrame and levels arguments. The response of the stackTrace request may contain a totalFrames property that hints at the total number of frames in the stack. If a client needs this total number upfront, it can issue a request for a single (first) frame and depending on the value of totalFrames decide how to proceed. In any case a client should be prepared to receive less frames than requested, which is an indication that the end of the stack has been reached.
 type StackTraceRequest struct {
 	Request
 
@@ -1544,6 +1576,7 @@ type Capabilities struct {
 	SupportsValueFormattingOptions     bool                         `json:"supportsValueFormattingOptions,omitempty"`
 	SupportsExceptionInfoRequest       bool                         `json:"supportsExceptionInfoRequest,omitempty"`
 	SupportTerminateDebuggee           bool                         `json:"supportTerminateDebuggee,omitempty"`
+	SupportSuspendDebuggee             bool                         `json:"supportSuspendDebuggee,omitempty"`
 	SupportsDelayedStackTraceLoading   bool                         `json:"supportsDelayedStackTraceLoading,omitempty"`
 	SupportsLoadedSourcesRequest       bool                         `json:"supportsLoadedSourcesRequest,omitempty"`
 	SupportsLogPoints                  bool                         `json:"supportsLogPoints,omitempty"`
@@ -1558,13 +1591,17 @@ type Capabilities struct {
 	SupportsClipboardContext           bool                         `json:"supportsClipboardContext,omitempty"`
 	SupportsSteppingGranularity        bool                         `json:"supportsSteppingGranularity,omitempty"`
 	SupportsInstructionBreakpoints     bool                         `json:"supportsInstructionBreakpoints,omitempty"`
+	SupportsExceptionFilterOptions     bool                         `json:"supportsExceptionFilterOptions,omitempty"`
 }
 
-// ExceptionBreakpointsFilter: An ExceptionBreakpointsFilter is shown in the UI as an option for configuring how exceptions are dealt with.
+// ExceptionBreakpointsFilter: An ExceptionBreakpointsFilter is shown in the UI as an filter option for configuring how exceptions are dealt with.
 type ExceptionBreakpointsFilter struct {
-	Filter  string `json:"filter"`
-	Label   string `json:"label"`
-	Default bool   `json:"default,omitempty"`
+	Filter               string `json:"filter"`
+	Label                string `json:"label"`
+	Description          string `json:"description,omitempty"`
+	Default              bool   `json:"default,omitempty"`
+	SupportsCondition    bool   `json:"supportsCondition,omitempty"`
+	ConditionDescription string `json:"conditionDescription,omitempty"`
 }
 
 // ErrorMessage: A structured message object. Used to return errors from requests.
@@ -1644,6 +1681,7 @@ type StackFrame struct {
 	Column                      int         `json:"column"`
 	EndLine                     int         `json:"endLine,omitempty"`
 	EndColumn                   int         `json:"endColumn,omitempty"`
+	CanRestart                  bool        `json:"canRestart,omitempty"`
 	InstructionPointerReference string      `json:"instructionPointerReference,omitempty"`
 	ModuleId                    interface{} `json:"moduleId,omitempty"`
 	PresentationHint            string      `json:"presentationHint,omitempty"`
@@ -1809,6 +1847,12 @@ type StackFrameFormat struct {
 	IncludeAll      bool `json:"includeAll,omitempty"`
 }
 
+// ExceptionFilterOptions: An ExceptionFilterOptions is used to specify an exception filter together with a condition for the setExceptionsFilter request.
+type ExceptionFilterOptions struct {
+	FilterId  string `json:"filterId"`
+	Condition string `json:"condition,omitempty"`
+}
+
 // ExceptionOptions: An ExceptionOptions assigns configuration options to a set of exceptions.
 type ExceptionOptions struct {
 	Path      []ExceptionPathSegment `json:"path,omitempty"`
@@ -1852,3 +1896,6 @@ type DisassembledInstruction struct {
 	EndLine          int    `json:"endLine,omitempty"`
 	EndColumn        int    `json:"endColumn,omitempty"`
 }
+
+// InvalidatedAreas: Logical areas that can be invalidated by the 'invalidated' event.
+type InvalidatedAreas string
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 1d945805..8de15d3d 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -4,7 +4,7 @@ github.com/cosiner/argv
 github.com/cpuguy83/go-md2man/md2man
 # github.com/creack/pty v1.1.9
 github.com/creack/pty
-# github.com/google/go-dap v0.4.0
+# github.com/google/go-dap v0.5.0
 github.com/google/go-dap
 # github.com/hashicorp/golang-lru v0.5.4
 github.com/hashicorp/golang-lru/simplelru


Test patch:
diff --git a/Documentation/usage/dlv_test.md b/Documentation/usage/dlv_test.md
index e86207cd..59198414 100644
--- a/Documentation/usage/dlv_test.md
+++ b/Documentation/usage/dlv_test.md
@@ -10,7 +10,11 @@ Compiles a test binary with optimizations disabled and begins a new debug sessio
 The test command allows you to begin a new debug session in the context of your
 unit tests. By default Delve will debug the tests in the current directory.
 Alternatively you can specify a package name, and Delve will debug the tests in
-that package instead.
+that package instead. Double-dashes `--` can be used to pass arguments to the test program:
+
+dlv test [package] -- -test.v -other-argument
+
+See also: 'go help testflag'.
 
 ```
 dlv test [package]
diff --git a/service/dap/server_test.go b/service/dap/server_test.go
index bd920d26..b041aa63 100644
--- a/service/dap/server_test.go
+++ b/service/dap/server_test.go
@@ -341,22 +341,51 @@ func TestAttachStopOnEntry(t *testing.T) {
 			t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=10 Result=2", evResp)
 		}
 
-		// We cannot contiue here like in the launch case becase the program
-		// will never terminate. Since we won't receive a terminate event and
-		// there are no breakpoints to stop on, we just detach.
+		// 12 >> continue, << continue
+		client.ContinueRequest(1)
+		cResp := client.ExpectContinueResponse(t)
+		if cResp.Seq != 0 || cResp.RequestSeq != 12 {
+			t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=12", cResp)
+		}
 
 		// TODO(polina): once https://github.com/go-delve/delve/issues/2259 is
 		// fixed, test with kill=false.
 
-		// 12 >> disconnect, << disconnect
+		// 13 >> disconnect, << disconnect
 		client.DisconnectRequestWithKillOption(true)
-		oed := client.ExpectOutputEventDetachingKill(t)
-		if oed.Seq != 0 || oed.Body.Category != "console" {
-			t.Errorf("\ngot %#v\nwant Seq=0 Category='console'", oed)
+
+		// Both of these scenarios are somehow possible.
+		// Even though the program has an infininte loop,
+		// it apears that a halt can cause it to terminate.
+		// Since we are in async mode while running, we might receive messages in either order.
+		msg := client.ExpectMessage(t)
+		switch m := msg.(type) {
+		case *dap.StoppedEvent:
+			if m.Seq != 0 || m.Body.Reason != "pause" { // continue is interrupted
+				t.Errorf("\ngot %#v\nwant Seq=0 Reason='pause'", m)
+			}
+			oed := client.ExpectOutputEventDetachingKill(t)
+			if oed.Seq != 0 || oed.Body.Category != "console" {
+				t.Errorf("\ngot %#v\nwant Seq=0 Category='console'", oed)
+			}
+		case *dap.TerminatedEvent:
+			if m.Seq != 0 {
+				t.Errorf("\ngot %#v\nwant Seq=0'", m)
+			}
+			oep := client.ExpectOutputEventProcessExited(t, 0)
+			if oep.Seq != 0 || oep.Body.Category != "console" {
+				t.Errorf("\ngot %#v\nwant Seq=0 Category='console'", oep)
+			}
+			oed := client.ExpectOutputEventDetaching(t)
+			if oed.Seq != 0 || oed.Body.Category != "console" {
+				t.Errorf("\ngot %#v\nwant Seq=0 Category='console'", oed)
+			}
+		default:
+			t.Fatalf("got %#v, want StoppedEvent or TerminatedEvent", m)
 		}
 		dResp := client.ExpectDisconnectResponse(t)
-		if dResp.Seq != 0 || dResp.RequestSeq != 12 {
-			t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=12", dResp)
+		if dResp.Seq != 0 || dResp.RequestSeq != 13 {
+			t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=13", dResp)
 		}
 	})
 }
@@ -384,35 +413,47 @@ func TestContinueOnEntry(t *testing.T) {
 		// 5 >> configurationDone, << configurationDone
 		client.ConfigurationDoneRequest()
 		client.ExpectConfigurationDoneResponse(t)
-		// "Continue" happens behind the scenes
+		// "Continue" happens behind the scenes on another goroutine
 
-		// For now continue is blocking and runs until a stop or
-		// termination. But once we upgrade the server to be async,
-		// a simultaneous threads request can be made while continue
-		// is running. Note that vscode-go just keeps track of the
-		// continue state and would just return a dummy response
-		// without talking to debugger if continue was in progress.
-		// TODO(polina): test this once it is possible
-
-		client.ExpectTerminatedEvent(t)
+		// 6 >> threads, << threads
+		client.ThreadsRequest()
+		// Since we are in async mode while running, we might receive messages in either order.
+		for i := 0; i < 2; i++ {
+			msg := client.ExpectMessage(t)
+			switch m := msg.(type) {
+			case *dap.ThreadsResponse:
+				if m.Seq != 0 || m.RequestSeq != 6 {
+					t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=6", m)
+				}
+				// Single current thread is sent when the program is running
+				// because DAP spec expects at least one thread.
+				// TODO(polina): accept empty already-terminated response here as well?
+				if len(m.Body.Threads) != 1 || m.Body.Threads[0].Id != -1 || m.Body.Threads[0].Name != "Current" {
+					t.Errorf("\ngot  %#v\nwant Id=-1, Name=\"Current\"", m.Body.Threads)
+				}
+			case *dap.TerminatedEvent:
+			default:
+				t.Fatalf("got %#v, want ThreadsResponse or TerminatedEvent", m)
+			}
+		}
 
 		// It is possible for the program to terminate before the initial
-		// threads request is processed.
-
-		// 6 >> threads, << threads
+		// threads request is processed. And in that case, the
+		// response can be empty
+		// 7 >> threads, << threads
 		client.ThreadsRequest()
 		tResp := client.ExpectThreadsResponse(t)
-		if tResp.Seq != 0 || tResp.RequestSeq != 6 || len(tResp.Body.Threads) != 0 {
-			t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=6 len(Threads)=0", tResp)
+		if tResp.Seq != 0 || tResp.RequestSeq != 7 || len(tResp.Body.Threads) != 0 {
+			t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=7 len(Threads)=0", tResp)
 		}
 
-		// 7 >> disconnect, << disconnect
+		// 8 >> disconnect, << disconnect
 		client.DisconnectRequest()
 		client.ExpectOutputEventProcessExited(t, 0)
 		client.ExpectOutputEventDetaching(t)
 		dResp := client.ExpectDisconnectResponse(t)
-		if dResp.Seq != 0 || dResp.RequestSeq != 7 {
-			t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=7", dResp)
+		if dResp.Seq != 0 || dResp.RequestSeq != 8 {
+			t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=8", dResp)
 		}
 	})
 }
@@ -443,19 +484,28 @@ func TestPreSetBreakpoint(t *testing.T) {
 
 		client.ConfigurationDoneRequest()
 		client.ExpectConfigurationDoneResponse(t)
-		// This triggers "continue"
+		// This triggers "continue" on a separate goroutine
 
-		// TODO(polina): add a no-op threads request
-		// with dummy response here once server becomes async
-		// to match what happens in VS Code.
-
-		stopEvent1 := client.ExpectStoppedEvent(t)
-		if stopEvent1.Body.Reason != "breakpoint" ||
-			stopEvent1.Body.ThreadId != 1 ||
-			!stopEvent1.Body.AllThreadsStopped {
-			t.Errorf("got %#v, want Body={Reason=\"breakpoint\", ThreadId=1, AllThreadsStopped=true}", stopEvent1)
+		client.ThreadsRequest()
+		// Since we are in async mode while running, we might receive messages in either order.
+		for i := 0; i < 2; i++ {
+			msg := client.ExpectMessage(t)
+			switch m := msg.(type) {
+			case *dap.ThreadsResponse:
+				if len(m.Body.Threads) != 1 || m.Body.Threads[0].Id != -1 || m.Body.Threads[0].Name != "Current" {
+					t.Errorf("\ngot  %#v\nwant Id=-1, Name=\"Current\"", m.Body.Threads)
+				}
+			case *dap.StoppedEvent:
+				if m.Body.Reason != "breakpoint" || m.Body.ThreadId != 1 || !m.Body.AllThreadsStopped {
+					t.Errorf("got %#v, want Body={Reason=\"breakpoint\", ThreadId=1, AllThreadsStopped=true}", m)
+				}
+			default:
+				t.Fatalf("got %#v, want ThreadsResponse or StoppedEvent", m)
+			}
 		}
 
+		// Threads-StackTrace-Scopes-Variables request waterfall is
+		// triggered on stop event.
 		client.ThreadsRequest()
 		tResp := client.ExpectThreadsResponse(t)
 		if len(tResp.Body.Threads) < 2 { // 1 main + runtime
@@ -2415,14 +2465,14 @@ func TestBadAccess(t *testing.T) {
 
 					expectStoppedOnError := func(errorPrefix string) {
 						t.Helper()
-						se := client.ExpectStoppedEvent(t)
-						if se.Body.ThreadId != 1 || se.Body.Reason != "runtime error" || !strings.HasPrefix(se.Body.Text, errorPrefix) {
-							t.Errorf("\ngot  %#v\nwant ThreadId=1 Reason=\"runtime error\" Text=\"%s\"", se, errorPrefix)
-						}
 						oe := client.ExpectOutputEvent(t)
 						if oe.Body.Category != "stderr" || !strings.HasPrefix(oe.Body.Output, "ERROR: "+errorPrefix) {
 							t.Errorf("\ngot  %#v\nwant Category=\"stderr\" Output=\"%s ...\"", oe, errorPrefix)
 						}
+						se := client.ExpectStoppedEvent(t)
+						if se.Body.ThreadId != 1 || se.Body.Reason != "runtime error" || !strings.HasPrefix(se.Body.Text, errorPrefix) {
+							t.Errorf("\ngot  %#v\nwant ThreadId=1 Reason=\"runtime error\" Text=\"%s\"", se, errorPrefix)
+						}
 					}
 
 					client.ContinueRequest(1)