Repository: go-delve/delve
Pull request #2455 (2021-04-27 08:31:21)

service/dap: auto-loading for fully missing children of nested vars
This change provides support for automatically reloading fully missing pointers, structs, arrays and maps (subject to partial config limits on reload). It does not attempt to reload partially loaded variables, which need a different way of handling (TBD). 

This change reuses the code used for auto-loading interfaces, factored out into a helper `reloadVariable`. Since VariablesRequest focuses on looking up already loaded variables, we auto-load the children proactively when looking up their outer container variable. This allows us to update the inlined string value of the parent (but not grandparent) variable as well and avoid supplying references that resolve to nothing if auto-loading fails.

Pointers loading has a couple of quirks comparing to other types. If a pointer was not loaded because `FollowPointers=false` (hypothetical for now because users have no way of altering this, but that could change if we merge his server with the rpc server), then we must flip `FollowPointers=true` before reloading or surgically reload just the child data. Also discovered a new pointer use case - &<variable>, a special case in delve that returns a usable pointer-expression, which is not loaded by default even with FollowPointers=true. Because this case corresponds to 0x0 pointer address, it cannot be loaded directly, but instead by reloading its child data only. Should it be at all?

This is an alternative implementation to https://github.com/go-delve/delve/pull/2453 (see [comment](https://github.com/go-delve/delve/pull/2453#issuecomment-827290735)).

Updates #1515
Updates https://github.com/golang/vscode-go/issues/1052
Updates https://github.com/golang/vscode-go/issues/1450

polinasok: Full disclaimer:  I am not quite happy with how the support for pointers looks like right now. Detailed thoughts are in the code. 
polinasok: cc @hyangah 
hyangah: I am happy to abandon #2453 if you want. My intention was to free you from other distractions but clearly I failed :-).

A couple of comments 
  - In this PR, it looks like the partially or unloaded child causes prefetching (loading with EvalVariableInScope). On the other hand, I tried to avoid the prefetching until user triggers another variables request (so I didn't care about the FollowPointers setting - user's request implies the user wants to follow the pointer).
  - screenshot may be helpful to show how the expansion through reloading is presented.
  - may be want to keep track of the frame/goroutine? Or is it unnecessary?
polinasok: Thank you for a quick response!

> * In this PR, it looks like the partially or unloaded child causes prefetching (loading with EvalVariableInScope). 

Only fully missing for now. Partial ones are untouched in his change.

> On the other hand, I tried to avoid the prefetching until user triggers another variables request 

I played with both options before sending out a prototype for interfaces and decided against that approach.
The dap protocol is set up such that you load and communicate back information on the child variables (i.e. prefetch them) when responding to a Variables request for the parent container. So this is consistent with the protocol. And this way we can actually have a valid inlined value that's reflecting the additional loading. If you load later, you won't be able to update it in the UI anymore. With your approach you have to remove the "loaded x/y" labels and add expansion toggle before you actually know that things can be loaded.

> (so I didn't care about the FollowPointers setting - user's request implies the user wants to follow the pointer).

The updated test shows no evidence that your approach worked with FollowPointers=false because the value is still the old unloaded one (minus the "not loaded" label) and while you flipped noChildren=>hasChildren, you have not actually loaded those children in the test, so I don't know if the auto-loading was successful. From reading the code it seems that you would have he same issue because you mark the pointer, not the data child as partial, so when the parent is looked up, you try to reload it, which would still be subject to FollowPointers=false.

> * screenshot may be helpful to show how the expansion through reloading is presented.

Will do this next.

> * may be want to keep track of the frame/goroutine? Or is it unnecessary?

It is not necessary if we use special expressions. There is already a test confirming that we can do auto-loading for any frame.

polinasok: Before:
![image](https://user-images.githubusercontent.com/51177946/116301677-26cd9c80-a755-11eb-8db5-7a52afa8f779.png)
After:
![image](https://user-images.githubusercontent.com/51177946/116304291-41a11080-a757-11eb-842c-185ffd2465b3.png)

polinasok: With call:
![image](https://user-images.githubusercontent.com/51177946/116305419-9db86480-a758-11eb-8512-41f778dea6a2.png)

hyangah: Thanks. As long as prefetching can handle large data structure, a large number of variables in the scope without noticeable performance penalty and still allow navigating deeply nested structures, I am happy. I will drop my PR to stop wasting more time. 

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/regnum/arm64.go b/pkg/dwarf/regnum/arm64.go
index 13b15c1e..3f7805b9 100644
--- a/pkg/dwarf/regnum/arm64.go
+++ b/pkg/dwarf/regnum/arm64.go
@@ -10,13 +10,13 @@ import (
 // http://infocenter.arm.com/help/topic/com.arm.doc.ihi0040b/IHI0040B_aadwarf.pdf
 
 const (
-	ARM64_X0 = 0  // X1 through X30 follow
-	ARM64_BP = 29 // also X29
-	ARM64_LR = 30 // also X30
-	ARM64_SP = 31
-	ARM64_PC = 32
-	ARM64_V0 = 64 // V1 through V31 follow
-
+	ARM64_X0         = 0  // X1 through X30 follow
+	ARM64_BP         = 29 // also X29
+	ARM64_LR         = 30 // also X30
+	ARM64_SP         = 31
+	ARM64_PC         = 32
+	ARM64_V0         = 64 // V1 through V31 follow
+	_ARM64_MaxRegNum = ARM64_V0 + 31
 )
 
 func ARM64ToName(num uint64) string {
@@ -33,3 +33,24 @@ func ARM64ToName(num uint64) string {
 		return fmt.Sprintf("unknown%d", num)
 	}
 }
+
+func ARM64MaxRegNum() uint64 {
+	return _ARM64_MaxRegNum
+}
+
+var ARM64NameToDwarf = func() map[string]int {
+	r := make(map[string]int)
+	for i := 0; i <= 32; i++ {
+		r[fmt.Sprintf("x%d", i)] = ARM64_X0 + i
+	}
+	r["fp"] = 29
+	r["lr"] = 30
+	r["sp"] = 31
+	r["pc"] = 32
+
+	for i := 0; i <= 31; i++ {
+		r[fmt.Sprintf("v%d", i)] = ARM64_V0 + i
+	}
+
+	return r
+}()
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/amd64_arch.go b/pkg/proc/amd64_arch.go
index 6a20bbfe..1e53d5cf 100644
--- a/pkg/proc/amd64_arch.go
+++ b/pkg/proc/amd64_arch.go
@@ -38,6 +38,7 @@ func AMD64Arch(goos string) *Arch {
 		SPRegNum:                         regnum.AMD64_Rsp,
 		BPRegNum:                         regnum.AMD64_Rbp,
 		ContextRegNum:                    regnum.AMD64_Rdx,
+		asmRegisters:                     amd64AsmRegisters,
 	}
 }
 
@@ -255,11 +256,11 @@ func amd64RegSize(rn uint64) int {
 	return 8
 }
 
-func amd64RegistersToDwarfRegisters(staticBase uint64, regs Registers) op.DwarfRegisters {
+func amd64RegistersToDwarfRegisters(staticBase uint64, regs Registers) *op.DwarfRegisters {
 	dregs := initDwarfRegistersFromSlice(int(regnum.AMD64MaxRegNum()), regs, regnum.AMD64NameToDwarf)
 	dr := op.NewDwarfRegisters(staticBase, dregs, binary.LittleEndian, regnum.AMD64_Rip, regnum.AMD64_Rsp, regnum.AMD64_Rbp, 0)
 	dr.SetLoadMoreCallback(loadMoreDwarfRegistersFromSliceFunc(dr, regs, regnum.AMD64NameToDwarf))
-	return *dr
+	return dr
 }
 
 func initDwarfRegistersFromSlice(maxRegs int, regs Registers, nameToDwarf map[string]int) []*op.DwarfRegister {
diff --git a/pkg/proc/amd64_disasm.go b/pkg/proc/amd64_disasm.go
index cbacd726..be855e19 100644
--- a/pkg/proc/amd64_disasm.go
+++ b/pkg/proc/amd64_disasm.go
@@ -5,10 +5,13 @@
 package proc
 
 import (
+	"github.com/go-delve/delve/pkg/dwarf/op"
+	"github.com/go-delve/delve/pkg/dwarf/regnum"
+
 	"golang.org/x/arch/x86/x86asm"
 )
 
-func amd64AsmDecode(asmInst *AsmInstruction, mem []byte, regs Registers, memrw MemoryReadWriter, bi *BinaryInfo) error {
+func amd64AsmDecode(asmInst *AsmInstruction, mem []byte, regs *op.DwarfRegisters, memrw MemoryReadWriter, bi *BinaryInfo) error {
 	return x86AsmDecode(asmInst, mem, regs, memrw, bi, 64)
 }
 
@@ -36,3 +39,81 @@ func init() {
 		}
 	}
 }
+
+var amd64AsmRegisters = map[int]asmRegister{
+	// 8-bit
+	int(x86asm.AL):   asmRegister{regnum.AMD64_Rax, 0, mask8},
+	int(x86asm.CL):   asmRegister{regnum.AMD64_Rcx, 0, mask8},
+	int(x86asm.DL):   asmRegister{regnum.AMD64_Rdx, 0, mask8},
+	int(x86asm.BL):   asmRegister{regnum.AMD64_Rbx, 0, mask8},
+	int(x86asm.AH):   asmRegister{regnum.AMD64_Rax, 8, mask8},
+	int(x86asm.CH):   asmRegister{regnum.AMD64_Rcx, 8, mask8},
+	int(x86asm.DH):   asmRegister{regnum.AMD64_Rdx, 8, mask8},
+	int(x86asm.BH):   asmRegister{regnum.AMD64_Rbx, 8, mask8},
+	int(x86asm.SPB):  asmRegister{regnum.AMD64_Rsp, 0, mask8},
+	int(x86asm.BPB):  asmRegister{regnum.AMD64_Rbp, 0, mask8},
+	int(x86asm.SIB):  asmRegister{regnum.AMD64_Rsi, 0, mask8},
+	int(x86asm.DIB):  asmRegister{regnum.AMD64_Rdi, 0, mask8},
+	int(x86asm.R8B):  asmRegister{regnum.AMD64_R8, 0, mask8},
+	int(x86asm.R9B):  asmRegister{regnum.AMD64_R9, 0, mask8},
+	int(x86asm.R10B): asmRegister{regnum.AMD64_R10, 0, mask8},
+	int(x86asm.R11B): asmRegister{regnum.AMD64_R11, 0, mask8},
+	int(x86asm.R12B): asmRegister{regnum.AMD64_R12, 0, mask8},
+	int(x86asm.R13B): asmRegister{regnum.AMD64_R13, 0, mask8},
+	int(x86asm.R14B): asmRegister{regnum.AMD64_R14, 0, mask8},
+	int(x86asm.R15B): asmRegister{regnum.AMD64_R15, 0, mask8},
+
+	// 16-bit
+	int(x86asm.AX):   asmRegister{regnum.AMD64_Rax, 0, mask16},
+	int(x86asm.CX):   asmRegister{regnum.AMD64_Rcx, 0, mask16},
+	int(x86asm.DX):   asmRegister{regnum.AMD64_Rdx, 0, mask16},
+	int(x86asm.BX):   asmRegister{regnum.AMD64_Rbx, 0, mask16},
+	int(x86asm.SP):   asmRegister{regnum.AMD64_Rsp, 0, mask16},
+	int(x86asm.BP):   asmRegister{regnum.AMD64_Rbp, 0, mask16},
+	int(x86asm.SI):   asmRegister{regnum.AMD64_Rsi, 0, mask16},
+	int(x86asm.DI):   asmRegister{regnum.AMD64_Rdi, 0, mask16},
+	int(x86asm.R8W):  asmRegister{regnum.AMD64_R8, 0, mask16},
+	int(x86asm.R9W):  asmRegister{regnum.AMD64_R9, 0, mask16},
+	int(x86asm.R10W): asmRegister{regnum.AMD64_R10, 0, mask16},
+	int(x86asm.R11W): asmRegister{regnum.AMD64_R11, 0, mask16},
+	int(x86asm.R12W): asmRegister{regnum.AMD64_R12, 0, mask16},
+	int(x86asm.R13W): asmRegister{regnum.AMD64_R13, 0, mask16},
+	int(x86asm.R14W): asmRegister{regnum.AMD64_R14, 0, mask16},
+	int(x86asm.R15W): asmRegister{regnum.AMD64_R15, 0, mask16},
+
+	// 32-bit
+	int(x86asm.EAX):  asmRegister{regnum.AMD64_Rax, 0, mask32},
+	int(x86asm.ECX):  asmRegister{regnum.AMD64_Rcx, 0, mask32},
+	int(x86asm.EDX):  asmRegister{regnum.AMD64_Rdx, 0, mask32},
+	int(x86asm.EBX):  asmRegister{regnum.AMD64_Rbx, 0, mask32},
+	int(x86asm.ESP):  asmRegister{regnum.AMD64_Rsp, 0, mask32},
+	int(x86asm.EBP):  asmRegister{regnum.AMD64_Rbp, 0, mask32},
+	int(x86asm.ESI):  asmRegister{regnum.AMD64_Rsi, 0, mask32},
+	int(x86asm.EDI):  asmRegister{regnum.AMD64_Rdi, 0, mask32},
+	int(x86asm.R8L):  asmRegister{regnum.AMD64_R8, 0, mask32},
+	int(x86asm.R9L):  asmRegister{regnum.AMD64_R9, 0, mask32},
+	int(x86asm.R10L): asmRegister{regnum.AMD64_R10, 0, mask32},
+	int(x86asm.R11L): asmRegister{regnum.AMD64_R11, 0, mask32},
+	int(x86asm.R12L): asmRegister{regnum.AMD64_R12, 0, mask32},
+	int(x86asm.R13L): asmRegister{regnum.AMD64_R13, 0, mask32},
+	int(x86asm.R14L): asmRegister{regnum.AMD64_R14, 0, mask32},
+	int(x86asm.R15L): asmRegister{regnum.AMD64_R15, 0, mask32},
+
+	// 64-bit
+	int(x86asm.RAX): asmRegister{regnum.AMD64_Rax, 0, 0},
+	int(x86asm.RCX): asmRegister{regnum.AMD64_Rcx, 0, 0},
+	int(x86asm.RDX): asmRegister{regnum.AMD64_Rdx, 0, 0},
+	int(x86asm.RBX): asmRegister{regnum.AMD64_Rbx, 0, 0},
+	int(x86asm.RSP): asmRegister{regnum.AMD64_Rsp, 0, 0},
+	int(x86asm.RBP): asmRegister{regnum.AMD64_Rbp, 0, 0},
+	int(x86asm.RSI): asmRegister{regnum.AMD64_Rsi, 0, 0},
+	int(x86asm.RDI): asmRegister{regnum.AMD64_Rdi, 0, 0},
+	int(x86asm.R8):  asmRegister{regnum.AMD64_R8, 0, 0},
+	int(x86asm.R9):  asmRegister{regnum.AMD64_R9, 0, 0},
+	int(x86asm.R10): asmRegister{regnum.AMD64_R10, 0, 0},
+	int(x86asm.R11): asmRegister{regnum.AMD64_R11, 0, 0},
+	int(x86asm.R12): asmRegister{regnum.AMD64_R12, 0, 0},
+	int(x86asm.R13): asmRegister{regnum.AMD64_R13, 0, 0},
+	int(x86asm.R14): asmRegister{regnum.AMD64_R14, 0, 0},
+	int(x86asm.R15): asmRegister{regnum.AMD64_R15, 0, 0},
+}
diff --git a/pkg/proc/arch.go b/pkg/proc/arch.go
index 39d2ec23..9aadc6bd 100644
--- a/pkg/proc/arch.go
+++ b/pkg/proc/arch.go
@@ -1,6 +1,8 @@
 package proc
 
 import (
+	"fmt"
+
 	"github.com/go-delve/delve/pkg/dwarf/frame"
 	"github.com/go-delve/delve/pkg/dwarf/op"
 )
@@ -24,7 +26,7 @@ type Arch struct {
 
 	// asmDecode decodes the assembly instruction starting at mem[0:] into asmInst.
 	// It assumes that the Loc and AtPC fields of asmInst have already been filled.
-	asmDecode func(asmInst *AsmInstruction, mem []byte, regs Registers, memrw MemoryReadWriter, bi *BinaryInfo) error
+	asmDecode func(asmInst *AsmInstruction, mem []byte, regs *op.DwarfRegisters, memrw MemoryReadWriter, bi *BinaryInfo) error
 	// fixFrameUnwindContext applies architecture specific rules for unwinding a stack frame
 	// on the given arch.
 	fixFrameUnwindContext func(*frame.FrameContext, uint64, *BinaryInfo) *frame.FrameContext
@@ -34,7 +36,7 @@ type Arch struct {
 	// regSize returns the size (in bytes) of register regnum.
 	regSize func(uint64) int
 	// RegistersToDwarfRegisters maps hardware registers to DWARF registers.
-	RegistersToDwarfRegisters func(uint64, Registers) op.DwarfRegisters
+	RegistersToDwarfRegisters func(uint64, Registers) *op.DwarfRegisters
 	// addrAndStackRegsToDwarfRegisters returns DWARF registers from the passed in
 	// PC, SP, and BP registers in the format used by the DWARF expression interpreter.
 	addrAndStackRegsToDwarfRegisters func(uint64, uint64, uint64, uint64, uint64) op.DwarfRegisters
@@ -45,6 +47,9 @@ type Arch struct {
 	// inhibitStepInto returns whether StepBreakpoint can be set at pc.
 	inhibitStepInto func(bi *BinaryInfo, pc uint64) bool
 
+	// asmRegisters maps assembly register numbers to dwarf registers.
+	asmRegisters map[int]asmRegister
+
 	// crosscall2fn is the DIE of crosscall2, a function used by the go runtime
 	// to call C functions. This function in go 1.9 (and previous versions) had
 	// a bad frame descriptor which needs to be fixed to generate good stack
@@ -57,6 +62,18 @@ type Arch struct {
 	sigreturnfn *Function
 }
 
+type asmRegister struct {
+	dwarfNum uint64
+	offset   uint
+	mask     uint64
+}
+
+const (
+	mask8  = 0x000000ff
+	mask16 = 0x0000ffff
+	mask32 = 0xffffffff
+)
+
 // PtrSize returns the size of a pointer for the architecture.
 func (a *Arch) PtrSize() int {
 	return a.ptrSize
@@ -95,6 +112,25 @@ func (a *Arch) DerefTLS() bool {
 	return a.derefTLS
 }
 
+// getAsmRegister returns the value of the asm register asmreg using the asmRegisters table of arch.
+// The interpretation of asmreg is architecture specific and defined by the disassembler.
+// A mask value of 0 inside asmRegisters is equivalent to ^uint64(0).
+func (arch *Arch) getAsmRegister(regs *op.DwarfRegisters, asmreg int) (uint64, error) {
+	hwreg, ok := arch.asmRegisters[asmreg]
+	if !ok {
+		return 0, ErrUnknownRegister
+	}
+	reg := regs.Reg(hwreg.dwarfNum)
+	if reg == nil {
+		return 0, fmt.Errorf("register %#x not found", asmreg)
+	}
+	n := (reg.Uint64Val >> hwreg.offset)
+	if hwreg.mask != 0 {
+		n = n & hwreg.mask
+	}
+	return n, nil
+}
+
 // crosscall2 is defined in $GOROOT/src/runtime/cgo/asm_amd64.s.
 const (
 	crosscall2SPOffsetBad        = 0x8
diff --git a/pkg/proc/arm64_arch.go b/pkg/proc/arm64_arch.go
index 8196ef96..30c35765 100644
--- a/pkg/proc/arm64_arch.go
+++ b/pkg/proc/arm64_arch.go
@@ -9,7 +9,6 @@ import (
 	"github.com/go-delve/delve/pkg/dwarf/frame"
 	"github.com/go-delve/delve/pkg/dwarf/op"
 	"github.com/go-delve/delve/pkg/dwarf/regnum"
-	"golang.org/x/arch/arm64/arm64asm"
 )
 
 var arm64BreakInstruction = []byte{0x0, 0x0, 0x20, 0xd4}
@@ -36,6 +35,7 @@ func ARM64Arch(goos string) *Arch {
 		usesLR:                           true,
 		PCRegNum:                         regnum.ARM64_PC,
 		SPRegNum:                         regnum.ARM64_SP,
+		asmRegisters:                     arm64AsmRegisters,
 	}
 }
 
@@ -235,78 +235,6 @@ func arm64RegSize(regnum uint64) int {
 	return 8 // general registers
 }
 
-// The mapping between hardware registers and DWARF registers is specified
-// in the DWARF for the ARM® Architecture page 7,
-// Table 1
-// http://infocenter.arm.com/help/topic/com.arm.doc.ihi0040b/IHI0040B_aadwarf.pdf
-var arm64DwarfToHardware = map[int]arm64asm.Reg{
-	0:  arm64asm.X0,
-	1:  arm64asm.X1,
-	2:  arm64asm.X2,
-	3:  arm64asm.X3,
-	4:  arm64asm.X4,
-	5:  arm64asm.X5,
-	6:  arm64asm.X6,
-	7:  arm64asm.X7,
-	8:  arm64asm.X8,
-	9:  arm64asm.X9,
-	10: arm64asm.X10,
-	11: arm64asm.X11,
-	12: arm64asm.X12,
-	13: arm64asm.X13,
-	14: arm64asm.X14,
-	15: arm64asm.X15,
-	16: arm64asm.X16,
-	17: arm64asm.X17,
-	18: arm64asm.X18,
-	19: arm64asm.X19,
-	20: arm64asm.X20,
-	21: arm64asm.X21,
-	22: arm64asm.X22,
-	23: arm64asm.X23,
-	24: arm64asm.X24,
-	25: arm64asm.X25,
-	26: arm64asm.X26,
-	27: arm64asm.X27,
-	28: arm64asm.X28,
-	29: arm64asm.X29,
-	30: arm64asm.X30,
-	31: arm64asm.SP,
-
-	64: arm64asm.V0,
-	65: arm64asm.V1,
-	66: arm64asm.V2,
-	67: arm64asm.V3,
-	68: arm64asm.V4,
-	69: arm64asm.V5,
-	70: arm64asm.V6,
-	71: arm64asm.V7,
-	72: arm64asm.V8,
-	73: arm64asm.V9,
-	74: arm64asm.V10,
-	75: arm64asm.V11,
-	76: arm64asm.V12,
-	77: arm64asm.V13,
-	78: arm64asm.V14,
-	79: arm64asm.V15,
-	80: arm64asm.V16,
-	81: arm64asm.V17,
-	82: arm64asm.V18,
-	83: arm64asm.V19,
-	84: arm64asm.V20,
-	85: arm64asm.V21,
-	86: arm64asm.V22,
-	87: arm64asm.V23,
-	88: arm64asm.V24,
-	89: arm64asm.V25,
-	90: arm64asm.V26,
-	91: arm64asm.V27,
-	92: arm64asm.V28,
-	93: arm64asm.V29,
-	94: arm64asm.V30,
-	95: arm64asm.V31,
-}
-
 var arm64NameToDwarf = func() map[string]int {
 	r := make(map[string]int)
 	for i := 0; i <= 30; i++ {
@@ -321,36 +249,11 @@ var arm64NameToDwarf = func() map[string]int {
 	return r
 }()
 
-func maxArm64DwarfRegister() int {
-	max := int(regnum.ARM64_PC)
-	for i := range arm64DwarfToHardware {
-		if i > max {
-			max = i
-		}
-	}
-	return max
-}
-
-func arm64RegistersToDwarfRegisters(staticBase uint64, regs Registers) op.DwarfRegisters {
-	dregs := make([]*op.DwarfRegister, maxArm64DwarfRegister()+1)
-
-	dregs[regnum.ARM64_PC] = op.DwarfRegisterFromUint64(regs.PC())
-	dregs[regnum.ARM64_SP] = op.DwarfRegisterFromUint64(regs.SP())
-	dregs[regnum.ARM64_BP] = op.DwarfRegisterFromUint64(regs.BP())
-	if lr, err := regs.Get(int(arm64asm.X30)); err != nil {
-		dregs[regnum.ARM64_LR] = op.DwarfRegisterFromUint64(lr)
-	}
-
-	for dwarfReg, asmReg := range arm64DwarfToHardware {
-		v, err := regs.Get(int(asmReg))
-		if err == nil {
-			dregs[dwarfReg] = op.DwarfRegisterFromUint64(v)
-		}
-	}
-
+func arm64RegistersToDwarfRegisters(staticBase uint64, regs Registers) *op.DwarfRegisters {
+	dregs := initDwarfRegistersFromSlice(int(regnum.ARM64MaxRegNum()), regs, regnum.ARM64NameToDwarf)
 	dr := op.NewDwarfRegisters(staticBase, dregs, binary.LittleEndian, regnum.ARM64_PC, regnum.ARM64_SP, regnum.ARM64_BP, regnum.ARM64_LR)
 	dr.SetLoadMoreCallback(loadMoreDwarfRegistersFromSliceFunc(dr, regs, arm64NameToDwarf))
-	return *dr
+	return dr
 }
 
 func arm64AddrAndStackRegsToDwarfRegisters(staticBase, pc, sp, bp, lr uint64) op.DwarfRegisters {
diff --git a/pkg/proc/arm64_disasm.go b/pkg/proc/arm64_disasm.go
index 8a137a1b..bd918f4a 100644
--- a/pkg/proc/arm64_disasm.go
+++ b/pkg/proc/arm64_disasm.go
@@ -5,10 +5,13 @@
 package proc
 
 import (
+	"github.com/go-delve/delve/pkg/dwarf/op"
+	"github.com/go-delve/delve/pkg/dwarf/regnum"
+
 	"golang.org/x/arch/arm64/arm64asm"
 )
 
-func arm64AsmDecode(asmInst *AsmInstruction, mem []byte, regs Registers, memrw MemoryReadWriter, bi *BinaryInfo) error {
+func arm64AsmDecode(asmInst *AsmInstruction, mem []byte, regs *op.DwarfRegisters, memrw MemoryReadWriter, bi *BinaryInfo) error {
 	asmInst.Size = 4
 	asmInst.Bytes = mem[:asmInst.Size]
 
@@ -37,7 +40,7 @@ func arm64AsmDecode(asmInst *AsmInstruction, mem []byte, regs Registers, memrw M
 	return nil
 }
 
-func resolveCallArgARM64(inst *arm64asm.Inst, instAddr uint64, currentGoroutine bool, regs Registers, mem MemoryReadWriter, bininfo *BinaryInfo) *Location {
+func resolveCallArgARM64(inst *arm64asm.Inst, instAddr uint64, currentGoroutine bool, regs *op.DwarfRegisters, mem MemoryReadWriter, bininfo *BinaryInfo) *Location {
 	switch inst.Op {
 	case arm64asm.BL, arm64asm.BLR, arm64asm.B, arm64asm.BR:
 		//ok
@@ -55,7 +58,7 @@ func resolveCallArgARM64(inst *arm64asm.Inst, instAddr uint64, currentGoroutine
 		if !currentGoroutine || regs == nil {
 			return nil
 		}
-		pc, err = regs.Get(int(arg))
+		pc, err = bininfo.Arch.getAsmRegister(regs, int(arg))
 		if err != nil {
 			return nil
 		}
@@ -118,3 +121,11 @@ func (inst *arm64ArchInst) OpcodeEquals(op uint64) bool {
 	}
 	return uint64(inst.Op) == op
 }
+
+var arm64AsmRegisters = func() map[int]asmRegister {
+	r := make(map[int]asmRegister)
+	for i := arm64asm.X0; i <= arm64asm.X30; i++ {
+		r[int(i)] = asmRegister{regnum.ARM64_X0 + uint64(i-arm64asm.X0), 0, 0}
+	}
+	return r
+}()
diff --git a/pkg/proc/core/delve_core.go b/pkg/proc/core/delve_core.go
index 3d2fefd2..8dbc88db 100644
--- a/pkg/proc/core/delve_core.go
+++ b/pkg/proc/core/delve_core.go
@@ -3,7 +3,6 @@ package core
 import (
 	"bytes"
 	"encoding/binary"
-	"errors"
 	"fmt"
 	"strconv"
 	"strings"
@@ -156,10 +155,6 @@ func (regs *delveRegisters) Copy() (proc.Registers, error) {
 	return regs, nil
 }
 
-func (regs *delveRegisters) Get(int) (uint64, error) {
-	return 0, errors.New("not supported")
-}
-
 func (regs *delveRegisters) Slice(bool) ([]proc.Register, error) {
 	return regs.slice, nil
 }
diff --git a/pkg/proc/disasm.go b/pkg/proc/disasm.go
index 5566bb82..161f81b7 100644
--- a/pkg/proc/disasm.go
+++ b/pkg/proc/disasm.go
@@ -1,6 +1,10 @@
 package proc
 
-import "fmt"
+import (
+	"fmt"
+
+	"github.com/go-delve/delve/pkg/dwarf/op"
+)
 
 // AsmInstruction represents one assembly instruction.
 type AsmInstruction struct {
@@ -124,6 +128,11 @@ func Disassemble(mem MemoryReadWriter, regs Registers, breakpoints *BreakpointMa
 }
 
 func disassemble(memrw MemoryReadWriter, regs Registers, breakpoints *BreakpointMap, bi *BinaryInfo, startAddr, endAddr uint64, singleInstr bool) ([]AsmInstruction, error) {
+	var dregs *op.DwarfRegisters
+	if regs != nil {
+		dregs = bi.Arch.RegistersToDwarfRegisters(0, regs)
+	}
+
 	mem := make([]byte, int(endAddr-startAddr))
 	_, err := memrw.ReadMemory(mem, startAddr)
 	if err != nil {
@@ -153,7 +162,7 @@ func disassemble(memrw MemoryReadWriter, regs Registers, breakpoints *Breakpoint
 		inst.Breakpoint = atbp
 		inst.AtPC = (regs != nil) && (curpc == pc)
 
-		bi.Arch.asmDecode(&inst, mem, regs, memrw, bi)
+		bi.Arch.asmDecode(&inst, mem, dregs, memrw, bi)
 
 		r = append(r, inst)
 
diff --git a/pkg/proc/fbsdutil/regs.go b/pkg/proc/fbsdutil/regs.go
index aa29a61b..4b9cd80b 100644
--- a/pkg/proc/fbsdutil/regs.go
+++ b/pkg/proc/fbsdutil/regs.go
@@ -1,8 +1,6 @@
 package fbsdutil
 
 import (
-	"golang.org/x/arch/x86/x86asm"
-
 	"github.com/go-delve/delve/pkg/proc"
 	"github.com/go-delve/delve/pkg/proc/amd64util"
 )
@@ -154,164 +152,6 @@ func (r *AMD64Registers) GAddr() (uint64, bool) {
 	return 0, false
 }
 
-// Get returns the value of the n-th register (in x86asm order).
-func (r *AMD64Registers) Get(n int) (uint64, error) {
-	reg := x86asm.Reg(n)
-	const (
-		mask8  = 0x000000ff
-		mask16 = 0x0000ffff
-		mask32 = 0xffffffff
-	)
-
-	switch reg {
-	// 8-bit
-	case x86asm.AL:
-		return uint64(r.Regs.Rax) & mask8, nil
-	case x86asm.CL:
-		return uint64(r.Regs.Rcx) & mask8, nil
-	case x86asm.DL:
-		return uint64(r.Regs.Rdx) & mask8, nil
-	case x86asm.BL:
-		return uint64(r.Regs.Rbx) & mask8, nil
-	case x86asm.AH:
-		return (uint64(r.Regs.Rax) >> 8) & mask8, nil
-	case x86asm.CH:
-		return (uint64(r.Regs.Rcx) >> 8) & mask8, nil
-	case x86asm.DH:
-		return (uint64(r.Regs.Rdx) >> 8) & mask8, nil
-	case x86asm.BH:
-		return (uint64(r.Regs.Rbx) >> 8) & mask8, nil
-	case x86asm.SPB:
-		return uint64(r.Regs.Rsp) & mask8, nil
-	case x86asm.BPB:
-		return uint64(r.Regs.Rbp) & mask8, nil
-	case x86asm.SIB:
-		return uint64(r.Regs.Rsi) & mask8, nil
-	case x86asm.DIB:
-		return uint64(r.Regs.Rdi) & mask8, nil
-	case x86asm.R8B:
-		return uint64(r.Regs.R8) & mask8, nil
-	case x86asm.R9B:
-		return uint64(r.Regs.R9) & mask8, nil
-	case x86asm.R10B:
-		return uint64(r.Regs.R10) & mask8, nil
-	case x86asm.R11B:
-		return uint64(r.Regs.R11) & mask8, nil
-	case x86asm.R12B:
-		return uint64(r.Regs.R12) & mask8, nil
-	case x86asm.R13B:
-		return uint64(r.Regs.R13) & mask8, nil
-	case x86asm.R14B:
-		return uint64(r.Regs.R14) & mask8, nil
-	case x86asm.R15B:
-		return uint64(r.Regs.R15) & mask8, nil
-
-	// 16-bit
-	case x86asm.AX:
-		return uint64(r.Regs.Rax) & mask16, nil
-	case x86asm.CX:
-		return uint64(r.Regs.Rcx) & mask16, nil
-	case x86asm.DX:
-		return uint64(r.Regs.Rdx) & mask16, nil
-	case x86asm.BX:
-		return uint64(r.Regs.Rbx) & mask16, nil
-	case x86asm.SP:
-		return uint64(r.Regs.Rsp) & mask16, nil
-	case x86asm.BP:
-		return uint64(r.Regs.Rbp) & mask16, nil
-	case x86asm.SI:
-		return uint64(r.Regs.Rsi) & mask16, nil
-	case x86asm.DI:
-		return uint64(r.Regs.Rdi) & mask16, nil
-	case x86asm.R8W:
-		return uint64(r.Regs.R8) & mask16, nil
-	case x86asm.R9W:
-		return uint64(r.Regs.R9) & mask16, nil
-	case x86asm.R10W:
-		return uint64(r.Regs.R10) & mask16, nil
-	case x86asm.R11W:
-		return uint64(r.Regs.R11) & mask16, nil
-	case x86asm.R12W:
-		return uint64(r.Regs.R12) & mask16, nil
-	case x86asm.R13W:
-		return uint64(r.Regs.R13) & mask16, nil
-	case x86asm.R14W:
-		return uint64(r.Regs.R14) & mask16, nil
-	case x86asm.R15W:
-		return uint64(r.Regs.R15) & mask16, nil
-
-	// 32-bit
-	case x86asm.EAX:
-		return uint64(r.Regs.Rax) & mask32, nil
-	case x86asm.ECX:
-		return uint64(r.Regs.Rcx) & mask32, nil
-	case x86asm.EDX:
-		return uint64(r.Regs.Rdx) & mask32, nil
-	case x86asm.EBX:
-		return uint64(r.Regs.Rbx) & mask32, nil
-	case x86asm.ESP:
-		return uint64(r.Regs.Rsp) & mask32, nil
-	case x86asm.EBP:
-		return uint64(r.Regs.Rbp) & mask32, nil
-	case x86asm.ESI:
-		return uint64(r.Regs.Rsi) & mask32, nil
-	case x86asm.EDI:
-		return uint64(r.Regs.Rdi) & mask32, nil
-	case x86asm.R8L:
-		return uint64(r.Regs.R8) & mask32, nil
-	case x86asm.R9L:
-		return uint64(r.Regs.R9) & mask32, nil
-	case x86asm.R10L:
-		return uint64(r.Regs.R10) & mask32, nil
-	case x86asm.R11L:
-		return uint64(r.Regs.R11) & mask32, nil
-	case x86asm.R12L:
-		return uint64(r.Regs.R12) & mask32, nil
-	case x86asm.R13L:
-		return uint64(r.Regs.R13) & mask32, nil
-	case x86asm.R14L:
-		return uint64(r.Regs.R14) & mask32, nil
-	case x86asm.R15L:
-		return uint64(r.Regs.R15) & mask32, nil
-
-	// 64-bit
-	case x86asm.RAX:
-		return uint64(r.Regs.Rax), nil
-	case x86asm.RCX:
-		return uint64(r.Regs.Rcx), nil
-	case x86asm.RDX:
-		return uint64(r.Regs.Rdx), nil
-	case x86asm.RBX:
-		return uint64(r.Regs.Rbx), nil
-	case x86asm.RSP:
-		return uint64(r.Regs.Rsp), nil
-	case x86asm.RBP:
-		return uint64(r.Regs.Rbp), nil
-	case x86asm.RSI:
-		return uint64(r.Regs.Rsi), nil
-	case x86asm.RDI:
-		return uint64(r.Regs.Rdi), nil
-	case x86asm.R8:
-		return uint64(r.Regs.R8), nil
-	case x86asm.R9:
-		return uint64(r.Regs.R9), nil
-	case x86asm.R10:
-		return uint64(r.Regs.R10), nil
-	case x86asm.R11:
-		return uint64(r.Regs.R11), nil
-	case x86asm.R12:
-		return uint64(r.Regs.R12), nil
-	case x86asm.R13:
-		return uint64(r.Regs.R13), nil
-	case x86asm.R14:
-		return uint64(r.Regs.R14), nil
-	case x86asm.R15:
-		return uint64(r.Regs.R15), nil
-	}
-
-	return 0, proc.ErrUnknownRegister
-}
-
 // Copy returns a copy of these registers that is guaranteed not to change.
 func (r *AMD64Registers) Copy() (proc.Registers, error) {
 	if r.loadFpRegs != nil {
diff --git a/pkg/proc/fncall.go b/pkg/proc/fncall.go
index d50f3297..3b35c12a 100644
--- a/pkg/proc/fncall.go
+++ b/pkg/proc/fncall.go
@@ -16,9 +16,9 @@ import (
 	"github.com/go-delve/delve/pkg/dwarf/godwarf"
 	"github.com/go-delve/delve/pkg/dwarf/op"
 	"github.com/go-delve/delve/pkg/dwarf/reader"
+	"github.com/go-delve/delve/pkg/dwarf/regnum"
 	"github.com/go-delve/delve/pkg/goversion"
 	"github.com/go-delve/delve/pkg/logflags"
-	"golang.org/x/arch/x86/x86asm"
 )
 
 // This file implements the function call injection introduced in go1.11.
@@ -289,8 +289,7 @@ func evalFunctionCall(scope *EvalScope, node *ast.CallExpr) (*Variable, error) {
 	if regs.SP()-256 <= stacklo {
 		return nil, errNotEnoughStack
 	}
-	_, err = regs.Get(int(x86asm.RAX))
-	if err != nil {
+	if bi.Arch.RegistersToDwarfRegisters(0, regs).Reg(regnum.AMD64_Rax) == nil { //TODO(aarzilli): make this generic when call injection is supported on other architectures
 		return nil, errFuncCallUnsupportedBackend
 	}
 
@@ -705,7 +704,7 @@ func funcCallStep(callScope *EvalScope, fncall *functionCallState, thread Thread
 		return true
 	}
 
-	rax, _ := regs.Get(int(x86asm.RAX))
+	rax := bi.Arch.RegistersToDwarfRegisters(0, regs).Uint64Val(regnum.AMD64_Rax) //TODO(aarzilli): make this generic when call injection is supported on other architectures
 
 	if logflags.FnCall() {
 		loc, _ := thread.Location()
diff --git a/pkg/proc/gdbserial/gdbserver.go b/pkg/proc/gdbserial/gdbserver.go
index 12d550f6..d35da689 100644
--- a/pkg/proc/gdbserial/gdbserver.go
+++ b/pkg/proc/gdbserial/gdbserver.go
@@ -77,9 +77,6 @@ import (
 	"sync"
 	"time"
 
-	"golang.org/x/arch/arm64/arm64asm"
-	"golang.org/x/arch/x86/x86asm"
-
 	"github.com/go-delve/delve/pkg/dwarf/op"
 	"github.com/go-delve/delve/pkg/elfwriter"
 	"github.com/go-delve/delve/pkg/logflags"
@@ -333,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
 				}
 			}
@@ -1835,375 +1835,6 @@ func (regs *gdbRegisters) byName(name string) uint64 {
 	return binary.LittleEndian.Uint64(reg.value)
 }
 
-func (regs *gdbRegisters) Get(n int) (uint64, error) {
-	const (
-		mask8  = 0xff
-		mask16 = 0xffff
-		mask32 = 0xffffffff
-	)
-
-	if regs.arch.Name == "arm64" {
-		reg := arm64asm.Reg(n)
-
-		switch reg {
-		// 64-bit
-		case arm64asm.X0:
-			return regs.byName("x0"), nil
-		case arm64asm.X1:
-			return regs.byName("x1"), nil
-		case arm64asm.X2:
-			return regs.byName("x2"), nil
-		case arm64asm.X3:
-			return regs.byName("x3"), nil
-		case arm64asm.X4:
-			return regs.byName("x4"), nil
-		case arm64asm.X5:
-			return regs.byName("x5"), nil
-		case arm64asm.X6:
-			return regs.byName("x6"), nil
-		case arm64asm.X7:
-			return regs.byName("x7"), nil
-		case arm64asm.X8:
-			return regs.byName("x8"), nil
-		case arm64asm.X9:
-			return regs.byName("x9"), nil
-		case arm64asm.X10:
-			return regs.byName("x10"), nil
-		case arm64asm.X11:
-			return regs.byName("x11"), nil
-		case arm64asm.X12:
-			return regs.byName("x12"), nil
-		case arm64asm.X13:
-			return regs.byName("x13"), nil
-		case arm64asm.X14:
-			return regs.byName("x14"), nil
-		case arm64asm.X15:
-			return regs.byName("x15"), nil
-		case arm64asm.X16:
-			return regs.byName("x16"), nil
-		case arm64asm.X17:
-			return regs.byName("x17"), nil
-		case arm64asm.X18:
-			return regs.byName("x18"), nil
-		case arm64asm.X19:
-			return regs.byName("x19"), nil
-		case arm64asm.X20:
-			return regs.byName("x20"), nil
-		case arm64asm.X21:
-			return regs.byName("x21"), nil
-		case arm64asm.X22:
-			return regs.byName("x22"), nil
-		case arm64asm.X23:
-			return regs.byName("x23"), nil
-		case arm64asm.X24:
-			return regs.byName("x24"), nil
-		case arm64asm.X25:
-			return regs.byName("x25"), nil
-		case arm64asm.X26:
-			return regs.byName("x26"), nil
-		case arm64asm.X27:
-			return regs.byName("x27"), nil
-		case arm64asm.X28:
-			return regs.byName("x28"), nil
-		case arm64asm.X29:
-			return regs.byName("fp"), nil
-		case arm64asm.X30:
-			return regs.byName("lr"), nil
-		case arm64asm.SP:
-			return regs.byName("sp"), nil
-		}
-
-		// 64-bit
-		switch reg {
-		case arm64asm.W0:
-			return regs.byName("w0"), nil
-		case arm64asm.W1:
-			return regs.byName("w1"), nil
-		case arm64asm.W2:
-			return regs.byName("w2"), nil
-		case arm64asm.W3:
-			return regs.byName("w3"), nil
-		case arm64asm.W4:
-			return regs.byName("w4"), nil
-		case arm64asm.W5:
-			return regs.byName("w5"), nil
-		case arm64asm.W6:
-			return regs.byName("w6"), nil
-		case arm64asm.W7:
-			return regs.byName("w7"), nil
-		case arm64asm.W8:
-			return regs.byName("w8"), nil
-		case arm64asm.W9:
-			return regs.byName("w9"), nil
-		case arm64asm.W10:
-			return regs.byName("w10"), nil
-		case arm64asm.W11:
-			return regs.byName("w11"), nil
-		case arm64asm.W12:
-			return regs.byName("w12"), nil
-		case arm64asm.W13:
-			return regs.byName("w13"), nil
-		case arm64asm.W14:
-			return regs.byName("w14"), nil
-		case arm64asm.W15:
-			return regs.byName("w15"), nil
-		case arm64asm.W16:
-			return regs.byName("w16"), nil
-		case arm64asm.W17:
-			return regs.byName("w17"), nil
-		case arm64asm.W18:
-			return regs.byName("w18"), nil
-		case arm64asm.W19:
-			return regs.byName("w19"), nil
-		case arm64asm.W20:
-			return regs.byName("w20"), nil
-		case arm64asm.W21:
-			return regs.byName("w21"), nil
-		case arm64asm.W22:
-			return regs.byName("w22"), nil
-		case arm64asm.W23:
-			return regs.byName("w23"), nil
-		case arm64asm.W24:
-			return regs.byName("w24"), nil
-		case arm64asm.W25:
-			return regs.byName("w25"), nil
-		case arm64asm.W26:
-			return regs.byName("w26"), nil
-		case arm64asm.W27:
-			return regs.byName("w27"), nil
-		case arm64asm.W28:
-			return regs.byName("w28"), nil
-			// TODO: not sure about these ones, they are not returned by debugserver
-			// probably need to take the x-register and bitmask them
-			/*case arm64asm.W29:
-				return regs.byName("w29"), nil
-			case arm64asm.W30:
-				return regs.byName("w30"), nil
-				/*case arm64asm.WSP:
-				return regs.byName("wsp"), nil*/
-		}
-
-		// vector registers
-		// 64-bit
-		switch reg {
-		case arm64asm.V0:
-			return regs.byName("v0"), nil
-		case arm64asm.V1:
-			return regs.byName("v1"), nil
-		case arm64asm.V2:
-			return regs.byName("v2"), nil
-		case arm64asm.V3:
-			return regs.byName("v3"), nil
-		case arm64asm.V4:
-			return regs.byName("v4"), nil
-		case arm64asm.V5:
-			return regs.byName("v5"), nil
-		case arm64asm.V6:
-			return regs.byName("v6"), nil
-		case arm64asm.V7:
-			return regs.byName("v7"), nil
-		case arm64asm.V8:
-			return regs.byName("v8"), nil
-		case arm64asm.V9:
-			return regs.byName("v9"), nil
-		case arm64asm.V10:
-			return regs.byName("v10"), nil
-		case arm64asm.V11:
-			return regs.byName("v11"), nil
-		case arm64asm.V12:
-			return regs.byName("v12"), nil
-		case arm64asm.V13:
-			return regs.byName("v13"), nil
-		case arm64asm.V14:
-			return regs.byName("v14"), nil
-		case arm64asm.V15:
-			return regs.byName("v15"), nil
-		case arm64asm.V16:
-			return regs.byName("v16"), nil
-		case arm64asm.V17:
-			return regs.byName("v17"), nil
-		case arm64asm.V18:
-			return regs.byName("v18"), nil
-		case arm64asm.V19:
-			return regs.byName("v19"), nil
-		case arm64asm.V20:
-			return regs.byName("v20"), nil
-		case arm64asm.V21:
-			return regs.byName("v21"), nil
-		case arm64asm.V22:
-			return regs.byName("v22"), nil
-		case arm64asm.V23:
-			return regs.byName("v23"), nil
-		case arm64asm.V24:
-			return regs.byName("v24"), nil
-		case arm64asm.V25:
-			return regs.byName("v25"), nil
-		case arm64asm.V26:
-			return regs.byName("v26"), nil
-		case arm64asm.V27:
-			return regs.byName("v27"), nil
-		case arm64asm.V28:
-			return regs.byName("v28"), nil
-		case arm64asm.V29:
-			return regs.byName("v29"), nil
-		case arm64asm.V30:
-			return regs.byName("v30"), nil
-		case arm64asm.V31:
-			return regs.byName("v31"), nil
-		}
-	} else {
-		reg := x86asm.Reg(n)
-
-		switch reg {
-		// 8-bit
-		case x86asm.AL:
-			return regs.byName("rax") & mask8, nil
-		case x86asm.CL:
-			return regs.byName("rcx") & mask8, nil
-		case x86asm.DL:
-			return regs.byName("rdx") & mask8, nil
-		case x86asm.BL:
-			return regs.byName("rbx") & mask8, nil
-		case x86asm.AH:
-			return (regs.byName("rax") >> 8) & mask8, nil
-		case x86asm.CH:
-			return (regs.byName("rcx") >> 8) & mask8, nil
-		case x86asm.DH:
-			return (regs.byName("rdx") >> 8) & mask8, nil
-		case x86asm.BH:
-			return (regs.byName("rbx") >> 8) & mask8, nil
-		case x86asm.SPB:
-			return regs.byName("rsp") & mask8, nil
-		case x86asm.BPB:
-			return regs.byName("rbp") & mask8, nil
-		case x86asm.SIB:
-			return regs.byName("rsi") & mask8, nil
-		case x86asm.DIB:
-			return regs.byName("rdi") & mask8, nil
-		case x86asm.R8B:
-			return regs.byName("r8") & mask8, nil
-		case x86asm.R9B:
-			return regs.byName("r9") & mask8, nil
-		case x86asm.R10B:
-			return regs.byName("r10") & mask8, nil
-		case x86asm.R11B:
-			return regs.byName("r11") & mask8, nil
-		case x86asm.R12B:
-			return regs.byName("r12") & mask8, nil
-		case x86asm.R13B:
-			return regs.byName("r13") & mask8, nil
-		case x86asm.R14B:
-			return regs.byName("r14") & mask8, nil
-		case x86asm.R15B:
-			return regs.byName("r15") & mask8, nil
-
-		// 16-bit
-		case x86asm.AX:
-			return regs.byName("rax") & mask16, nil
-		case x86asm.CX:
-			return regs.byName("rcx") & mask16, nil
-		case x86asm.DX:
-			return regs.byName("rdx") & mask16, nil
-		case x86asm.BX:
-			return regs.byName("rbx") & mask16, nil
-		case x86asm.SP:
-			return regs.byName("rsp") & mask16, nil
-		case x86asm.BP:
-			return regs.byName("rbp") & mask16, nil
-		case x86asm.SI:
-			return regs.byName("rsi") & mask16, nil
-		case x86asm.DI:
-			return regs.byName("rdi") & mask16, nil
-		case x86asm.R8W:
-			return regs.byName("r8") & mask16, nil
-		case x86asm.R9W:
-			return regs.byName("r9") & mask16, nil
-		case x86asm.R10W:
-			return regs.byName("r10") & mask16, nil
-		case x86asm.R11W:
-			return regs.byName("r11") & mask16, nil
-		case x86asm.R12W:
-			return regs.byName("r12") & mask16, nil
-		case x86asm.R13W:
-			return regs.byName("r13") & mask16, nil
-		case x86asm.R14W:
-			return regs.byName("r14") & mask16, nil
-		case x86asm.R15W:
-			return regs.byName("r15") & mask16, nil
-
-		// 32-bit
-		case x86asm.EAX:
-			return regs.byName("rax") & mask32, nil
-		case x86asm.ECX:
-			return regs.byName("rcx") & mask32, nil
-		case x86asm.EDX:
-			return regs.byName("rdx") & mask32, nil
-		case x86asm.EBX:
-			return regs.byName("rbx") & mask32, nil
-		case x86asm.ESP:
-			return regs.byName("rsp") & mask32, nil
-		case x86asm.EBP:
-			return regs.byName("rbp") & mask32, nil
-		case x86asm.ESI:
-			return regs.byName("rsi") & mask32, nil
-		case x86asm.EDI:
-			return regs.byName("rdi") & mask32, nil
-		case x86asm.R8L:
-			return regs.byName("r8") & mask32, nil
-		case x86asm.R9L:
-			return regs.byName("r9") & mask32, nil
-		case x86asm.R10L:
-			return regs.byName("r10") & mask32, nil
-		case x86asm.R11L:
-			return regs.byName("r11") & mask32, nil
-		case x86asm.R12L:
-			return regs.byName("r12") & mask32, nil
-		case x86asm.R13L:
-			return regs.byName("r13") & mask32, nil
-		case x86asm.R14L:
-			return regs.byName("r14") & mask32, nil
-		case x86asm.R15L:
-			return regs.byName("r15") & mask32, nil
-
-		// 64-bit
-		case x86asm.RAX:
-			return regs.byName("rax"), nil
-		case x86asm.RCX:
-			return regs.byName("rcx"), nil
-		case x86asm.RDX:
-			return regs.byName("rdx"), nil
-		case x86asm.RBX:
-			return regs.byName("rbx"), nil
-		case x86asm.RSP:
-			return regs.byName("rsp"), nil
-		case x86asm.RBP:
-			return regs.byName("rbp"), nil
-		case x86asm.RSI:
-			return regs.byName("rsi"), nil
-		case x86asm.RDI:
-			return regs.byName("rdi"), nil
-		case x86asm.R8:
-			return regs.byName("r8"), nil
-		case x86asm.R9:
-			return regs.byName("r9"), nil
-		case x86asm.R10:
-			return regs.byName("r10"), nil
-		case x86asm.R11:
-			return regs.byName("r11"), nil
-		case x86asm.R12:
-			return regs.byName("r12"), nil
-		case x86asm.R13:
-			return regs.byName("r13"), nil
-		case x86asm.R14:
-			return regs.byName("r14"), nil
-		case x86asm.R15:
-			return regs.byName("r15"), nil
-		}
-	}
-
-	return 0, proc.ErrUnknownRegister
-}
-
 func (r *gdbRegisters) FloatLoadError() error {
 	return nil
 }
diff --git a/pkg/proc/i386_arch.go b/pkg/proc/i386_arch.go
index bf22d359..5a33c1cc 100644
--- a/pkg/proc/i386_arch.go
+++ b/pkg/proc/i386_arch.go
@@ -34,6 +34,7 @@ func I386Arch(goos string) *Arch {
 		asmDecode:                        i386AsmDecode,
 		PCRegNum:                         regnum.I386_Eip,
 		SPRegNum:                         regnum.I386_Esp,
+		asmRegisters:                     i386AsmRegisters,
 	}
 }
 
@@ -184,12 +185,12 @@ func i386RegSize(regnum uint64) int {
 	return 4
 }
 
-func i386RegistersToDwarfRegisters(staticBase uint64, regs Registers) op.DwarfRegisters {
+func i386RegistersToDwarfRegisters(staticBase uint64, regs Registers) *op.DwarfRegisters {
 	dregs := initDwarfRegistersFromSlice(regnum.I386MaxRegNum(), regs, regnum.I386NameToDwarf)
 	dr := op.NewDwarfRegisters(staticBase, dregs, binary.LittleEndian, regnum.I386_Eip, regnum.I386_Esp, regnum.I386_Ebp, 0)
 	dr.SetLoadMoreCallback(loadMoreDwarfRegistersFromSliceFunc(dr, regs, regnum.I386NameToDwarf))
 
-	return *dr
+	return dr
 }
 
 func i386AddrAndStackRegsToDwarfRegisters(staticBase, pc, sp, bp, lr uint64) op.DwarfRegisters {
diff --git a/pkg/proc/i386_disasm.go b/pkg/proc/i386_disasm.go
index 6803375d..b1762278 100644
--- a/pkg/proc/i386_disasm.go
+++ b/pkg/proc/i386_disasm.go
@@ -5,10 +5,13 @@
 package proc
 
 import (
+	"github.com/go-delve/delve/pkg/dwarf/op"
+	"github.com/go-delve/delve/pkg/dwarf/regnum"
+
 	"golang.org/x/arch/x86/x86asm"
 )
 
-func i386AsmDecode(asmInst *AsmInstruction, mem []byte, regs Registers, memrw MemoryReadWriter, bi *BinaryInfo) error {
+func i386AsmDecode(asmInst *AsmInstruction, mem []byte, regs *op.DwarfRegisters, memrw MemoryReadWriter, bi *BinaryInfo) error {
 	return x86AsmDecode(asmInst, mem, regs, memrw, bi, 32)
 }
 
@@ -37,3 +40,39 @@ func init() {
 		}
 	}
 }
+
+var i386AsmRegisters = map[int]asmRegister{
+	// 8-bit
+	int(x86asm.AL):  asmRegister{regnum.I386_Eax, 0, mask8},
+	int(x86asm.CL):  asmRegister{regnum.I386_Ecx, 0, mask8},
+	int(x86asm.DL):  asmRegister{regnum.I386_Edx, 0, mask8},
+	int(x86asm.BL):  asmRegister{regnum.I386_Ebx, 0, mask8},
+	int(x86asm.AH):  asmRegister{regnum.I386_Eax, 8, mask8},
+	int(x86asm.CH):  asmRegister{regnum.I386_Ecx, 8, mask8},
+	int(x86asm.DH):  asmRegister{regnum.I386_Edx, 8, mask8},
+	int(x86asm.BH):  asmRegister{regnum.I386_Ebx, 8, mask8},
+	int(x86asm.SPB): asmRegister{regnum.I386_Esp, 0, mask8},
+	int(x86asm.BPB): asmRegister{regnum.I386_Ebp, 0, mask8},
+	int(x86asm.SIB): asmRegister{regnum.I386_Esi, 0, mask8},
+	int(x86asm.DIB): asmRegister{regnum.I386_Edi, 0, mask8},
+
+	// 16-bit
+	int(x86asm.AX): asmRegister{regnum.I386_Eax, 0, mask16},
+	int(x86asm.CX): asmRegister{regnum.I386_Ecx, 0, mask16},
+	int(x86asm.DX): asmRegister{regnum.I386_Edx, 0, mask16},
+	int(x86asm.BX): asmRegister{regnum.I386_Ebx, 0, mask16},
+	int(x86asm.SP): asmRegister{regnum.I386_Esp, 0, mask16},
+	int(x86asm.BP): asmRegister{regnum.I386_Ebp, 0, mask16},
+	int(x86asm.SI): asmRegister{regnum.I386_Esi, 0, mask16},
+	int(x86asm.DI): asmRegister{regnum.I386_Edi, 0, mask16},
+
+	// 32-bit
+	int(x86asm.EAX): asmRegister{regnum.I386_Eax, 0, mask32},
+	int(x86asm.ECX): asmRegister{regnum.I386_Ecx, 0, mask32},
+	int(x86asm.EDX): asmRegister{regnum.I386_Edx, 0, mask32},
+	int(x86asm.EBX): asmRegister{regnum.I386_Ebx, 0, mask32},
+	int(x86asm.ESP): asmRegister{regnum.I386_Esp, 0, mask32},
+	int(x86asm.EBP): asmRegister{regnum.I386_Ebp, 0, mask32},
+	int(x86asm.ESI): asmRegister{regnum.I386_Esi, 0, mask32},
+	int(x86asm.EDI): asmRegister{regnum.I386_Edi, 0, mask32},
+}
diff --git a/pkg/proc/linutil/regs_amd64_arch.go b/pkg/proc/linutil/regs_amd64_arch.go
index 6d6f0cf4..dbea3396 100644
--- a/pkg/proc/linutil/regs_amd64_arch.go
+++ b/pkg/proc/linutil/regs_amd64_arch.go
@@ -3,8 +3,6 @@ package linutil
 import (
 	"fmt"
 
-	"golang.org/x/arch/x86/x86asm"
-
 	"github.com/go-delve/delve/pkg/dwarf/op"
 	"github.com/go-delve/delve/pkg/dwarf/regnum"
 	"github.com/go-delve/delve/pkg/proc"
@@ -131,164 +129,6 @@ func (r *AMD64Registers) GAddr() (uint64, bool) {
 	return 0, false
 }
 
-// Get returns the value of the n-th register (in x86asm order).
-func (r *AMD64Registers) Get(n int) (uint64, error) {
-	reg := x86asm.Reg(n)
-	const (
-		mask8  = 0x000000ff
-		mask16 = 0x0000ffff
-		mask32 = 0xffffffff
-	)
-
-	switch reg {
-	// 8-bit
-	case x86asm.AL:
-		return r.Regs.Rax & mask8, nil
-	case x86asm.CL:
-		return r.Regs.Rcx & mask8, nil
-	case x86asm.DL:
-		return r.Regs.Rdx & mask8, nil
-	case x86asm.BL:
-		return r.Regs.Rbx & mask8, nil
-	case x86asm.AH:
-		return (r.Regs.Rax >> 8) & mask8, nil
-	case x86asm.CH:
-		return (r.Regs.Rcx >> 8) & mask8, nil
-	case x86asm.DH:
-		return (r.Regs.Rdx >> 8) & mask8, nil
-	case x86asm.BH:
-		return (r.Regs.Rbx >> 8) & mask8, nil
-	case x86asm.SPB:
-		return r.Regs.Rsp & mask8, nil
-	case x86asm.BPB:
-		return r.Regs.Rbp & mask8, nil
-	case x86asm.SIB:
-		return r.Regs.Rsi & mask8, nil
-	case x86asm.DIB:
-		return r.Regs.Rdi & mask8, nil
-	case x86asm.R8B:
-		return r.Regs.R8 & mask8, nil
-	case x86asm.R9B:
-		return r.Regs.R9 & mask8, nil
-	case x86asm.R10B:
-		return r.Regs.R10 & mask8, nil
-	case x86asm.R11B:
-		return r.Regs.R11 & mask8, nil
-	case x86asm.R12B:
-		return r.Regs.R12 & mask8, nil
-	case x86asm.R13B:
-		return r.Regs.R13 & mask8, nil
-	case x86asm.R14B:
-		return r.Regs.R14 & mask8, nil
-	case x86asm.R15B:
-		return r.Regs.R15 & mask8, nil
-
-	// 16-bit
-	case x86asm.AX:
-		return r.Regs.Rax & mask16, nil
-	case x86asm.CX:
-		return r.Regs.Rcx & mask16, nil
-	case x86asm.DX:
-		return r.Regs.Rdx & mask16, nil
-	case x86asm.BX:
-		return r.Regs.Rbx & mask16, nil
-	case x86asm.SP:
-		return r.Regs.Rsp & mask16, nil
-	case x86asm.BP:
-		return r.Regs.Rbp & mask16, nil
-	case x86asm.SI:
-		return r.Regs.Rsi & mask16, nil
-	case x86asm.DI:
-		return r.Regs.Rdi & mask16, nil
-	case x86asm.R8W:
-		return r.Regs.R8 & mask16, nil
-	case x86asm.R9W:
-		return r.Regs.R9 & mask16, nil
-	case x86asm.R10W:
-		return r.Regs.R10 & mask16, nil
-	case x86asm.R11W:
-		return r.Regs.R11 & mask16, nil
-	case x86asm.R12W:
-		return r.Regs.R12 & mask16, nil
-	case x86asm.R13W:
-		return r.Regs.R13 & mask16, nil
-	case x86asm.R14W:
-		return r.Regs.R14 & mask16, nil
-	case x86asm.R15W:
-		return r.Regs.R15 & mask16, nil
-
-	// 32-bit
-	case x86asm.EAX:
-		return r.Regs.Rax & mask32, nil
-	case x86asm.ECX:
-		return r.Regs.Rcx & mask32, nil
-	case x86asm.EDX:
-		return r.Regs.Rdx & mask32, nil
-	case x86asm.EBX:
-		return r.Regs.Rbx & mask32, nil
-	case x86asm.ESP:
-		return r.Regs.Rsp & mask32, nil
-	case x86asm.EBP:
-		return r.Regs.Rbp & mask32, nil
-	case x86asm.ESI:
-		return r.Regs.Rsi & mask32, nil
-	case x86asm.EDI:
-		return r.Regs.Rdi & mask32, nil
-	case x86asm.R8L:
-		return r.Regs.R8 & mask32, nil
-	case x86asm.R9L:
-		return r.Regs.R9 & mask32, nil
-	case x86asm.R10L:
-		return r.Regs.R10 & mask32, nil
-	case x86asm.R11L:
-		return r.Regs.R11 & mask32, nil
-	case x86asm.R12L:
-		return r.Regs.R12 & mask32, nil
-	case x86asm.R13L:
-		return r.Regs.R13 & mask32, nil
-	case x86asm.R14L:
-		return r.Regs.R14 & mask32, nil
-	case x86asm.R15L:
-		return r.Regs.R15 & mask32, nil
-
-	// 64-bit
-	case x86asm.RAX:
-		return r.Regs.Rax, nil
-	case x86asm.RCX:
-		return r.Regs.Rcx, nil
-	case x86asm.RDX:
-		return r.Regs.Rdx, nil
-	case x86asm.RBX:
-		return r.Regs.Rbx, nil
-	case x86asm.RSP:
-		return r.Regs.Rsp, nil
-	case x86asm.RBP:
-		return r.Regs.Rbp, nil
-	case x86asm.RSI:
-		return r.Regs.Rsi, nil
-	case x86asm.RDI:
-		return r.Regs.Rdi, nil
-	case x86asm.R8:
-		return r.Regs.R8, nil
-	case x86asm.R9:
-		return r.Regs.R9, nil
-	case x86asm.R10:
-		return r.Regs.R10, nil
-	case x86asm.R11:
-		return r.Regs.R11, nil
-	case x86asm.R12:
-		return r.Regs.R12, nil
-	case x86asm.R13:
-		return r.Regs.R13, nil
-	case x86asm.R14:
-		return r.Regs.R14, nil
-	case x86asm.R15:
-		return r.Regs.R15, nil
-	}
-
-	return 0, proc.ErrUnknownRegister
-}
-
 // Copy returns a copy of these registers that is guaranteed not to change.
 func (r *AMD64Registers) Copy() (proc.Registers, error) {
 	if r.loadFpRegs != nil {
diff --git a/pkg/proc/linutil/regs_arm64_arch.go b/pkg/proc/linutil/regs_arm64_arch.go
index 5141e579..acb2d497 100644
--- a/pkg/proc/linutil/regs_arm64_arch.go
+++ b/pkg/proc/linutil/regs_arm64_arch.go
@@ -2,7 +2,6 @@ package linutil
 
 import (
 	"fmt"
-	"golang.org/x/arch/arm64/arm64asm"
 
 	"github.com/go-delve/delve/pkg/proc"
 )
@@ -116,17 +115,6 @@ func (r *ARM64Registers) GAddr() (uint64, bool) {
 	return r.Regs.Regs[28], !r.iscgo
 }
 
-// Get returns the value of the n-th register (in arm64asm order).
-func (r *ARM64Registers) Get(n int) (uint64, error) {
-	reg := arm64asm.Reg(n)
-
-	if reg >= arm64asm.X0 && reg <= arm64asm.X30 {
-		return r.Regs.Regs[reg-arm64asm.X0], nil
-	}
-
-	return 0, proc.ErrUnknownRegister
-}
-
 // Copy returns a copy of these registers that is guaranteed not to change.
 func (r *ARM64Registers) Copy() (proc.Registers, error) {
 	if r.loadFpRegs != nil {
diff --git a/pkg/proc/linutil/regs_i386_arch.go b/pkg/proc/linutil/regs_i386_arch.go
index 823505cb..d456ff00 100644
--- a/pkg/proc/linutil/regs_i386_arch.go
+++ b/pkg/proc/linutil/regs_i386_arch.go
@@ -1,8 +1,6 @@
 package linutil
 
 import (
-	"golang.org/x/arch/x86/x86asm"
-
 	"github.com/go-delve/delve/pkg/proc"
 	"github.com/go-delve/delve/pkg/proc/amd64util"
 )
@@ -113,80 +111,6 @@ func (r *I386Registers) GAddr() (uint64, bool) {
 	return 0, false
 }
 
-// Get returns the value of the n-th register (in x86asm order).
-func (r *I386Registers) Get(n int) (uint64, error) {
-	reg := x86asm.Reg(n)
-	const (
-		mask8  = 0x000000ff
-		mask16 = 0x0000ffff
-	)
-
-	switch reg {
-	// 8-bit
-	case x86asm.AL:
-		return uint64(r.Regs.Eax) & mask8, nil
-	case x86asm.CL:
-		return uint64(r.Regs.Ecx) & mask8, nil
-	case x86asm.DL:
-		return uint64(r.Regs.Edx) & mask8, nil
-	case x86asm.BL:
-		return uint64(r.Regs.Ebx) & mask8, nil
-	case x86asm.AH:
-		return (uint64(r.Regs.Eax) >> 8) & mask8, nil
-	case x86asm.CH:
-		return (uint64(r.Regs.Ecx) >> 8) & mask8, nil
-	case x86asm.DH:
-		return (uint64(r.Regs.Edx) >> 8) & mask8, nil
-	case x86asm.BH:
-		return (uint64(r.Regs.Ebx) >> 8) & mask8, nil
-	case x86asm.SPB:
-		return uint64(r.Regs.Esp) & mask8, nil
-	case x86asm.BPB:
-		return uint64(r.Regs.Ebp) & mask8, nil
-	case x86asm.SIB:
-		return uint64(r.Regs.Esi) & mask8, nil
-	case x86asm.DIB:
-		return uint64(r.Regs.Edi) & mask8, nil
-
-	// 16-bit
-	case x86asm.AX:
-		return uint64(r.Regs.Eax) & mask16, nil
-	case x86asm.CX:
-		return uint64(r.Regs.Ecx) & mask16, nil
-	case x86asm.DX:
-		return uint64(r.Regs.Edx) & mask16, nil
-	case x86asm.BX:
-		return uint64(r.Regs.Ebx) & mask16, nil
-	case x86asm.SP:
-		return uint64(r.Regs.Esp) & mask16, nil
-	case x86asm.BP:
-		return uint64(r.Regs.Ebp) & mask16, nil
-	case x86asm.SI:
-		return uint64(r.Regs.Esi) & mask16, nil
-	case x86asm.DI:
-		return uint64(r.Regs.Edi) & mask16, nil
-
-	// 32-bit
-	case x86asm.EAX:
-		return uint64(uint32(r.Regs.Eax)), nil
-	case x86asm.ECX:
-		return uint64(uint32(r.Regs.Ecx)), nil
-	case x86asm.EDX:
-		return uint64(uint32(r.Regs.Edx)), nil
-	case x86asm.EBX:
-		return uint64(uint32(r.Regs.Ebx)), nil
-	case x86asm.ESP:
-		return uint64(uint32(r.Regs.Esp)), nil
-	case x86asm.EBP:
-		return uint64(uint32(r.Regs.Ebp)), nil
-	case x86asm.ESI:
-		return uint64(uint32(r.Regs.Esi)), nil
-	case x86asm.EDI:
-		return uint64(uint32(r.Regs.Edi)), nil
-	}
-	return 0, proc.ErrUnknownRegister
-}
-
 // Copy returns a copy of these registers that is guaranteed not to change.
 func (r *I386Registers) Copy() (proc.Registers, error) {
 	if r.loadFpRegs != nil {
diff --git a/pkg/proc/registers.go b/pkg/proc/registers.go
index 22c79f6f..83a58650 100644
--- a/pkg/proc/registers.go
+++ b/pkg/proc/registers.go
@@ -20,7 +20,6 @@ type Registers interface {
 	TLS() uint64
 	// GAddr returns the address of the G variable if it is known, 0 and false otherwise
 	GAddr() (uint64, bool)
-	Get(int) (uint64, error)
 	Slice(floatingPoint bool) ([]Register, error)
 	// Copy returns a copy of the registers that is guaranteed not to change
 	// when the registers of the associated thread change.
diff --git a/pkg/proc/stack.go b/pkg/proc/stack.go
index 0e05d474..1befb68f 100644
--- a/pkg/proc/stack.go
+++ b/pkg/proc/stack.go
@@ -100,7 +100,7 @@ func ThreadStacktrace(thread Thread, depth int) ([]Stackframe, error) {
 			return nil, err
 		}
 		so := thread.BinInfo().PCToImage(regs.PC())
-		dwarfRegs := thread.BinInfo().Arch.RegistersToDwarfRegisters(so.StaticBase, regs)
+		dwarfRegs := *(thread.BinInfo().Arch.RegistersToDwarfRegisters(so.StaticBase, regs))
 		dwarfRegs.ChangeFunc = thread.SetReg
 		it := newStackIterator(thread.BinInfo(), thread.ProcessMemory(), dwarfRegs, 0, nil, -1, nil, 0)
 		return it.stacktrace(depth)
@@ -121,7 +121,7 @@ func (g *G) stackIterator(opts StacktraceOptions) (*stackIterator, error) {
 			return nil, err
 		}
 		so := bi.PCToImage(regs.PC())
-		dwarfRegs := bi.Arch.RegistersToDwarfRegisters(so.StaticBase, regs)
+		dwarfRegs := *(bi.Arch.RegistersToDwarfRegisters(so.StaticBase, regs))
 		dwarfRegs.ChangeFunc = g.Thread.SetReg
 		return newStackIterator(
 			bi, g.variable.mem,
diff --git a/pkg/proc/winutil/regs.go b/pkg/proc/winutil/regs.go
index 3822e415..a2c482e9 100644
--- a/pkg/proc/winutil/regs.go
+++ b/pkg/proc/winutil/regs.go
@@ -6,8 +6,6 @@ import (
 	"fmt"
 	"unsafe"
 
-	"golang.org/x/arch/x86/x86asm"
-
 	"github.com/go-delve/delve/pkg/proc"
 )
 
@@ -164,164 +162,6 @@ func (r *AMD64Registers) GAddr() (uint64, bool) {
 	return 0, false
 }
 
-// Get returns the value of the n-th register (in x86asm order).
-func (r *AMD64Registers) Get(n int) (uint64, error) {
-	reg := x86asm.Reg(n)
-	const (
-		mask8  = 0x000f
-		mask16 = 0x00ff
-		mask32 = 0xffff
-	)
-
-	switch reg {
-	// 8-bit
-	case x86asm.AL:
-		return r.rax & mask8, nil
-	case x86asm.CL:
-		return r.rcx & mask8, nil
-	case x86asm.DL:
-		return r.rdx & mask8, nil
-	case x86asm.BL:
-		return r.rbx & mask8, nil
-	case x86asm.AH:
-		return (r.rax >> 8) & mask8, nil
-	case x86asm.CH:
-		return (r.rcx >> 8) & mask8, nil
-	case x86asm.DH:
-		return (r.rdx >> 8) & mask8, nil
-	case x86asm.BH:
-		return (r.rbx >> 8) & mask8, nil
-	case x86asm.SPB:
-		return r.rsp & mask8, nil
-	case x86asm.BPB:
-		return r.rbp & mask8, nil
-	case x86asm.SIB:
-		return r.rsi & mask8, nil
-	case x86asm.DIB:
-		return r.rdi & mask8, nil
-	case x86asm.R8B:
-		return r.r8 & mask8, nil
-	case x86asm.R9B:
-		return r.r9 & mask8, nil
-	case x86asm.R10B:
-		return r.r10 & mask8, nil
-	case x86asm.R11B:
-		return r.r11 & mask8, nil
-	case x86asm.R12B:
-		return r.r12 & mask8, nil
-	case x86asm.R13B:
-		return r.r13 & mask8, nil
-	case x86asm.R14B:
-		return r.r14 & mask8, nil
-	case x86asm.R15B:
-		return r.r15 & mask8, nil
-
-	// 16-bit
-	case x86asm.AX:
-		return r.rax & mask16, nil
-	case x86asm.CX:
-		return r.rcx & mask16, nil
-	case x86asm.DX:
-		return r.rdx & mask16, nil
-	case x86asm.BX:
-		return r.rbx & mask16, nil
-	case x86asm.SP:
-		return r.rsp & mask16, nil
-	case x86asm.BP:
-		return r.rbp & mask16, nil
-	case x86asm.SI:
-		return r.rsi & mask16, nil
-	case x86asm.DI:
-		return r.rdi & mask16, nil
-	case x86asm.R8W:
-		return r.r8 & mask16, nil
-	case x86asm.R9W:
-		return r.r9 & mask16, nil
-	case x86asm.R10W:
-		return r.r10 & mask16, nil
-	case x86asm.R11W:
-		return r.r11 & mask16, nil
-	case x86asm.R12W:
-		return r.r12 & mask16, nil
-	case x86asm.R13W:
-		return r.r13 & mask16, nil
-	case x86asm.R14W:
-		return r.r14 & mask16, nil
-	case x86asm.R15W:
-		return r.r15 & mask16, nil
-
-	// 32-bit
-	case x86asm.EAX:
-		return r.rax & mask32, nil
-	case x86asm.ECX:
-		return r.rcx & mask32, nil
-	case x86asm.EDX:
-		return r.rdx & mask32, nil
-	case x86asm.EBX:
-		return r.rbx & mask32, nil
-	case x86asm.ESP:
-		return r.rsp & mask32, nil
-	case x86asm.EBP:
-		return r.rbp & mask32, nil
-	case x86asm.ESI:
-		return r.rsi & mask32, nil
-	case x86asm.EDI:
-		return r.rdi & mask32, nil
-	case x86asm.R8L:
-		return r.r8 & mask32, nil
-	case x86asm.R9L:
-		return r.r9 & mask32, nil
-	case x86asm.R10L:
-		return r.r10 & mask32, nil
-	case x86asm.R11L:
-		return r.r11 & mask32, nil
-	case x86asm.R12L:
-		return r.r12 & mask32, nil
-	case x86asm.R13L:
-		return r.r13 & mask32, nil
-	case x86asm.R14L:
-		return r.r14 & mask32, nil
-	case x86asm.R15L:
-		return r.r15 & mask32, nil
-
-	// 64-bit
-	case x86asm.RAX:
-		return r.rax, nil
-	case x86asm.RCX:
-		return r.rcx, nil
-	case x86asm.RDX:
-		return r.rdx, nil
-	case x86asm.RBX:
-		return r.rbx, nil
-	case x86asm.RSP:
-		return r.rsp, nil
-	case x86asm.RBP:
-		return r.rbp, nil
-	case x86asm.RSI:
-		return r.rsi, nil
-	case x86asm.RDI:
-		return r.rdi, nil
-	case x86asm.R8:
-		return r.r8, nil
-	case x86asm.R9:
-		return r.r9, nil
-	case x86asm.R10:
-		return r.r10, nil
-	case x86asm.R11:
-		return r.r11, nil
-	case x86asm.R12:
-		return r.r12, nil
-	case x86asm.R13:
-		return r.r13, nil
-	case x86asm.R14:
-		return r.r14, nil
-	case x86asm.R15:
-		return r.r15, nil
-	}
-
-	return 0, proc.ErrUnknownRegister
-}
-
 // Copy returns a copy of these registers that is guaranteed not to change.
 func (r *AMD64Registers) Copy() (proc.Registers, error) {
 	var rr AMD64Registers
diff --git a/pkg/proc/x86_disasm.go b/pkg/proc/x86_disasm.go
index f2ed572d..96e20701 100644
--- a/pkg/proc/x86_disasm.go
+++ b/pkg/proc/x86_disasm.go
@@ -1,6 +1,8 @@
 package proc
 
 import (
+	"github.com/go-delve/delve/pkg/dwarf/op"
+
 	"golang.org/x/arch/x86/x86asm"
 )
 
@@ -8,7 +10,7 @@ type x86Inst x86asm.Inst
 
 // AsmDecode decodes the assembly instruction starting at mem[0:] into asmInst.
 // It assumes that the Loc and AtPC fields of asmInst have already been filled.
-func x86AsmDecode(asmInst *AsmInstruction, mem []byte, regs Registers, memrw MemoryReadWriter, bi *BinaryInfo, bit int) error {
+func x86AsmDecode(asmInst *AsmInstruction, mem []byte, regs *op.DwarfRegisters, memrw MemoryReadWriter, bi *BinaryInfo, bit int) error {
 	inst, err := x86asm.Decode(mem, bit)
 	if err != nil {
 		asmInst.Inst = (*x86Inst)(nil)
@@ -76,7 +78,7 @@ func (inst *x86Inst) OpcodeEquals(op uint64) bool {
 	return uint64(inst.Op) == op
 }
 
-func resolveCallArgX86(inst *x86asm.Inst, instAddr uint64, currentGoroutine bool, regs Registers, mem MemoryReadWriter, bininfo *BinaryInfo) *Location {
+func resolveCallArgX86(inst *x86asm.Inst, instAddr uint64, currentGoroutine bool, regs *op.DwarfRegisters, mem MemoryReadWriter, bininfo *BinaryInfo) *Location {
 	switch inst.Op {
 	case x86asm.CALL, x86asm.LCALL, x86asm.JMP, x86asm.LJMP:
 		// ok
@@ -94,7 +96,7 @@ func resolveCallArgX86(inst *x86asm.Inst, instAddr uint64, currentGoroutine bool
 		if !currentGoroutine || regs == nil {
 			return nil
 		}
-		pc, err = regs.Get(int(arg))
+		pc, err = bininfo.Arch.getAsmRegister(regs, int(arg))
 		if err != nil {
 			return nil
 		}
@@ -105,8 +107,8 @@ func resolveCallArgX86(inst *x86asm.Inst, instAddr uint64, currentGoroutine bool
 		if arg.Segment != 0 {
 			return nil
 		}
-		base, err1 := regs.Get(int(arg.Base))
-		index, err2 := regs.Get(int(arg.Index))
+		base, err1 := bininfo.Arch.getAsmRegister(regs, int(arg.Base))
+		index, err2 := bininfo.Arch.getAsmRegister(regs, int(arg.Index))
 		if err1 != nil || err2 != nil {
 			return nil
 		}
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 be33f727..d786a35a 100644
--- a/service/dap/server.go
+++ b/service/dap/server.go
@@ -21,6 +21,7 @@ import (
 	"reflect"
 	"regexp"
 	"runtime"
+	"runtime/debug"
 	"strings"
 	"sync"
 
@@ -36,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.
@@ -52,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
@@ -85,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.
@@ -93,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
@@ -123,6 +149,7 @@ var defaultArgs = launchAttachArgs{
 
 // DefaultLoadConfig controls how variables are loaded from the target's memory, borrowing the
 // default value from the original vscode-go debug adapter and rpc server.
+// With dlv-dap, users currently do not have a way to adjust these.
 // TODO(polina): Support setting config via launch/attach args or only rely on on-demand loading?
 var DefaultLoadConfig = proc.LoadConfig{
 	FollowPointers:     true,
@@ -216,6 +243,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)
@@ -237,8 +265,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
@@ -313,65 +339,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.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
@@ -380,16 +478,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)
@@ -399,6 +510,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
@@ -408,20 +522,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)
@@ -435,28 +566,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)
@@ -475,6 +590,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)
 }
 
@@ -755,9 +875,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 {
@@ -765,10 +891,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())
@@ -793,12 +920,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")
@@ -863,8 +995,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},
@@ -872,16 +1020,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 {
@@ -891,11 +1041,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) {
@@ -995,23 +1149,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) {
@@ -1023,8 +1177,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)
@@ -1042,7 +1201,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.
@@ -1060,6 +1219,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)
@@ -1099,6 +1260,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 {
@@ -1333,33 +1497,88 @@ func (s *Server) convertVariableWithOpts(v *proc.Variable, qualifiedNameOrExpr s
 	if v.Unreadable != nil {
 		return
 	}
-	typeName := api.PrettyTypeName(v.DwarfType)
 
-	// As per https://github.com/go-delve/delve/blob/master/Documentation/api/ClientHowto.md#looking-into-variables,
-	// some of the types might be fully or partially not loaded based on LoadConfig. For now, clearly
-	// communicate when values are not fully loaded. TODO(polina): look into loading on demand.
+	// Some of the types might be fully or partially not loaded based on LoadConfig.
+	// Those that are fully missing (e.g. due to hitting MaxVariableRecurse), can be reloaded in place.
+	// Those that are partially missing (e.g. MaxArrayValues from a large array), need a more creative solution
+	// that is still to be determined. For now, clearly communicate when that happens with additional value labels.
+	// TODO(polina): look into layered/paged loading for truncated strings, arrays, maps and structs.
+	var 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.
+		// TODO(polina): Get *proc.Variable object from debugger instead. Export and set v.loaded to false
+		// and call v.loadValue gain. It's more efficient, and it's guaranteed to keep working with generics.
+		value = api.ConvertVar(v).SinglelineString()
+		typeName := api.PrettyTypeName(v.DwarfType)
+		loadExpr := fmt.Sprintf("*(*%q)(%#x)", typeName, v.Addr)
+		s.log.Debugf("loading %s (type %s) with %s", qualifiedNameOrExpr, typeName, loadExpr)
+		// Make sure we can load the pointers directly, not by updating just the child
+		// This is not really necessary now because users have no way of setting FollowPointers to false.
+		config := DefaultLoadConfig
+		config.FollowPointers = true
+		vLoaded, err := s.debugger.EvalVariableInScope(-1, 0, 0, loadExpr, config)
+		if err != nil {
+			value += fmt.Sprintf(" - FAILED TO LOAD: %s", err)
+		} else {
+			v.Children = vLoaded.Children
+			value = api.ConvertVar(v).SinglelineString()
+		}
+		return value
+	}
 
 	switch v.Kind {
 	case reflect.UnsafePointer:
 		// Skip child reference
 	case reflect.Ptr:
 		if v.DwarfType != nil && len(v.Children) > 0 && v.Children[0].Addr != 0 && v.Children[0].Kind != reflect.Invalid {
-			if v.Children[0].OnlyAddr {
-				value = "(not loaded) " + value
-			} else {
+			if v.Children[0].OnlyAddr { // Not loaded
+				if v.Addr == 0 {
+					// This is equvalent to the following with the cli:
+					//    (dlv) p &a7
+					//    (**main.FooBar)(0xc0000a3918)
+					//
+					// TODO(polina): what is more appropriate?
+					// Option 1: leave it unloaded because it is a special case
+					// Option 2: load it, but then we have to load the child, not the parent, unlike all others
+					// TODO(polina): see if reloadVariable can be reused here
+					cTypeName := api.PrettyTypeName(v.Children[0].DwarfType)
+					cLoadExpr := fmt.Sprintf("*(*%q)(%#x)", cTypeName, v.Children[0].Addr)
+					s.log.Debugf("loading *(%s) (type %s) with %s", qualifiedNameOrExpr, cTypeName, cLoadExpr)
+					cLoaded, err := s.debugger.EvalVariableInScope(-1, 0, 0, cLoadExpr, DefaultLoadConfig)
+					if err != nil {
+						value += fmt.Sprintf(" - FAILED TO LOAD: %s", err)
+					} else {
+						cLoaded.Name = v.Children[0].Name // otherwise, this will be the pointer expression
+						v.Children = []proc.Variable{*cLoaded}
+						value = api.ConvertVar(v).SinglelineString()
+					}
+				} else {
+					value = reloadVariable(v, qualifiedNameOrExpr)
+				}
+			}
+			if !v.Children[0].OnlyAddr {
 				variablesReference = maybeCreateVariableHandle(v)
 			}
 		}
 	case reflect.Slice, reflect.Array:
 		if v.Len > int64(len(v.Children)) { // Not fully loaded
-			value = fmt.Sprintf("(loaded %d/%d) ", len(v.Children), v.Len) + value
+			if v.Base != 0 && len(v.Children) == 0 { // Fully missing
+				value = reloadVariable(v, qualifiedNameOrExpr)
+			} else { // Partially missing (TODO)
+				value = fmt.Sprintf("(loaded %d/%d) ", len(v.Children), v.Len) + value
+			}
 		}
 		if v.Base != 0 && len(v.Children) > 0 {
 			variablesReference = maybeCreateVariableHandle(v)
 		}
 	case reflect.Map:
 		if v.Len > int64(len(v.Children)/2) { // Not fully loaded
-			value = fmt.Sprintf("(loaded %d/%d) ", len(v.Children)/2, v.Len) + value
+			if len(v.Children) == 0 { // Fully missing
+				value = reloadVariable(v, qualifiedNameOrExpr)
+			} else { // Partially missing (TODO)
+				value = fmt.Sprintf("(loaded %d/%d) ", len(v.Children)/2, v.Len) + value
+			}
 		}
 		if v.Base != 0 && len(v.Children) > 0 {
 			variablesReference = maybeCreateVariableHandle(v)
@@ -1368,27 +1587,20 @@ func (s *Server) convertVariableWithOpts(v *proc.Variable, qualifiedNameOrExpr s
 		// TODO(polina): implement auto-loading here
 	case reflect.Interface:
 		if v.Addr != 0 && len(v.Children) > 0 && v.Children[0].Kind != reflect.Invalid && v.Children[0].Addr != 0 {
-			if v.Children[0].OnlyAddr { // Not fully loaded
-				// We might be loading variables from the frame that's not topmost, so use
-				// frame-independent address-based expression, not fully-qualified name.
-				loadExpr := fmt.Sprintf("*(*%q)(%#x)", typeName, v.Addr)
-				s.log.Debugf("loading %s (type %s) with %s", qualifiedNameOrExpr, typeName, loadExpr)
-				vLoaded, err := s.debugger.EvalVariableInScope(-1, 0, 0, loadExpr, DefaultLoadConfig)
-				if err != nil {
-					value += fmt.Sprintf(" - FAILED TO LOAD: %s", err)
-				} else {
-					v.Children = vLoaded.Children
-					value = api.ConvertVar(v).SinglelineString()
-				}
+			if v.Children[0].OnlyAddr { // Not loaded
+				value = reloadVariable(v, qualifiedNameOrExpr)
 			}
-			// Provide a reference to the child only if it fully loaded
 			if !v.Children[0].OnlyAddr {
 				variablesReference = maybeCreateVariableHandle(v)
 			}
 		}
 	case reflect.Struct:
 		if v.Len > int64(len(v.Children)) { // Not fully loaded
-			value = fmt.Sprintf("(loaded %d/%d) ", len(v.Children), v.Len) + value
+			if len(v.Children) == 0 { // Fully missing
+				value = reloadVariable(v, qualifiedNameOrExpr)
+			} else { // Partially missing (TODO)
+				value = fmt.Sprintf("(loaded %d/%d) ", len(v.Children), v.Len) + value
+			}
 		}
 		if len(v.Children) > 0 {
 			variablesReference = maybeCreateVariableHandle(v)
@@ -1429,6 +1641,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
@@ -1455,6 +1668,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),
@@ -1675,16 +1891,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
 	}
 
@@ -1695,9 +1913,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"
 		}
@@ -1709,7 +1933,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"
@@ -1722,7 +1945,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.
@@ -1737,6 +1959,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 850419a9..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
@@ -1313,8 +1313,7 @@ func (d *Debugger) ThreadRegisters(threadID int, floatingPoint bool) (*op.DwarfR
 	if err != nil {
 		return nil, err
 	}
-	dregs := d.target.BinInfo().Arch.RegistersToDwarfRegisters(0, regs)
-	return &dregs, nil
+	return d.target.BinInfo().Arch.RegistersToDwarfRegisters(0, regs), nil
 }
 
 // ScopeRegisters returns registers for the specified scope.
@@ -1712,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/_fixtures/testvariables2.go b/_fixtures/testvariables2.go
index 7ffeaec0..f5988e3a 100644
--- a/_fixtures/testvariables2.go
+++ b/_fixtures/testvariables2.go
@@ -315,6 +315,7 @@ func main() {
 	zsvmap := map[string]struct{}{"testkey": struct{}{}}
 	var tm truncatedMap
 	tm.v = []map[string]astruct{m1}
+	var rettm = func() truncatedMap { return tm }
 
 	emptyslice := []string{}
 	emptymap := make(map[string]string)
@@ -362,5 +363,5 @@ func main() {
 	longslice := make([]int, 100, 100)
 
 	runtime.Breakpoint()
-	fmt.Println(i1, i2, i3, p1, pp1, amb1, s1, s3, a0, a1, p2, p3, s2, as1, str1, f1, fn1, fn2, nilslice, nilptr, ch1, chnil, m1, mnil, m2, m3, m4, m5, upnil, up1, i4, i5, i6, err1, err2, errnil, iface1, iface2, ifacenil, arr1, parr, cpx1, const1, iface3, iface4, recursive1, recursive1.x, iface5, iface2fn1, iface2fn2, bencharr, benchparr, mapinf, mainMenu, b, b2, sd, anonstruct1, anonstruct2, anoniface1, anonfunc, mapanonstruct1, ifacearr, efacearr, ni8, ni16, ni32, ni64, pinf, ninf, nan, zsvmap, zsslice, zsvar, tm, errtypednil, emptyslice, emptymap, byteslice, runeslice, bytearray, runearray, longstr, nilstruct, as2, as2.NonPointerRecieverMethod, s4, iface2map, issue1578, ll, unread, w2, w3, w4, w5, longarr, longslice)
+	fmt.Println(i1, i2, i3, p1, pp1, amb1, s1, s3, a0, a1, p2, p3, s2, as1, str1, f1, fn1, fn2, nilslice, nilptr, ch1, chnil, m1, mnil, m2, m3, m4, m5, upnil, up1, i4, i5, i6, err1, err2, errnil, iface1, iface2, ifacenil, arr1, parr, cpx1, const1, iface3, iface4, recursive1, recursive1.x, iface5, iface2fn1, iface2fn2, bencharr, benchparr, mapinf, mainMenu, b, b2, sd, anonstruct1, anonstruct2, anoniface1, anonfunc, mapanonstruct1, ifacearr, efacearr, ni8, ni16, ni32, ni64, pinf, ninf, nan, zsvmap, zsslice, zsvar, tm, rettm, errtypednil, emptyslice, emptymap, byteslice, runeslice, bytearray, runearray, longstr, nilstruct, as2, as2.NonPointerRecieverMethod, s4, iface2map, issue1578, ll, unread, w2, w3, w4, w5, longarr, longslice)
 }
diff --git a/pkg/proc/dwarf_export_test.go b/pkg/proc/dwarf_export_test.go
index 57f723fc..b28c8bba 100644
--- a/pkg/proc/dwarf_export_test.go
+++ b/pkg/proc/dwarf_export_test.go
@@ -17,5 +17,5 @@ func NewCompositeMemory(p *Target, pieces []op.Piece) (*compositeMemory, error)
 	dwarfregs := arch.RegistersToDwarfRegisters(0, regs)
 	dwarfregs.ChangeFunc = p.CurrentThread().SetReg
 
-	return newCompositeMemory(p.Memory(), arch, dwarfregs, pieces)
+	return newCompositeMemory(p.Memory(), arch, *dwarfregs, pieces)
 }
diff --git a/pkg/proc/dwarf_expr_test.go b/pkg/proc/dwarf_expr_test.go
index 573707f8..ed514c64 100644
--- a/pkg/proc/dwarf_expr_test.go
+++ b/pkg/proc/dwarf_expr_test.go
@@ -102,8 +102,8 @@ func uintExprCheck(t *testing.T, scope *proc.EvalScope, expr string, tgt uint64)
 	}
 }
 
-func fakeScope(mem proc.MemoryReadWriter, regs op.DwarfRegisters, bi *proc.BinaryInfo, fn *proc.Function) *proc.EvalScope {
-	return &proc.EvalScope{Location: proc.Location{PC: 0x40100, Fn: fn}, Regs: regs, Mem: mem, BinInfo: bi}
+func fakeScope(mem proc.MemoryReadWriter, regs *op.DwarfRegisters, bi *proc.BinaryInfo, fn *proc.Function) *proc.EvalScope {
+	return &proc.EvalScope{Location: proc.Location{PC: 0x40100, Fn: fn}, Regs: *regs, Mem: mem, BinInfo: bi}
 }
 
 func dwarfExprCheck(t *testing.T, scope *proc.EvalScope, testCases map[string]uint16) {
@@ -112,7 +112,7 @@ func dwarfExprCheck(t *testing.T, scope *proc.EvalScope, testCases map[string]ui
 	}
 }
 
-func dwarfRegisters(bi *proc.BinaryInfo, regs *linutil.AMD64Registers) op.DwarfRegisters {
+func dwarfRegisters(bi *proc.BinaryInfo, regs *linutil.AMD64Registers) *op.DwarfRegisters {
 	a := proc.AMD64Arch("linux")
 	so := bi.PCToImage(regs.PC())
 	dwarfRegs := a.RegistersToDwarfRegisters(so.StaticBase, regs)
@@ -271,7 +271,7 @@ func TestDwarfExprLoclist(t *testing.T) {
 	const PC = 0x40100
 	regs := linutil.AMD64Registers{Regs: &linutil.AMD64PtraceRegs{Rip: PC}}
 
-	scope := &proc.EvalScope{Location: proc.Location{PC: PC, Fn: mainfn}, Regs: dwarfRegisters(bi, &regs), Mem: mem, BinInfo: bi}
+	scope := &proc.EvalScope{Location: proc.Location{PC: PC, Fn: mainfn}, Regs: *dwarfRegisters(bi, &regs), Mem: mem, BinInfo: bi}
 
 	uintExprCheck(t, scope, "a", before)
 	scope.PC = 0x40800
diff --git a/pkg/proc/fbsdutil/regs_test.go b/pkg/proc/fbsdutil/regs_test.go
deleted file mode 100644
index 19aa7641..00000000
--- a/pkg/proc/fbsdutil/regs_test.go
+++ /dev/null
@@ -1,60 +0,0 @@
-package fbsdutil
-
-import (
-	"testing"
-
-	"golang.org/x/arch/x86/x86asm"
-)
-
-func TestAMD64Get(t *testing.T) {
-	val := int64(0x7fffffffdeadbeef)
-	regs := AMD64Registers{
-		Regs: &AMD64PtraceRegs{
-			Rax: val,
-		},
-	}
-	// Test AL, low 8 bits of RAX
-	al, err := regs.Get(int(x86asm.AL))
-	if err != nil {
-		t.Fatal(err)
-	}
-	if al != 0xef {
-		t.Fatalf("expected %#v, got %#v\n", 0xef, al)
-	}
-
-	// Test AH, high 8 bits of RAX
-	ah, err := regs.Get(int(x86asm.AH))
-	if err != nil {
-		t.Fatal(err)
-	}
-	if ah != 0xBE {
-		t.Fatalf("expected %#v, got %#v\n", 0xbe, ah)
-	}
-
-	// Test AX, lower 16 bits of RAX
-	ax, err := regs.Get(int(x86asm.AX))
-	if err != nil {
-		t.Fatal(err)
-	}
-	if ax != 0xBEEF {
-		t.Fatalf("expected %#v, got %#v\n", 0xbeef, ax)
-	}
-
-	// Test EAX, lower 32 bits of RAX
-	eax, err := regs.Get(int(x86asm.EAX))
-	if err != nil {
-		t.Fatal(err)
-	}
-	if eax != 0xDEADBEEF {
-		t.Fatalf("expected %#v, got %#v\n", uint64(0xdeadbeef), eax)
-	}
-
-	// Test RAX, full 64 bits of register
-	rax, err := regs.Get(int(x86asm.RAX))
-	if err != nil {
-		t.Fatal(err)
-	}
-	if rax != uint64(val) {
-		t.Fatalf("expected %#v, got %#v\n", val, rax)
-	}
-}
diff --git a/pkg/proc/linutil/regs_test.go b/pkg/proc/linutil/regs_test.go
deleted file mode 100644
index a9ed0fbf..00000000
--- a/pkg/proc/linutil/regs_test.go
+++ /dev/null
@@ -1,60 +0,0 @@
-package linutil
-
-import (
-	"testing"
-
-	"golang.org/x/arch/x86/x86asm"
-)
-
-func TestAMD64Get(t *testing.T) {
-	val := uint64(0xffffffffdeadbeef)
-	regs := AMD64Registers{
-		Regs: &AMD64PtraceRegs{
-			Rax: val,
-		},
-	}
-	// Test AL, low 8 bits of RAX
-	al, err := regs.Get(int(x86asm.AL))
-	if err != nil {
-		t.Fatal(err)
-	}
-	if al != 0xef {
-		t.Fatalf("expected %#v, got %#v\n", 0xef, al)
-	}
-
-	// Test AH, high 8 bits of RAX
-	ah, err := regs.Get(int(x86asm.AH))
-	if err != nil {
-		t.Fatal(err)
-	}
-	if ah != 0xBE {
-		t.Fatalf("expected %#v, got %#v\n", 0xbe, ah)
-	}
-
-	// Test AX, lower 16 bits of RAX
-	ax, err := regs.Get(int(x86asm.AX))
-	if err != nil {
-		t.Fatal(err)
-	}
-	if ax != 0xBEEF {
-		t.Fatalf("expected %#v, got %#v\n", 0xbeef, ax)
-	}
-
-	// Test EAX, lower 32 bits of RAX
-	eax, err := regs.Get(int(x86asm.EAX))
-	if err != nil {
-		t.Fatal(err)
-	}
-	if eax != 0xDEADBEEF {
-		t.Fatalf("expected %#v, got %#v\n", uint64(0xdeadbeef), eax)
-	}
-
-	// Test RAX, full 64 bits of register
-	rax, err := regs.Get(int(x86asm.RAX))
-	if err != nil {
-		t.Fatal(err)
-	}
-	if rax != val {
-		t.Fatalf("expected %#v, got %#v\n", val, rax)
-	}
-}
diff --git a/pkg/proc/proc_test.go b/pkg/proc/proc_test.go
index 3820ae9d..36144115 100644
--- a/pkg/proc/proc_test.go
+++ b/pkg/proc/proc_test.go
@@ -4994,7 +4994,7 @@ func TestDump(t *testing.T) {
 		assertNoError(err, t, fmt.Sprintf("Thread registers %d", thread.ThreadID()))
 		arch := thread.BinInfo().Arch
 		dregs := arch.RegistersToDwarfRegisters(0, regs)
-		return fmt.Sprintf("%08d %s", thread.ThreadID(), convertRegisters(arch, dregs))
+		return fmt.Sprintf("%08d %s", thread.ThreadID(), convertRegisters(arch, *dregs))
 	}
 
 	convertThreads := func(threads []proc.Thread) []string {
diff --git a/service/dap/server_test.go b/service/dap/server_test.go
index bd920d26..65e011ad 100644
--- a/service/dap/server_test.go
+++ b/service/dap/server_test.go
@@ -33,8 +33,12 @@ const noChildren bool = false
 var testBackend string
 
 func TestMain(m *testing.M) {
+	logOutputVal := ""
+	if _, isTeamCityTest := os.LookupEnv("TEAMCITY_VERSION"); isTeamCityTest {
+		logOutputVal = "debugger,dap"
+	}
 	var logOutput string
-	flag.StringVar(&logOutput, "log-output", "", "configures log output")
+	flag.StringVar(&logOutput, "log-output", logOutputVal, "configures log output")
 	flag.Parse()
 	logflags.Setup(logOutput != "", logOutput, "")
 	protest.DefaultTestBackend(&testBackend)
@@ -341,22 +345,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 +417,47 @@ func TestContinueOnEntry(t *testing.T) {
 		// 5 >> configurationDone, << configurationDone
 		client.ConfigurationDoneRequest()
 		client.ExpectConfigurationDoneResponse(t)
-		// "Continue" happens behind the scenes
-
-		// 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
+		// "Continue" happens behind the scenes on another goroutine
 
-		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 +488,28 @@ func TestPreSetBreakpoint(t *testing.T) {
 
 		client.ConfigurationDoneRequest()
 		client.ExpectConfigurationDoneResponse(t)
-		// This triggers "continue"
-
-		// TODO(polina): add a no-op threads request
-		// with dummy response here once server becomes async
-		// to match what happens in VS Code.
+		// This triggers "continue" on a separate goroutine
 
-		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
@@ -1374,7 +1428,7 @@ func TestVariablesLoading(t *testing.T) {
 						expectChildren(t, sd, "sd", 5)
 					}
 
-					// Struct fully missing due to hitting LoadConfig.MaxVariableRecurse (also tests evaluateName)
+					// Fully missing struct auto-loaded when reaching LoadConfig.MaxVariableRecurse (also tests evaluateName corner case)
 					ref = expectVarRegex(t, locals, -1, "c1", "c1", `main\.cstruct {pb: \*main\.bstruct {a: \(\*main\.astruct\)\(0x[0-9a-f]+\)}, sa: []\*main\.astruct len: 3, cap: 3, [\*\(\*main\.astruct\)\(0x[0-9a-f]+\),\*\(\*main\.astruct\)\(0x[0-9a-f]+\),\*\(\*main.astruct\)\(0x[0-9a-f]+\)]}`, hasChildren)
 					if ref > 0 {
 						client.VariablesRequest(ref)
@@ -1387,16 +1441,16 @@ func TestVariablesLoading(t *testing.T) {
 							expectChildren(t, c1sa, "c1.sa", 3)
 							ref = expectVarRegex(t, c1sa, 0, `\[0\]`, `c1\.sa\[0\]`, `\*\(\*main\.astruct\)\(0x[0-9a-f]+\)`, hasChildren)
 							if ref > 0 {
+								// Auto-loading of fully missing struc children happens here
 								client.VariablesRequest(ref)
 								c1sa0 := client.ExpectVariablesResponse(t)
 								expectChildren(t, c1sa0, "c1.sa[0]", 1)
-								// TODO(polina): there should be children here once we support auto loading
-								expectVarRegex(t, c1sa0, 0, "", `\(\*c1\.sa\[0\]\)`, `\(loaded 0/2\) \(\*main\.astruct\)\(0x[0-9a-f]+\)`, noChildren)
+								expectVarExact(t, c1sa0, 0, "", "(*c1.sa[0])", "main.astruct {A: 1, B: 2}", hasChildren)
 							}
 						}
 					}
 
-					// Struct fully missing due to hitting LoadConfig.MaxVariableRecurse (also tests evaluteName)
+					// Fully missing struct auto-loaded when hitting LoadConfig.MaxVariableRecurse (also tests evaluteName corner case)
 					ref = expectVarRegex(t, locals, -1, "aas", "aas", `\[\]main\.a len: 1, cap: 1, \[{aas: \[\]main\.a len: 1, cap: 1, \[\(\*main\.a\)\(0x[0-9a-f]+\)\]}\]`, hasChildren)
 					if ref > 0 {
 						client.VariablesRequest(ref)
@@ -1409,16 +1463,22 @@ func TestVariablesLoading(t *testing.T) {
 							expectChildren(t, aas0, "aas[0]", 1)
 							ref = expectVarRegex(t, aas0, 0, "aas", `aas\[0\]\.aas`, `\[\]main\.a len: 1, cap: 1, \[\(\*main\.a\)\(0x[0-9a-f]+\)\]`, hasChildren)
 							if ref > 0 {
+								// Auto-loading of fully missing struct children happens here
 								client.VariablesRequest(ref)
 								aas0aas := client.ExpectVariablesResponse(t)
 								expectChildren(t, aas0aas, "aas[0].aas", 1)
-								// TODO(polina): there should be a child here once we support auto loading - test for "aas[0].aas[0].aas"
-								expectVarRegex(t, aas0aas, 0, "[0]", `aas\[0\]\.aas\[0\]`, `\(loaded 0/1\) \(\*main\.a\)\(0x[0-9a-f]+\)`, noChildren)
+								ref = expectVarRegex(t, aas0aas, 0, "[0]", `aas\[0\]\.aas\[0\]`, `main\.a {aas: \[\]main\.a len: 1, cap: 1, \[\(\*main\.a\)\(0x[0-9a-f]+\)\]}`, hasChildren)
+								if ref > 0 {
+									client.VariablesRequest(ref)
+									aas0aas0 := client.ExpectVariablesResponse(t)
+									expectChildren(t, aas0aas, "aas[0].aas[0]", 1)
+									expectVarRegex(t, aas0aas0, 0, "aas", `aas\[0\]\.aas\[0\]\.aas`, `\[\]main\.a len: 1, cap: 1, \[\(\*main\.a\)\(0x[0-9a-f]+\)\]`, hasChildren)
+								}
 							}
 						}
 					}
 
-					// Map fully missing due to hitting LoadConfig.MaxVariableRecurse (also tests evaluateName)
+					// Fully missing map auto-loaded when hitting LoadConfig.MaxVariableRecurse (also tests evaluateName corner case)
 					ref = expectVarExact(t, locals, -1, "tm", "tm", "main.truncatedMap {v: []map[string]main.astruct len: 1, cap: 1, [[...]]}", hasChildren)
 					if ref > 0 {
 						client.VariablesRequest(ref)
@@ -1426,13 +1486,54 @@ func TestVariablesLoading(t *testing.T) {
 						expectChildren(t, tm, "tm", 1)
 						ref = expectVarExact(t, tm, 0, "v", "tm.v", "[]map[string]main.astruct len: 1, cap: 1, [[...]]", hasChildren)
 						if ref > 0 {
+							// Auto-loading of fully missing map chidlren happens here, but they get trancated at MaxArrayValuess
 							client.VariablesRequest(ref)
 							tmV := client.ExpectVariablesResponse(t)
 							expectChildren(t, tmV, "tm.v", 1)
-							// TODO(polina): there should be children here once we support auto loading - test for "tm.v[0]["gutters"]"
-							expectVarExact(t, tmV, 0, "[0]", "tm.v[0]", "(loaded 0/66) map[string]main.astruct [...]", noChildren)
+							ref = expectVarRegex(t, tmV, 0, `\[0\]`, `tm\.v\[0\]`, `map\[string\]main\.astruct \[.+\.\.\.\+2 more\]`, hasChildren)
+							if ref > 0 {
+								client.VariablesRequest(ref)
+								tmV0 := client.ExpectVariablesResponse(t)
+								expectChildren(t, tmV0, "tm.v[0]", 64)
+							}
 						}
 					}
+
+					// Auto-loading works with call return variables as well
+					protest.MustSupportFunctionCalls(t, testBackend)
+					client.EvaluateRequest("call rettm()", 1000, "repl")
+					got := client.ExpectEvaluateResponse(t)
+					ref = expectEval(t, got, "main.truncatedMap {v: []map[string]main.astruct len: 1, cap: 1, [[...]]}", hasChildren)
+					if ref > 0 {
+						client.VariablesRequest(ref)
+						rv := client.ExpectVariablesResponse(t)
+						expectChildren(t, rv, "rv", 1)
+						ref = expectVarExact(t, rv, 0, "~r0", "", "main.truncatedMap {v: []map[string]main.astruct len: 1, cap: 1, [[...]]}", hasChildren)
+						if ref > 0 {
+							client.VariablesRequest(ref)
+							tm := client.ExpectVariablesResponse(t)
+							expectChildren(t, tm, "tm", 1)
+							ref = expectVarExact(t, tm, 0, "v", "", "[]map[string]main.astruct len: 1, cap: 1, [[...]]", hasChildren)
+							if ref > 0 {
+								// Auto-loading of fully missing map chidlren happens here, but they get trancated at MaxArrayValuess
+								client.VariablesRequest(ref)
+								tmV := client.ExpectVariablesResponse(t)
+								expectChildren(t, tmV, "tm.v", 1)
+								// TODO(polina): this evaluate name is not usable - it should be empty
+								ref = expectVarRegex(t, tmV, 0, `\[0\]`, `\[0\]`, `map\[string\]main\.astruct \[.+\.\.\.\+2 more\]`, hasChildren)
+								if ref > 0 {
+									client.VariablesRequest(ref)
+									tmV0 := client.ExpectVariablesResponse(t)
+									expectChildren(t, tmV0, "tm.v[0]", 64)
+								}
+							}
+						}
+					}
+
+					// TODO(polina): need fully missing array/slice test case
+
+					// Zero slices, structs and maps are not treated as fully missing
+					// See zsvar, zsslice,, emptyslice, emptymap, a0
 				},
 				disconnect: true,
 			}})
@@ -1491,9 +1592,26 @@ func TestVariablesLoading(t *testing.T) {
 							}
 						}
 
-						// Pointer value not loaded with LoadConfig.FollowPointers=false
-						expectVarRegex(t, locals, -1, "a7", "a7", `\(not loaded\) \(\*main\.FooBar\)\(0x[0-9a-f]+\)`, noChildren)
+						// Pointer values loaded even with LoadConfig.FollowPointers=false
+
+						expectVarExact(t, locals, -1, "a7", "a7", "*main.FooBar {Baz: 5, Bur: \"strum\"}", hasChildren)
+
+						// Auto-loading works on results of evaluate expressions as well
+						client.EvaluateRequest("a7", frame, "repl")
+						expectEval(t, client.ExpectEvaluateResponse(t), "*main.FooBar {Baz: 5, Bur: \"strum\"}", hasChildren)
+
+						client.EvaluateRequest("&a7", frame, "repl")
+						pa7 := client.ExpectEvaluateResponse(t)
+						ref = expectEvalRegex(t, pa7, `\*\(\*main\.FooBar\)\(0x[0-9a-f]+\)`, hasChildren)
+						if ref > 0 {
+							client.VariablesRequest(ref)
+							a7 := client.ExpectVariablesResponse(t)
+							expectVarExact(t, a7, 0, "a7", "(*(&a7))", "*main.FooBar {Baz: 5, Bur: \"strum\"}", hasChildren)
+						}
 					}
+
+					// Frame-independent loading expressions allow us to auto-load
+					// variables in any frame, not just topmost.
 					loadvars(1000 /*first topmost frame*/)
 					// step into another function
 					client.StepInRequest(1)
@@ -1938,7 +2056,14 @@ func TestEvaluateRequest(t *testing.T) {
 					// Type casts of integer constants into any pointer type and vice versa
 					client.EvaluateRequest("(*int)(2)", 1000, "this context will be ignored")
 					got = client.ExpectEvaluateResponse(t)
-					expectEval(t, got, "(not loaded) (*int)(0x2)", noChildren)
+					ref = expectEvalRegex(t, got, `\*\(unreadable .+\)`, hasChildren)
+					if ref > 0 {
+						client.VariablesRequest(ref)
+						val := client.ExpectVariablesResponse(t)
+						expectChildren(t, val, "*(*int)(2)", 1)
+						expectVarRegex(t, val, 0, "^$", `\(\*\(\(\*int\)\(2\)\)\)`, `\(unreadable .+\)`, noChildren)
+						validateEvaluateName(t, client, val, 0)
+					}
 
 					// Type casts between string, []byte and []rune
 					client.EvaluateRequest("[]byte(\"ABC€\")", 1000, "this context will be ignored")
@@ -2415,14 +2540,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)