Repository: go-delve/delve
Pull request #2415 (2021-04-06 07:32:44)

proc: remove duplicate Registers.Get implementations
Moves the implementation of Registers.Get out of the backends and into
proc where it can be implemented only once per architecture.



Patch:
diff --git a/Documentation/cli/README.md b/Documentation/cli/README.md
index 5ecb1087..3d1804e4 100644
--- a/Documentation/cli/README.md
+++ b/Documentation/cli/README.md
@@ -280,14 +280,19 @@ Aliases: ed
 Examine memory:
 
 	examinemem [-fmt <format>] [-count|-len <count>] [-size <size>] <address>
+	examinemem [-fmt <format>] [-count|-len <count>] [-size <size>] -x <expression>
 
 Format represents the data format and the value is one of this list (default hex): bin(binary), oct(octal), dec(decimal), hex(hexadecimal), addr(address).
 Length is the number of bytes (default 1) and must be less than or equal to 1000.
 Address is the memory location of the target to examine. Please note '-len' is deprecated by '-count and -size'.
+Expression can be an integer expression or pointer value of the memory location to examine.
 
 For example:
 
     x -fmt hex -count 20 -size 1 0xc00008af38
+    x -fmt hex -count 20 -size 1 -x 0xc00008af38 + 8
+    x -fmt hex -count 20 -size 1 -x &myVar
+    x -fmt hex -count 20 -size 1 -x myPtrVar
 
 Aliases: x
 
diff --git a/Documentation/cli/getting_started.md b/Documentation/cli/getting_started.md
index 23a9d96c..504a287e 100644
--- a/Documentation/cli/getting_started.md
+++ b/Documentation/cli/getting_started.md
@@ -25,7 +25,7 @@ For example given this project layout:
 │       └── bar_test.go
 ```
 
-If you are in the directory `github.com/me/foo/cmd/foo` you can simple run `dlv debug`
+If you are in the directory `github.com/me/foo/cmd/foo` you can simply run `dlv debug`
 from the command line. From anywhere else, say the project root, you can simply
 provide the package: `dlv debug github.com/me/foo/cmd/foo`. To pass flags to your program 
 separate them with `--`: `dlv debug github.com/me/foo/cmd/foo -- -arg1 value`.
@@ -86,6 +86,6 @@ As you can see, we began debugging the test binary, found our test function via
 the `funcs` command which takes a regexp to filter the list of functions, set a
 breakpoint and then continued execution until we hit that breakpoint.
 
-For more information on subcommands you can use type `dlv help`, and once in a
+For more information on subcommands you can use, type `dlv help`, and once in a
 debug session you can see all of the commands available to you by typing `help`
 at any time.
diff --git a/_scripts/make.go b/_scripts/make.go
index 40e35072..3883ebbb 100644
--- a/_scripts/make.go
+++ b/_scripts/make.go
@@ -213,7 +213,7 @@ func installedExecutablePath() string {
 // i.e. cgo enabled and the legacy SDK headers:
 // https://forums.developer.apple.com/thread/104296
 func canMacnative() bool {
-	if runtime.GOOS != "darwin" {
+	if !(runtime.GOOS == "darwin" && runtime.GOARCH == "amd64") {
 		return false
 	}
 	if strings.TrimSpace(getoutput("go", "env", "CGO_ENABLED")) != "1" {
@@ -221,13 +221,18 @@ func canMacnative() bool {
 	}
 
 	macOSVersion := strings.Split(strings.TrimSpace(getoutput("/usr/bin/sw_vers", "-productVersion")), ".")
+
+	major, err := strconv.ParseInt(macOSVersion[0], 10, 64)
+	if err != nil {
+		return false
+	}
 	minor, err := strconv.ParseInt(macOSVersion[1], 10, 64)
 	if err != nil {
 		return false
 	}
 
 	typesHeader := "/usr/include/sys/types.h"
-	if minor >= 15 {
+	if major >= 11 || (major == 10 && minor >= 15) {
 		typesHeader = "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/sys/types.h"
 	}
 	_, err = os.Stat(typesHeader)
diff --git a/pkg/dwarf/godwarf/type.go b/pkg/dwarf/godwarf/type.go
index c9605ec2..15547d89 100644
--- a/pkg/dwarf/godwarf/type.go
+++ b/pkg/dwarf/godwarf/type.go
@@ -1084,13 +1084,3 @@ func zeroArray(t Type) {
 		t = at.Type
 	}
 }
-
-func resolveTypedef(typ Type) Type {
-	for {
-		if tt, ok := typ.(*TypedefType); ok {
-			typ = tt.Type
-		} else {
-			return typ
-		}
-	}
-}
diff --git a/pkg/dwarf/line/state_machine.go b/pkg/dwarf/line/state_machine.go
index 7626f58f..ddc83410 100644
--- a/pkg/dwarf/line/state_machine.go
+++ b/pkg/dwarf/line/state_machine.go
@@ -49,14 +49,6 @@ type StateMachine struct {
 	ptrSize     int
 }
 
-type opcodeKind uint8
-
-const (
-	specialOpcode opcodeKind = iota
-	standardOpcode
-	extendedOpcode
-)
-
 type opcodefn func(*StateMachine, *bytes.Buffer)
 
 // Special opcodes
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/buf.go b/pkg/dwarf/util/buf.go
index 52349fe0..d7b1ea31 100644
--- a/pkg/dwarf/util/buf.go
+++ b/pkg/dwarf/util/buf.go
@@ -53,14 +53,6 @@ func MakeBuf(d *dwarf.Data, format dataFormat, name string, off dwarf.Offset, da
 	return buf{d, format, name, off, data, nil}
 }
 
-func (b *buf) slice(length int) buf {
-	n := *b
-	data := b.data
-	b.skip(length) // Will validate length.
-	n.data = data[:length]
-	return n
-}
-
 func (b *buf) Uint8() uint8 {
 	if len(b.data) < 1 {
 		b.error("underflow")
@@ -72,34 +64,6 @@ func (b *buf) Uint8() uint8 {
 	return val
 }
 
-func (b *buf) bytes(n int) []byte {
-	if len(b.data) < n {
-		b.error("underflow")
-		return nil
-	}
-	data := b.data[0:n]
-	b.data = b.data[n:]
-	b.off += dwarf.Offset(n)
-	return data
-}
-
-func (b *buf) skip(n int) { b.bytes(n) }
-
-// string returns the NUL-terminated (C-like) string at the start of the buffer.
-// The terminal NUL is discarded.
-func (b *buf) string() string {
-	for i := 0; i < len(b.data); i++ {
-		if b.data[i] == 0 {
-			s := string(b.data[0:i])
-			b.data = b.data[i+1:]
-			b.off += dwarf.Offset(i + 1)
-			return s
-		}
-	}
-	b.error("underflow")
-	return ""
-}
-
 // Read a varint, which is 7 bits per byte, little endian.
 // the 0x80 bit means read another byte.
 func (b *buf) Varint() (c uint64, bits uint) {
diff --git a/pkg/gobuild/gobuild.go b/pkg/gobuild/gobuild.go
index 33943d51..0f3cc051 100644
--- a/pkg/gobuild/gobuild.go
+++ b/pkg/gobuild/gobuild.go
@@ -54,32 +54,58 @@ func optflags(args []string) []string {
 // GoBuild builds non-test files in 'pkgs' with the specified 'buildflags'
 // and writes the output at 'debugname'.
 func GoBuild(debugname string, pkgs []string, buildflags string) error {
-	args := []string{"-o", debugname}
-	args = optflags(args)
-	if buildflags != "" {
-		args = append(args, config.SplitQuotedFields(buildflags, '\'')...)
-	}
-	args = append(args, pkgs...)
-	return gocommand("build", args...)
+	args := goBuildArgs(debugname, pkgs, buildflags, false)
+	return gocommandRun("build", args...)
 }
 
-// GoBuild builds test files 'pkgs' with the specified 'buildflags'
+// GoBuildCombinedOutput builds non-test files in 'pkgs' with the specified 'buildflags'
+// and writes the output at 'debugname'.
+func GoBuildCombinedOutput(debugname string, pkgs []string, buildflags string) ([]byte, error) {
+	args := goBuildArgs(debugname, pkgs, buildflags, false)
+	return gocommandCombinedOutput("build", args...)
+}
+
+// GoTestBuild builds test files 'pkgs' with the specified 'buildflags'
 // and writes the output at 'debugname'.
 func GoTestBuild(debugname string, pkgs []string, buildflags string) error {
-	args := []string{"-c", "-o", debugname}
+	args := goBuildArgs(debugname, pkgs, buildflags, true)
+	return gocommandRun("test", args...)
+}
+
+// GoTestBuildCombinedOutput builds test files 'pkgs' with the specified 'buildflags'
+// and writes the output at 'debugname'.
+func GoTestBuildCombinedOutput(debugname string, pkgs []string, buildflags string) ([]byte, error) {
+	args := goBuildArgs(debugname, pkgs, buildflags, true)
+	return gocommandCombinedOutput("test", args...)
+}
+
+func goBuildArgs(debugname string, pkgs []string, buildflags string, isTest bool) []string {
+	args := []string{"-o", debugname}
+	if isTest {
+		args = append([]string{"-c"}, args...)
+	}
 	args = optflags(args)
 	if buildflags != "" {
 		args = append(args, config.SplitQuotedFields(buildflags, '\'')...)
 	}
 	args = append(args, pkgs...)
-	return gocommand("test", args...)
+	return args
 }
 
-func gocommand(command string, args ...string) error {
+func gocommandRun(command string, args ...string) error {
+	goBuild := gocommandExecCmd(command, args...)
+	goBuild.Stderr = os.Stdout
+	goBuild.Stdout = os.Stderr
+	return goBuild.Run()
+}
+
+func gocommandCombinedOutput(command string, args ...string) ([]byte, error) {
+	return gocommandExecCmd(command, args...).CombinedOutput()
+}
+
+func gocommandExecCmd(command string, args ...string) *exec.Cmd {
 	allargs := []string{command}
 	allargs = append(allargs, args...)
 	goBuild := exec.Command("go", allargs...)
-	goBuild.Stderr = os.Stderr
-	goBuild.Stdout = os.Stdout
-	return goBuild.Run()
+	return goBuild
 }
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 7952ab04..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,
 	}
 }
 
@@ -129,7 +129,6 @@ func arm64FixFrameUnwindContext(fctxt *frame.FrameContext, pc uint64, bi *Binary
 
 const arm64cgocallSPOffsetSaveSlot = 0x8
 const prevG0schedSPOffsetSaveSlot = 0x10
-const spAlign = 16
 
 func arm64SwitchStack(it *stackIterator, callFrameRegs *op.DwarfRegisters) bool {
 	if it.frame.Current.Fn != nil {
@@ -236,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++ {
@@ -322,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/bininfo.go b/pkg/proc/bininfo.go
index fde4cb45..87c8559e 100644
--- a/pkg/proc/bininfo.go
+++ b/pkg/proc/bininfo.go
@@ -69,9 +69,6 @@ type BinaryInfo struct {
 
 	lastModified time.Time // Time the executable of this process was last modified
 
-	closer         io.Closer
-	sepDebugCloser io.Closer
-
 	// PackageMap maps package names to package paths, needed to lookup types inside DWARF info.
 	// On Go1.12 this mapping is determined by using the last element of a package path, for example:
 	//   github.com/go-delve/delve
@@ -1402,23 +1399,6 @@ func (bi *BinaryInfo) parseDebugFramePE(image *Image, exe *pe.File, debugInfoByt
 	bi.parseDebugFrameGeneral(image, debugFrameBytes, ".debug_frame", err, nil, 0, "", frame.DwarfEndian(debugInfoBytes))
 }
 
-// Borrowed from https://golang.org/src/cmd/internal/objfile/pe.go
-func findPESymbol(f *pe.File, name string) (*pe.Symbol, error) {
-	for _, s := range f.Symbols {
-		if s.Name != name {
-			continue
-		}
-		if s.SectionNumber <= 0 {
-			return nil, fmt.Errorf("symbol %s: invalid section number %d", name, s.SectionNumber)
-		}
-		if len(f.Sections) < int(s.SectionNumber) {
-			return nil, fmt.Errorf("symbol %s: section number %d is larger than max %d", name, s.SectionNumber, len(f.Sections))
-		}
-		return s, nil
-	}
-	return nil, fmt.Errorf("no %s symbol found", name)
-}
-
 // MACH-O ////////////////////////////////////////////////////////////
 
 // loadBinaryInfoMacho specifically loads information from a Mach-O binary.
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 026ea39d..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.
@@ -50,13 +50,11 @@ var (
 	errFuncCallUnsupported        = errors.New("function calls not supported by this version of Go")
 	errFuncCallUnsupportedBackend = errors.New("backend does not support function calls")
 	errFuncCallInProgress         = errors.New("cannot call function while another function call is already in progress")
-	errNotACallExpr               = errors.New("not a function call")
 	errNoGoroutine                = errors.New("no goroutine selected")
 	errGoroutineNotRunning        = errors.New("selected goroutine not running")
 	errNotEnoughStack             = errors.New("not enough stack space")
 	errTooManyArguments           = errors.New("too many arguments")
 	errNotEnoughArguments         = errors.New("not enough arguments")
-	errNoAddrUnsupported          = errors.New("arguments to a function call must have an address")
 	errNotAGoFunction             = errors.New("not a Go function")
 	errFuncCallNotAllowed         = errors.New("function calls not allowed without using 'call'")
 	errFuncCallNotAllowedStrAlloc = errors.New("literal string can not be allocated because function calls are not allowed without using 'call'")
@@ -291,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
 	}
 
@@ -389,7 +386,7 @@ type fncallPanicErr struct {
 }
 
 func (err fncallPanicErr) Error() string {
-	return fmt.Sprintf("panic calling a function")
+	return "panic calling a function"
 }
 
 func fncallLog(fmtstr string, args ...interface{}) {
@@ -707,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()
@@ -890,13 +887,6 @@ func fakeFunctionEntryScope(scope *EvalScope, fn *Function, cfa int64, sp uint64
 	return nil
 }
 
-func (fncall *functionCallState) returnValues() []*Variable {
-	if fncall.panicvar != nil {
-		return []*Variable{fncall.panicvar}
-	}
-	return fncall.retvars
-}
-
 // allocString allocates spaces for the contents of v if it needs to be allocated
 func allocString(scope *EvalScope, v *Variable) error {
 	if v.Base != 0 || v.Len == 0 {
diff --git a/pkg/proc/gdbserial/gdbserver.go b/pkg/proc/gdbserial/gdbserver.go
index 71c15b14..4a1cab30 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"
@@ -730,7 +727,7 @@ func (p *gdbProcess) Valid() (bool, error) {
 		return false, proc.ErrProcessDetached
 	}
 	if p.exited {
-		return false, &proc.ErrProcessExited{Pid: p.Pid()}
+		return false, proc.ErrProcessExited{Pid: p.Pid()}
 	}
 	return true, nil
 }
@@ -780,7 +777,7 @@ const (
 // a breakpoint is hit or signal is received.
 func (p *gdbProcess) ContinueOnce() (proc.Thread, proc.StopReason, error) {
 	if p.exited {
-		return nil, proc.StopExited, &proc.ErrProcessExited{Pid: p.conn.pid}
+		return nil, proc.StopExited, proc.ErrProcessExited{Pid: p.conn.pid}
 	}
 
 	if p.conn.direction == proc.Forward {
@@ -1835,375 +1832,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/native/proc.go b/pkg/proc/native/proc.go
index 511b2772..1e0d463f 100644
--- a/pkg/proc/native/proc.go
+++ b/pkg/proc/native/proc.go
@@ -140,7 +140,7 @@ func (dbp *nativeProcess) Valid() (bool, error) {
 		return false, proc.ErrProcessDetached
 	}
 	if dbp.exited {
-		return false, &proc.ErrProcessExited{Pid: dbp.Pid()}
+		return false, proc.ErrProcessExited{Pid: dbp.Pid()}
 	}
 	return true, nil
 }
@@ -185,7 +185,7 @@ func (dbp *nativeProcess) Breakpoints() *proc.BreakpointMap {
 // sends SIGSTOP to all threads.
 func (dbp *nativeProcess) RequestManualStop() error {
 	if dbp.exited {
-		return &proc.ErrProcessExited{Pid: dbp.Pid()}
+		return proc.ErrProcessExited{Pid: dbp.Pid()}
 	}
 	dbp.stopMu.Lock()
 	defer dbp.stopMu.Unlock()
@@ -222,7 +222,7 @@ func (dbp *nativeProcess) EraseBreakpoint(bp *proc.Breakpoint) error {
 // This could be the result of a breakpoint or signal.
 func (dbp *nativeProcess) ContinueOnce() (proc.Thread, proc.StopReason, error) {
 	if dbp.exited {
-		return nil, proc.StopExited, &proc.ErrProcessExited{Pid: dbp.Pid()}
+		return nil, proc.StopExited, proc.ErrProcessExited{Pid: dbp.Pid()}
 	}
 
 	for {
diff --git a/pkg/proc/native/proc_darwin.go b/pkg/proc/native/proc_darwin.go
index f44aa312..1e533ef9 100644
--- a/pkg/proc/native/proc_darwin.go
+++ b/pkg/proc/native/proc_darwin.go
@@ -424,7 +424,7 @@ func (dbp *nativeProcess) resume() error {
 // stop stops all running threads and sets breakpoints
 func (dbp *nativeProcess) stop(trapthread *nativeThread) (*nativeThread, error) {
 	if dbp.exited {
-		return nil, &proc.ErrProcessExited{Pid: dbp.Pid()}
+		return nil, proc.ErrProcessExited{Pid: dbp.Pid()}
 	}
 	for _, th := range dbp.threads {
 		if !th.Stopped() {
diff --git a/pkg/proc/native/proc_freebsd.go b/pkg/proc/native/proc_freebsd.go
index a2a3ab16..26879000 100644
--- a/pkg/proc/native/proc_freebsd.go
+++ b/pkg/proc/native/proc_freebsd.go
@@ -349,7 +349,7 @@ func (dbp *nativeProcess) resume() error {
 // stop stops all running threads and sets breakpoints
 func (dbp *nativeProcess) stop(trapthread *nativeThread) (*nativeThread, error) {
 	if dbp.exited {
-		return nil, &proc.ErrProcessExited{Pid: dbp.Pid()}
+		return nil, proc.ErrProcessExited{Pid: dbp.Pid()}
 	}
 	// set breakpoints on all threads
 	for _, th := range dbp.threads {
diff --git a/pkg/proc/native/proc_linux.go b/pkg/proc/native/proc_linux.go
index 3bed3a9a..a4376891 100644
--- a/pkg/proc/native/proc_linux.go
+++ b/pkg/proc/native/proc_linux.go
@@ -505,7 +505,7 @@ func (dbp *nativeProcess) resume() error {
 // stop stops all running threads and sets breakpoints
 func (dbp *nativeProcess) stop(trapthread *nativeThread) (*nativeThread, error) {
 	if dbp.exited {
-		return nil, &proc.ErrProcessExited{Pid: dbp.Pid()}
+		return nil, proc.ErrProcessExited{Pid: dbp.Pid()}
 	}
 
 	for _, th := range dbp.threads {
diff --git a/pkg/proc/native/proc_windows.go b/pkg/proc/native/proc_windows.go
index dbad2370..a4ccddd4 100644
--- a/pkg/proc/native/proc_windows.go
+++ b/pkg/proc/native/proc_windows.go
@@ -414,7 +414,7 @@ func (dbp *nativeProcess) resume() error {
 // stop stops all running threads threads and sets breakpoints
 func (dbp *nativeProcess) stop(trapthread *nativeThread) (*nativeThread, error) {
 	if dbp.exited {
-		return nil, &proc.ErrProcessExited{Pid: dbp.Pid()}
+		return nil, proc.ErrProcessExited{Pid: dbp.Pid()}
 	}
 
 	dbp.os.running = false
diff --git a/pkg/proc/native/threads_linux.go b/pkg/proc/native/threads_linux.go
index d0e978c0..4c9f4a55 100644
--- a/pkg/proc/native/threads_linux.go
+++ b/pkg/proc/native/threads_linux.go
@@ -14,7 +14,6 @@ type waitStatus sys.WaitStatus
 // process details.
 type osSpecificDetails struct {
 	delayedSignal int
-	registers     sys.PtraceRegs
 	running       bool
 	setbp         bool
 }
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/target.go b/pkg/proc/target.go
index 4673613b..9bc0e59c 100644
--- a/pkg/proc/target.go
+++ b/pkg/proc/target.go
@@ -359,3 +359,8 @@ func (t *Target) createFatalThrowBreakpoint() {
 func (t *Target) CurrentThread() Thread {
 	return t.currentThread
 }
+
+// SetNextBreakpointID sets the breakpoint ID of the next breakpoint
+func (t *Target) SetNextBreakpointID(id int) {
+	t.Breakpoints().breakpointIDCounter = id
+}
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/pkg/terminal/command.go b/pkg/terminal/command.go
index 4f5e0665..5b1b1143 100644
--- a/pkg/terminal/command.go
+++ b/pkg/terminal/command.go
@@ -408,14 +408,19 @@ If locspec is omitted edit will open the current source file in the editor, othe
 		{aliases: []string{"examinemem", "x"}, group: dataCmds, cmdFn: examineMemoryCmd, helpMsg: `Examine memory:
 
 	examinemem [-fmt <format>] [-count|-len <count>] [-size <size>] <address>
+	examinemem [-fmt <format>] [-count|-len <count>] [-size <size>] -x <expression>
 
 Format represents the data format and the value is one of this list (default hex): bin(binary), oct(octal), dec(decimal), hex(hexadecimal), addr(address).
 Length is the number of bytes (default 1) and must be less than or equal to 1000.
 Address is the memory location of the target to examine. Please note '-len' is deprecated by '-count and -size'.
+Expression can be an integer expression or pointer value of the memory location to examine.
 
 For example:
 
-    x -fmt hex -count 20 -size 1 0xc00008af38`},
+    x -fmt hex -count 20 -size 1 0xc00008af38
+    x -fmt hex -count 20 -size 1 -x 0xc00008af38 + 8
+    x -fmt hex -count 20 -size 1 -x &myVar
+    x -fmt hex -count 20 -size 1 -x myPtrVar`},
 
 		{aliases: []string{"display"}, group: dataCmds, cmdFn: display, helpMsg: `Print value of an expression every time the program stops.
 
@@ -1659,27 +1664,42 @@ func edit(t *Term, ctx callContext, args string) error {
 	return cmd.Run()
 }
 
-func examineMemoryCmd(t *Term, ctx callContext, args string) error {
-	v := strings.FieldsFunc(args, func(c rune) bool {
-		return c == ' '
-	})
-
+func examineMemoryCmd(t *Term, ctx callContext, argstr string) error {
 	var (
 		address uint64
 		err     error
 		ok      bool
+		args    = strings.Split(argstr, " ")
 	)
 
 	// Default value
 	priFmt := byte('x')
 	count := 1
 	size := 1
+	isExpr := false
+
+	// nextArg returns the next argument that is not an empty string, if any, and
+	// advances the args slice to the position after that.
+	nextArg := func() string {
+		for len(args) > 0 {
+			arg := args[0]
+			args = args[1:]
+			if arg != "" {
+				return arg
+			}
+		}
+		return ""
+	}
 
-	for i := 0; i < len(v); i++ {
-		switch v[i] {
+loop:
+	for {
+		switch cmd := nextArg(); cmd {
+		case "":
+			// no more arguments
+			break loop
 		case "-fmt":
-			i++
-			if i >= len(v) {
+			arg := nextArg()
+			if arg == "" {
 				return fmt.Errorf("expected argument after -fmt")
 			}
 			fmtMapToPriFmt := map[string]byte{
@@ -1692,39 +1712,39 @@ func examineMemoryCmd(t *Term, ctx callContext, args string) error {
 				"bin":         'b',
 				"binary":      'b',
 			}
-			priFmt, ok = fmtMapToPriFmt[v[i]]
+			priFmt, ok = fmtMapToPriFmt[arg]
 			if !ok {
-				return fmt.Errorf("%q is not a valid format", v[i])
+				return fmt.Errorf("%q is not a valid format", arg)
 			}
 		case "-count", "-len":
-			i++
-			if i >= len(v) {
+			arg := nextArg()
+			if arg == "" {
 				return fmt.Errorf("expected argument after -count/-len")
 			}
 			var err error
-			count, err = strconv.Atoi(v[i])
+			count, err = strconv.Atoi(arg)
 			if err != nil || count <= 0 {
 				return fmt.Errorf("count/len must be a positive integer")
 			}
 		case "-size":
-			i++
-			if i >= len(v) {
+			arg := nextArg()
+			if arg == "" {
 				return fmt.Errorf("expected argument after -size")
 			}
 			var err error
-			size, err = strconv.Atoi(v[i])
+			size, err = strconv.Atoi(arg)
 			if err != nil || size <= 0 || size > 8 {
 				return fmt.Errorf("size must be a positive integer (<=8)")
 			}
+		case "-x":
+			isExpr = true
+			break loop // remaining args are going to be interpreted as expression
 		default:
-			if i != len(v)-1 {
-				return fmt.Errorf("unknown option %q", v[i])
-			}
-			// TODO, maybe we can support expression.
-			address, err = strconv.ParseUint(v[len(v)-1], 0, 64)
-			if err != nil {
-				return fmt.Errorf("convert address into uintptr type failed, %s", err)
+			if len(args) > 0 {
+				return fmt.Errorf("unknown option %q", args[0])
 			}
+			args = []string{cmd}
+			break loop // only one arg left to be evaluated as a uint
 		}
 	}
 
@@ -1733,10 +1753,39 @@ func examineMemoryCmd(t *Term, ctx callContext, args string) error {
 		return fmt.Errorf("read memory range (count*size) must be less than or equal to 1000 bytes")
 	}
 
-	if address == 0 {
+	if len(args) == 0 {
 		return fmt.Errorf("no address specified")
 	}
 
+	if isExpr {
+		expr := strings.Join(args, " ")
+		val, err := t.client.EvalVariable(ctx.Scope, expr, t.loadConfig())
+		if err != nil {
+			return err
+		}
+
+		// "-x &myVar" or "-x myPtrVar"
+		if val.Kind == reflect.Ptr {
+			if len(val.Children) < 1 {
+				return fmt.Errorf("bug? invalid pointer: %#v", val)
+			}
+			address = val.Children[0].Addr
+			// "-x 0xc000079f20 + 8" or -x 824634220320 + 8
+		} else if val.Kind == reflect.Int && val.Value != "" {
+			address, err = strconv.ParseUint(val.Value, 0, 64)
+			if err != nil {
+				return fmt.Errorf("bad expression result: %q: %s", val.Value, err)
+			}
+		} else {
+			return fmt.Errorf("unsupported expression type: %s", val.Kind)
+		}
+	} else {
+		address, err = strconv.ParseUint(args[0], 0, 64)
+		if err != nil {
+			return fmt.Errorf("convert address into uintptr type failed, %s", err)
+		}
+	}
+
 	memArea, isLittleEndian, err := t.client.ExamineMemory(address, count*size)
 	if err != nil {
 		return err
diff --git a/pkg/terminal/starbind/starlark.go b/pkg/terminal/starbind/starlark.go
index 8c441480..f37e01a7 100644
--- a/pkg/terminal/starbind/starlark.go
+++ b/pkg/terminal/starbind/starlark.go
@@ -266,19 +266,6 @@ func (env *Env) callMain(thread *starlark.Thread, globals starlark.StringDict, m
 	return starlark.Call(thread, mainfn, argtuple, nil)
 }
 
-type argument struct {
-	name         string
-	defaultValue defaultValue
-}
-
-type defaultValue uint8
-
-const (
-	defaultNone = iota
-	defaultScope
-	defaultLoadConfig
-)
-
 func isCancelled(thread *starlark.Thread) error {
 	if ctx, ok := thread.Local(dlvContextName).(context.Context); ok {
 		select {
diff --git a/service/dap/error_ids.go b/service/dap/error_ids.go
index 8b1ac928..03ec41e7 100644
--- a/service/dap/error_ids.go
+++ b/service/dap/error_ids.go
@@ -21,4 +21,5 @@ const (
 	UnableToLookupVariable     = 2008
 	UnableToEvaluateExpression = 2009
 	// Add more codes as we support more requests
+	DisconnectError = 5000
 )
diff --git a/service/dap/server.go b/service/dap/server.go
index 39e74a82..be33f727 100644
--- a/service/dap/server.go
+++ b/service/dap/server.go
@@ -25,6 +25,7 @@ import (
 	"sync"
 
 	"github.com/go-delve/delve/pkg/gobuild"
+	"github.com/go-delve/delve/pkg/locspec"
 	"github.com/go-delve/delve/pkg/logflags"
 	"github.com/go-delve/delve/pkg/proc"
 	"github.com/go-delve/delve/service"
@@ -35,32 +36,42 @@ import (
 )
 
 // Server implements a DAP server that can accept a single client for
-// a single debug session. It does not support restarting.
-// The server operates via two goroutines:
+// a single debug session (for now). It does yet not 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.
+//
+// The DAP server operates via the following goroutines:
+//
 // (1) Main goroutine where the server is created via NewServer(),
-// started via Run() and stopped via Stop().
-// (2) Run goroutine started from Run() that accepts a client connection,
+// started via Run() and stopped via Stop(). Once the server is
+// started, this goroutine blocks until it receives a stop-server
+// signal that can come from an OS interrupt (such as Ctrl-C) or
+// config.DisconnectChan (passed to NewServer()) as a result of
+// client connection failure or closure or a DAP disconnect request.
+//
+// (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.
+//
+// TODO(polina): add another layer of per-client goroutines to support multiple clients
 // TODO(polina): make it asynchronous (i.e. launch goroutine per request)
 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
-	// conn is the accepted client connection.
-	conn net.Conn
-	// stopChan is closed when the server is Stop()-ed. This can be used to signal
+	// 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.
-	stopChan chan struct{}
+	stopTriggered chan struct{}
 	// reader is used to read requests from the connection.
 	reader *bufio.Reader
-	// debugger is the underlying debugger service.
-	debugger *debugger.Debugger
 	// log is used for structured logging.
 	log *logrus.Entry
-	// binaryToRemove is the compiled binary to be removed on disconnect.
-	binaryToRemove string
 	// stackFrameHandles maps frames of each goroutine to unique ids across all goroutines.
 	// Reset at every stop.
 	stackFrameHandles *handlesMap
@@ -71,8 +82,15 @@ type Server struct {
 	// args tracks special settings for handling debug session requests.
 	args launchAttachArgs
 
+	// 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.
+	debugger *debugger.Debugger
+	// binaryToRemove is the temp compiled binary to be removed on disconnect (if any).
+	binaryToRemove string
 	// noDebugProcess is set for the noDebug launch process.
 	noDebugProcess *exec.Cmd
 }
@@ -86,13 +104,21 @@ type launchAttachArgs struct {
 	stackTraceDepth int
 	// showGlobalVariables indicates if global package variables should be loaded.
 	showGlobalVariables bool
+	// substitutePathClientToServer indicates rules for converting file paths between client and debugger.
+	// These must be directory paths.
+	substitutePathClientToServer [][2]string
+	// substitutePathServerToClient indicates rules for converting file paths between debugger and client.
+	// These must be directory paths.
+	substitutePathServerToClient [][2]string
 }
 
 // defaultArgs borrows the defaults for the arguments from the original vscode-go adapter.
 var defaultArgs = launchAttachArgs{
-	stopOnEntry:         false,
-	stackTraceDepth:     50,
-	showGlobalVariables: false,
+	stopOnEntry:                  false,
+	stackTraceDepth:              50,
+	showGlobalVariables:          false,
+	substitutePathClientToServer: [][2]string{},
+	substitutePathServerToClient: [][2]string{},
 }
 
 // DefaultLoadConfig controls how variables are loaded from the target's memory, borrowing the
@@ -107,9 +133,10 @@ var DefaultLoadConfig = proc.LoadConfig{
 }
 
 // NewServer creates a new DAP Server. It takes an opened Listener
-// via config and assumes its ownership. config.disconnectChan has to be set;
-// it will be closed by the server when the client disconnects or requests
-// shutdown. Once disconnectChan is closed, Server.Stop() must be called.
+// via config and assumes its ownership. config.DisconnectChan has to be set;
+// it will be closed by the server when the client fails to connect,
+// disconnects or requests shutdown. Once config.DisconnectChan is closed,
+// Server.Stop() must be called to shutdown this single-user server.
 func NewServer(config *service.Config) *Server {
 	logger := logflags.DAPLogger()
 	logflags.WriteDAPListeningMessage(config.Listener.Addr().String())
@@ -117,7 +144,7 @@ func NewServer(config *service.Config) *Server {
 	return &Server{
 		config:            config,
 		listener:          config.Listener,
-		stopChan:          make(chan struct{}),
+		stopTriggered:     make(chan struct{}),
 		log:               logger,
 		stackFrameHandles: newHandlesMap(),
 		variableHandles:   newVariablesHandlesMap(),
@@ -127,7 +154,7 @@ func NewServer(config *service.Config) *Server {
 
 // If user-specified options are provided via Launch/AttachRequest,
 // we override the defaults for optional args.
-func (s *Server) setLaunchAttachArgs(request dap.LaunchAttachRequest) {
+func (s *Server) setLaunchAttachArgs(request dap.LaunchAttachRequest) error {
 	stop, ok := request.GetArguments()["stopOnEntry"].(bool)
 	if ok {
 		s.args.stopOnEntry = stop
@@ -140,15 +167,48 @@ func (s *Server) setLaunchAttachArgs(request dap.LaunchAttachRequest) {
 	if ok {
 		s.args.showGlobalVariables = globals
 	}
+	paths, ok := request.GetArguments()["substitutePath"]
+	if ok {
+		typeMismatchError := fmt.Errorf("'substitutePath' attribute '%v' in debug configuration is not a []{'from': string, 'to': string}", paths)
+		pathsParsed, ok := paths.([]interface{})
+		if !ok {
+			return typeMismatchError
+		}
+		clientToServer := make([][2]string, 0, len(pathsParsed))
+		serverToClient := make([][2]string, 0, len(pathsParsed))
+		for _, arg := range pathsParsed {
+			pathMapping, ok := arg.(map[string]interface{})
+			if !ok {
+				return typeMismatchError
+			}
+			from, ok := pathMapping["from"].(string)
+			if !ok {
+				return typeMismatchError
+			}
+			to, ok := pathMapping["to"].(string)
+			if !ok {
+				return typeMismatchError
+			}
+			clientToServer = append(clientToServer, [2]string{from, to})
+			serverToClient = append(serverToClient, [2]string{to, from})
+		}
+		s.args.substitutePathClientToServer = clientToServer
+		s.args.substitutePathServerToClient = serverToClient
+	}
+	return nil
 }
 
 // Stop stops the DAP debugger service, closes the listener and the client
 // connection. It shuts down the underlying debugger and kills the target
-// process if it was launched by it. This method mustn't be called more than
-// once.
+// process if it was launched by it or stops the noDebug process.
+// This method mustn't be called more than once.
 func (s *Server) Stop() {
+	s.log.Debug("DAP server stopping...")
+	close(s.stopTriggered)
 	_ = s.listener.Close()
-	close(s.stopChan)
+
+	s.mu.Lock()
+	defer s.mu.Unlock()
 	if s.conn != nil {
 		// Unless Stop() was called after serveDAPCodec()
 		// returned, this will result in closed connection error
@@ -157,48 +217,45 @@ func (s *Server) Stop() {
 		_ = s.conn.Close()
 	}
 	if s.debugger != nil {
-		kill := s.config.Debugger.AttachPid == 0
-		if err := s.debugger.Detach(kill); err != nil {
-			switch err.(type) {
-			case *proc.ErrProcessExited:
-				s.log.Debug(err)
-			default:
-				s.log.Error(err)
-			}
-		}
+		killProcess := s.config.Debugger.AttachPid == 0
+		s.stopDebugSession(killProcess)
 	} else {
 		s.stopNoDebugProcess()
 	}
+	// The binary is no longer in use by the debugger. It is safe to remove it.
+	if s.binaryToRemove != "" {
+		gobuild.Remove(s.binaryToRemove)
+		s.binaryToRemove = ""
+	}
+	s.log.Debug("DAP server stopped")
 }
 
-// signalDisconnect closes config.DisconnectChan if not nil, which
-// signals that the client disconnected or there was a client
-// connection failure. Since the server currently services only one
-// client, this can be used as a signal to the entire server via
-// Stop(). The function safeguards agaist closing the channel more
+// triggerServerStop closes config.DisconnectChan if not nil, which
+// signals that client sent a disconnect request or there was connection
+// failure or closure. Since the server currently services only one
+// client, this is used as a signal to stop the entire server.
+// 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) signalDisconnect() {
+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
 	// following sequence of events:
 	// -- run goroutine: calls onDisconnectRequest()
-	// -- run goroutine: calls signalDisconnect()
+	// -- run goroutine: calls triggerServerStop()
 	// -- main goroutine: calls Stop()
-	// -- main goroutine: Stop() closes client connection
+	// -- main goroutine: Stop() closes client connection (or client closed it)
 	// -- run goroutine: serveDAPCodec() gets "closed network connection"
-	// -- run goroutine: serveDAPCodec() returns
-	// -- run goroutine: serveDAPCodec calls signalDisconnect()
+	// -- run goroutine: serveDAPCodec() returns and calls triggerServerStop()
 	if s.config.DisconnectChan != nil {
 		close(s.config.DisconnectChan)
 		s.config.DisconnectChan = nil
 	}
-	if s.binaryToRemove != "" {
-		gobuild.Remove(s.binaryToRemove)
-		s.binaryToRemove = ""
-	}
+	// There should be no logic here after the stop-server
+	// signal that might cause everything to shutdown before this
+	// logic gets executed.
 }
 
 // Run launches a new goroutine where it accepts a client connection
@@ -210,26 +267,27 @@ func (s *Server) signalDisconnect() {
 // so the editor needs to launch delve only once?
 func (s *Server) Run() {
 	go func() {
-		conn, err := s.listener.Accept()
+		conn, err := s.listener.Accept() // listener is closed in Stop()
 		if err != nil {
 			select {
-			case <-s.stopChan:
+			case <-s.stopTriggered:
 			default:
 				s.log.Errorf("Error accepting client connection: %s\n", err)
+				s.triggerServerStop()
 			}
-			s.signalDisconnect()
 			return
 		}
-		s.conn = conn
+		s.mu.Lock()
+		s.conn = conn // closed in Stop()
+		s.mu.Unlock()
 		s.serveDAPCodec()
 	}()
 }
 
 // serveDAPCodec reads and decodes requests from the client
 // until it encounters an error or EOF, when it sends
-// the disconnect signal and returns.
+// a disconnect signal and returns.
 func (s *Server) serveDAPCodec() {
-	defer s.signalDisconnect()
 	s.reader = bufio.NewReader(s.conn)
 	for {
 		request, err := dap.ReadProtocolMessage(s.reader)
@@ -241,14 +299,13 @@ func (s *Server) serveDAPCodec() {
 		// TODO(polina): to support this add Seq to
 		// dap.DecodeProtocolMessageFieldError.
 		if err != nil {
-			stopRequested := false
 			select {
-			case <-s.stopChan:
-				stopRequested = true
+			case <-s.stopTriggered:
 			default:
-			}
-			if err != io.EOF && !stopRequested {
-				s.log.Error("DAP error: ", err)
+				if err != io.EOF {
+					s.log.Error("DAP error: ", err)
+				}
+				s.triggerServerStop()
 			}
 			return
 		}
@@ -421,6 +478,15 @@ func (s *Server) send(message dap.Message) {
 	_ = dap.WriteProtocolMessage(s.conn, message)
 }
 
+func (s *Server) logToConsole(msg string) {
+	s.send(&dap.OutputEvent{
+		Event: *newEvent("output"),
+		Body: dap.OutputEventBody{
+			Output:   msg + "\n",
+			Category: "console",
+		}})
+}
+
 func (s *Server) onInitializeRequest(request *dap.InitializeRequest) {
 	// TODO(polina): Respond with an error if debug session is in progress?
 	response := &dap.InitializeResponse{Response: *newResponse(request.Request)}
@@ -500,23 +566,32 @@ func (s *Server) onLaunchRequest(request *dap.LaunchRequest) {
 		}
 
 		s.log.Debugf("building binary at %s", debugbinary)
+		var out []byte
 		switch mode {
 		case "debug":
-			err = gobuild.GoBuild(debugbinary, []string{program}, buildFlags)
+			out, err = gobuild.GoBuildCombinedOutput(debugbinary, []string{program}, buildFlags)
 		case "test":
-			err = gobuild.GoTestBuild(debugbinary, []string{program}, buildFlags)
+			out, err = gobuild.GoTestBuildCombinedOutput(debugbinary, []string{program}, buildFlags)
 		}
 		if err != nil {
 			s.sendErrorResponse(request.Request,
 				FailedToLaunch, "Failed to launch",
-				fmt.Sprintf("Build error: %s", err.Error()))
+				fmt.Sprintf("Build error: %s (%s)", strings.TrimSpace(string(out)), err.Error()))
 			return
 		}
 		program = debugbinary
+		s.mu.Lock()
 		s.binaryToRemove = debugbinary
+		s.mu.Unlock()
 	}
 
-	s.setLaunchAttachArgs(request)
+	err := s.setLaunchAttachArgs(request)
+	if err != nil {
+		s.sendErrorResponse(request.Request,
+			FailedToLaunch, "Failed to launch",
+			err.Error())
+		return
+	}
 
 	var targetArgs []string
 	args, ok := request.Arguments["args"]
@@ -544,20 +619,23 @@ func (s *Server) onLaunchRequest(request *dap.LaunchRequest) {
 	s.config.Debugger.WorkingDir = filepath.Dir(program)
 
 	// Set the WorkingDir for this program to the one specified in the request arguments.
-	wd, ok := request.Arguments["wd"]
+	wd, ok := request.Arguments["cwd"]
 	if ok {
 		wdParsed, ok := wd.(string)
 		if !ok {
 			s.sendErrorResponse(request.Request,
 				FailedToLaunch, "Failed to launch",
-				fmt.Sprintf("'wd' attribute '%v' in debug configuration is not a string.", wd))
+				fmt.Sprintf("'cwd' attribute '%v' in debug configuration is not a string.", wd))
 			return
 		}
 		s.config.Debugger.WorkingDir = wdParsed
 	}
 
+	s.log.Debugf("running program in %s\n", s.config.Debugger.WorkingDir)
 	if noDebug, ok := request.Arguments["noDebug"].(bool); ok && noDebug {
+		s.mu.Lock()
 		cmd, err := s.startNoDebugProcess(program, targetArgs, s.config.Debugger.WorkingDir)
+		s.mu.Unlock()
 		if err != nil {
 			s.sendErrorResponse(request.Request, FailedToLaunch, "Failed to launch", err.Error())
 			return
@@ -568,9 +646,8 @@ func (s *Server) onLaunchRequest(request *dap.LaunchRequest) {
 
 		// Then, block until the program terminates or is stopped.
 		if err := cmd.Wait(); err != nil {
-			s.log.Debugf("program exited: %v", err)
+			s.log.Debugf("program exited with error: %v", err)
 		}
-
 		stopped := false
 		s.mu.Lock()
 		stopped = s.noDebugProcess == nil // if it was stopped, this should be nil.
@@ -578,23 +655,32 @@ func (s *Server) onLaunchRequest(request *dap.LaunchRequest) {
 		s.mu.Unlock()
 
 		if !stopped {
+			s.logToConsole(proc.ErrProcessExited{Pid: cmd.ProcessState.Pid(), Status: cmd.ProcessState.ExitCode()}.Error())
 			s.send(&dap.TerminatedEvent{Event: *newEvent("terminated")})
 		}
 		return
 	}
 
-	var err error
-	if s.debugger, err = debugger.New(&s.config.Debugger, s.config.ProcessArgs); err != nil {
+	func() {
+		s.mu.Lock()
+		defer s.mu.Unlock() // Make sure to unlock in case of panic that will become internal error
+		s.debugger, err = debugger.New(&s.config.Debugger, s.config.ProcessArgs)
+	}()
+	if err != nil {
 		s.sendErrorResponse(request.Request, FailedToLaunch, "Failed to launch", err.Error())
 		return
 	}
+
+	// Notify the client that the debugger is ready to start accepting
+	// configuration requests for setting breakpoints, etc. The client
+	// will end the configuration sequence with 'configurationDone'.
 	s.send(&dap.InitializedEvent{Event: *newEvent("initialized")})
 	s.send(&dap.LaunchResponse{Response: *newResponse(request.Request)})
 }
 
+// startNoDebugProcess is called from onLaunchRequest (run goroutine) and
+// requires holding mu lock.
 func (s *Server) startNoDebugProcess(program string, targetArgs []string, wd string) (*exec.Cmd, error) {
-	s.mu.Lock()
-	defer s.mu.Unlock()
 	if s.noDebugProcess != nil {
 		return nil, fmt.Errorf("another launch request is in progress")
 	}
@@ -607,16 +693,21 @@ func (s *Server) startNoDebugProcess(program string, targetArgs []string, wd str
 	return cmd, nil
 }
 
+// stopNoDebugProcess is called from Stop (main goroutine) and
+// onDisconnectRequest (run goroutine) and requires holding mu lock.
 func (s *Server) stopNoDebugProcess() {
-	s.mu.Lock()
-	p := s.noDebugProcess
-	s.noDebugProcess = nil
-	defer s.mu.Unlock()
-
-	// TODO(hyangah): gracefully terminate the process and its children processes.
-	if p != nil && !p.ProcessState.Exited() {
-		p.Process.Kill() // Don't check error. Process killing and self-termination may race.
+	if s.noDebugProcess == nil {
+		// We already handled termination or there was never a process
+		return
+	}
+	if s.noDebugProcess.ProcessState.Exited() {
+		s.logToConsole(proc.ErrProcessExited{Pid: s.noDebugProcess.ProcessState.Pid(), Status: s.noDebugProcess.ProcessState.ExitCode()}.Error())
+	} else {
+		// TODO(hyangah): gracefully terminate the process and its children processes.
+		s.logToConsole(fmt.Sprintf("Terminating process %d", s.noDebugProcess.Process.Pid))
+		s.noDebugProcess.Process.Kill() // Don't check error. Process killing and self-termination may race.
 	}
+	s.noDebugProcess = nil
 }
 
 // TODO(polina): support "remote" mode
@@ -633,40 +724,73 @@ func isValidLaunchMode(launchMode interface{}) bool {
 // it disconnects the debuggee and signals that the debug adaptor
 // (in our case this TCP server) can be terminated.
 func (s *Server) onDisconnectRequest(request *dap.DisconnectRequest) {
-	s.send(&dap.DisconnectResponse{Response: *newResponse(request.Request)})
+	defer s.triggerServerStop()
+	s.mu.Lock()
+	defer s.mu.Unlock()
+
+	var err error
 	if s.debugger != nil {
-		_, err := s.debugger.Command(&api.DebuggerCommand{Name: api.Halt}, nil)
-		if err != nil {
-			switch err.(type) {
-			case *proc.ErrProcessExited:
-				s.log.Debug(err)
-			default:
-				s.log.Error(err)
-			}
-		}
-		// We always kill launched programs
-		kill := s.config.Debugger.AttachPid == 0
+		// We always kill launched programs.
 		// In case of attach, we leave the program
-		// running but default, which can be
+		// running by default, which can be
 		// overridden by an explicit request to terminate.
-		if request.Arguments.TerminateDebuggee {
-			kill = true
-		}
-		err = s.debugger.Detach(kill)
-		if err != nil {
-			switch err.(type) {
-			case *proc.ErrProcessExited:
-				s.log.Debug(err)
-			default:
-				s.log.Error(err)
-			}
-		}
+		killProcess := s.config.Debugger.AttachPid == 0 || request.Arguments.TerminateDebuggee
+		err = s.stopDebugSession(killProcess)
 	} else {
 		s.stopNoDebugProcess()
 	}
+	if err != nil {
+		s.sendErrorResponse(request.Request, DisconnectError, "Error while disconnecting", err.Error())
+	} else {
+		s.send(&dap.DisconnectResponse{Response: *newResponse(request.Request)})
+	}
+}
 
-	// TODO(polina): make thread-safe when handlers become asynchronous.
-	s.signalDisconnect()
+// stopDebugSession is called from Stop (main goroutine) and
+// onDisconnectRequest (run goroutine) and requires holding mu lock.
+// Returns any detach error other than proc.ErrProcessExited.
+func (s *Server) stopDebugSession(killProcess bool) error {
+	if s.debugger == nil {
+		return nil
+	}
+	var err error
+	var exited error
+	state, err := s.debugger.Command(&api.DebuggerCommand{Name: api.Halt}, nil)
+	if err == proc.ErrProcessDetached {
+		s.log.Debug(err)
+		return nil
+	}
+	if err != nil {
+		switch err.(type) {
+		case proc.ErrProcessExited:
+			exited = err
+		default:
+			s.log.Error(err)
+		}
+	} else if state.Exited {
+		exited = proc.ErrProcessExited{Pid: s.debugger.ProcessPid(), Status: state.ExitStatus}
+	}
+	if exited != nil {
+		s.logToConsole(exited.Error())
+		s.logToConsole("Detaching")
+	} else if killProcess {
+		s.logToConsole("Detaching and terminating target process")
+	} else {
+		s.logToConsole("Detaching without terminating target processs")
+	}
+	err = s.debugger.Detach(killProcess)
+	s.debugger = nil
+	if err != nil {
+		switch err.(type) {
+		case proc.ErrProcessExited:
+			s.log.Debug(err)
+			s.logToConsole(exited.Error())
+			err = nil
+		default:
+			s.log.Error(err)
+		}
+	}
+	return err
 }
 
 func (s *Server) onSetBreakpointsRequest(request *dap.SetBreakpointsRequest) {
@@ -681,6 +805,9 @@ func (s *Server) onSetBreakpointsRequest(request *dap.SetBreakpointsRequest) {
 		return
 	}
 
+	clientPath := request.Arguments.Source.Path
+	serverPath := s.toServerPath(clientPath)
+
 	// According to the spec we should "set multiple breakpoints for a single source
 	// and clear all previous breakpoints in that source." The simplest way is
 	// to clear all and then set all.
@@ -701,7 +828,7 @@ func (s *Server) onSetBreakpointsRequest(request *dap.SetBreakpointsRequest) {
 		}
 		// Skip other source files.
 		// TODO(polina): should this be normalized because of different OSes?
-		if bp.File != request.Arguments.Source.Path {
+		if bp.File != serverPath {
 			continue
 		}
 		_, err := s.debugger.ClearBreakpoint(bp)
@@ -716,7 +843,7 @@ func (s *Server) onSetBreakpointsRequest(request *dap.SetBreakpointsRequest) {
 	response.Body.Breakpoints = make([]dap.Breakpoint, len(request.Arguments.Breakpoints))
 	for i, want := range request.Arguments.Breakpoints {
 		got, err := s.debugger.CreateBreakpoint(
-			&api.Breakpoint{File: request.Arguments.Source.Path, Line: want.Line, Cond: want.Condition})
+			&api.Breakpoint{File: serverPath, Line: want.Line, Cond: want.Condition})
 		response.Body.Breakpoints[i].Verified = (err == nil)
 		if err != nil {
 			response.Body.Breakpoints[i].Line = want.Line
@@ -724,7 +851,7 @@ func (s *Server) onSetBreakpointsRequest(request *dap.SetBreakpointsRequest) {
 		} else {
 			response.Body.Breakpoints[i].Id = got.ID
 			response.Body.Breakpoints[i].Line = got.Line
-			response.Body.Breakpoints[i].Source = dap.Source{Name: request.Arguments.Source.Name, Path: request.Arguments.Source.Path}
+			response.Body.Breakpoints[i].Source = dap.Source{Name: request.Arguments.Source.Name, Path: clientPath}
 		}
 	}
 	s.send(response)
@@ -772,7 +899,7 @@ func (s *Server) onThreadsRequest(request *dap.ThreadsRequest) {
 	gs, _, err := s.debugger.Goroutines(0, 0)
 	if err != nil {
 		switch err.(type) {
-		case *proc.ErrProcessExited:
+		case proc.ErrProcessExited:
 			// If the program exits very quickly, the initial threads request will complete after it has exited.
 			// A TerminatedEvent has already been sent. Ignore the err returned in this case.
 			s.send(&dap.ThreadsResponse{Response: *newResponse(request.Request)})
@@ -838,11 +965,18 @@ func (s *Server) onAttachRequest(request *dap.AttachRequest) {
 			return
 		}
 		s.config.Debugger.AttachPid = int(pid)
-		s.setLaunchAttachArgs(request)
-		var err error
-		if s.debugger, err = debugger.New(&s.config.Debugger, nil); err != nil {
-			s.sendErrorResponse(request.Request,
-				FailedToAttach, "Failed to attach", err.Error())
+		err := s.setLaunchAttachArgs(request)
+		if err != nil {
+			s.sendErrorResponse(request.Request, FailedToAttach, "Failed to attach", err.Error())
+			return
+		}
+		func() {
+			s.mu.Lock()
+			defer s.mu.Unlock() // Make sure to unlock in case of panic that will become internal error
+			s.debugger, err = debugger.New(&s.config.Debugger, nil)
+		}()
+		if err != nil {
+			s.sendErrorResponse(request.Request, FailedToAttach, "Failed to attach", err.Error())
 			return
 		}
 	} else {
@@ -880,19 +1014,28 @@ func (s *Server) onStepOutRequest(request *dap.StepOutRequest) {
 	s.doStepCommand(api.StepOut, request.Arguments.ThreadId)
 }
 
+func stoppedGoroutineID(state *api.DebuggerState) (id int) {
+	if state.SelectedGoroutine != nil {
+		id = state.SelectedGoroutine.ID
+	} else if state.CurrentThread != nil {
+		id = state.CurrentThread.GoroutineID
+	}
+	return id
+}
+
 func (s *Server) doStepCommand(command string, threadId int) {
 	// Use SwitchGoroutine to change the current goroutine.
-	state, err := s.debugger.Command(&api.DebuggerCommand{Name: api.SwitchGoroutine, GoroutineID: threadId}, nil)
+	_, err := s.debugger.Command(&api.DebuggerCommand{Name: api.SwitchGoroutine, GoroutineID: threadId}, nil)
 	if err != nil {
-		s.log.Errorf("Error switching goroutines while stepping: %e", err)
+		s.log.Errorf("Error switching goroutines while stepping: %v", err)
 		// If we encounter an error, we will have to send a stopped event
 		// since we already sent the step response.
 		stopped := &dap.StoppedEvent{Event: *newEvent("stopped")}
 		stopped.Body.AllThreadsStopped = true
-		if state.SelectedGoroutine != nil {
-			stopped.Body.ThreadId = state.SelectedGoroutine.ID
-		} else if state.CurrentThread != nil {
-			stopped.Body.ThreadId = state.CurrentThread.GoroutineID
+		if state, err := s.debugger.State(false); err != nil {
+			s.log.Errorf("Error retrieving state: %e", err)
+		} else {
+			stopped.Body.ThreadId = stoppedGoroutineID(state)
 		}
 		stopped.Body.Reason = "error"
 		stopped.Body.Text = err.Error()
@@ -931,7 +1074,8 @@ func (s *Server) onStackTraceRequest(request *dap.StackTraceRequest) {
 		uniqueStackFrameID := s.stackFrameHandles.create(stackFrame{goroutineID, i})
 		stackFrames[i] = dap.StackFrame{Id: uniqueStackFrameID, Line: loc.Line, Name: fnName(loc)}
 		if loc.File != "<autogenerated>" {
-			stackFrames[i].Source = dap.Source{Name: filepath.Base(loc.File), Path: loc.File}
+			clientPath := s.toClientPath(loc.File)
+			stackFrames[i].Source = dap.Source{Name: filepath.Base(clientPath), Path: clientPath}
 		}
 		stackFrames[i].Column = 0
 	}
@@ -981,8 +1125,6 @@ func (s *Server) onScopesRequest(request *dap.ScopesRequest) {
 	}
 	locScope := &fullyQualifiedVariable{&proc.Variable{Name: "Locals", Children: slicePtrVarToSliceVar(locals)}, "", true}
 
-	// TODO(polina): Annotate shadowed variables
-
 	scopeArgs := dap.Scope{Name: argScope.Name, VariablesReference: s.variableHandles.create(argScope)}
 	scopeLocals := dap.Scope{Name: locScope.Name, VariablesReference: s.variableHandles.create(locScope)}
 	scopes := []dap.Scope{scopeArgs, scopeLocals}
@@ -1135,8 +1277,18 @@ func (s *Server) onVariablesRequest(request *dap.VariablesRequest) {
 				cfqname = "" // complex children are not struct fields and can't be accessed directly
 			}
 			cvalue, cvarref := s.convertVariable(c, cfqname)
+
+			// Annotate any shadowed variables to "(name)" in order
+			// to distinguish from non-shadowed variables.
+			// TODO(suzmue): should we support a special evaluateName syntax that
+			// can access shadowed variables?
+			name := c.Name
+			if c.Flags&proc.VariableShadowed == proc.VariableShadowed {
+				name = fmt.Sprintf("(%s)", name)
+			}
+
 			children[i] = dap.Variable{
-				Name:               c.Name,
+				Name:               name,
 				EvaluateName:       cfqname,
 				Value:              cvalue,
 				VariablesReference: cvarref,
@@ -1272,7 +1424,7 @@ func (s *Server) convertVariableWithOpts(v *proc.Variable, qualifiedNameOrExpr s
 // variables, so consider also adding the following:
 // -- print {expression} - return the result as a string like from dlv cli
 func (s *Server) onEvaluateRequest(request *dap.EvaluateRequest) {
-	showErrorToUser := request.Arguments.Context != "watch"
+	showErrorToUser := request.Arguments.Context != "watch" && request.Arguments.Context != "repl"
 	if s.debugger == nil {
 		s.sendErrorResponseWithOpts(request.Request, UnableToEvaluateExpression, "Unable to evaluate expression", "debugger is nil", showErrorToUser)
 		return
@@ -1339,12 +1491,10 @@ func (s *Server) onEvaluateRequest(request *dap.EvaluateRequest) {
 		if retVars == nil {
 			// The call got interrupted by a stop (e.g. breakpoint in injected
 			// function call or in another goroutine)
-			s.resetHandlesForStop()
+			s.resetHandlesForStoppedEvent()
 			stopped := &dap.StoppedEvent{Event: *newEvent("stopped")}
 			stopped.Body.AllThreadsStopped = true
-			if state.SelectedGoroutine != nil {
-				stopped.Body.ThreadId = state.SelectedGoroutine.ID
-			}
+			stopped.Body.ThreadId = stoppedGoroutineID(state)
 			stopped.Body.Reason = s.debugger.StopReason().String()
 			s.send(stopped)
 			// TODO(polina): once this is asynchronous, we could wait to reply until the user
@@ -1518,7 +1668,7 @@ func newEvent(event string) *dap.Event {
 const BetterBadAccessError = `invalid memory address or nil pointer dereference [signal SIGSEGV: segmentation violation]
 Unable to propagate EXC_BAD_ACCESS signal to target process and panic (see https://github.com/go-delve/delve/issues/852)`
 
-func (s *Server) resetHandlesForStop() {
+func (s *Server) resetHandlesForStoppedEvent() {
 	s.stackFrameHandles.reset()
 	s.variableHandles.reset()
 }
@@ -1538,14 +1688,12 @@ func (s *Server) doCommand(command string) {
 		return
 	}
 
-	s.resetHandlesForStop()
+	s.resetHandlesForStoppedEvent()
 	stopped := &dap.StoppedEvent{Event: *newEvent("stopped")}
 	stopped.Body.AllThreadsStopped = true
 
 	if err == nil {
-		if state.SelectedGoroutine != nil {
-			stopped.Body.ThreadId = state.SelectedGoroutine.ID
-		}
+		stopped.Body.ThreadId = stoppedGoroutineID(state)
 
 		switch s.debugger.StopReason() {
 		case proc.StopNextFinished:
@@ -1553,7 +1701,7 @@ func (s *Server) doCommand(command string) {
 		default:
 			stopped.Body.Reason = "breakpoint"
 		}
-		if state.CurrentThread.Breakpoint != nil {
+		if state.CurrentThread != nil && state.CurrentThread.Breakpoint != nil {
 			switch state.CurrentThread.Breakpoint.Name {
 			case proc.FatalThrow:
 				stopped.Body.Reason = "fatal error"
@@ -1572,7 +1720,7 @@ func (s *Server) doCommand(command string) {
 		}
 		state, err := s.debugger.State( /*nowait*/ true)
 		if err == nil {
-			stopped.Body.ThreadId = state.CurrentThread.GoroutineID
+			stopped.Body.ThreadId = stoppedGoroutineID(state)
 		}
 		s.send(stopped)
 
@@ -1590,3 +1738,25 @@ func (s *Server) doCommand(command string) {
 			}})
 	}
 }
+
+func (s *Server) toClientPath(path string) string {
+	if len(s.args.substitutePathServerToClient) == 0 {
+		return path
+	}
+	clientPath := locspec.SubstitutePath(path, s.args.substitutePathServerToClient)
+	if clientPath != path {
+		s.log.Debugf("server path=%s converted to client path=%s\n", path, clientPath)
+	}
+	return clientPath
+}
+
+func (s *Server) toServerPath(path string) string {
+	if len(s.args.substitutePathClientToServer) == 0 {
+		return path
+	}
+	serverPath := locspec.SubstitutePath(path, s.args.substitutePathClientToServer)
+	if serverPath != path {
+		s.log.Debugf("client path=%s converted to server path=%s\n", path, serverPath)
+	}
+	return serverPath
+}
diff --git a/service/debugger/debugger.go b/service/debugger/debugger.go
index af306043..053b8787 100644
--- a/service/debugger/debugger.go
+++ b/service/debugger/debugger.go
@@ -376,8 +376,6 @@ func (d *Debugger) LastModified() time.Time {
 	return d.target.BinInfo().LastModified()
 }
 
-const deferReturn = "runtime.deferreturn"
-
 // FunctionReturnLocations returns all return locations
 // for the given function, a list of addresses corresponding
 // to 'ret' or 'call runtime.deferreturn'.
@@ -511,23 +509,27 @@ func (d *Debugger) Restart(rerecord bool, pos string, resetArgs bool, newArgs []
 	discarded := []api.DiscardedBreakpoint{}
 	breakpoints := api.ConvertBreakpoints(d.breakpoints())
 	d.target = p
+	maxID := 0
 	for _, oldBp := range breakpoints {
 		if oldBp.ID < 0 {
 			continue
 		}
+		if oldBp.ID > maxID {
+			maxID = oldBp.ID
+		}
 		if len(oldBp.File) > 0 {
 			addrs, err := proc.FindFileLocation(p, oldBp.File, oldBp.Line)
 			if err != nil {
 				discarded = append(discarded, api.DiscardedBreakpoint{Breakpoint: oldBp, Reason: err.Error()})
 				continue
 			}
-			createLogicalBreakpoint(d, addrs, oldBp)
+			createLogicalBreakpoint(d, addrs, oldBp, oldBp.ID)
 		} else {
 			// Avoid setting a breakpoint based on address when rebuilding
 			if rebuild {
 				continue
 			}
-			newBp, err := p.SetBreakpoint(oldBp.Addr, proc.UserBreakpoint, nil)
+			newBp, err := p.SetBreakpointWithID(oldBp.ID, oldBp.Addr)
 			if err != nil {
 				return nil, err
 			}
@@ -536,6 +538,12 @@ func (d *Debugger) Restart(rerecord bool, pos string, resetArgs bool, newArgs []
 			}
 		}
 	}
+	for _, bp := range d.disabledBreakpoints {
+		if bp.ID > maxID {
+			maxID = bp.ID
+		}
+	}
+	d.target.SetNextBreakpointID(maxID)
 	return discarded, nil
 }
 
@@ -576,7 +584,7 @@ func (d *Debugger) state(retLoadCfg *proc.LoadConfig) (*api.DebuggerState, error
 
 	exited := false
 	if _, err := d.target.Valid(); err != nil {
-		_, exited = err.(*proc.ErrProcessExited)
+		_, exited = err.(proc.ErrProcessExited)
 	}
 
 	state = &api.DebuggerState{
@@ -654,7 +662,7 @@ func (d *Debugger) CreateBreakpoint(requestedBp *api.Breakpoint) (*api.Breakpoin
 		return nil, err
 	}
 
-	createdBp, err := createLogicalBreakpoint(d, addrs, requestedBp)
+	createdBp, err := createLogicalBreakpoint(d, addrs, requestedBp, 0)
 	if err != nil {
 		return nil, err
 	}
@@ -664,7 +672,7 @@ func (d *Debugger) CreateBreakpoint(requestedBp *api.Breakpoint) (*api.Breakpoin
 
 // createLogicalBreakpoint creates one physical breakpoint for each address
 // in addrs and associates all of them with the same logical breakpoint.
-func createLogicalBreakpoint(d *Debugger, addrs []uint64, requestedBp *api.Breakpoint) (*api.Breakpoint, error) {
+func createLogicalBreakpoint(d *Debugger, addrs []uint64, requestedBp *api.Breakpoint, id int) (*api.Breakpoint, error) {
 	p := d.target
 
 	if dbp, ok := d.disabledBreakpoints[requestedBp.ID]; ok {
@@ -674,7 +682,11 @@ func createLogicalBreakpoint(d *Debugger, addrs []uint64, requestedBp *api.Break
 	bps := make([]*proc.Breakpoint, len(addrs))
 	var err error
 	for i := range addrs {
-		bps[i], err = p.SetBreakpoint(addrs[i], proc.UserBreakpoint, nil)
+		if id > 0 {
+			bps[i], err = p.SetBreakpointWithID(id, addrs[i])
+		} else {
+			bps[i], err = p.SetBreakpoint(addrs[i], proc.UserBreakpoint, nil)
+		}
 		if err != nil {
 			break
 		}
@@ -873,7 +885,7 @@ func (d *Debugger) FindBreakpoint(id int) *api.Breakpoint {
 func (d *Debugger) findBreakpoint(id int) []*proc.Breakpoint {
 	var bps []*proc.Breakpoint
 	for _, bp := range d.target.Breakpoints().M {
-		if bp.LogicalID == id {
+		if bp.IsUser() && bp.LogicalID == id {
 			bps = append(bps, bp)
 		}
 	}
@@ -1083,7 +1095,8 @@ func (d *Debugger) Command(command *api.DebuggerCommand, resumeNotify chan struc
 		withBreakpointInfo = false
 	case api.SwitchGoroutine:
 		d.log.Debugf("switching to goroutine %d", command.GoroutineID)
-		g, err := proc.FindGoroutine(d.target, command.GoroutineID)
+		var g *proc.G
+		g, err = proc.FindGoroutine(d.target, command.GoroutineID)
 		if err == nil {
 			err = d.target.SwitchGoroutine(g)
 		}
@@ -1300,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.
diff --git a/service/rpc1/server.go b/service/rpc1/server.go
index da3e3c8d..558594dd 100644
--- a/service/rpc1/server.go
+++ b/service/rpc1/server.go
@@ -35,11 +35,7 @@ func (s *RPCServer) ProcessPid(arg1 interface{}, pid *int) error {
 }
 
 func (s *RPCServer) Detach(kill bool, ret *int) error {
-	err := s.debugger.Detach(kill)
-	if s.config.DisconnectChan != nil {
-		close(s.config.DisconnectChan)
-	}
-	return err
+	return s.debugger.Detach(kill)
 }
 
 func (s *RPCServer) Restart(arg1 interface{}, arg2 *int) error {
diff --git a/service/rpc2/server.go b/service/rpc2/server.go
index a6f7bc73..81ee8aac 100644
--- a/service/rpc2/server.go
+++ b/service/rpc2/server.go
@@ -58,12 +58,7 @@ type DetachOut struct {
 
 // Detach detaches the debugger, optionally killing the process.
 func (s *RPCServer) Detach(arg DetachIn, out *DetachOut) error {
-	err := s.debugger.Detach(arg.Kill)
-	if s.config.DisconnectChan != nil {
-		close(s.config.DisconnectChan)
-		s.config.DisconnectChan = nil
-	}
-	return err
+	return s.debugger.Detach(arg.Kill)
 }
 
 type RestartIn struct {
diff --git a/service/rpccommon/server.go b/service/rpccommon/server.go
index d3eb47dc..affdd114 100644
--- a/service/rpccommon/server.go
+++ b/service/rpccommon/server.go
@@ -89,8 +89,8 @@ func NewServer(config *service.Config) *ServerImpl {
 
 // Stop stops the JSON-RPC server.
 func (s *ServerImpl) Stop() error {
+	close(s.stopChan)
 	if s.config.AcceptMulti {
-		close(s.stopChan)
 		s.listener.Close()
 	}
 	kill := s.config.Debugger.AttachPid == 0
@@ -323,6 +323,10 @@ func (s *ServerImpl) serveJSONCodec(conn io.ReadWriteCloser) {
 				s.log.Debugf("-> %T%s error: %q", replyv.Interface(), replyvbytes, errmsg)
 			}
 			s.sendResponse(sending, &req, &resp, replyv.Interface(), codec, errmsg)
+			if req.ServiceMethod == "RPCServer.Detach" && s.config.DisconnectChan != nil {
+				close(s.config.DisconnectChan)
+				s.config.DisconnectChan = nil
+			}
 		} else {
 			if logflags.RPC() {
 				argvbytes, _ := json.Marshal(argv.Interface())


Test patch:
diff --git a/Documentation/backend_test_health.md b/Documentation/backend_test_health.md
index c79bd2a8..e49f5b20 100644
--- a/Documentation/backend_test_health.md
+++ b/Documentation/backend_test_health.md
@@ -1,23 +1,23 @@
 Tests skipped by each supported backend:
 
-* 386 skipped = 2.7% (4/149)
+* 386 skipped = 2.7% (4/147)
 	* 1 broken
 	* 3 broken - cgo stacktraces
-* arm64 skipped = 2.7% (4/149)
-	* 2 broken
+* arm64 skipped = 2% (3/147)
+	* 1 broken
 	* 1 broken - cgo stacktraces
 	* 1 broken - global variable symbolication
-* darwin/arm64 skipped = 0.67% (1/149)
+* darwin/arm64 skipped = 0.68% (1/147)
 	* 1 broken - cgo stacktraces
-* darwin/lldb skipped = 0.67% (1/149)
+* darwin/lldb skipped = 0.68% (1/147)
 	* 1 upstream issue
-* freebsd skipped = 8.1% (12/149)
+* freebsd skipped = 8.2% (12/147)
 	* 11 broken
 	* 1 not implemented
-* linux/386/pie skipped = 0.67% (1/149)
+* linux/386/pie skipped = 0.68% (1/147)
 	* 1 broken
-* pie skipped = 0.67% (1/149)
+* pie skipped = 0.68% (1/147)
 	* 1 upstream issue - https://github.com/golang/go/issues/29322
-* windows skipped = 1.3% (2/149)
+* windows skipped = 1.4% (2/147)
 	* 1 broken
 	* 1 upstream issue
diff --git a/_scripts/test_linux.sh b/_scripts/test_linux.sh
index 387a1345..2125af5f 100755
--- a/_scripts/test_linux.sh
+++ b/_scripts/test_linux.sh
@@ -3,7 +3,7 @@ set -e
 set -x
 
 apt-get -qq update
-apt-get install -y dwz wget make git gcc curl jq
+apt-get install -y dwz wget make git gcc curl jq lsof
 dwz --version
 
 version=$1
diff --git a/pkg/dwarf/line/state_machine_test.go b/pkg/dwarf/line/state_machine_test.go
index cea41693..24e62762 100644
--- a/pkg/dwarf/line/state_machine_test.go
+++ b/pkg/dwarf/line/state_machine_test.go
@@ -30,11 +30,6 @@ func slurpGzip(path string) ([]byte, error) {
 	return ioutil.ReadAll(gzin)
 }
 
-const (
-	newCompileUnit = "NEW COMPILE UNIT"
-	debugLineEnd   = "END"
-)
-
 func TestGrafana(t *testing.T) {
 	// Compares a full execution of our state machine on the debug_line section
 	// of grafana to the output generated using debug/dwarf.LineReader on the
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 c7935dac..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
@@ -321,9 +321,6 @@ func TestIssue1419(t *testing.T) {
 }
 
 func TestLocationCovers(t *testing.T) {
-	const before = 0x1234
-	const after = 0x4321
-
 	dwb := dwarfbuilder.New()
 
 	uint16off := dwb.AddBaseType("uint16", dwarfbuilder.DW_ATE_unsigned, 2)
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 c25f7799..36144115 100644
--- a/pkg/proc/proc_test.go
+++ b/pkg/proc/proc_test.go
@@ -1310,23 +1310,9 @@ func TestFrameEvaluation(t *testing.T) {
 			found[vval] = true
 		}
 
-		firsterr := false
-		if goversion.VersionAfterOrEqual(runtime.Version(), 1, 14) {
-			// We try to make sure that all goroutines are stopped at a sensible place
-			// before reading their stacktrace, but due to the nature of the test
-			// program there is no guarantee that we always find them in a reasonable
-			// state.
-			// Asynchronous preemption in Go 1.14 exacerbates this problem, to avoid
-			// unnecessary flakiness allow a single goroutine to be in a bad state.
-			firsterr = true
-		}
 		for i := range found {
 			if !found[i] {
-				if firsterr {
-					firsterr = false
-				} else {
-					t.Fatalf("Goroutine %d not found\n", i)
-				}
+				t.Fatalf("Goroutine %d not found\n", i)
 			}
 		}
 
@@ -1734,24 +1720,6 @@ func TestStepIntoFunction(t *testing.T) {
 	})
 }
 
-func TestIssue384(t *testing.T) {
-	// Crash related to reading uninitialized memory, introduced by the memory prefetching optimization
-
-	ver, _ := goversion.Parse(runtime.Version())
-	if ver.Major < 0 || ver.AfterOrEqual(goversion.GoVersion{Major: 1, Minor: 10, Rev: -1}) {
-		// go 1.10 emits DW_AT_decl_line and we won't be able to evaluate 'st'
-		// which is declared after line 13.
-		t.Skip("can not evaluate not-yet-declared variables with go 1.10")
-	}
-
-	protest.AllowRecording(t)
-	withTestProcess("issue384", t, func(p *proc.Target, fixture protest.Fixture) {
-		setFileBreakpoint(p, t, fixture.Source, 13)
-		assertNoError(p.Continue(), t, "Continue()")
-		evalVariable(p, t, "st")
-	})
-}
-
 func TestIssue332_Part1(t *testing.T) {
 	// Next shouldn't step inside a function call
 	protest.AllowRecording(t)
@@ -1817,17 +1785,6 @@ func TestIssue332_Part2(t *testing.T) {
 	})
 }
 
-func TestIssue396(t *testing.T) {
-	if goversion.VersionAfterOrEqual(runtime.Version(), 1, 13) {
-		// CL 161337 in Go 1.13 and later removes the autogenerated init function
-		// https://go-review.googlesource.com/c/go/+/161337
-		t.Skip("no autogenerated init function in Go 1.13 or later")
-	}
-	withTestProcess("callme", t, func(p *proc.Target, fixture protest.Fixture) {
-		findFunctionLocation(p, t, "main.init")
-	})
-}
-
 func TestIssue414(t *testing.T) {
 	skipOn(t, "broken", "linux", "386", "pie") // test occasionally hangs on linux/386/pie
 	// Stepping until the program exits
@@ -2507,8 +2464,6 @@ func TestStepOutDeferReturnAndDirectCall(t *testing.T) {
 		{contStepout, 28}})
 }
 
-var maxInstructionLength uint64
-
 func TestStepOnCallPtrInstr(t *testing.T) {
 	protest.AllowRecording(t)
 	withTestProcess("teststepprog", t, func(p *proc.Target, fixture protest.Fixture) {
@@ -2824,9 +2779,6 @@ func TestAttachDetach(t *testing.T) {
 	if testBackend == "rr" {
 		return
 	}
-	if os.Getenv("CI") == "true" {
-		t.Skip("disabled on Github actions")
-	}
 	var buildFlags protest.BuildFlags
 	if buildMode == "pie" {
 		buildFlags |= protest.BuildModePIE
@@ -2878,12 +2830,16 @@ func TestAttachDetach(t *testing.T) {
 
 	assertNoError(p.Detach(false), t, "Detach")
 
-	resp, err := http.Get("http://127.0.0.1:9191/nobp")
-	assertNoError(err, t, "Page request after detach")
-	bs, err := ioutil.ReadAll(resp.Body)
-	assertNoError(err, t, "Reading /nobp page")
-	if out := string(bs); !strings.Contains(out, "hello, world!") {
-		t.Fatalf("/nobp page does not contain \"hello, world!\": %q", out)
+	if runtime.GOOS != "darwin" {
+		// Debugserver sometimes will leave a zombie process after detaching, this
+		// seems to be a bug with debugserver.
+		resp, err := http.Get("http://127.0.0.1:9191/nobp")
+		assertNoError(err, t, "Page request after detach")
+		bs, err := ioutil.ReadAll(resp.Body)
+		assertNoError(err, t, "Reading /nobp page")
+		if out := string(bs); !strings.Contains(out, "hello, world!") {
+			t.Fatalf("/nobp page does not contain \"hello, world!\": %q", out)
+		}
 	}
 
 	cmd.Process.Kill()
@@ -3960,7 +3916,6 @@ func TestIssue951(t *testing.T) {
 }
 
 func TestDWZCompression(t *testing.T) {
-	skipOn(t, "broken", "arm64")
 	// If dwz is not available in the system, skip this test
 	if _, err := exec.LookPath("dwz"); err != nil {
 		t.Skip("dwz not installed")
@@ -5039,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/pkg/terminal/command_test.go b/pkg/terminal/command_test.go
index 14069a5a..86fcac1d 100644
--- a/pkg/terminal/command_test.go
+++ b/pkg/terminal/command_test.go
@@ -254,9 +254,6 @@ func TestIssue354(t *testing.T) {
 }
 
 func TestIssue411(t *testing.T) {
-	if runtime.GOARCH == "arm64" {
-		t.Skip("test is not valid on ARM64")
-	}
 	test.AllowRecording(t)
 	withTestTerminal("math", t, func(term *FakeTerminal) {
 		term.MustExec("break _fixtures/math.go:8")
@@ -270,9 +267,6 @@ func TestIssue411(t *testing.T) {
 }
 
 func TestTrace(t *testing.T) {
-	if runtime.GOARCH == "arm64" {
-		t.Skip("test is not valid on ARM64")
-	}
 	test.AllowRecording(t)
 	withTestTerminal("issue573", t, func(term *FakeTerminal) {
 		term.MustExec("trace foo")
@@ -289,9 +283,6 @@ func TestTrace(t *testing.T) {
 }
 
 func TestTraceWithName(t *testing.T) {
-	if runtime.GOARCH == "arm64" {
-		t.Skip("test is not valid on ARM64")
-	}
 	test.AllowRecording(t)
 	withTestTerminal("issue573", t, func(term *FakeTerminal) {
 		term.MustExec("trace foobar foo")
@@ -308,9 +299,6 @@ func TestTraceWithName(t *testing.T) {
 }
 
 func TestTraceOnNonFunctionEntry(t *testing.T) {
-	if runtime.GOARCH == "arm64" {
-		t.Skip("test is not valid on ARM64")
-	}
 	test.AllowRecording(t)
 	withTestTerminal("issue573", t, func(term *FakeTerminal) {
 		term.MustExec("trace foobar issue573.go:19")
@@ -480,9 +468,6 @@ func TestScopePrefix(t *testing.T) {
 }
 
 func TestOnPrefix(t *testing.T) {
-	if runtime.GOARCH == "arm64" {
-		t.Skip("test is not valid on ARM64")
-	}
 	if runtime.GOOS == "freebsd" {
 		t.Skip("test is not valid on FreeBSD")
 	}
@@ -539,9 +524,6 @@ func TestNoVars(t *testing.T) {
 }
 
 func TestOnPrefixLocals(t *testing.T) {
-	if runtime.GOARCH == "arm64" {
-		t.Skip("test is not valid on ARM64")
-	}
 	if runtime.GOOS == "freebsd" {
 		t.Skip("test is not valid on FreeBSD")
 	}
@@ -600,9 +582,6 @@ func countOccurrences(s, needle string) int {
 }
 
 func TestIssue387(t *testing.T) {
-	if runtime.GOARCH == "arm64" {
-		t.Skip("test is not valid on ARM64")
-	}
 	if runtime.GOOS == "freebsd" {
 		t.Skip("test is not valid on FreeBSD")
 	}
@@ -875,25 +854,6 @@ func TestConfig(t *testing.T) {
 	}
 }
 
-func TestDisassembleAutogenerated(t *testing.T) {
-	// Executing the 'disassemble' command on autogenerated code should work correctly
-
-	if goversion.VersionAfterOrEqual(runtime.Version(), 1, 13) {
-		// CL 161337 in Go 1.13 and later removes the autogenerated init function
-		// https://go-review.googlesource.com/c/go/+/161337
-		t.Skip("no autogenerated init function in Go 1.13 or later")
-	}
-
-	withTestTerminal("math", t, func(term *FakeTerminal) {
-		term.MustExec("break main.init")
-		term.MustExec("continue")
-		out := term.MustExec("disassemble")
-		if !strings.Contains(out, "TEXT main.init(SB) ") {
-			t.Fatalf("output of disassemble wasn't for the main.init function %q", out)
-		}
-	})
-}
-
 func TestIssue1090(t *testing.T) {
 	// Exit while executing 'next' should report the "Process exited" error
 	// message instead of crashing.
@@ -1000,9 +960,6 @@ func TestTruncateStacktrace(t *testing.T) {
 }
 
 func TestIssue1493(t *testing.T) {
-	if runtime.GOARCH == "arm64" {
-		t.Skip("arm64 does not support FpRegs for now")
-	}
 	// The 'regs' command without the '-a' option should only return
 	// general purpose registers.
 	withTestTerminal("continuetestprog", t, func(term *FakeTerminal) {
@@ -1072,6 +1029,66 @@ func TestExamineMemoryCmd(t *testing.T) {
 		if !strings.Contains(res, firstLine) {
 			t.Fatalf("expected first line: %s", firstLine)
 		}
+
+		// third examining memory: -x addr
+		res = term.MustExec("examinemem -x " + addressStr)
+		t.Logf("the third result of examining memory result \n%s", res)
+		firstLine = fmt.Sprintf("%#x:   0xff", address)
+		if !strings.Contains(res, firstLine) {
+			t.Fatalf("expected first line: %s", firstLine)
+		}
+
+		// fourth examining memory: -x addr + offset
+		res = term.MustExec("examinemem -x " + addressStr + " + 8")
+		t.Logf("the fourth result of examining memory result \n%s", res)
+		firstLine = fmt.Sprintf("%#x:   0x12", address+8)
+		if !strings.Contains(res, firstLine) {
+			t.Fatalf("expected first line: %s", firstLine)
+		}
+		// fifth examining memory: -x &var
+		res = term.MustExec("examinemem -x &bs[0]")
+		t.Logf("the fifth result of examining memory result \n%s", res)
+		firstLine = fmt.Sprintf("%#x:   0xff", address)
+		if !strings.Contains(res, firstLine) {
+			t.Fatalf("expected first line: %s", firstLine)
+		}
+
+		// sixth examining memory: -fmt and double spaces
+		res = term.MustExec("examinemem -fmt  hex  -x &bs[0]")
+		t.Logf("the sixth result of examining memory result \n%s", res)
+		firstLine = fmt.Sprintf("%#x:   0xff", address)
+		if !strings.Contains(res, firstLine) {
+			t.Fatalf("expected first line: %s", firstLine)
+		}
+	})
+
+	withTestTerminal("testvariables2", t, func(term *FakeTerminal) {
+		tests := []struct {
+			Expr string
+			Want int
+		}{
+			{Expr: "&i1", Want: 1},
+			{Expr: "&i2", Want: 2},
+			{Expr: "p1", Want: 1},
+			{Expr: "*pp1", Want: 1},
+			{Expr: "&str1[1]", Want: '1'},
+			{Expr: "c1.pb", Want: 1},
+			{Expr: "&c1.pb.a", Want: 1},
+			{Expr: "&c1.pb.a.A", Want: 1},
+			{Expr: "&c1.pb.a.B", Want: 2},
+		}
+		term.MustExec("continue")
+		for _, test := range tests {
+			res := term.MustExec("examinemem -fmt dec -x " + test.Expr)
+			// strip addr from output, e.g. "0xc0000160b8:   023" -> "023"
+			res = strings.TrimSpace(strings.Split(res, ":")[1])
+			got, err := strconv.Atoi(res)
+			if err != nil {
+				t.Fatalf("expr=%q err=%s", test.Expr, err)
+			} else if got != test.Want {
+				t.Errorf("expr=%q got=%d want=%d", test.Expr, got, test.Want)
+			}
+		}
 	})
 }
 
diff --git a/pkg/terminal/starlark_test.go b/pkg/terminal/starlark_test.go
index b96b5f15..1cdee0cb 100644
--- a/pkg/terminal/starlark_test.go
+++ b/pkg/terminal/starlark_test.go
@@ -2,15 +2,11 @@ package terminal
 
 import (
 	"fmt"
-	"runtime"
 	"strings"
 	"testing"
 )
 
 func TestStarlarkExamples(t *testing.T) {
-	if runtime.GOARCH == "arm64" {
-		t.Skip("test is not valid on ARM64")
-	}
 	withTestTerminal("testvariables2", t, func(term *FakeTerminal) {
 		term.MustExec("continue")
 		t.Run("goroutine_start_line", func(t *testing.T) { testStarlarkExampleGoroutineStartLine(t, term) })
diff --git a/service/dap/daptest/client.go b/service/dap/daptest/client.go
index 70c1d22e..e2390c8f 100644
--- a/service/dap/daptest/client.go
+++ b/service/dap/daptest/client.go
@@ -8,6 +8,7 @@ import (
 	"log"
 	"net"
 	"path/filepath"
+	"regexp"
 	"testing"
 
 	"github.com/google/go-dap"
@@ -50,18 +51,18 @@ func (c *Client) ReadMessage() (dap.Message, error) {
 	return dap.ReadProtocolMessage(c.reader)
 }
 
-func (c *Client) expectReadProtocolMessage(t *testing.T) dap.Message {
+func (c *Client) ExpectMessage(t *testing.T) dap.Message {
 	t.Helper()
 	m, err := dap.ReadProtocolMessage(c.reader)
 	if err != nil {
-		t.Error(err)
+		t.Fatal(err)
 	}
 	return m
 }
 
 func (c *Client) ExpectErrorResponse(t *testing.T) *dap.ErrorResponse {
 	t.Helper()
-	er := c.expectReadProtocolMessage(t).(*dap.ErrorResponse)
+	er := c.ExpectMessage(t).(*dap.ErrorResponse)
 	if er.Body.Error.ShowUser {
 		t.Errorf("\ngot %#v\nwant ShowUser=false", er)
 	}
@@ -70,7 +71,7 @@ func (c *Client) ExpectErrorResponse(t *testing.T) *dap.ErrorResponse {
 
 func (c *Client) ExpectVisibleErrorResponse(t *testing.T) *dap.ErrorResponse {
 	t.Helper()
-	er := c.expectReadProtocolMessage(t).(*dap.ErrorResponse)
+	er := c.ExpectMessage(t).(*dap.ErrorResponse)
 	if !er.Body.Error.ShowUser {
 		t.Errorf("\ngot %#v\nwant ShowUser=true", er)
 	}
@@ -79,7 +80,7 @@ func (c *Client) ExpectVisibleErrorResponse(t *testing.T) *dap.ErrorResponse {
 
 func (c *Client) expectErrorResponse(t *testing.T, id int, message string) *dap.ErrorResponse {
 	t.Helper()
-	er := c.expectReadProtocolMessage(t).(*dap.ErrorResponse)
+	er := c.ExpectMessage(t).(*dap.ErrorResponse)
 	if er.Body.Error.Id != id || er.Message != message {
 		t.Errorf("\ngot %#v\nwant Id=%d Message=%q", er, id, message)
 	}
@@ -98,37 +99,37 @@ func (c *Client) ExpectUnsupportedCommandErrorResponse(t *testing.T) *dap.ErrorR
 
 func (c *Client) ExpectDisconnectResponse(t *testing.T) *dap.DisconnectResponse {
 	t.Helper()
-	return c.expectReadProtocolMessage(t).(*dap.DisconnectResponse)
+	return c.ExpectMessage(t).(*dap.DisconnectResponse)
 }
 
 func (c *Client) ExpectContinueResponse(t *testing.T) *dap.ContinueResponse {
 	t.Helper()
-	return c.expectReadProtocolMessage(t).(*dap.ContinueResponse)
+	return c.ExpectMessage(t).(*dap.ContinueResponse)
 }
 
 func (c *Client) ExpectNextResponse(t *testing.T) *dap.NextResponse {
 	t.Helper()
-	return c.expectReadProtocolMessage(t).(*dap.NextResponse)
+	return c.ExpectMessage(t).(*dap.NextResponse)
 }
 
 func (c *Client) ExpectStepInResponse(t *testing.T) *dap.StepInResponse {
 	t.Helper()
-	return c.expectReadProtocolMessage(t).(*dap.StepInResponse)
+	return c.ExpectMessage(t).(*dap.StepInResponse)
 }
 
 func (c *Client) ExpectStepOutResponse(t *testing.T) *dap.StepOutResponse {
 	t.Helper()
-	return c.expectReadProtocolMessage(t).(*dap.StepOutResponse)
+	return c.ExpectMessage(t).(*dap.StepOutResponse)
 }
 
 func (c *Client) ExpectTerminatedEvent(t *testing.T) *dap.TerminatedEvent {
 	t.Helper()
-	return c.expectReadProtocolMessage(t).(*dap.TerminatedEvent)
+	return c.ExpectMessage(t).(*dap.TerminatedEvent)
 }
 
 func (c *Client) ExpectInitializeResponse(t *testing.T) *dap.InitializeResponse {
 	t.Helper()
-	initResp := c.expectReadProtocolMessage(t).(*dap.InitializeResponse)
+	initResp := c.ExpectMessage(t).(*dap.InitializeResponse)
 	if !initResp.Body.SupportsConfigurationDoneRequest {
 		t.Errorf("got %#v, want SupportsConfigurationDoneRequest=true", initResp)
 	}
@@ -137,167 +138,196 @@ func (c *Client) ExpectInitializeResponse(t *testing.T) *dap.InitializeResponse
 
 func (c *Client) ExpectInitializedEvent(t *testing.T) *dap.InitializedEvent {
 	t.Helper()
-	return c.expectReadProtocolMessage(t).(*dap.InitializedEvent)
+	return c.ExpectMessage(t).(*dap.InitializedEvent)
 }
 
 func (c *Client) ExpectLaunchResponse(t *testing.T) *dap.LaunchResponse {
 	t.Helper()
-	return c.expectReadProtocolMessage(t).(*dap.LaunchResponse)
+	return c.ExpectMessage(t).(*dap.LaunchResponse)
 }
 
 func (c *Client) ExpectAttachResponse(t *testing.T) *dap.AttachResponse {
 	t.Helper()
-	return c.expectReadProtocolMessage(t).(*dap.AttachResponse)
+	return c.ExpectMessage(t).(*dap.AttachResponse)
 }
 
 func (c *Client) ExpectSetExceptionBreakpointsResponse(t *testing.T) *dap.SetExceptionBreakpointsResponse {
 	t.Helper()
-	return c.expectReadProtocolMessage(t).(*dap.SetExceptionBreakpointsResponse)
+	return c.ExpectMessage(t).(*dap.SetExceptionBreakpointsResponse)
 }
 
 func (c *Client) ExpectSetBreakpointsResponse(t *testing.T) *dap.SetBreakpointsResponse {
 	t.Helper()
-	return c.expectReadProtocolMessage(t).(*dap.SetBreakpointsResponse)
+	return c.ExpectMessage(t).(*dap.SetBreakpointsResponse)
 }
 
 func (c *Client) ExpectStoppedEvent(t *testing.T) *dap.StoppedEvent {
 	t.Helper()
-	return c.expectReadProtocolMessage(t).(*dap.StoppedEvent)
+	return c.ExpectMessage(t).(*dap.StoppedEvent)
 }
 
 func (c *Client) ExpectOutputEvent(t *testing.T) *dap.OutputEvent {
 	t.Helper()
-	return c.expectReadProtocolMessage(t).(*dap.OutputEvent)
+	return c.ExpectMessage(t).(*dap.OutputEvent)
+}
+
+func (c *Client) ExpectOutputEventRegex(t *testing.T, want string) *dap.OutputEvent {
+	t.Helper()
+	e := c.ExpectMessage(t).(*dap.OutputEvent)
+	if matched, _ := regexp.MatchString(want, e.Body.Output); !matched {
+		t.Errorf("\ngot %#v\nwant Output=%q", e, want)
+	}
+	return e
+}
+
+func (c *Client) ExpectOutputEventProcessExited(t *testing.T, status int) *dap.OutputEvent {
+	t.Helper()
+	return c.ExpectOutputEventRegex(t, fmt.Sprintf(`Process [0-9]+ has exited with status %d\n`, status))
+}
+
+func (c *Client) ExpectOutputEventDetaching(t *testing.T) *dap.OutputEvent {
+	t.Helper()
+	return c.ExpectOutputEventRegex(t, `Detaching\n`)
+}
+
+func (c *Client) ExpectOutputEventDetachingKill(t *testing.T) *dap.OutputEvent {
+	t.Helper()
+	return c.ExpectOutputEventRegex(t, `Detaching and terminating target process\n`)
+}
+
+func (c *Client) ExpectOutputEventDetachingNoKill(t *testing.T) *dap.OutputEvent {
+	t.Helper()
+	return c.ExpectOutputEventRegex(t, `Detaching without terminating target process\n`)
 }
 
 func (c *Client) ExpectConfigurationDoneResponse(t *testing.T) *dap.ConfigurationDoneResponse {
 	t.Helper()
-	return c.expectReadProtocolMessage(t).(*dap.ConfigurationDoneResponse)
+	return c.ExpectMessage(t).(*dap.ConfigurationDoneResponse)
 }
 
 func (c *Client) ExpectThreadsResponse(t *testing.T) *dap.ThreadsResponse {
 	t.Helper()
-	return c.expectReadProtocolMessage(t).(*dap.ThreadsResponse)
+	return c.ExpectMessage(t).(*dap.ThreadsResponse)
 }
 
 func (c *Client) ExpectStackTraceResponse(t *testing.T) *dap.StackTraceResponse {
 	t.Helper()
-	return c.expectReadProtocolMessage(t).(*dap.StackTraceResponse)
+	return c.ExpectMessage(t).(*dap.StackTraceResponse)
 }
 
 func (c *Client) ExpectScopesResponse(t *testing.T) *dap.ScopesResponse {
 	t.Helper()
-	return c.expectReadProtocolMessage(t).(*dap.ScopesResponse)
+	return c.ExpectMessage(t).(*dap.ScopesResponse)
 }
 
 func (c *Client) ExpectVariablesResponse(t *testing.T) *dap.VariablesResponse {
 	t.Helper()
-	return c.expectReadProtocolMessage(t).(*dap.VariablesResponse)
+	return c.ExpectMessage(t).(*dap.VariablesResponse)
 }
 
 func (c *Client) ExpectEvaluateResponse(t *testing.T) *dap.EvaluateResponse {
 	t.Helper()
-	return c.expectReadProtocolMessage(t).(*dap.EvaluateResponse)
+	return c.ExpectMessage(t).(*dap.EvaluateResponse)
 }
 
 func (c *Client) ExpectTerminateResponse(t *testing.T) *dap.TerminateResponse {
 	t.Helper()
-	return c.expectReadProtocolMessage(t).(*dap.TerminateResponse)
+	return c.ExpectMessage(t).(*dap.TerminateResponse)
 }
 
 func (c *Client) ExpectRestartResponse(t *testing.T) *dap.RestartResponse {
 	t.Helper()
-	return c.expectReadProtocolMessage(t).(*dap.RestartResponse)
+	return c.ExpectMessage(t).(*dap.RestartResponse)
 }
 
 func (c *Client) ExpectSetFunctionBreakpointsResponse(t *testing.T) *dap.SetFunctionBreakpointsResponse {
 	t.Helper()
-	return c.expectReadProtocolMessage(t).(*dap.SetFunctionBreakpointsResponse)
+	return c.ExpectMessage(t).(*dap.SetFunctionBreakpointsResponse)
 }
 
 func (c *Client) ExpectStepBackResponse(t *testing.T) *dap.StepBackResponse {
 	t.Helper()
-	return c.expectReadProtocolMessage(t).(*dap.StepBackResponse)
+	return c.ExpectMessage(t).(*dap.StepBackResponse)
 }
 
 func (c *Client) ExpectReverseContinueResponse(t *testing.T) *dap.ReverseContinueResponse {
 	t.Helper()
-	return c.expectReadProtocolMessage(t).(*dap.ReverseContinueResponse)
+	return c.ExpectMessage(t).(*dap.ReverseContinueResponse)
 }
 
 func (c *Client) ExpectRestartFrameResponse(t *testing.T) *dap.RestartFrameResponse {
 	t.Helper()
-	return c.expectReadProtocolMessage(t).(*dap.RestartFrameResponse)
+	return c.ExpectMessage(t).(*dap.RestartFrameResponse)
 }
 
 func (c *Client) ExpectSetExpressionResponse(t *testing.T) *dap.SetExpressionResponse {
 	t.Helper()
-	return c.expectReadProtocolMessage(t).(*dap.SetExpressionResponse)
+	return c.ExpectMessage(t).(*dap.SetExpressionResponse)
 }
 
 func (c *Client) ExpectTerminateThreadsResponse(t *testing.T) *dap.TerminateThreadsResponse {
 	t.Helper()
-	return c.expectReadProtocolMessage(t).(*dap.TerminateThreadsResponse)
+	return c.ExpectMessage(t).(*dap.TerminateThreadsResponse)
 }
 
 func (c *Client) ExpectStepInTargetsResponse(t *testing.T) *dap.StepInTargetsResponse {
 	t.Helper()
-	return c.expectReadProtocolMessage(t).(*dap.StepInTargetsResponse)
+	return c.ExpectMessage(t).(*dap.StepInTargetsResponse)
 }
 
 func (c *Client) ExpectGotoTargetsResponse(t *testing.T) *dap.GotoTargetsResponse {
 	t.Helper()
-	return c.expectReadProtocolMessage(t).(*dap.GotoTargetsResponse)
+	return c.ExpectMessage(t).(*dap.GotoTargetsResponse)
 }
 
 func (c *Client) ExpectCompletionsResponse(t *testing.T) *dap.CompletionsResponse {
 	t.Helper()
-	return c.expectReadProtocolMessage(t).(*dap.CompletionsResponse)
+	return c.ExpectMessage(t).(*dap.CompletionsResponse)
 }
 
 func (c *Client) ExpectExceptionInfoResponse(t *testing.T) *dap.ExceptionInfoResponse {
 	t.Helper()
-	return c.expectReadProtocolMessage(t).(*dap.ExceptionInfoResponse)
+	return c.ExpectMessage(t).(*dap.ExceptionInfoResponse)
 }
 
 func (c *Client) ExpectLoadedSourcesResponse(t *testing.T) *dap.LoadedSourcesResponse {
 	t.Helper()
-	return c.expectReadProtocolMessage(t).(*dap.LoadedSourcesResponse)
+	return c.ExpectMessage(t).(*dap.LoadedSourcesResponse)
 }
 
 func (c *Client) ExpectDataBreakpointInfoResponse(t *testing.T) *dap.DataBreakpointInfoResponse {
 	t.Helper()
-	return c.expectReadProtocolMessage(t).(*dap.DataBreakpointInfoResponse)
+	return c.ExpectMessage(t).(*dap.DataBreakpointInfoResponse)
 }
 
 func (c *Client) ExpectSetDataBreakpointsResponse(t *testing.T) *dap.SetDataBreakpointsResponse {
 	t.Helper()
-	return c.expectReadProtocolMessage(t).(*dap.SetDataBreakpointsResponse)
+	return c.ExpectMessage(t).(*dap.SetDataBreakpointsResponse)
 }
 
 func (c *Client) ExpectReadMemoryResponse(t *testing.T) *dap.ReadMemoryResponse {
 	t.Helper()
-	return c.expectReadProtocolMessage(t).(*dap.ReadMemoryResponse)
+	return c.ExpectMessage(t).(*dap.ReadMemoryResponse)
 }
 
 func (c *Client) ExpectDisassembleResponse(t *testing.T) *dap.DisassembleResponse {
 	t.Helper()
-	return c.expectReadProtocolMessage(t).(*dap.DisassembleResponse)
+	return c.ExpectMessage(t).(*dap.DisassembleResponse)
 }
 
 func (c *Client) ExpectCancelResponse(t *testing.T) *dap.CancelResponse {
 	t.Helper()
-	return c.expectReadProtocolMessage(t).(*dap.CancelResponse)
+	return c.ExpectMessage(t).(*dap.CancelResponse)
 }
 
 func (c *Client) ExpectBreakpointLocationsResponse(t *testing.T) *dap.BreakpointLocationsResponse {
 	t.Helper()
-	return c.expectReadProtocolMessage(t).(*dap.BreakpointLocationsResponse)
+	return c.ExpectMessage(t).(*dap.BreakpointLocationsResponse)
 }
 
 func (c *Client) ExpectModulesResponse(t *testing.T) *dap.ModulesResponse {
 	t.Helper()
-	return c.expectReadProtocolMessage(t).(*dap.ModulesResponse)
+	return c.ExpectMessage(t).(*dap.ModulesResponse)
 }
 
 // InitializeRequest sends an 'initialize' request.
@@ -317,7 +347,7 @@ func (c *Client) InitializeRequest() {
 }
 
 // LaunchRequest sends a 'launch' request with the specified args.
-func (c *Client) LaunchRequest(mode string, program string, stopOnEntry bool) {
+func (c *Client) LaunchRequest(mode, program string, stopOnEntry bool) {
 	request := &dap.LaunchRequest{Request: *c.newRequest("launch")}
 	request.Arguments = map[string]interface{}{
 		"request":     "launch",
diff --git a/service/dap/server_test.go b/service/dap/server_test.go
index 6ae40e08..bd920d26 100644
--- a/service/dap/server_test.go
+++ b/service/dap/server_test.go
@@ -14,7 +14,6 @@ import (
 	"regexp"
 	"runtime"
 	"strings"
-	"sync"
 	"testing"
 	"time"
 
@@ -64,22 +63,19 @@ func runTest(t *testing.T, name string, test func(c *daptest.Client, f protest.F
 	// Give server time to start listening for clients
 	time.Sleep(100 * time.Millisecond)
 
-	var stopOnce sync.Once
 	// Run a goroutine that stops the server when disconnectChan is signaled.
 	// This helps us test that certain events cause the server to stop as
 	// expected.
 	go func() {
 		<-disconnectChan
-		stopOnce.Do(func() { server.Stop() })
+		server.Stop()
 	}()
 
 	client := daptest.NewClient(listener.Addr().String())
+	// This will close the client connectinon, which will cause a connection error
+	// on the server side and signal disconnect to unblock Stop() above.
 	defer client.Close()
 
-	defer func() {
-		stopOnce.Do(func() { server.Stop() })
-	}()
-
 	test(client, fixture)
 }
 
@@ -113,6 +109,8 @@ func runTest(t *testing.T, name string, test func(c *daptest.Client, f protest.F
 //                                 : 12 << continue
 // - Program runs to completion    :    << terminated event
 //                                 : 13 >> disconnect
+//                                 :    << output event (Process exited)
+//                                 :    << output event (Detaching)
 //                                 : 13 << disconnect
 // This test exhaustively tests Seq and RequestSeq on all messages from the
 // server. Other tests do not necessarily need to repeat all these checks.
@@ -197,7 +195,7 @@ func TestLaunchStopOnEntry(t *testing.T) {
 
 		// 10 >> evaluate, << error
 		client.EvaluateRequest("foo", 0 /*no frame specified*/, "repl")
-		erResp := client.ExpectVisibleErrorResponse(t)
+		erResp := client.ExpectErrorResponse(t)
 		if erResp.Seq != 0 || erResp.RequestSeq != 10 || erResp.Body.Error.Id != 2009 {
 			t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=10 Id=2009", erResp)
 		}
@@ -222,6 +220,14 @@ func TestLaunchStopOnEntry(t *testing.T) {
 
 		// 13 >> disconnect, << disconnect
 		client.DisconnectRequest()
+		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)
+		}
 		dResp := client.ExpectDisconnectResponse(t)
 		if dResp.Seq != 0 || dResp.RequestSeq != 13 {
 			t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=13", dResp)
@@ -323,7 +329,7 @@ func TestAttachStopOnEntry(t *testing.T) {
 
 		// 10 >> evaluate, << error
 		client.EvaluateRequest("foo", 0 /*no frame specified*/, "repl")
-		erResp := client.ExpectVisibleErrorResponse(t)
+		erResp := client.ExpectErrorResponse(t)
 		if erResp.Seq != 0 || erResp.RequestSeq != 10 || erResp.Body.Error.Id != 2009 {
 			t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=10 Id=2009", erResp)
 		}
@@ -344,6 +350,10 @@ func TestAttachStopOnEntry(t *testing.T) {
 
 		// 12 >> 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)
+		}
 		dResp := client.ExpectDisconnectResponse(t)
 		if dResp.Seq != 0 || dResp.RequestSeq != 12 {
 			t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=12", dResp)
@@ -398,6 +408,8 @@ func TestContinueOnEntry(t *testing.T) {
 
 		// 7 >> 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)
@@ -511,6 +523,8 @@ func TestPreSetBreakpoint(t *testing.T) {
 
 		client.ExpectTerminatedEvent(t)
 		client.DisconnectRequest()
+		client.ExpectOutputEventProcessExited(t, 0)
+		client.ExpectOutputEventDetaching(t)
 		client.ExpectDisconnectResponse(t)
 	})
 }
@@ -678,7 +692,6 @@ func validateEvaluateName(t *testing.T, client *daptest.Client, got *dap.Variabl
 func TestStackTraceRequest(t *testing.T) {
 	runTest(t, "increment", func(client *daptest.Client, fixture protest.Fixture) {
 		var stResp *dap.StackTraceResponse
-		const StartHandle = 1000 // from handles.go
 		runDebugSessionWithBPs(t, client, "launch",
 			// Launch
 			func() {
@@ -1562,6 +1575,46 @@ func TestGlobalScopeAndVariables(t *testing.T) {
 	})
 }
 
+// TestShadowedVariables executes to a breakpoint and checks the shadowed
+// variable is named correctly.
+func TestShadowedVariables(t *testing.T) {
+	runTest(t, "testshadow", func(client *daptest.Client, fixture protest.Fixture) {
+		runDebugSessionWithBPs(t, client, "launch",
+			// Launch
+			func() {
+				client.LaunchRequestWithArgs(map[string]interface{}{
+					"mode": "exec", "program": fixture.Path, "showGlobalVariables": true,
+				})
+			},
+			// Breakpoints are set within the program
+			fixture.Source, []int{},
+			[]onBreakpoint{{
+				// Stop at line 13
+				execute: func() {
+					client.StackTraceRequest(1, 0, 20)
+					stack := client.ExpectStackTraceResponse(t)
+					expectStackFrames(t, stack, "main.main", 13, 1000, 3, 3)
+
+					client.ScopesRequest(1000)
+					scopes := client.ExpectScopesResponse(t)
+					expectScope(t, scopes, 0, "Arguments", 1000)
+					expectScope(t, scopes, 1, "Locals", 1001)
+					expectScope(t, scopes, 2, "Globals (package main)", 1002)
+
+					client.VariablesRequest(1001)
+					locals := client.ExpectVariablesResponse(t)
+
+					expectVarExact(t, locals, 0, "(a)", "a", "0", !hasChildren)
+					expectVarExact(t, locals, 1, "a", "a", "1", !hasChildren)
+
+					// Check that the non-shadowed of "a" is returned from evaluate request.
+					validateEvaluateName(t, client, locals, 1)
+				},
+				disconnect: false,
+			}})
+	})
+}
+
 // Tests that 'stackTraceDepth' from LaunchRequest is parsed and passed to
 // stacktrace requests handlers.
 func TestLaunchRequestWithStackTraceDepth(t *testing.T) {
@@ -1686,6 +1739,95 @@ func TestSetBreakpoint(t *testing.T) {
 	})
 }
 
+// TestLaunchSubstitutePath sets a breakpoint using a path
+// that does not exist and expects the substitutePath attribute
+// in the launch configuration to take care of the mapping.
+func TestLaunchSubstitutePath(t *testing.T) {
+	runTest(t, "loopprog", func(client *daptest.Client, fixture protest.Fixture) {
+		substitutePathTestHelper(t, fixture, client, "launch", map[string]interface{}{"mode": "exec", "program": fixture.Path})
+	})
+}
+
+// TestAttachSubstitutePath sets a breakpoint using a path
+// that does not exist and expects the substitutePath attribute
+// in the launch configuration to take care of the mapping.
+func TestAttachSubstitutePath(t *testing.T) {
+	if runtime.GOOS == "freebsd" {
+		t.SkipNow()
+	}
+	if runtime.GOOS == "windows" {
+		t.Skip("test skipped on windows, see https://delve.beta.teamcity.com/project/Delve_windows for details")
+	}
+	runTest(t, "loopprog", func(client *daptest.Client, fixture protest.Fixture) {
+		cmd := execFixture(t, fixture)
+
+		substitutePathTestHelper(t, fixture, client, "attach", map[string]interface{}{"mode": "local", "processId": cmd.Process.Pid})
+	})
+}
+
+func substitutePathTestHelper(t *testing.T, fixture protest.Fixture, client *daptest.Client, request string, launchAttachConfig map[string]interface{}) {
+	t.Helper()
+	nonexistentDir := filepath.Join(string(filepath.Separator), "path", "that", "does", "not", "exist")
+	if runtime.GOOS == "windows" {
+		nonexistentDir = "C:" + nonexistentDir
+	}
+
+	launchAttachConfig["stopOnEntry"] = false
+	// The rules in 'substitutePath' will be applied as follows:
+	// - mapping paths from client to server:
+	//		The first rule["from"] to match a prefix of 'path' will be applied:
+	//			strings.Replace(path, rule["from"], rule["to"], 1)
+	// - mapping paths from server to client:
+	//		The first rule["to"] to match a prefix of 'path' will be applied:
+	//			strings.Replace(path, rule["to"], rule["from"], 1)
+	launchAttachConfig["substitutePath"] = []map[string]string{
+		{"from": nonexistentDir, "to": filepath.Dir(fixture.Source)},
+		// Since the path mappings are ordered, when converting from client path to
+		// server path, this mapping will not apply, because nonexistentDir appears in
+		// an earlier rule.
+		{"from": nonexistentDir, "to": "this_is_a_bad_path"},
+		// Since the path mappings are ordered, when converting from server path to
+		// client path, this mapping will not apply, because filepath.Dir(fixture.Source)
+		// appears in an earlier rule.
+		{"from": "this_is_a_bad_path", "to": filepath.Dir(fixture.Source)},
+	}
+
+	runDebugSessionWithBPs(t, client, request,
+		func() {
+			switch request {
+			case "attach":
+				client.AttachRequest(launchAttachConfig)
+			case "launch":
+				client.LaunchRequestWithArgs(launchAttachConfig)
+			default:
+				t.Fatalf("invalid request: %s", request)
+			}
+		},
+		// Set breakpoints
+		filepath.Join(nonexistentDir, "loopprog.go"), []int{8},
+		[]onBreakpoint{{
+
+			execute: func() {
+				handleStop(t, client, 1, "main.loop", 8)
+			},
+			disconnect: true,
+		}})
+}
+
+// execFixture runs the binary fixture.Path and hooks up stdout and stderr
+// to os.Stdout and os.Stderr.
+func execFixture(t *testing.T, fixture protest.Fixture) *exec.Cmd {
+	t.Helper()
+	// TODO(polina): do I need to sanity check testBackend and runtime.GOOS?
+	cmd := exec.Command(fixture.Path)
+	cmd.Stdout = os.Stdout
+	cmd.Stderr = os.Stderr
+	if err := cmd.Start(); err != nil {
+		t.Fatal(err)
+	}
+	return cmd
+}
+
 // TestWorkingDir executes to a breakpoint and tests that the specified
 // working directory is the one used to run the program.
 func TestWorkingDir(t *testing.T) {
@@ -1702,7 +1844,7 @@ func TestWorkingDir(t *testing.T) {
 					"mode":        "exec",
 					"program":     fixture.Path,
 					"stopOnEntry": false,
-					"wd":          wd,
+					"cwd":         wd,
 				})
 			},
 			// Set breakpoints
@@ -2019,8 +2161,8 @@ func TestEvaluateCallRequest(t *testing.T) {
 					}
 					// Call can exit
 					client.EvaluateRequest("call callexit()", 1000, "this context will be ignored")
-					client.ExpectTerminatedEvent(t)
 				},
+				terminated: true,
 				disconnect: true,
 			}})
 	})
@@ -2060,9 +2202,12 @@ func TestNextAndStep(t *testing.T) {
 					client.ExpectStepInResponse(t)
 					expectStop("main.inlineThis", 5)
 
-					client.NextRequest(-10000 /*this is ignored*/)
+					client.NextRequest(-1000)
 					client.ExpectNextResponse(t)
-					expectStop("main.inlineThis", 6)
+					if se := client.ExpectStoppedEvent(t); se.Body.Reason != "error" || se.Body.Text != "unknown goroutine -1000" {
+						t.Errorf("got %#v, want Reaspon=\"error\", Text=\"unknown goroutine -1000\"", se)
+					}
+					handleStop(t, client, 1, "main.inlineThis", 5)
 				},
 				disconnect: false,
 			}})
@@ -2176,6 +2321,8 @@ func testStepParkedHelper(t *testing.T, client *daptest.Client, fixture protest.
 	return goroutineId
 }
 
+// TestStepOutPreservesGoroutine is inspired by proc_test.TestStepOutPreservesGoroutine
+// and checks that StepOut preserves the currently selected goroutine.
 func TestStepOutPreservesGoroutine(t *testing.T) {
 	// Checks that StepOut preserves the currently selected goroutine.
 	if runtime.GOOS == "freebsd" {
@@ -2234,9 +2381,15 @@ func TestStepOutPreservesGoroutine(t *testing.T) {
 					client.StepOutRequest(goroutineId)
 					client.ExpectStepOutResponse(t)
 
-					se = client.ExpectStoppedEvent(t)
-					if se.Body.ThreadId != goroutineId {
-						t.Fatalf("StepIn did not continue on the selected goroutine, expected %d got %d", goroutineId, se.Body.ThreadId)
+					switch e := client.ExpectMessage(t).(type) {
+					case *dap.StoppedEvent:
+						if e.Body.ThreadId != goroutineId {
+							t.Fatalf("StepOut did not continue on the selected goroutine, expected %d got %d", goroutineId, e.Body.ThreadId)
+						}
+					case *dap.TerminatedEvent:
+						t.Logf("program terminated")
+					default:
+						t.Fatalf("Unexpected event type: expect stopped or terminated event, got %#v", e)
 					}
 				},
 				disconnect: false,
@@ -2420,9 +2573,12 @@ func handleStop(t *testing.T, client *daptest.Client, thread int, name string, l
 // disconnect is true, the test harness will abort the program
 // execution. Otherwise, a continue will be issued and the
 // program will continue to the next breakpoint or termination.
+// If terminated is true, we expect requests at this breakpoint
+// to result in termination.
 type onBreakpoint struct {
 	execute    func()
 	disconnect bool
+	terminated bool
 }
 
 // runDebugSessionWithBPs is a helper for executing the common init and shutdown
@@ -2467,7 +2623,16 @@ func runDebugSessionWithBPs(t *testing.T, client *daptest.Client, cmd string, cm
 		client.ExpectStoppedEvent(t)
 		onBP.execute()
 		if onBP.disconnect {
+			if onBP.terminated {
+				client.ExpectTerminatedEvent(t)
+			}
 			client.DisconnectRequestWithKillOption(true)
+			if onBP.terminated {
+				client.ExpectOutputEventProcessExited(t, 0)
+				client.ExpectOutputEventDetaching(t)
+			} else {
+				client.ExpectOutputEventDetachingKill(t)
+			}
 			client.ExpectDisconnectResponse(t)
 			return
 		}
@@ -2480,6 +2645,12 @@ func runDebugSessionWithBPs(t *testing.T, client *daptest.Client, cmd string, cm
 		client.ExpectTerminatedEvent(t)
 	}
 	client.DisconnectRequestWithKillOption(true)
+	if cmd == "launch" {
+		client.ExpectOutputEventProcessExited(t, 0)
+		client.ExpectOutputEventDetaching(t)
+	} else if cmd == "attach" {
+		client.ExpectOutputEventDetachingKill(t)
+	}
 	client.ExpectDisconnectResponse(t)
 }
 
@@ -2495,13 +2666,14 @@ func TestLaunchDebugRequest(t *testing.T) {
 	r, w, _ := os.Pipe()
 	os.Stderr = w
 
+	tmpBin := "__tmpBin"
 	runTest(t, "increment", func(client *daptest.Client, fixture protest.Fixture) {
 		// We reuse the harness that builds, but ignore the built binary,
 		// only relying on the source to be built in response to LaunchRequest.
 		runDebugSession(t, client, "launch", func() {
 			wd, _ := os.Getwd()
 			client.LaunchRequestWithArgs(map[string]interface{}{
-				"mode": "debug", "program": fixture.Source, "output": filepath.Join(wd, "__mybin")})
+				"mode": "debug", "program": fixture.Source, "output": filepath.Join(wd, tmpBin)})
 		}, fixture.Source)
 	})
 	// Wait for the test to finish to capture all stderr
@@ -2523,6 +2695,12 @@ func TestLaunchDebugRequest(t *testing.T) {
 		if runtime.GOOS != "windows" || !strings.Contains(rmErr, "Access is denied") {
 			t.Fatalf("Binary removal failure:\n%s\n", rmErr)
 		}
+	} else {
+		// We did not get a removal error, but did we even try to remove before exiting?
+		// Confirm that the binary did get removed.
+		if _, err := os.Stat(tmpBin); err == nil || os.IsExist(err) {
+			t.Fatal("Failed to remove temp binary", tmpBin)
+		}
 	}
 }
 
@@ -2559,21 +2737,33 @@ func TestLaunchRequestDefaults(t *testing.T) {
 	})
 }
 
-func TestLaunchRequestNoDebug(t *testing.T) {
+func TestLaunchRequestNoDebug_GoodStatus(t *testing.T) {
 	runTest(t, "increment", func(client *daptest.Client, fixture protest.Fixture) {
 		runNoDebugDebugSession(t, client, func() {
 			client.LaunchRequestWithArgs(map[string]interface{}{
 				"noDebug": true,
-				"mode":    "", /*"debug" by default*/
+				"mode":    "debug",
 				"program": fixture.Source,
 				"output":  cleanExeName("__mybin")})
-		}, fixture.Source, []int{8})
+		}, fixture.Source, []int{8}, 0)
+	})
+}
+
+func TestLaunchRequestNoDebug_BadStatus(t *testing.T) {
+	runTest(t, "issue1101", func(client *daptest.Client, fixture protest.Fixture) {
+		runNoDebugDebugSession(t, client, func() {
+			client.LaunchRequestWithArgs(map[string]interface{}{
+				"noDebug": true,
+				"mode":    "debug",
+				"program": fixture.Source,
+				"output":  cleanExeName("__mybin")})
+		}, fixture.Source, []int{8}, 2)
 	})
 }
 
 // runNoDebugDebugSession tests the session started with noDebug=true runs uninterrupted
 // even when breakpoint is set.
-func runNoDebugDebugSession(t *testing.T, client *daptest.Client, cmdRequest func(), source string, breakpoints []int) {
+func runNoDebugDebugSession(t *testing.T, client *daptest.Client, cmdRequest func(), source string, breakpoints []int, status int) {
 	client.InitializeRequest()
 	client.ExpectInitializeResponse(t)
 
@@ -2582,6 +2772,7 @@ func runNoDebugDebugSession(t *testing.T, client *daptest.Client, cmdRequest fun
 	// noDebug mode applies only to "launch" requests.
 	client.ExpectLaunchResponse(t)
 
+	client.ExpectOutputEventProcessExited(t, status)
 	client.ExpectTerminatedEvent(t)
 	client.DisconnectRequestWithKillOption(true)
 	client.ExpectDisconnectResponse(t)
@@ -2639,13 +2830,7 @@ func TestAttachRequest(t *testing.T) {
 	}
 	runTest(t, "loopprog", func(client *daptest.Client, fixture protest.Fixture) {
 		// Start the program to attach to
-		// TODO(polina): do I need to sanity check testBackend and runtime.GOOS?
-		cmd := exec.Command(fixture.Path)
-		cmd.Stdout = os.Stdout
-		cmd.Stderr = os.Stderr
-		if err := cmd.Start(); err != nil {
-			t.Fatal(err)
-		}
+		cmd := execFixture(t, fixture)
 
 		runDebugSessionWithBPs(t, client, "attach",
 			// Attach
@@ -2814,6 +2999,14 @@ func TestBadLaunchRequests(t *testing.T) {
 			}
 		}
 
+		expectFailedToLaunchWithMessageRegex := func(response *dap.ErrorResponse, errmsg string) {
+			t.Helper()
+			expectFailedToLaunch(response)
+			if matched, _ := regexp.MatchString(errmsg, response.Body.Error.Format); !matched {
+				t.Errorf("\ngot  %q\nwant %q", response.Body.Error.Format, errmsg)
+			}
+		}
+
 		// Test for the DAP-specific detailed error message.
 		client.LaunchRequest("exec", "", stopOnEntry)
 		expectFailedToLaunchWithMessage(client.ExpectErrorResponse(t),
@@ -2871,32 +3064,47 @@ func TestBadLaunchRequests(t *testing.T) {
 		expectFailedToLaunchWithMessage(client.ExpectErrorResponse(t),
 			"Failed to launch: 'buildFlags' attribute '123' in debug configuration is not a string.")
 
-		client.LaunchRequestWithArgs(map[string]interface{}{"mode": "debug", "program": fixture.Source, "wd": 123})
+		client.LaunchRequestWithArgs(map[string]interface{}{"mode": "debug", "program": fixture.Source, "substitutePath": 123})
+		expectFailedToLaunchWithMessage(client.ExpectErrorResponse(t),
+			"Failed to launch: 'substitutePath' attribute '123' in debug configuration is not a []{'from': string, 'to': string}")
+
+		client.LaunchRequestWithArgs(map[string]interface{}{"mode": "debug", "program": fixture.Source, "substitutePath": []interface{}{123}})
+		expectFailedToLaunchWithMessage(client.ExpectErrorResponse(t),
+			"Failed to launch: 'substitutePath' attribute '[123]' in debug configuration is not a []{'from': string, 'to': string}")
+
+		client.LaunchRequestWithArgs(map[string]interface{}{"mode": "debug", "program": fixture.Source, "substitutePath": []interface{}{map[string]interface{}{"to": "path2"}}})
+		expectFailedToLaunchWithMessage(client.ExpectErrorResponse(t),
+			"Failed to launch: 'substitutePath' attribute '[map[to:path2]]' in debug configuration is not a []{'from': string, 'to': string}")
+
+		client.LaunchRequestWithArgs(map[string]interface{}{"mode": "debug", "program": fixture.Source, "substitutePath": []interface{}{map[string]interface{}{"from": "path1", "to": 123}}})
+		expectFailedToLaunchWithMessage(client.ExpectErrorResponse(t),
+			"Failed to launch: 'substitutePath' attribute '[map[from:path1 to:123]]' in debug configuration is not a []{'from': string, 'to': string}")
+		client.LaunchRequestWithArgs(map[string]interface{}{"mode": "debug", "program": fixture.Source, "cwd": 123})
 		expectFailedToLaunchWithMessage(client.ExpectErrorResponse(t),
-			"Failed to launch: 'wd' attribute '123' in debug configuration is not a string.")
+			"Failed to launch: 'cwd' attribute '123' in debug configuration is not a string.")
 
 		// Skip detailed message checks for potentially different OS-specific errors.
 		client.LaunchRequest("exec", fixture.Path+"_does_not_exist", stopOnEntry)
 		expectFailedToLaunch(client.ExpectErrorResponse(t)) // No such file or directory
 
 		client.LaunchRequest("debug", fixture.Path+"_does_not_exist", stopOnEntry)
-		expectFailedToLaunchWithMessage(client.ExpectErrorResponse(t), "Failed to launch: Build error: exit status 1")
+		expectFailedToLaunch(client.ExpectErrorResponse(t))
 
 		client.LaunchRequest("" /*debug by default*/, fixture.Path+"_does_not_exist", stopOnEntry)
-		expectFailedToLaunchWithMessage(client.ExpectErrorResponse(t), "Failed to launch: Build error: exit status 1")
+		expectFailedToLaunch(client.ExpectErrorResponse(t))
 
 		client.LaunchRequest("exec", fixture.Source, stopOnEntry)
 		expectFailedToLaunch(client.ExpectErrorResponse(t)) // Not an executable
 
-		client.LaunchRequestWithArgs(map[string]interface{}{"mode": "debug", "program": fixture.Source, "buildFlags": "bad flags"})
-		expectFailedToLaunchWithMessage(client.ExpectErrorResponse(t), "Failed to launch: Build error: exit status 1")
-		client.LaunchRequestWithArgs(map[string]interface{}{"mode": "debug", "program": fixture.Source, "noDebug": true, "buildFlags": "bad flags"})
-		expectFailedToLaunchWithMessage(client.ExpectErrorResponse(t), "Failed to launch: Build error: exit status 1")
+		client.LaunchRequestWithArgs(map[string]interface{}{"mode": "debug", "program": fixture.Source, "buildFlags": "-bad -flags"})
+		expectFailedToLaunchWithMessageRegex(client.ExpectErrorResponse(t), `Failed to launch: Build error: .*flag provided but not defined.*`)
+		client.LaunchRequestWithArgs(map[string]interface{}{"mode": "debug", "program": fixture.Source, "noDebug": true, "buildFlags": "-bad -flags"})
+		expectFailedToLaunchWithMessageRegex(client.ExpectErrorResponse(t), `Failed to launch: Build error: .*flag provided but not defined.*`)
 
 		// Bad "wd".
-		client.LaunchRequestWithArgs(map[string]interface{}{"mode": "debug", "program": fixture.Source, "noDebug": false, "wd": "dir/invalid"})
+		client.LaunchRequestWithArgs(map[string]interface{}{"mode": "debug", "program": fixture.Source, "noDebug": false, "cwd": "dir/invalid"})
 		expectFailedToLaunch(client.ExpectErrorResponse(t)) // invalid directory, the error message is system-dependent.
-		client.LaunchRequestWithArgs(map[string]interface{}{"mode": "debug", "program": fixture.Source, "noDebug": true, "wd": "dir/invalid"})
+		client.LaunchRequestWithArgs(map[string]interface{}{"mode": "debug", "program": fixture.Source, "noDebug": true, "cwd": "dir/invalid"})
 		expectFailedToLaunch(client.ExpectErrorResponse(t)) // invalid directory, the error message is system-dependent.
 
 		// We failed to launch the program. Make sure shutdown still works.
diff --git a/service/test/integration1_test.go b/service/test/integration1_test.go
index 2e77edb2..740c09d4 100644
--- a/service/test/integration1_test.go
+++ b/service/test/integration1_test.go
@@ -813,18 +813,18 @@ func Test1Issue355(t *testing.T) {
 		state = <-ch
 		assertError(state.Err, t, "Continue()")
 
-		_, err = c.Next()
-		assertError(err, t, "Next()")
-		_, err = c.Step()
-		assertError(err, t, "Step()")
-		_, err = c.StepInstruction()
-		assertError(err, t, "StepInstruction()")
-		_, err = c.SwitchThread(tid)
-		assertError(err, t, "SwitchThread()")
-		_, err = c.SwitchGoroutine(gid)
-		assertError(err, t, "SwitchGoroutine()")
-		_, err = c.Halt()
-		assertError(err, t, "Halt()")
+		s, err := c.Next()
+		assertErrorOrExited(s, err, t, "Next()")
+		s, err = c.Step()
+		assertErrorOrExited(s, err, t, "Step()")
+		s, err = c.StepInstruction()
+		assertErrorOrExited(s, err, t, "StepInstruction()")
+		s, err = c.SwitchThread(tid)
+		assertErrorOrExited(s, err, t, "SwitchThread()")
+		s, err = c.SwitchGoroutine(gid)
+		assertErrorOrExited(s, err, t, "SwitchGoroutine()")
+		s, err = c.Halt()
+		assertErrorOrExited(s, err, t, "Halt()")
 		_, err = c.CreateBreakpoint(&api.Breakpoint{FunctionName: "main.main", Line: -1})
 		assertError(err, t, "CreateBreakpoint()")
 		_, err = c.ClearBreakpoint(bp.ID)
diff --git a/service/test/integration2_test.go b/service/test/integration2_test.go
index 51502c3f..ad36d42e 100644
--- a/service/test/integration2_test.go
+++ b/service/test/integration2_test.go
@@ -1095,6 +1095,16 @@ func TestClientServer_FullStacktrace(t *testing.T) {
 	})
 }
 
+func assertErrorOrExited(s *api.DebuggerState, err error, t *testing.T, reason string) {
+	if err != nil {
+		return
+	}
+	if s != nil && s.Exited {
+		return
+	}
+	t.Fatalf("%s (no error and no exited status)", reason)
+}
+
 func TestIssue355(t *testing.T) {
 	// After the target process has terminated should return an error but not crash
 	protest.AllowRecording(t)
@@ -1116,18 +1126,18 @@ func TestIssue355(t *testing.T) {
 		state = <-ch
 		assertError(state.Err, t, "Continue()")
 
-		_, err = c.Next()
-		assertError(err, t, "Next()")
-		_, err = c.Step()
-		assertError(err, t, "Step()")
-		_, err = c.StepInstruction()
-		assertError(err, t, "StepInstruction()")
-		_, err = c.SwitchThread(tid)
-		assertError(err, t, "SwitchThread()")
-		_, err = c.SwitchGoroutine(gid)
-		assertError(err, t, "SwitchGoroutine()")
-		_, err = c.Halt()
-		assertError(err, t, "Halt()")
+		s, err := c.Next()
+		assertErrorOrExited(s, err, t, "Next()")
+		s, err = c.Step()
+		assertErrorOrExited(s, err, t, "Step()")
+		s, err = c.StepInstruction()
+		assertErrorOrExited(s, err, t, "StepInstruction()")
+		s, err = c.SwitchThread(tid)
+		assertErrorOrExited(s, err, t, "SwitchThread()")
+		s, err = c.SwitchGoroutine(gid)
+		assertErrorOrExited(s, err, t, "SwitchGoroutine()")
+		s, err = c.Halt()
+		assertErrorOrExited(s, err, t, "Halt()")
 		_, err = c.CreateBreakpoint(&api.Breakpoint{FunctionName: "main.main", Line: -1})
 		if testBackend != "rr" {
 			assertError(err, t, "CreateBreakpoint()")
@@ -2277,3 +2287,65 @@ func TestDetachLeaveRunning(t *testing.T) {
 	defer server.Stop()
 	assertNoError(client.Detach(false), t, "Detach")
 }
+
+func assertNoDuplicateBreakpoints(t *testing.T, c service.Client) {
+	t.Helper()
+	bps, _ := c.ListBreakpoints()
+	seen := make(map[int]bool)
+	for _, bp := range bps {
+		t.Logf("%#v\n", bp)
+		if seen[bp.ID] {
+			t.Fatalf("duplicate breakpoint ID %d", bp.ID)
+		}
+		seen[bp.ID] = true
+	}
+}
+
+func TestToggleBreakpointRestart(t *testing.T) {
+	// Checks that breakpoints IDs do not overlap after Restart if there are disabled breakpoints.
+	withTestClient2("testtoggle", t, func(c service.Client) {
+		bp1, err := c.CreateBreakpoint(&api.Breakpoint{FunctionName: "main.main", Line: 1, Name: "firstbreakpoint"})
+		assertNoError(err, t, "CreateBreakpoint 1")
+		_, err = c.CreateBreakpoint(&api.Breakpoint{FunctionName: "main.main", Line: 2, Name: "secondbreakpoint"})
+		assertNoError(err, t, "CreateBreakpoint 2")
+		_, err = c.ToggleBreakpoint(bp1.ID)
+		assertNoError(err, t, "ToggleBreakpoint")
+		_, err = c.Restart(false)
+		assertNoError(err, t, "Restart")
+		assertNoDuplicateBreakpoints(t, c)
+		_, err = c.CreateBreakpoint(&api.Breakpoint{FunctionName: "main.main", Line: 3, Name: "thirdbreakpoint"})
+		assertNoError(err, t, "CreateBreakpoint 3")
+		assertNoDuplicateBreakpoints(t, c)
+	})
+}
+
+func TestStopServerWithClosedListener(t *testing.T) {
+	// Checks that the error erturned by listener.Accept() is ignored when we
+	// are trying to shutdown. See issue #1633.
+	if testBackend == "rr" || buildMode == "pie" {
+		t.Skip("N/A")
+	}
+	listener, err := net.Listen("tcp", "localhost:0")
+	assertNoError(err, t, "listener")
+	fixture := protest.BuildFixture("math", 0)
+	server := rpccommon.NewServer(&service.Config{
+		Listener:           listener,
+		AcceptMulti:        false,
+		APIVersion:         2,
+		CheckLocalConnUser: true,
+		DisconnectChan:     make(chan struct{}),
+		ProcessArgs:        []string{fixture.Path},
+		Debugger: debugger.Config{
+			WorkingDir:  ".",
+			Backend:     "default",
+			Foreground:  false,
+			BuildFlags:  "",
+			ExecuteKind: debugger.ExecutingGeneratedFile,
+		},
+	})
+	assertNoError(server.Run(), t, "blah")
+	time.Sleep(1 * time.Second) // let server start
+	server.Stop()
+	listener.Close()
+	time.Sleep(1 * time.Second) // give time to server to panic
+}