Repository: go-delve/delve
Pull request #2236 (2020-11-25 13:08:37)

Add more logging to TestClientServer_FullStacktrace to debug linux/arm64 failures



derekparker: @aarzilli did you actually want this reviewed or just testing?
aarzilli: I actually wanted it reviewed, I wanted to see if, with more logging, we can figure out why the test sometimes still fails on linux/arm64 in CI (I can't reproduce the failure locally).

Patch:
diff --git a/.gitignore b/.gitignore
index 82378981..b117424d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,3 +6,6 @@ cmd/dlv/dlv
 .vagrant
 **/*.swp
 localtests
+.idea
+*.iml
+.teamcity/target
diff --git a/.teamcity/pom.xml b/.teamcity/pom.xml
new file mode 100644
index 00000000..1292800a
--- /dev/null
+++ b/.teamcity/pom.xml
@@ -0,0 +1,104 @@
+<?xml version="1.0"?>
+<project>
+  <modelVersion>4.0.0</modelVersion>
+  <name>Delve Config DSL Script</name>
+  <groupId>Delve</groupId>
+  <artifactId>Delve_dsl</artifactId>
+  <version>1.0-SNAPSHOT</version>
+
+  <parent>
+    <groupId>org.jetbrains.teamcity</groupId>
+    <artifactId>configs-dsl-kotlin-parent</artifactId>
+    <version>1.0-SNAPSHOT</version>
+  </parent>
+
+  <repositories>
+    <repository>
+      <id>jetbrains-all</id>
+      <url>https://download.jetbrains.com/teamcity-repository</url>
+      <snapshots>
+        <enabled>true</enabled>
+      </snapshots>
+    </repository>
+    <repository>
+      <id>teamcity-server</id>
+      <url>https://delve.beta.teamcity.com/app/dsl-plugins-repository</url>
+      <snapshots>
+        <enabled>true</enabled>
+      </snapshots>
+    </repository>
+  </repositories>
+
+  <pluginRepositories>
+    <pluginRepository>
+      <id>JetBrains</id>
+      <url>https://download.jetbrains.com/teamcity-repository</url>
+    </pluginRepository>
+  </pluginRepositories>
+
+  <build>
+    <sourceDirectory>${basedir}</sourceDirectory>
+    <plugins>
+      <plugin>
+        <artifactId>kotlin-maven-plugin</artifactId>
+        <groupId>org.jetbrains.kotlin</groupId>
+        <version>${kotlin.version}</version>
+
+        <configuration/>
+        <executions>
+          <execution>
+            <id>compile</id>
+            <phase>process-sources</phase>
+            <goals>
+              <goal>compile</goal>
+            </goals>
+          </execution>
+          <execution>
+            <id>test-compile</id>
+            <phase>process-test-sources</phase>
+            <goals>
+              <goal>test-compile</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <groupId>org.jetbrains.teamcity</groupId>
+        <artifactId>teamcity-configs-maven-plugin</artifactId>
+        <version>${teamcity.dsl.version}</version>
+        <configuration>
+          <format>kotlin</format>
+          <dstDir>target/generated-configs</dstDir>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.jetbrains.teamcity</groupId>
+      <artifactId>configs-dsl-kotlin</artifactId>
+      <version>${teamcity.dsl.version}</version>
+      <scope>compile</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.jetbrains.teamcity</groupId>
+      <artifactId>configs-dsl-kotlin-plugins</artifactId>
+      <version>1.0-SNAPSHOT</version>
+      <type>pom</type>
+      <scope>compile</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.jetbrains.kotlin</groupId>
+      <artifactId>kotlin-stdlib-jdk8</artifactId>
+      <version>${kotlin.version}</version>
+      <scope>compile</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.jetbrains.kotlin</groupId>
+      <artifactId>kotlin-script-runtime</artifactId>
+      <version>${kotlin.version}</version>
+      <scope>compile</scope>
+    </dependency>
+  </dependencies>
+</project>
\ No newline at end of file
diff --git a/.teamcity/settings.kts b/.teamcity/settings.kts
new file mode 100644
index 00000000..ec8b3cb5
--- /dev/null
+++ b/.teamcity/settings.kts
@@ -0,0 +1,214 @@
+import jetbrains.buildServer.configs.kotlin.v2019_2.*
+import jetbrains.buildServer.configs.kotlin.v2019_2.buildFeatures.PullRequests
+import jetbrains.buildServer.configs.kotlin.v2019_2.buildFeatures.commitStatusPublisher
+import jetbrains.buildServer.configs.kotlin.v2019_2.buildFeatures.golang
+import jetbrains.buildServer.configs.kotlin.v2019_2.buildFeatures.pullRequests
+import jetbrains.buildServer.configs.kotlin.v2019_2.buildSteps.dockerCommand
+import jetbrains.buildServer.configs.kotlin.v2019_2.buildSteps.powerShell
+import jetbrains.buildServer.configs.kotlin.v2019_2.failureConditions.BuildFailureOnMetric
+import jetbrains.buildServer.configs.kotlin.v2019_2.failureConditions.failOnMetricChange
+import jetbrains.buildServer.configs.kotlin.v2019_2.triggers.vcs
+
+/*
+The settings script is an entry point for defining a TeamCity
+project hierarchy. The script should contain a single call to the
+project() function with a Project instance or an init function as
+an argument.
+
+VcsRoots, BuildTypes, Templates, and subprojects can be
+registered inside the project using the vcsRoot(), buildType(),
+template(), and subProject() methods respectively.
+
+To debug settings scripts in command-line, run the
+
+    mvnDebug org.jetbrains.teamcity:teamcity-configs-maven-plugin:generate
+
+command and attach your debugger to the port 8000.
+
+To debug in IntelliJ Idea, open the 'Maven Projects' tool window (View
+-> Tool Windows -> Maven Projects), find the generate task node
+(Plugins -> teamcity-configs -> teamcity-configs:generate), the
+'Debug' option is available in the context menu for the task.
+*/
+
+version = "2020.2"
+
+val targets = arrayOf(
+	"linux/amd64/1.13",
+	"linux/amd64/1.14",
+        "linux/amd64/1.15",
+        "linux/amd64/1.16",
+        "linux/amd64/tip",
+
+        "linux/386/1.15",
+
+        "windows/amd64/1.15",
+        "windows/amd64/1.16",
+        "windows/amd64/tip"
+)
+
+project {
+    val tests = targets.map { target ->
+        val (os, arch, version) = target.split("/")
+        TestBuild(os, arch, version, AbsoluteId("Delve_${os}_${arch}_${version.replace('.', '_')}"))
+    }
+    tests.map { test ->
+        test.os
+    }.distinct().forEach { os ->
+        subProject(OSProject(os, tests.filter { test ->
+            test.os == os
+        }))
+    }
+    buildType(AggregatorBuild(tests))
+}
+
+class AggregatorBuild(tests: Collection<BuildType>) : BuildType({
+    name = "Aggregator"
+    type = Type.COMPOSITE
+
+    vcs {
+        root(DslContext.settingsRoot)
+    }
+
+    triggers {
+        vcs {
+        }
+    }
+
+    dependencies {
+        tests.forEach { test ->
+            snapshot(test) {
+            }
+        }
+    }
+
+    features {
+        pullRequests {
+            vcsRootExtId = "${DslContext.settingsRoot.id}"
+            provider = github {
+                authType = token {
+                    token = "credentialsJSON:5dc93081-e0b2-41e2-b8f0-dea3c96e6426"
+                }
+                filterAuthorRole = PullRequests.GitHubRoleFilter.EVERYBODY
+            }
+        }
+        commitStatusPublisher {
+            vcsRootExtId = "${DslContext.settingsRoot.id}"
+            publisher = github {
+                githubUrl = "https://api.github.com"
+                authType = personalToken {
+                    token = "credentialsJSON:48af6e38-536d-4acb-ae2d-2fba57b6f3db"
+                }
+            }
+            param("github_oauth_user", "")
+        }
+    }
+})
+
+class OSProject(os: String, tests: List<TestBuild>) : Project({
+    id = AbsoluteId("Delve_$os")
+    name = os.capitalize()
+
+    tests.map { test ->
+        test.arch
+    }.distinct().forEach { arch ->
+        subProject(ArchProject(os, arch, tests.filter { test ->
+            test.arch == arch
+        }))
+    }
+})
+
+class ArchProject(os: String, arch: String, tests: List<TestBuild>) : Project({
+    id = AbsoluteId("Delve_${os}_${arch}")
+    name = arch
+
+    tests.forEach { test ->
+        buildType(test)
+    }
+})
+
+class TestBuild(val os: String, val arch: String, version: String, buildId: AbsoluteId) : BuildType({
+    id = buildId
+    name = version
+
+    vcs {
+        root(DslContext.settingsRoot)
+    }
+
+    failureConditions {
+        executionTimeoutMin = 30
+
+        if (version != "tip") {
+            failOnMetricChange {
+                 metric = BuildFailureOnMetric.MetricType.TEST_COUNT
+                 units = BuildFailureOnMetric.MetricUnit.DEFAULT_UNIT
+                 comparison = BuildFailureOnMetric.MetricComparison.LESS
+                 compareTo = value()
+            }
+        }
+    }
+
+    steps {
+        when (os) {
+            "linux" -> {
+                val dockerArch = if (arch == "386") "i$arch" else arch
+                dockerCommand {
+                    name = "Pull Ubuntu"
+                    commandType = other {
+                        subCommand = "pull"
+                        commandArgs = "$dockerArch/ubuntu:20.04"
+                    }
+                }
+                dockerCommand {
+                    name = "Test"
+                    commandType = other {
+                        subCommand = "run"
+                        commandArgs = """
+                        -v %teamcity.build.checkoutDir%:/delve
+                        --env TEAMCITY_VERSION=${'$'}TEAMCITY_VERSION
+                        --env CI=true
+                        --privileged
+                        $dockerArch/ubuntu:20.04
+                        /delve/_scripts/test_linux.sh ${"go$version"} $arch
+                    """.trimIndent()
+                    }
+                }
+            }
+            "windows" -> {
+                powerShell {
+                    name = "Test"
+                    scriptMode = file {
+                        path = "_scripts/test_windows.ps1"
+                    }
+                    param("jetbrains_powershell_scriptArguments", "-version ${"go$version"} -arch $arch")
+                }
+            }
+        }
+    }
+
+    requirements {
+        when (os) {
+            "linux" -> {
+                matches("teamcity.agent.jvm.os.family", "Linux")
+            }
+            "windows" -> {
+                matches("teamcity.agent.jvm.os.family", "Windows")
+            }
+        }
+    }
+
+    features {
+        pullRequests {
+            vcsRootExtId = "${DslContext.settingsRoot.id}"
+            provider = github {
+                authType = token {
+                    token = "credentialsJSON:5dc93081-e0b2-41e2-b8f0-dea3c96e6426"
+                }
+                filterAuthorRole = PullRequests.GitHubRoleFilter.EVERYBODY
+            }
+        }
+        golang {
+            testFormat = "json"
+        }
+    }
+})
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index f67ecf3d..00000000
--- a/.travis.yml
+++ /dev/null
@@ -1,59 +0,0 @@
-language: go
-sudo: required
-go_import_path: github.com/go-delve/delve
-
-os:
-  - linux
-
-arch:
-  - amd64
-  - arm64
-
-go:
-  - 1.15.3 # hold back version of Go, see issue #42484
-  - tip
-
-matrix:
-  allow_failures:
-    - go: tip
-  exclude:
-    - arch: arm64
-      go: tip
-
-before_install:
-  - export GOFLAGS=-mod=vendor
-  - if [ $TRAVIS_OS_NAME = "linux" ]; then sudo apt-get -qq update; sudo apt-get install -y dwz; echo "dwz version $(dwz --version)"; fi
-
-
-# 386 linux
-jobs:
-  include:
-    -  os: linux
-       services: docker
-       env: go_32_version=1.15.2 # Linux/i386 tests will fail on go1.15 prior to 1.15.2 (see issue #2134)
-
-script: >-
-    if [ $TRAVIS_OS_NAME = "linux" ] && [ $go_32_version ]; then
-      docker pull i386/centos:7;
-      docker run \
-      -v $(pwd):/delve \
-      --env TRAVIS=true \
-      --env CI=true \
-      --privileged i386/centos:7 \
-      /bin/bash -c "set -x && \
-           cd delve && \
-           yum -y update && yum -y upgrade && \
-           yum -y install wget make git gcc && \
-           wget -q https://dl.google.com/go/go${go_32_version}.linux-386.tar.gz && \
-           tar -C /usr/local -xzf go${go_32_version}.linux-386.tar.gz && \
-           export PATH=$PATH:/usr/local/go/bin && \
-           go version && \
-           uname -a && \
-           make test";
-    else
-      make test;
-    fi
-  
-cache:
-  directories:
-    - $HOME/AppData/Local/Temp/chocolatey
diff --git a/Documentation/usage/dlv.md b/Documentation/usage/dlv.md
index 48684855..fdd3ca33 100644
--- a/Documentation/usage/dlv.md
+++ b/Documentation/usage/dlv.md
@@ -23,7 +23,7 @@ Pass flags to the program you are debugging using `--`, for example:
       --allow-non-terminal-interactive   Allows interactive sessions of Delve that don't have a terminal as stdin, stdout and stderr
       --api-version int                  Selects API version when headless. New clients should use v2. Can be reset via RPCServer.SetApiVersion. See Documentation/api/json-rpc/README.md. (default 1)
       --backend string                   Backend selection (see 'dlv help backend'). (default "default")
-      --build-flags string               Build flags, to be passed to the compiler.
+      --build-flags string               Build flags, to be passed to the compiler. For example: --build-flags="-tags=integration -mod=vendor -cover -v"
       --check-go-version                 Checks that the version of Go in use is compatible with Delve. (default true)
       --disable-aslr                     Disables address space randomization
       --headless                         Run debug server only, in headless mode.
diff --git a/Documentation/usage/dlv_attach.md b/Documentation/usage/dlv_attach.md
index 12515960..b8527952 100644
--- a/Documentation/usage/dlv_attach.md
+++ b/Documentation/usage/dlv_attach.md
@@ -29,7 +29,7 @@ dlv attach pid [executable]
       --allow-non-terminal-interactive   Allows interactive sessions of Delve that don't have a terminal as stdin, stdout and stderr
       --api-version int                  Selects API version when headless. New clients should use v2. Can be reset via RPCServer.SetApiVersion. See Documentation/api/json-rpc/README.md. (default 1)
       --backend string                   Backend selection (see 'dlv help backend'). (default "default")
-      --build-flags string               Build flags, to be passed to the compiler.
+      --build-flags string               Build flags, to be passed to the compiler. For example: --build-flags="-tags=integration -mod=vendor -cover -v"
       --check-go-version                 Checks that the version of Go in use is compatible with Delve. (default true)
       --disable-aslr                     Disables address space randomization
       --headless                         Run debug server only, in headless mode.
diff --git a/Documentation/usage/dlv_backend.md b/Documentation/usage/dlv_backend.md
index c7ca4685..540629c0 100644
--- a/Documentation/usage/dlv_backend.md
+++ b/Documentation/usage/dlv_backend.md
@@ -22,7 +22,7 @@ are:
       --allow-non-terminal-interactive   Allows interactive sessions of Delve that don't have a terminal as stdin, stdout and stderr
       --api-version int                  Selects API version when headless. New clients should use v2. Can be reset via RPCServer.SetApiVersion. See Documentation/api/json-rpc/README.md. (default 1)
       --backend string                   Backend selection (see 'dlv help backend'). (default "default")
-      --build-flags string               Build flags, to be passed to the compiler.
+      --build-flags string               Build flags, to be passed to the compiler. For example: --build-flags="-tags=integration -mod=vendor -cover -v"
       --check-go-version                 Checks that the version of Go in use is compatible with Delve. (default true)
       --disable-aslr                     Disables address space randomization
       --headless                         Run debug server only, in headless mode.
diff --git a/Documentation/usage/dlv_connect.md b/Documentation/usage/dlv_connect.md
index 0c232a0f..c5406481 100644
--- a/Documentation/usage/dlv_connect.md
+++ b/Documentation/usage/dlv_connect.md
@@ -18,7 +18,7 @@ dlv connect addr
       --allow-non-terminal-interactive   Allows interactive sessions of Delve that don't have a terminal as stdin, stdout and stderr
       --api-version int                  Selects API version when headless. New clients should use v2. Can be reset via RPCServer.SetApiVersion. See Documentation/api/json-rpc/README.md. (default 1)
       --backend string                   Backend selection (see 'dlv help backend'). (default "default")
-      --build-flags string               Build flags, to be passed to the compiler.
+      --build-flags string               Build flags, to be passed to the compiler. For example: --build-flags="-tags=integration -mod=vendor -cover -v"
       --check-go-version                 Checks that the version of Go in use is compatible with Delve. (default true)
       --disable-aslr                     Disables address space randomization
       --headless                         Run debug server only, in headless mode.
diff --git a/Documentation/usage/dlv_core.md b/Documentation/usage/dlv_core.md
index fe9706cc..e7d0382f 100644
--- a/Documentation/usage/dlv_core.md
+++ b/Documentation/usage/dlv_core.md
@@ -24,7 +24,7 @@ dlv core <executable> <core>
       --allow-non-terminal-interactive   Allows interactive sessions of Delve that don't have a terminal as stdin, stdout and stderr
       --api-version int                  Selects API version when headless. New clients should use v2. Can be reset via RPCServer.SetApiVersion. See Documentation/api/json-rpc/README.md. (default 1)
       --backend string                   Backend selection (see 'dlv help backend'). (default "default")
-      --build-flags string               Build flags, to be passed to the compiler.
+      --build-flags string               Build flags, to be passed to the compiler. For example: --build-flags="-tags=integration -mod=vendor -cover -v"
       --check-go-version                 Checks that the version of Go in use is compatible with Delve. (default true)
       --disable-aslr                     Disables address space randomization
       --headless                         Run debug server only, in headless mode.
diff --git a/Documentation/usage/dlv_dap.md b/Documentation/usage/dlv_dap.md
index 14bb4322..e194c6b9 100644
--- a/Documentation/usage/dlv_dap.md
+++ b/Documentation/usage/dlv_dap.md
@@ -7,12 +7,16 @@
 
 [EXPERIMENTAL] Starts a TCP server communicating via Debug Adaptor Protocol (DAP).
 
-The server supports debugging of a precompiled binary akin to 'dlv exec' via a launch request.
-It does not yet support support specification of program arguments.
-It does not yet support launch requests with 'debug' and 'test' modes that require compilation.
-It does not yet support attach requests to debug a running process like with 'dlv attach'.
-It does not yet support asynchronous request-response communication.
-The server does not accept multiple client connections.
+The server is headless and requires a DAP client like vscode to connect and request a binary
+to be launched or process to be attached to. The following modes are supported:
+- launch + exec (executes precompiled binary, like 'dlv exec')
+- launch + debug (builds and launches, like 'dlv debug')
+- launch + test (builds and tests, like 'dlv test')
+- attach + local (attaches to a running process, like 'dlv attach')
+The server does not yet support asynchronous request-response communication, so features
+like pausing or setting breakpoints while the program is running are not yet available.
+The server does not accept multiple client connections (--accept-multiclient),
+a feature that is often relied on to enable --continue with remote debugging.
 
 ```
 dlv dap
@@ -25,7 +29,7 @@ dlv dap
       --allow-non-terminal-interactive   Allows interactive sessions of Delve that don't have a terminal as stdin, stdout and stderr
       --api-version int                  Selects API version when headless. New clients should use v2. Can be reset via RPCServer.SetApiVersion. See Documentation/api/json-rpc/README.md. (default 1)
       --backend string                   Backend selection (see 'dlv help backend'). (default "default")
-      --build-flags string               Build flags, to be passed to the compiler.
+      --build-flags string               Build flags, to be passed to the compiler. For example: --build-flags="-tags=integration -mod=vendor -cover -v"
       --check-go-version                 Checks that the version of Go in use is compatible with Delve. (default true)
       --disable-aslr                     Disables address space randomization
       --headless                         Run debug server only, in headless mode.
diff --git a/Documentation/usage/dlv_debug.md b/Documentation/usage/dlv_debug.md
index d3366136..3fd6286e 100644
--- a/Documentation/usage/dlv_debug.md
+++ b/Documentation/usage/dlv_debug.md
@@ -31,7 +31,7 @@ dlv debug [package]
       --allow-non-terminal-interactive   Allows interactive sessions of Delve that don't have a terminal as stdin, stdout and stderr
       --api-version int                  Selects API version when headless. New clients should use v2. Can be reset via RPCServer.SetApiVersion. See Documentation/api/json-rpc/README.md. (default 1)
       --backend string                   Backend selection (see 'dlv help backend'). (default "default")
-      --build-flags string               Build flags, to be passed to the compiler.
+      --build-flags string               Build flags, to be passed to the compiler. For example: --build-flags="-tags=integration -mod=vendor -cover -v"
       --check-go-version                 Checks that the version of Go in use is compatible with Delve. (default true)
       --disable-aslr                     Disables address space randomization
       --headless                         Run debug server only, in headless mode.
diff --git a/Documentation/usage/dlv_exec.md b/Documentation/usage/dlv_exec.md
index 6e266f81..8c55f483 100644
--- a/Documentation/usage/dlv_exec.md
+++ b/Documentation/usage/dlv_exec.md
@@ -31,7 +31,7 @@ dlv exec <path/to/binary>
       --allow-non-terminal-interactive   Allows interactive sessions of Delve that don't have a terminal as stdin, stdout and stderr
       --api-version int                  Selects API version when headless. New clients should use v2. Can be reset via RPCServer.SetApiVersion. See Documentation/api/json-rpc/README.md. (default 1)
       --backend string                   Backend selection (see 'dlv help backend'). (default "default")
-      --build-flags string               Build flags, to be passed to the compiler.
+      --build-flags string               Build flags, to be passed to the compiler. For example: --build-flags="-tags=integration -mod=vendor -cover -v"
       --check-go-version                 Checks that the version of Go in use is compatible with Delve. (default true)
       --disable-aslr                     Disables address space randomization
       --headless                         Run debug server only, in headless mode.
diff --git a/Documentation/usage/dlv_log.md b/Documentation/usage/dlv_log.md
index 3dd2c4cf..e1138f89 100644
--- a/Documentation/usage/dlv_log.md
+++ b/Documentation/usage/dlv_log.md
@@ -37,7 +37,7 @@ and dap modes.
       --allow-non-terminal-interactive   Allows interactive sessions of Delve that don't have a terminal as stdin, stdout and stderr
       --api-version int                  Selects API version when headless. New clients should use v2. Can be reset via RPCServer.SetApiVersion. See Documentation/api/json-rpc/README.md. (default 1)
       --backend string                   Backend selection (see 'dlv help backend'). (default "default")
-      --build-flags string               Build flags, to be passed to the compiler.
+      --build-flags string               Build flags, to be passed to the compiler. For example: --build-flags="-tags=integration -mod=vendor -cover -v"
       --check-go-version                 Checks that the version of Go in use is compatible with Delve. (default true)
       --disable-aslr                     Disables address space randomization
       --headless                         Run debug server only, in headless mode.
diff --git a/Documentation/usage/dlv_redirect.md b/Documentation/usage/dlv_redirect.md
index f8ef2bfc..97ad4008 100644
--- a/Documentation/usage/dlv_redirect.md
+++ b/Documentation/usage/dlv_redirect.md
@@ -25,7 +25,7 @@ File redirects can also be changed using the 'restart' command.
       --allow-non-terminal-interactive   Allows interactive sessions of Delve that don't have a terminal as stdin, stdout and stderr
       --api-version int                  Selects API version when headless. New clients should use v2. Can be reset via RPCServer.SetApiVersion. See Documentation/api/json-rpc/README.md. (default 1)
       --backend string                   Backend selection (see 'dlv help backend'). (default "default")
-      --build-flags string               Build flags, to be passed to the compiler.
+      --build-flags string               Build flags, to be passed to the compiler. For example: --build-flags="-tags=integration -mod=vendor -cover -v"
       --check-go-version                 Checks that the version of Go in use is compatible with Delve. (default true)
       --disable-aslr                     Disables address space randomization
       --headless                         Run debug server only, in headless mode.
diff --git a/Documentation/usage/dlv_replay.md b/Documentation/usage/dlv_replay.md
index 527b4d86..17f9d685 100644
--- a/Documentation/usage/dlv_replay.md
+++ b/Documentation/usage/dlv_replay.md
@@ -22,7 +22,7 @@ dlv replay [trace directory]
       --allow-non-terminal-interactive   Allows interactive sessions of Delve that don't have a terminal as stdin, stdout and stderr
       --api-version int                  Selects API version when headless. New clients should use v2. Can be reset via RPCServer.SetApiVersion. See Documentation/api/json-rpc/README.md. (default 1)
       --backend string                   Backend selection (see 'dlv help backend'). (default "default")
-      --build-flags string               Build flags, to be passed to the compiler.
+      --build-flags string               Build flags, to be passed to the compiler. For example: --build-flags="-tags=integration -mod=vendor -cover -v"
       --check-go-version                 Checks that the version of Go in use is compatible with Delve. (default true)
       --disable-aslr                     Disables address space randomization
       --headless                         Run debug server only, in headless mode.
diff --git a/Documentation/usage/dlv_run.md b/Documentation/usage/dlv_run.md
index b39653f2..ef57d384 100644
--- a/Documentation/usage/dlv_run.md
+++ b/Documentation/usage/dlv_run.md
@@ -18,7 +18,7 @@ dlv run
       --allow-non-terminal-interactive   Allows interactive sessions of Delve that don't have a terminal as stdin, stdout and stderr
       --api-version int                  Selects API version when headless. New clients should use v2. Can be reset via RPCServer.SetApiVersion. See Documentation/api/json-rpc/README.md. (default 1)
       --backend string                   Backend selection (see 'dlv help backend'). (default "default")
-      --build-flags string               Build flags, to be passed to the compiler.
+      --build-flags string               Build flags, to be passed to the compiler. For example: --build-flags="-tags=integration -mod=vendor -cover -v"
       --check-go-version                 Checks that the version of Go in use is compatible with Delve. (default true)
       --disable-aslr                     Disables address space randomization
       --headless                         Run debug server only, in headless mode.
diff --git a/Documentation/usage/dlv_trace.md b/Documentation/usage/dlv_trace.md
index 34afd913..d0d59f70 100644
--- a/Documentation/usage/dlv_trace.md
+++ b/Documentation/usage/dlv_trace.md
@@ -36,7 +36,7 @@ dlv trace [package] regexp
       --allow-non-terminal-interactive   Allows interactive sessions of Delve that don't have a terminal as stdin, stdout and stderr
       --api-version int                  Selects API version when headless. New clients should use v2. Can be reset via RPCServer.SetApiVersion. See Documentation/api/json-rpc/README.md. (default 1)
       --backend string                   Backend selection (see 'dlv help backend'). (default "default")
-      --build-flags string               Build flags, to be passed to the compiler.
+      --build-flags string               Build flags, to be passed to the compiler. For example: --build-flags="-tags=integration -mod=vendor -cover -v"
       --check-go-version                 Checks that the version of Go in use is compatible with Delve. (default true)
       --disable-aslr                     Disables address space randomization
       --headless                         Run debug server only, in headless mode.
diff --git a/Documentation/usage/dlv_version.md b/Documentation/usage/dlv_version.md
index c5d7a2ea..0cb563dd 100644
--- a/Documentation/usage/dlv_version.md
+++ b/Documentation/usage/dlv_version.md
@@ -18,7 +18,7 @@ dlv version
       --allow-non-terminal-interactive   Allows interactive sessions of Delve that don't have a terminal as stdin, stdout and stderr
       --api-version int                  Selects API version when headless. New clients should use v2. Can be reset via RPCServer.SetApiVersion. See Documentation/api/json-rpc/README.md. (default 1)
       --backend string                   Backend selection (see 'dlv help backend'). (default "default")
-      --build-flags string               Build flags, to be passed to the compiler.
+      --build-flags string               Build flags, to be passed to the compiler. For example: --build-flags="-tags=integration -mod=vendor -cover -v"
       --check-go-version                 Checks that the version of Go in use is compatible with Delve. (default true)
       --disable-aslr                     Disables address space randomization
       --headless                         Run debug server only, in headless mode.
diff --git a/README.md b/README.md
index 8d2595f9..73fb876b 100644
--- a/README.md
+++ b/README.md
@@ -2,8 +2,7 @@
 
 [![license](http://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/go-delve/delve/master/LICENSE)
 [![GoDoc](https://godoc.org/github.com/go-delve/delve?status.svg)](https://godoc.org/github.com/go-delve/delve)
-[![Build Status](https://travis-ci.org/go-delve/delve.svg?branch=master)](https://travis-ci.org/go-delve/delve)
-[![Build status](https://ci.appveyor.com/api/projects/status/0v8r99smxost466s/branch/master?svg=true)](https://ci.appveyor.com/project/derekparker/delve-facy3/branch/master)
+[![Build Status](https://delve.beta.teamcity.com/app/rest/builds/buildType:(id:Delve_AggregatorBuild)/statusIcon.svg)](https://delve.beta.teamcity.com/viewType.html?buildTypeId=Delve_AggregatorBuild&guest=1)
 
 The GitHub issue tracker is for **bugs** only. Please use the [developer mailing list](https://groups.google.com/forum/#!forum/delve-dev) for any feature proposals and discussions.
 
diff --git a/_scripts/gen-travis.go b/_scripts/gen-travis.go
index 2f4cf25b..a6028c92 100644
--- a/_scripts/gen-travis.go
+++ b/_scripts/gen-travis.go
@@ -55,87 +55,12 @@ func main() {
 		}
 	}
 
-	travisfh, err := os.Create(".travis.yml")
-	if err != nil {
-		fmt.Fprintf(os.Stderr, "could not create .travis.yml: %v")
-		os.Exit(1)
-	}
-
-	out := bufio.NewWriter(travisfh)
-	err = template.Must(template.New("travis.yml").Parse(`language: go
-sudo: required
-go_import_path: github.com/go-delve/delve
-
-os:
-  - linux
-
-arch:
-  - amd64
-  - arm64
-
-go:
-  - {{index .GoVersions 0}}
-  - tip
-
-matrix:
-  allow_failures:
-    - go: tip
-  exclude:
-    - arch: arm64
-      go: tip
-
-before_install:
-  - export GOFLAGS=-mod=vendor
-  - if [ $TRAVIS_OS_NAME = "linux" ]; then sudo apt-get -qq update; sudo apt-get install -y dwz; echo "dwz version $(dwz --version)"; fi
-
-
-# 386 linux
-jobs:
-  include:
-    -  os: linux
-       services: docker
-       env: go_32_version={{index .GoVersions 0}}.2 # Linux/i386 tests will fail on go1.15 prior to 1.15.2 (see issue #2134)
-
-script: >-
-    if [ $TRAVIS_OS_NAME = "linux" ] && [ $go_32_version ]; then
-      docker pull i386/centos:7;
-      docker run \
-      -v $(pwd):/delve \
-      --env TRAVIS=true \
-      --env CI=true \
-      --privileged i386/centos:7 \
-      /bin/bash -c "set -x && \
-           cd delve && \
-           yum -y update && yum -y upgrade && \
-           yum -y install wget make git gcc && \
-           wget -q https://dl.google.com/go/go${go_32_version}.linux-386.tar.gz && \
-           tar -C /usr/local -xzf go${go_32_version}.linux-386.tar.gz && \
-           export PATH=$PATH:/usr/local/go/bin && \
-           go version && \
-           uname -a && \
-           make test";
-    else
-      make test;
-    fi
-  
-cache:
-  directories:
-    - $HOME/AppData/Local/Temp/chocolatey
-`)).Execute(out, args)
-
-	if err != nil {
-		fmt.Fprintf(os.Stderr, "Error executing template: %v", err)
-		os.Exit(1)
-	}
-	_ = out.Flush()
-	_ = travisfh.Close()
-
 	githubfh, err := os.Create(".github/workflows/test.yml")
 	if err != nil {
 		fmt.Fprintf(os.Stderr, "Could not create .github/test.yml: %v", err)
 		os.Exit(1)
 	}
-	out = bufio.NewWriter(githubfh)
+	out := bufio.NewWriter(githubfh)
 	err = template.Must(template.New(".github/workflows/test.yml").Parse(`name: Delve CI
 
 on: [push, pull_request]
diff --git a/_scripts/make.go b/_scripts/make.go
index 1580bb5c..40e35072 100644
--- a/_scripts/make.go
+++ b/_scripts/make.go
@@ -276,6 +276,9 @@ func testFlags() []string {
 		// Make test timeout shorter than Travis' own timeout so that Go can report which test hangs.
 		testFlags = append(testFlags, "-timeout", "9m")
 	}
+	if len(os.Getenv("TEAMCITY_VERSION")) > 0 {
+		testFlags = append(testFlags, "-json")
+	}
 	if runtime.GOOS == "darwin" {
 		testFlags = append(testFlags, "-exec="+wd+"/_scripts/testsign")
 	}
diff --git a/appveyor.yml b/appveyor.yml
deleted file mode 100644
index 74035298..00000000
--- a/appveyor.yml
+++ /dev/null
@@ -1,33 +0,0 @@
-version: '{build}'
-os: Windows Server 2012 R2
-clone_folder: c:\gopath\src\github.com\go-delve\delve
-environment:
-  GOPATH: C:\gopath
-install:
-   ps: |
-      # Install MinGW.
-      if (-Not (Test-Path "C:\mingw64")) {
-              $file = "x86_64-4.9.2-release-win32-seh-rt_v4-rev3.7z"
-              $url  = "https://bintray.com/artifact/download/drewwells/generic/"
-              $url += $file
-              Invoke-WebRequest -UserAgent wget -Uri $url -OutFile $file
-              &7z x -oC:\ $file > $null
-      }
-      # Install Procdump
-      if (-Not (Test-Path "C:\procdump")) {
-              mkdir c:\procdump
-              Invoke-WebRequest -UserAgent wget -Uri https://download.sysinternals.com/files/Procdump.zip -OutFile C:\procdump\procdump.zip
-              &7z x -oC:\procdump\ C:\procdump\procdump.zip > $null
-      }
-   set PATH=c:\procdump;c:\mingw64\bin;%GOPATH%\bin;%PATH%
-   echo %PATH%
-   echo %GOPATH%
-   go version
-   go env
-cache:
-   C:\mingw64
-   C:\procdump
-build_script:
- mingw32-make install
-test_script:
- mingw32-make test
diff --git a/cmd/dlv/cmds/commands.go b/cmd/dlv/cmds/commands.go
index aaa74759..8171feff 100644
--- a/cmd/dlv/cmds/commands.go
+++ b/cmd/dlv/cmds/commands.go
@@ -126,7 +126,7 @@ func New(docCall bool) *cobra.Command {
 	rootCommand.PersistentFlags().BoolVarP(&acceptMulti, "accept-multiclient", "", false, "Allows a headless server to accept multiple client connections.")
 	rootCommand.PersistentFlags().IntVar(&apiVersion, "api-version", 1, "Selects API version when headless. New clients should use v2. Can be reset via RPCServer.SetApiVersion. See Documentation/api/json-rpc/README.md.")
 	rootCommand.PersistentFlags().StringVar(&initFile, "init", "", "Init file, executed by the terminal client.")
-	rootCommand.PersistentFlags().StringVar(&buildFlags, "build-flags", buildFlagsDefault, "Build flags, to be passed to the compiler.")
+	rootCommand.PersistentFlags().StringVar(&buildFlags, "build-flags", buildFlagsDefault, "Build flags, to be passed to the compiler. For example: --build-flags=\"-tags=integration -mod=vendor -cover -v\"")
 	rootCommand.PersistentFlags().StringVar(&workingDir, "wd", "", "Working directory for running the program.")
 	rootCommand.PersistentFlags().BoolVarP(&checkGoVersion, "check-go-version", "", true, "Checks that the version of Go in use is compatible with Delve.")
 	rootCommand.PersistentFlags().BoolVarP(&checkLocalConnUser, "only-same-user", "", true, "Only connections from the same user that started this instance of Delve are allowed to connect.")
@@ -177,12 +177,16 @@ option to let the process continue or kill it.
 		Short: "[EXPERIMENTAL] Starts a TCP server communicating via Debug Adaptor Protocol (DAP).",
 		Long: `[EXPERIMENTAL] Starts a TCP server communicating via Debug Adaptor Protocol (DAP).
 
-The server supports debugging of a precompiled binary akin to 'dlv exec' via a launch request.
-It does not yet support support specification of program arguments.
-It does not yet support launch requests with 'debug' and 'test' modes that require compilation.
-It does not yet support attach requests to debug a running process like with 'dlv attach'.
-It does not yet support asynchronous request-response communication.
-The server does not accept multiple client connections.`,
+The server is headless and requires a DAP client like vscode to connect and request a binary
+to be launched or process to be attached to. The following modes are supported:
+- launch + exec (executes precompiled binary, like 'dlv exec')
+- launch + debug (builds and launches, like 'dlv debug')
+- launch + test (builds and tests, like 'dlv test')
+- attach + local (attaches to a running process, like 'dlv attach')
+The server does not yet support asynchronous request-response communication, so features
+like pausing or setting breakpoints while the program is running are not yet available.
+The server does not accept multiple client connections (--accept-multiclient),
+a feature that is often relied on to enable --continue with remote debugging.`,
 		Run: dapCmd,
 	}
 	rootCommand.AddCommand(dapCommand)
@@ -403,10 +407,10 @@ func dapCmd(cmd *cobra.Command, args []string) {
 		defer logflags.Close()
 
 		if headless {
-			fmt.Fprintf(os.Stderr, "Warning: headless mode not supported with dap\n")
+			fmt.Fprintf(os.Stderr, "Warning: dap mode is always headless\n")
 		}
 		if acceptMulti {
-			fmt.Fprintf(os.Stderr, "Warning: accept multiclient mode not supported with dap\n")
+			fmt.Fprintf(os.Stderr, "Warning: accept-multiclient mode not supported with dap\n")
 		}
 		if initFile != "" {
 			fmt.Fprint(os.Stderr, "Warning: init file ignored with dap\n")
diff --git a/pkg/dwarf/line/line_parser.go b/pkg/dwarf/line/line_parser.go
index a84b5cad..a6babe16 100644
--- a/pkg/dwarf/line/line_parser.go
+++ b/pkg/dwarf/line/line_parser.go
@@ -3,7 +3,7 @@ package line
 import (
 	"bytes"
 	"encoding/binary"
-	"path/filepath"
+	"path"
 	"strings"
 
 	"github.com/go-delve/delve/pkg/dwarf/util"
@@ -193,21 +193,37 @@ func readFileEntry(info *DebugLineInfo, buf *bytes.Buffer, exitOnEmptyPath bool)
 	}
 
 	if info.normalizeBackslash {
-		entry.Path = strings.Replace(entry.Path, "\\", "/", -1)
+		entry.Path = strings.ReplaceAll(entry.Path, "\\", "/")
 	}
 
 	entry.DirIdx, _ = util.DecodeULEB128(buf)
 	entry.LastModTime, _ = util.DecodeULEB128(buf)
 	entry.Length, _ = util.DecodeULEB128(buf)
-	if !filepath.IsAbs(entry.Path) {
+	if !pathIsAbs(entry.Path) {
 		if entry.DirIdx >= 0 && entry.DirIdx < uint64(len(info.IncludeDirs)) {
-			entry.Path = filepath.Join(info.IncludeDirs[entry.DirIdx], entry.Path)
+			entry.Path = path.Join(info.IncludeDirs[entry.DirIdx], entry.Path)
 		}
 	}
 
 	return entry
 }
 
+// pathIsAbs returns true if this is an absolute path.
+// We can not use path.IsAbs because it will not recognize windows paths as
+// absolute. We also can not use filepath.Abs because we want this
+// processing to be independent of the host operating system (we could be
+// reading an executable file produced on windows on a unix machine or vice
+// versa).
+func pathIsAbs(s string) bool {
+	if len(s) >= 1 && s[0] == '/' {
+		return true
+	}
+	if len(s) >= 2 && s[1] == ':' && (('a' <= s[0] && s[0] <= 'z') || ('A' <= s[0] && s[0] <= 'Z')) {
+		return true
+	}
+	return false
+}
+
 // parseFileEntries5 parses the file table for DWARF 5
 func parseFileEntries5(info *DebugLineInfo, buf *bytes.Buffer) {
 	fileEntryFormReader := readEntryFormat(buf, info.Logf)
@@ -217,13 +233,13 @@ func parseFileEntries5(info *DebugLineInfo, buf *bytes.Buffer) {
 		fileEntryFormReader.reset()
 		for fileEntryFormReader.next(buf) {
 			entry := new(FileEntry)
-			var path string
+			var p string
 			var diridx int = -1
 
 			switch fileEntryFormReader.contentType {
 			case _DW_LNCT_path:
 				if fileEntryFormReader.formCode != _DW_FORM_string {
-					path = fileEntryFormReader.str
+					p = fileEntryFormReader.str
 				} else {
 					//TODO(aarzilli): support debug_string, debug_line_str
 					info.Logf("unsupported string form %#x", fileEntryFormReader.formCode)
@@ -238,10 +254,14 @@ func parseFileEntries5(info *DebugLineInfo, buf *bytes.Buffer) {
 				// not implemented
 			}
 
-			if diridx >= 0 && !filepath.IsAbs(path) && diridx < len(info.IncludeDirs) {
-				path = filepath.Join(info.IncludeDirs[diridx], path)
+			if info.normalizeBackslash {
+				p = strings.ReplaceAll(p, "\\", "/")
+			}
+
+			if diridx >= 0 && !pathIsAbs(p) && diridx < len(info.IncludeDirs) {
+				p = path.Join(info.IncludeDirs[diridx], p)
 			}
-			entry.Path = path
+			entry.Path = p
 			info.FileNames = append(info.FileNames, entry)
 			info.Lookup[entry.Path] = entry
 		}
diff --git a/pkg/goversion/compat.go b/pkg/goversion/compat.go
index d1502fee..270effd2 100644
--- a/pkg/goversion/compat.go
+++ b/pkg/goversion/compat.go
@@ -8,7 +8,7 @@ var (
 	MinSupportedVersionOfGoMajor = 1
 	MinSupportedVersionOfGoMinor = 13
 	MaxSupportedVersionOfGoMajor = 1
-	MaxSupportedVersionOfGoMinor = 15
+	MaxSupportedVersionOfGoMinor = 16
 	goTooOldErr                  = fmt.Errorf("Version of Go is too old for this version of Delve (minimum supported version %d.%d, suppress this error with --check-go-version=false)", MinSupportedVersionOfGoMajor, MinSupportedVersionOfGoMinor)
 	dlvTooOldErr                 = fmt.Errorf("Version of Delve is too old for this version of Go (maximum supported version %d.%d, suppress this error with --check-go-version=false)", MaxSupportedVersionOfGoMajor, MaxSupportedVersionOfGoMinor)
 )
diff --git a/pkg/proc/amd64_arch.go b/pkg/proc/amd64_arch.go
index fc914269..21805c5a 100644
--- a/pkg/proc/amd64_arch.go
+++ b/pkg/proc/amd64_arch.go
@@ -161,7 +161,7 @@ func amd64SwitchStack(it *stackIterator, _ *op.DwarfRegisters) bool {
 		it.top = false
 		return true
 
-	case "runtime.cgocallback_gofunc":
+	case "runtime.cgocallback_gofunc", "runtime.cgocallback":
 		// For a detailed description of how this works read the long comment at
 		// the start of $GOROOT/src/runtime/cgocall.go and the source code of
 		// runtime.cgocallback_gofunc in $GOROOT/src/runtime/asm_amd64.s
diff --git a/pkg/proc/arm64_arch.go b/pkg/proc/arm64_arch.go
index e7b57a46..e12f2a69 100644
--- a/pkg/proc/arm64_arch.go
+++ b/pkg/proc/arm64_arch.go
@@ -139,7 +139,7 @@ const spAlign = 16
 func arm64SwitchStack(it *stackIterator, callFrameRegs *op.DwarfRegisters) bool {
 	if it.frame.Current.Fn != nil {
 		switch it.frame.Current.Fn.Name {
-		case "runtime.asmcgocall", "runtime.cgocallback_gofunc", "runtime.sigpanic":
+		case "runtime.asmcgocall", "runtime.cgocallback_gofunc", "runtime.sigpanic", "runtime.cgocallback":
 			//do nothing
 		case "runtime.goexit", "runtime.rt0_go", "runtime.mcall":
 			// Look for "top of stack" functions.
@@ -202,7 +202,7 @@ func arm64SwitchStack(it *stackIterator, callFrameRegs *op.DwarfRegisters) bool
 		callFrameRegs.Reg(callFrameRegs.SPRegNum).Uint64Val = uint64(int64(newsp))
 		return false
 
-	case "runtime.cgocallback_gofunc":
+	case "runtime.cgocallback_gofunc", "runtime.cgocallback":
 		// For a detailed description of how this works read the long comment at
 		// the start of $GOROOT/src/runtime/cgocall.go and the source code of
 		// runtime.cgocallback_gofunc in $GOROOT/src/runtime/asm_arm64.s
diff --git a/pkg/proc/bininfo.go b/pkg/proc/bininfo.go
index c755ef17..a4f2bb14 100644
--- a/pkg/proc/bininfo.go
+++ b/pkg/proc/bininfo.go
@@ -129,6 +129,7 @@ var (
 
 	supportedDarwinArch = map[macho.Cpu]bool{
 		macho.CpuAmd64: true,
+		macho.CpuArm64: true,
 	}
 )
 
@@ -1368,9 +1369,19 @@ func findPESymbol(f *pe.File, name string) (*pe.Symbol, error) {
 // loadBinaryInfoMacho specifically loads information from a Mach-O binary.
 func loadBinaryInfoMacho(bi *BinaryInfo, image *Image, path string, entryPoint uint64, wg *sync.WaitGroup) error {
 	exe, err := macho.Open(path)
+
 	if err != nil {
 		return err
 	}
+
+	if entryPoint != 0 {
+		// This is a little bit hacky. We use the entryPoint variable, but it
+		// actually holds the address of the mach-o header. We can use this
+		// to calculate the offset to the non-aslr location of the mach-o header
+		// (which is 0x100000000)
+		image.StaticBase = entryPoint - 0x100000000
+	}
+
 	image.closer = exe
 	if !supportedDarwinArch[exe.Cpu] {
 		return &ErrUnsupportedArch{os: "darwin", cpuArch: exe.Cpu}
diff --git a/pkg/proc/gdbserial/gdbserver.go b/pkg/proc/gdbserial/gdbserver.go
index 0b8e2c0f..17dc4081 100644
--- a/pkg/proc/gdbserial/gdbserver.go
+++ b/pkg/proc/gdbserial/gdbserver.go
@@ -77,6 +77,7 @@ import (
 	"sync"
 	"time"
 
+	"golang.org/x/arch/arm64/arm64asm"
 	"golang.org/x/arch/x86/x86asm"
 
 	"github.com/go-delve/delve/pkg/logflags"
@@ -192,6 +193,7 @@ type gdbRegisters struct {
 	gaddr    uint64
 	hasgaddr bool
 	buf      []byte
+	arch     *proc.Arch
 }
 
 type gdbRegister struct {
@@ -292,6 +294,13 @@ func (p *gdbProcess) Connect(conn net.Conn, path string, pid int, debugInfoDirs
 					p.gcmdok = false
 					break
 				}
+
+				// Workaround for darwin arm64. Apple's debugserver seems to have a problem
+				// with not selecting the correct thread in the 'g' command and the returned
+				// registers are empty / an E74 error is thrown.
+				if v[len("version:"):] == "1200" && p.bi.Arch.Name == "arm64" {
+					p.gcmdok = false
+				}
 			}
 		}
 	}
@@ -301,17 +310,19 @@ func (p *gdbProcess) Connect(conn net.Conn, path string, pid int, debugInfoDirs
 		return nil, err
 	}
 
-	// None of the stubs we support returns the value of fs_base or gs_base
-	// along with the registers, therefore we have to resort to executing a MOV
-	// instruction on the inferior to find out where the G struct of a given
-	// thread is located.
-	// Here we try to allocate some memory on the inferior which we will use to
-	// store the MOV instruction.
-	// If the stub doesn't support memory allocation reloadRegisters will
-	// overwrite some existing memory to store the MOV.
-	if addr, err := p.conn.allocMemory(256); err == nil {
-		if _, err := p.conn.writeMemory(addr, p.loadGInstr()); err == nil {
-			p.loadGInstrAddr = addr
+	if p.bi.Arch.Name != "arm64" {
+		// None of the stubs we support returns the value of fs_base or gs_base
+		// along with the registers, therefore we have to resort to executing a MOV
+		// instruction on the inferior to find out where the G struct of a given
+		// thread is located.
+		// Here we try to allocate some memory on the inferior which we will use to
+		// store the MOV instruction.
+		// If the stub doesn't support memory allocation reloadRegisters will
+		// overwrite some existing memory to store the MOV.
+		if addr, err := p.conn.allocMemory(256); err == nil {
+			if _, err := p.conn.writeMemory(addr, p.loadGInstr()); err == nil {
+				p.loadGInstrAddr = addr
+			}
 		}
 	}
 
@@ -557,12 +568,27 @@ func LLDBAttach(pid int, path string, debugInfoDirs []string) (*proc.Target, err
 // debugging PIEs.
 func (p *gdbProcess) EntryPoint() (uint64, error) {
 	var entryPoint uint64
-	if auxv, err := p.conn.readAuxv(); err == nil {
+	if p.bi.GOOS == "darwin" && p.bi.Arch.Name == "arm64" {
+		// There is no auxv on darwin, however, we can get the location of the mach-o
+		// header from the debugserver by going through the loaded libraries, which includes
+		// the exe itself
+		images, _ := p.conn.getLoadedDynamicLibraries()
+		for _, image := range images {
+			if image.MachHeader.FileType == macho.TypeExec {
+				// This is a bit hacky. This is technically not the entrypoint,
+				// but rather we use the variable to points at the mach-o header,
+				// so we can get the offset in bininfo
+				entryPoint = image.LoadAddress
+				break
+			}
+		}
+	} else if auxv, err := p.conn.readAuxv(); err == nil {
 		// If we can't read the auxiliary vector it just means it's not supported
 		// by the OS or by the stub. If we are debugging a PIE and the entry point
 		// is needed proc.LoadBinaryInfo will complain about it.
 		entryPoint = linutil.EntryPointFromAuxv(auxv, p.BinInfo().Arch.PtrSize())
 	}
+
 	return entryPoint, nil
 }
 
@@ -1436,7 +1462,8 @@ func (p *gdbProcess) loadGInstr() []byte {
 	return buf.Bytes()
 }
 
-func (regs *gdbRegisters) init(regsInfo []gdbRegisterInfo) {
+func (regs *gdbRegisters) init(regsInfo []gdbRegisterInfo, arch *proc.Arch) {
+	regs.arch = arch
 	regs.regs = make(map[string]gdbRegister)
 	regs.regsInfo = regsInfo
 
@@ -1458,7 +1485,7 @@ func (regs *gdbRegisters) init(regsInfo []gdbRegisterInfo) {
 // the stub can allocate memory, or reloadGAtPC, if the stub can't.
 func (t *gdbThread) reloadRegisters() error {
 	if t.regs.regs == nil {
-		t.regs.init(t.p.conn.regsInfo)
+		t.regs.init(t.p.conn.regsInfo, t.p.bi.Arch)
 	}
 
 	if t.p.gcmdok {
@@ -1488,10 +1515,21 @@ func (t *gdbThread) reloadRegisters() error {
 		}
 	}
 
-	if t.p.loadGInstrAddr > 0 {
-		return t.reloadGAlloc()
+	if t.p.bi.Arch.Name == "arm64" {
+		// no need to play around with the GInstr on ARM64 because
+		// the G addr is stored in a register
+
+		t.regs.gaddr = t.regs.byName("x28")
+		t.regs.hasgaddr = true
+		t.regs.tls = 0
+	} else {
+		if t.p.loadGInstrAddr > 0 {
+			return t.reloadGAlloc()
+		}
+		return t.reloadGAtPC()
 	}
-	return t.reloadGAtPC()
+
+	return nil
 }
 
 func (t *gdbThread) writeSomeRegisters(regNames ...string) error {
@@ -1590,7 +1628,7 @@ func (t *gdbThread) reloadGAtPC() error {
 
 	err = t.p.conn.step(t.strID, nil, true)
 	if err != nil {
-		if err == threadBlockedError {
+		if err == errThreadBlocked {
 			t.regs.tls = 0
 			t.regs.gaddr = 0
 			t.regs.hasgaddr = true
@@ -1643,7 +1681,7 @@ func (t *gdbThread) reloadGAlloc() error {
 
 	err = t.p.conn.step(t.strID, nil, true)
 	if err != nil {
-		if err == threadBlockedError {
+		if err == errThreadBlocked {
 			t.regs.tls = 0
 			t.regs.gaddr = 0
 			t.regs.hasgaddr = true
@@ -1742,157 +1780,369 @@ func (regs *gdbRegisters) byName(name string) uint64 {
 }
 
 func (regs *gdbRegisters) Get(n int) (uint64, error) {
-	reg := x86asm.Reg(n)
 	const (
-		mask8  = 0x000f
-		mask16 = 0x00ff
-		mask32 = 0xffff
+		mask8  = 0xff
+		mask16 = 0xffff
+		mask32 = 0xffffffff
 	)
 
-	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
+	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
@@ -1926,6 +2176,10 @@ func (t *gdbThread) SetSP(sp uint64) error {
 
 // SetDX will set the value of the DX register to the given value.
 func (t *gdbThread) SetDX(dx uint64) error {
+	if t.p.bi.Arch.Name == "arm64" {
+		return fmt.Errorf("not supported")
+	}
+
 	_, _ = t.Registers() // Registes must be loaded first
 	t.regs.setDX(dx)
 	if t.p.gcmdok {
@@ -1990,7 +2244,7 @@ func (regs *gdbRegisters) Slice(floatingPoint bool) ([]proc.Register, error) {
 
 func (regs *gdbRegisters) Copy() (proc.Registers, error) {
 	savedRegs := &gdbRegisters{}
-	savedRegs.init(regs.regsInfo)
+	savedRegs.init(regs.regsInfo, regs.arch)
 	copy(savedRegs.buf, regs.buf)
 	return savedRegs, nil
 }
diff --git a/pkg/proc/gdbserial/gdbserver_conn.go b/pkg/proc/gdbserial/gdbserver_conn.go
index fbd3aea5..d2936137 100644
--- a/pkg/proc/gdbserial/gdbserver_conn.go
+++ b/pkg/proc/gdbserial/gdbserver_conn.go
@@ -48,16 +48,6 @@ type gdbConn struct {
 	log *logrus.Entry
 }
 
-const (
-	regnamePC     = "rip"
-	regnameCX     = "rcx"
-	regnameSP     = "rsp"
-	regnameDX     = "rdx"
-	regnameBP     = "rbp"
-	regnameFsBase = "fs_base"
-	regnameGsBase = "gs_base"
-)
-
 var ErrTooManyAttempts = errors.New("too many transmit attempts")
 
 // GdbProtocolError is an error response (Exx) of Gdb Remote Serial Protocol
@@ -261,6 +251,7 @@ func (conn *gdbConn) readTargetXml() (err error) {
 		}
 		regnum++
 	}
+
 	if !pcFound {
 		return errors.New("could not find RIP register")
 	}
@@ -270,6 +261,7 @@ func (conn *gdbConn) readTargetXml() (err error) {
 	if !cxFound {
 		return errors.New("could not find RCX register")
 	}
+
 	return nil
 }
 
@@ -412,18 +404,18 @@ func (conn *gdbConn) qXfer(kind, annex string, binary bool) ([]byte, error) {
 	return out, nil
 }
 
-// setBreakpoint executes a 'Z' (insert breakpoint) command of type '0' and kind '1'
+// setBreakpoint executes a 'Z' (insert breakpoint) command of type '0' and kind '1' or '4'
 func (conn *gdbConn) setBreakpoint(addr uint64) error {
 	conn.outbuf.Reset()
-	fmt.Fprintf(&conn.outbuf, "$Z0,%x,1", addr)
+	fmt.Fprintf(&conn.outbuf, "$Z0,%x,%d", addr, breakpointKind)
 	_, err := conn.exec(conn.outbuf.Bytes(), "set breakpoint")
 	return err
 }
 
-// clearBreakpoint executes a 'z' (remove breakpoint) command of type '0' and kind '1'
+// clearBreakpoint executes a 'z' (remove breakpoint) command of type '0' and kind '1' or '4'
 func (conn *gdbConn) clearBreakpoint(addr uint64) error {
 	conn.outbuf.Reset()
-	fmt.Fprintf(&conn.outbuf, "$z0,%x,1", addr)
+	fmt.Fprintf(&conn.outbuf, "$z0,%x,%d", addr, breakpointKind)
 	_, err := conn.exec(conn.outbuf.Bytes(), "clear breakpoint")
 	return err
 }
@@ -632,7 +624,7 @@ func (conn *gdbConn) step(threadID string, tu *threadUpdater, ignoreFaultSignal
 	}
 }
 
-var threadBlockedError = errors.New("thread blocked")
+var errThreadBlocked = errors.New("thread blocked")
 
 func (conn *gdbConn) waitForvContStop(context string, threadID string, tu *threadUpdater) (string, uint8, error) {
 	count := 0
@@ -655,7 +647,7 @@ func (conn *gdbConn) waitForvContStop(context string, threadID string, tu *threa
 			}
 			count++
 		} else if failed {
-			return "", 0, threadBlockedError
+			return "", 0, errThreadBlocked
 		} else if err != nil {
 			return "", 0, err
 		} else {
@@ -999,8 +991,9 @@ type imageList struct {
 }
 
 type imageDescription struct {
-	Pathname   string     `json:"pathname"`
-	MachHeader machHeader `json:"mach_header"`
+	LoadAddress uint64     `json:"load_address"`
+	Pathname    string     `json:"pathname"`
+	MachHeader  machHeader `json:"mach_header"`
 }
 
 type machHeader struct {
@@ -1267,7 +1260,10 @@ func checksumok(packet, checksumBuf []byte) bool {
 	if err != nil {
 		return false
 	}
-	return sum == uint8(tgt)
+
+	tgt8 := uint8(tgt)
+
+	return sum == tgt8
 }
 
 func checksum(packet []byte) (sum uint8) {
diff --git a/pkg/proc/gdbserial/gdbserver_conn_386.go b/pkg/proc/gdbserial/gdbserver_conn_386.go
new file mode 100644
index 00000000..495998fe
--- /dev/null
+++ b/pkg/proc/gdbserial/gdbserver_conn_386.go
@@ -0,0 +1,15 @@
+//+build 386
+
+package gdbserial
+
+const (
+	regnamePC     = "rip"
+	regnameCX     = "rcx"
+	regnameSP     = "rsp"
+	regnameDX     = "rdx"
+	regnameBP     = "rbp"
+	regnameFsBase = "fs_base"
+	regnameGsBase = "gs_base"
+
+	breakpointKind = 1
+)
diff --git a/pkg/proc/gdbserial/gdbserver_conn_amd64.go b/pkg/proc/gdbserial/gdbserver_conn_amd64.go
new file mode 100644
index 00000000..d837948f
--- /dev/null
+++ b/pkg/proc/gdbserial/gdbserver_conn_amd64.go
@@ -0,0 +1,15 @@
+//+build amd64
+
+package gdbserial
+
+const (
+	regnamePC     = "rip"
+	regnameCX     = "rcx"
+	regnameSP     = "rsp"
+	regnameDX     = "rdx"
+	regnameBP     = "rbp"
+	regnameFsBase = "fs_base"
+	regnameGsBase = "gs_base"
+
+	breakpointKind = 1
+)
diff --git a/pkg/proc/gdbserial/gdbserver_conn_arm64.go b/pkg/proc/gdbserial/gdbserver_conn_arm64.go
new file mode 100644
index 00000000..e142cd56
--- /dev/null
+++ b/pkg/proc/gdbserial/gdbserver_conn_arm64.go
@@ -0,0 +1,16 @@
+//+build arm64
+
+package gdbserial
+
+const (
+	regnamePC = "pc"
+	regnameCX = "x0"
+	regnameSP = "sp"
+	regnameBP = "fp"
+
+	// not needed but needs to be declared
+	regnameFsBase = ""
+	regnameDX     = ""
+
+	breakpointKind = 4
+)
diff --git a/pkg/proc/native/proc_linux.go b/pkg/proc/native/proc_linux.go
index 40255276..78e92344 100644
--- a/pkg/proc/native/proc_linux.go
+++ b/pkg/proc/native/proc_linux.go
@@ -187,21 +187,33 @@ func initialize(dbp *nativeProcess) error {
 }
 
 // kill kills the target process.
-func (dbp *nativeProcess) kill() (err error) {
+func (dbp *nativeProcess) kill() error {
 	if dbp.exited {
 		return nil
 	}
 	if !dbp.threads[dbp.pid].Stopped() {
 		return errors.New("process must be stopped in order to kill it")
 	}
-	if err = sys.Kill(-dbp.pid, sys.SIGKILL); err != nil {
+	if err := sys.Kill(-dbp.pid, sys.SIGKILL); err != nil {
 		return errors.New("could not deliver signal " + err.Error())
 	}
-	if _, _, err = dbp.wait(dbp.pid, 0); err != nil {
-		return
+	// wait for other threads first or the thread group leader (dbp.pid) will never exit.
+	for threadID := range dbp.threads {
+		if threadID != dbp.pid {
+			dbp.wait(threadID, 0)
+		}
+	}
+	for {
+		wpid, status, err := dbp.wait(dbp.pid, 0)
+		if err != nil {
+			return err
+		}
+		if wpid == dbp.pid && status != nil && status.Signaled() && status.Signal() == sys.SIGKILL {
+			dbp.postExit()
+			return err
+		}
 	}
-	dbp.postExit()
-	return
+	return nil
 }
 
 func (dbp *nativeProcess) requestManualStop() (err error) {
@@ -386,10 +398,15 @@ func (dbp *nativeProcess) trapWaitInternal(pid int, options trapWaitOptions) (*n
 			th.os.running = false
 			return th, nil
 		} else if err := th.resumeWithSig(int(status.StopSignal())); err != nil {
-			if options&trapWaitDontCallExitGuard != 0 {
+			if err != sys.ESRCH {
 				return nil, err
 			}
-			return nil, dbp.exitGuard(err)
+			// do the same thing we do if a thread quit
+			if wpid == dbp.pid {
+				dbp.postExit()
+				return nil, proc.ErrProcessExited{Pid: wpid, Status: status.ExitStatus()}
+			}
+			delete(dbp.threads, wpid)
 		}
 	}
 }
@@ -591,9 +608,10 @@ func (dbp *nativeProcess) stop(trapthread *nativeThread) (*nativeThread, error)
 	}
 
 	if switchTrapthread {
+		trapthreadID := trapthread.ID
 		trapthread = nil
 		for _, th := range dbp.threads {
-			if th.os.setbp && th.ThreadID() != trapthread.ThreadID() {
+			if th.os.setbp && th.ThreadID() != trapthreadID {
 				return th, nil
 			}
 		}
diff --git a/pkg/proc/native/proc_windows.go b/pkg/proc/native/proc_windows.go
index d9ee5999..baba658b 100644
--- a/pkg/proc/native/proc_windows.go
+++ b/pkg/proc/native/proc_windows.go
@@ -85,9 +85,11 @@ func initialize(dbp *nativeProcess) error {
 	// Suspend all threads so that the call to _ContinueDebugEvent will
 	// not resume the target.
 	for _, thread := range dbp.threads {
-		_, err := _SuspendThread(thread.os.hThread)
-		if err != nil {
-			return err
+		if !thread.os.dbgUiRemoteBreakIn {
+			_, err := _SuspendThread(thread.os.hThread)
+			if err != nil {
+				return err
+			}
 		}
 	}
 
@@ -195,7 +197,7 @@ func (dbp *nativeProcess) updateThreadList() error {
 	return nil
 }
 
-func (dbp *nativeProcess) addThread(hThread syscall.Handle, threadID int, attach, suspendNewThreads bool) (*nativeThread, error) {
+func (dbp *nativeProcess) addThread(hThread syscall.Handle, threadID int, attach, suspendNewThreads bool, dbgUiRemoteBreakIn bool) (*nativeThread, error) {
 	if thread, ok := dbp.threads[threadID]; ok {
 		return thread, nil
 	}
@@ -204,12 +206,13 @@ func (dbp *nativeProcess) addThread(hThread syscall.Handle, threadID int, attach
 		dbp: dbp,
 		os:  new(osSpecificDetails),
 	}
+	thread.os.dbgUiRemoteBreakIn = dbgUiRemoteBreakIn
 	thread.os.hThread = hThread
 	dbp.threads[threadID] = thread
 	if dbp.memthread == nil {
 		dbp.memthread = dbp.threads[threadID]
 	}
-	if suspendNewThreads {
+	if suspendNewThreads && !dbgUiRemoteBreakIn {
 		_, err := _SuspendThread(thread.os.hThread)
 		if err != nil {
 			return nil, err
@@ -261,14 +264,16 @@ func (dbp *nativeProcess) waitForDebugEvent(flags waitForDebugEventFlags) (threa
 			}
 			dbp.os.entryPoint = uint64(debugInfo.BaseOfImage)
 			dbp.os.hProcess = debugInfo.Process
-			_, err = dbp.addThread(debugInfo.Thread, int(debugEvent.ThreadId), false, flags&waitSuspendNewThreads != 0)
+			_, err = dbp.addThread(debugInfo.Thread, int(debugEvent.ThreadId), false,
+				flags&waitSuspendNewThreads != 0, debugInfo.StartAddress == dbgUiRemoteBreakin.Addr())
 			if err != nil {
 				return 0, 0, err
 			}
 			break
 		case _CREATE_THREAD_DEBUG_EVENT:
 			debugInfo := (*_CREATE_THREAD_DEBUG_INFO)(unionPtr)
-			_, err = dbp.addThread(debugInfo.Thread, int(debugEvent.ThreadId), false, flags&waitSuspendNewThreads != 0)
+			_, err = dbp.addThread(debugInfo.Thread, int(debugEvent.ThreadId), false,
+				flags&waitSuspendNewThreads != 0, debugInfo.StartAddress == dbgUiRemoteBreakin.Addr())
 			if err != nil {
 				return 0, 0, err
 			}
@@ -430,9 +435,11 @@ func (dbp *nativeProcess) stop(trapthread *nativeThread) (*nativeThread, error)
 	}
 
 	for _, thread := range dbp.threads {
-		_, err := _SuspendThread(thread.os.hThread)
-		if err != nil {
-			return nil, err
+		if !thread.os.dbgUiRemoteBreakIn {
+			_, err := _SuspendThread(thread.os.hThread)
+			if err != nil {
+				return nil, err
+			}
 		}
 	}
 
diff --git a/pkg/proc/native/support_sentinel.go b/pkg/proc/native/support_sentinel.go
index 8dcdb5a8..d627be9a 100644
--- a/pkg/proc/native/support_sentinel.go
+++ b/pkg/proc/native/support_sentinel.go
@@ -1,5 +1,5 @@
 // This file is used to detect build on unsupported GOOS/GOARCH combinations.
 
-//+build !linux,!darwin,!windows,!freebsd linux,!amd64,!arm64,!386 darwin,!amd64 windows,!amd64 freebsd,!amd64
+//+build !linux,!darwin,!windows,!freebsd linux,!amd64,!arm64,!386 darwin,!amd64,!arm64 windows,!amd64 freebsd,!amd64
 
 package your_operating_system_and_architecture_combination_is_not_supported_by_delve
diff --git a/pkg/proc/native/threads_windows.go b/pkg/proc/native/threads_windows.go
index 887a639d..52662c7e 100644
--- a/pkg/proc/native/threads_windows.go
+++ b/pkg/proc/native/threads_windows.go
@@ -16,7 +16,8 @@ type waitStatus sys.WaitStatus
 // osSpecificDetails holds information specific to the Windows
 // operating system / kernel.
 type osSpecificDetails struct {
-	hThread syscall.Handle
+	hThread            syscall.Handle
+	dbgUiRemoteBreakIn bool // whether thread is an auxiliary DbgUiRemoteBreakIn thread created by Windows
 }
 
 func (t *nativeThread) singleStep() error {
@@ -77,9 +78,11 @@ func (t *nativeThread) singleStep() error {
 	}
 
 	for i := 0; i < suspendcnt; i++ {
-		_, err = _SuspendThread(t.os.hThread)
-		if err != nil {
-			return err
+		if !t.os.dbgUiRemoteBreakIn {
+			_, err = _SuspendThread(t.os.hThread)
+			if err != nil {
+				return err
+			}
 		}
 	}
 
diff --git a/pkg/proc/native/zsyscall_windows.go b/pkg/proc/native/zsyscall_windows.go
index 753528b8..a2950bcf 100644
--- a/pkg/proc/native/zsyscall_windows.go
+++ b/pkg/proc/native/zsyscall_windows.go
@@ -14,6 +14,7 @@ var (
 	modkernel32 = syscall.NewLazyDLL("kernel32.dll")
 
 	procNtQueryInformationThread   = modntdll.NewProc("NtQueryInformationThread")
+	dbgUiRemoteBreakin             = modntdll.NewProc("DbgUiRemoteBreakin")
 	procGetThreadContext           = modkernel32.NewProc("GetThreadContext")
 	procSetThreadContext           = modkernel32.NewProc("SetThreadContext")
 	procSuspendThread              = modkernel32.NewProc("SuspendThread")
diff --git a/pkg/proc/variables.go b/pkg/proc/variables.go
index 1db26346..3876db08 100644
--- a/pkg/proc/variables.go
+++ b/pkg/proc/variables.go
@@ -850,7 +850,10 @@ func (v *Variable) parseG() (*G, error) {
 	gopc := loadInt64Maybe("gopc")
 	startpc := loadInt64Maybe("startpc")
 	waitSince := loadInt64Maybe("waitsince")
-	waitReason := loadInt64Maybe("waitreason")
+	waitReason := int64(0)
+	if producer := v.bi.Producer(); producer != "" && goversion.ProducerAfterOrEqual(producer, 1, 11) {
+		waitReason = loadInt64Maybe("waitreason")
+	}
 	var stackhi, stacklo uint64
 	if stackVar := v.loadFieldNamed("stack"); stackVar != nil {
 		if stackhiVar := stackVar.fieldVariable("hi"); stackhiVar != nil {
diff --git a/pkg/terminal/command.go b/pkg/terminal/command.go
index 1761f3c1..c68dccbe 100644
--- a/pkg/terminal/command.go
+++ b/pkg/terminal/command.go
@@ -20,6 +20,7 @@ import (
 	"strconv"
 	"strings"
 	"text/tabwriter"
+	"time"
 
 	"github.com/cosiner/argv"
 	"github.com/go-delve/delve/pkg/locspec"
@@ -723,7 +724,12 @@ func goroutines(t *Term, ctx callContext, argstr string) error {
 		gslen = 0
 		gs    []*api.Goroutine
 	)
+	t.longCommandStart()
 	for start >= 0 {
+		if t.longCommandCanceled() {
+			fmt.Printf("interrupted\n")
+			return nil
+		}
 		gs, start, err = t.client.ListGoroutines(start, goroutineBatchSize)
 		if err != nil {
 			return err
@@ -917,11 +923,58 @@ func (t *Term) formatGoroutine(g *api.Goroutine, fgl formatGoroutineLoc) string
 		locname = "Start"
 		loc = g.StartLoc
 	}
-	thread := ""
+
+	buf := new(strings.Builder)
+	fmt.Fprintf(buf, "%d - %s: %s", g.ID, locname, t.formatLocation(loc))
 	if g.ThreadID != 0 {
-		thread = fmt.Sprintf(" (thread %d)", g.ThreadID)
+		fmt.Fprintf(buf, " (thread %d)", g.ThreadID)
 	}
-	return fmt.Sprintf("%d - %s: %s%s", g.ID, locname, t.formatLocation(loc), thread)
+
+	if (g.Status == api.GoroutineWaiting || g.Status == api.GoroutineSyscall) && g.WaitReason != 0 {
+		var wr string
+		if g.WaitReason > 0 && g.WaitReason < int64(len(waitReasonStrings)) {
+			wr = waitReasonStrings[g.WaitReason]
+		} else {
+			wr = fmt.Sprintf("unknown wait reason %d", g.WaitReason)
+		}
+		fmt.Fprintf(buf, " [%s", wr)
+		if g.WaitSince > 0 {
+			fmt.Fprintf(buf, " %s", time.Since(time.Unix(0, g.WaitSince)).String())
+		}
+		fmt.Fprintf(buf, "]")
+	}
+
+	return buf.String()
+}
+
+var waitReasonStrings = [...]string{
+	"",
+	"GC assist marking",
+	"IO wait",
+	"chan receive (nil chan)",
+	"chan send (nil chan)",
+	"dumping heap",
+	"garbage collection",
+	"garbage collection scan",
+	"panicwait",
+	"select",
+	"select (no cases)",
+	"GC assist wait",
+	"GC sweep wait",
+	"GC scavenge wait",
+	"chan receive",
+	"chan send",
+	"finalizer wait",
+	"force gc (idle)",
+	"semacquire",
+	"sleep",
+	"sync.Cond.Wait",
+	"timer goroutine (idle)",
+	"trace reader (blocked)",
+	"wait for GC cycle",
+	"GC worker (idle)",
+	"preempted",
+	"debug call",
 }
 
 func writeGoroutineLong(t *Term, w io.Writer, g *api.Goroutine, prefix string) {
diff --git a/pkg/terminal/terminal.go b/pkg/terminal/terminal.go
index c042859f..f61cec9d 100644
--- a/pkg/terminal/terminal.go
+++ b/pkg/terminal/terminal.go
@@ -66,6 +66,9 @@ type Term struct {
 	// should be resumed before quitting.
 	quitContinue bool
 
+	longCommandMu         sync.Mutex
+	longCommandCancelFlag bool
+
 	quittingMutex sync.Mutex
 	quitting      bool
 }
@@ -123,6 +126,7 @@ func (t *Term) Close() {
 
 func (t *Term) sigintGuard(ch <-chan os.Signal, multiClient bool) {
 	for range ch {
+		t.longCommandCancel()
 		t.starlarkEnv.Cancel()
 		state, err := t.client.GetStateNonBlocking()
 		if err == nil && state.Recording {
@@ -481,6 +485,24 @@ func (t *Term) onStop() {
 	t.printDisplays()
 }
 
+func (t *Term) longCommandCancel() {
+	t.longCommandMu.Lock()
+	defer t.longCommandMu.Unlock()
+	t.longCommandCancelFlag = true
+}
+
+func (t *Term) longCommandStart() {
+	t.longCommandMu.Lock()
+	defer t.longCommandMu.Unlock()
+	t.longCommandCancelFlag = false
+}
+
+func (t *Term) longCommandCanceled() bool {
+	t.longCommandMu.Lock()
+	defer t.longCommandMu.Unlock()
+	return t.longCommandCancelFlag
+}
+
 // isErrProcessExited returns true if `err` is an RPC error equivalent of proc.ErrProcessExited
 func isErrProcessExited(err error) bool {
 	rpcError, ok := err.(rpc.ServerError)
diff --git a/service/api/conversions.go b/service/api/conversions.go
index 76151528..29a59f55 100644
--- a/service/api/conversions.go
+++ b/service/api/conversions.go
@@ -298,6 +298,7 @@ func ConvertGoroutine(g *proc.G) *Goroutine {
 		WaitSince:      g.WaitSince,
 		WaitReason:     g.WaitReason,
 		Labels:         g.Labels(),
+		Status:         g.Status,
 	}
 }
 
diff --git a/service/api/prettyprint.go b/service/api/prettyprint.go
index 564bae57..e8ec44df 100644
--- a/service/api/prettyprint.go
+++ b/service/api/prettyprint.go
@@ -421,7 +421,7 @@ func PrettyExamineMemory(address uintptr, memArea []byte, isLittleEndian bool, f
 			}
 		}
 		fmt.Fprintln(w, "")
-		address += uintptr(cols)
+		address += uintptr(cols * colBytes)
 	}
 	w.Flush()
 	return b.String()
diff --git a/service/api/types.go b/service/api/types.go
index a64a5da9..7e4bdbd4 100644
--- a/service/api/types.go
+++ b/service/api/types.go
@@ -314,6 +314,7 @@ type Goroutine struct {
 	StartLoc Location `json:"startLoc"`
 	// ID of the associated thread for running goroutines
 	ThreadID   int    `json:"threadID"`
+	Status     uint64 `json:"status"`
 	WaitSince  int64  `json:"waitSince"`
 	WaitReason int64  `json:"waitReason"`
 	Unreadable string `json:"unreadable"`
@@ -321,6 +322,11 @@ type Goroutine struct {
 	Labels map[string]string `json:"labels,omitempty"`
 }
 
+const (
+	GoroutineWaiting = proc.Gwaiting
+	GoroutineSyscall = proc.Gsyscall
+)
+
 // DebuggerCommand is a command which changes the debugger's execution state.
 type DebuggerCommand struct {
 	// Name is the command to run.
diff --git a/service/dap/error_ids.go b/service/dap/error_ids.go
index ddb88c83..8b1ac928 100644
--- a/service/dap/error_ids.go
+++ b/service/dap/error_ids.go
@@ -11,7 +11,7 @@ const (
 	// Where applicable and for consistency only,
 	// values below are inspired the original vscode-go debug adaptor.
 	FailedToLaunch             = 3000
-	FailedtoAttach             = 3001
+	FailedToAttach             = 3001
 	UnableToSetBreakpoints     = 2002
 	UnableToDisplayThreads     = 2003
 	UnableToProduceStackTrace  = 2004
diff --git a/service/dap/handles.go b/service/dap/handles.go
index ec9bfe1e..d01baf91 100644
--- a/service/dap/handles.go
+++ b/service/dap/handles.go
@@ -14,6 +14,18 @@ type handlesMap struct {
 	handleToVal map[int]interface{}
 }
 
+type fullyQualifiedVariable struct {
+	*proc.Variable
+	// A way to load this variable by either using all names in the hierarchic
+	// sequence above this variable (most readable when referenced by the UI)
+	// if available or a special expression based on:
+	// https://github.com/go-delve/delve/blob/master/Documentation/api/ClientHowto.md#loading-more-of-a-variable
+	// Empty if the variable cannot or should not be reloaded.
+	fullyQualifiedNameOrExpr string
+	// True if this represents variable scope
+	isScope bool
+}
+
 func newHandlesMap() *handlesMap {
 	return &handlesMap{startHandle, make(map[int]interface{})}
 }
@@ -43,16 +55,16 @@ func newVariablesHandlesMap() *variablesHandlesMap {
 	return &variablesHandlesMap{newHandlesMap()}
 }
 
-func (hs *variablesHandlesMap) create(value *proc.Variable) int {
+func (hs *variablesHandlesMap) create(value *fullyQualifiedVariable) int {
 	return hs.m.create(value)
 }
 
-func (hs *variablesHandlesMap) get(handle int) (*proc.Variable, bool) {
+func (hs *variablesHandlesMap) get(handle int) (*fullyQualifiedVariable, bool) {
 	v, ok := hs.m.get(handle)
 	if !ok {
 		return nil, false
 	}
-	return v.(*proc.Variable), true
+	return v.(*fullyQualifiedVariable), true
 }
 
 func (hs *variablesHandlesMap) reset() {
diff --git a/service/dap/server.go b/service/dap/server.go
index 30127892..7206d133 100644
--- a/service/dap/server.go
+++ b/service/dap/server.go
@@ -106,6 +106,23 @@ 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) {
+	stop, ok := request.GetArguments()["stopOnEntry"].(bool)
+	if ok {
+		s.args.stopOnEntry = stop
+	}
+	depth, ok := request.GetArguments()["stackTraceDepth"].(float64)
+	if ok && depth > 0 {
+		s.args.stackTraceDepth = int(depth)
+	}
+	globals, ok := request.GetArguments()["showGlobalVariables"].(bool)
+	if ok {
+		s.args.showGlobalVariables = globals
+	}
+}
+
 // 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
@@ -233,7 +250,6 @@ func (s *Server) handleRequest(request dap.Message) {
 		s.onLaunchRequest(request)
 	case *dap.AttachRequest:
 		// Required
-		// TODO: implement this request in V0
 		s.onAttachRequest(request)
 	case *dap.DisconnectRequest:
 		// Required
@@ -384,6 +400,7 @@ func (s *Server) onInitializeRequest(request *dap.InitializeRequest) {
 	response.Body.SupportsConfigurationDoneRequest = true
 	response.Body.SupportsConditionalBreakpoints = true
 	response.Body.SupportsDelayedStackTraceLoading = true
+	response.Body.SupportTerminateDebuggee = true
 	// TODO(polina): support this to match vscode-go functionality
 	response.Body.SupportsSetVariable = false
 	// TODO(polina): support these requests in addition to vscode-go feature parity
@@ -465,19 +482,7 @@ func (s *Server) onLaunchRequest(request *dap.LaunchRequest) {
 		return
 	}
 
-	// If user-specified, overwrite the defaults for optional args.
-	stop, ok := request.Arguments["stopOnEntry"].(bool)
-	if ok {
-		s.args.stopOnEntry = stop
-	}
-	depth, ok := request.Arguments["stackTraceDepth"].(float64)
-	if ok && depth > 0 {
-		s.args.stackTraceDepth = int(depth)
-	}
-	globals, ok := request.Arguments["showGlobalVariables"].(bool)
-	if ok {
-		s.args.showGlobalVariables = globals
-	}
+	s.setLaunchAttachArgs(request)
 
 	var targetArgs []string
 	args, ok := request.Arguments["args"]
@@ -528,7 +533,14 @@ func (s *Server) onDisconnectRequest(request *dap.DisconnectRequest) {
 		if err != nil {
 			s.log.Error(err)
 		}
+		// We always kill launched programs
 		kill := s.config.Debugger.AttachPid == 0
+		// In case of attach, we leave the program
+		// running but 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 {
 			s.log.Error(err)
@@ -685,10 +697,41 @@ func (s *Server) onThreadsRequest(request *dap.ThreadsRequest) {
 	s.send(response)
 }
 
-// onAttachRequest sends a not-yet-implemented error response.
+// onAttachRequest handles 'attach' request.
 // This is a mandatory request to support.
-func (s *Server) onAttachRequest(request *dap.AttachRequest) { // TODO V0
-	s.sendNotYetImplementedErrorResponse(request.Request)
+func (s *Server) onAttachRequest(request *dap.AttachRequest) {
+	mode, ok := request.Arguments["mode"]
+	if !ok || mode == "" {
+		mode = "local"
+	}
+	if mode == "local" {
+		pid, ok := request.Arguments["processId"].(float64)
+		if !ok || pid == 0 {
+			s.sendErrorResponse(request.Request,
+				FailedToAttach, "Failed to attach",
+				"The 'processId' attribute is missing in debug configuration")
+			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())
+			return
+		}
+	} else {
+		// TODO(polina): support 'remote' mode with 'host' and 'port'
+		s.sendErrorResponse(request.Request,
+			FailedToAttach, "Failed to attach",
+			fmt.Sprintf("Unsupported 'mode' value %q in debug configuration", mode))
+		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.AttachResponse{Response: *newResponse(request.Request)})
 }
 
 // onNextRequest handles 'next' request.
@@ -789,7 +832,7 @@ func (s *Server) onScopesRequest(request *dap.ScopesRequest) {
 		s.sendErrorResponse(request.Request, UnableToListArgs, "Unable to list args", err.Error())
 		return
 	}
-	argScope := &proc.Variable{Name: "Arguments", Children: slicePtrVarToSliceVar(args)}
+	argScope := &fullyQualifiedVariable{&proc.Variable{Name: "Arguments", Children: slicePtrVarToSliceVar(args)}, "", true}
 
 	// Retrieve local variables
 	locals, err := s.debugger.LocalVariables(goid, frame, 0, cfg)
@@ -797,7 +840,7 @@ func (s *Server) onScopesRequest(request *dap.ScopesRequest) {
 		s.sendErrorResponse(request.Request, UnableToListLocals, "Unable to list locals", err.Error())
 		return
 	}
-	locScope := &proc.Variable{Name: "Locals", Children: slicePtrVarToSliceVar(locals)}
+	locScope := &fullyQualifiedVariable{&proc.Variable{Name: "Locals", Children: slicePtrVarToSliceVar(locals)}, "", true}
 
 	// TODO(polina): Annotate shadowed variables
 
@@ -832,10 +875,10 @@ func (s *Server) onScopesRequest(request *dap.ScopesRequest) {
 			globals[i].Name = strings.TrimPrefix(g.Name, currPkg+".")
 		}
 
-		globScope := &proc.Variable{
+		globScope := &fullyQualifiedVariable{&proc.Variable{
 			Name:     fmt.Sprintf("Globals (package %s)", currPkg),
 			Children: slicePtrVarToSliceVar(globals),
-		}
+		}, currPkg, true}
 		scopeGlobals := dap.Scope{Name: globScope.Name, VariablesReference: s.variableHandles.create(globScope)}
 		scopes = append(scopes, scopeGlobals)
 	}
@@ -872,30 +915,48 @@ func (s *Server) onVariablesRequest(request *dap.VariablesRequest) {
 			// A map will have twice as many children as there are key-value elements.
 			kvIndex := i / 2
 			// Process children in pairs: even indices are map keys, odd indices are values.
-			key, keyref := s.convertVariable(&v.Children[i])
-			val, valref := s.convertVariable(&v.Children[i+1])
+			keyv, valv := &v.Children[i], &v.Children[i+1]
+			keyexpr := fmt.Sprintf("(*(*%q)(%#x))", keyv.TypeString(), keyv.Addr)
+			valexpr := fmt.Sprintf("%s[%s]", v.fullyQualifiedNameOrExpr, keyexpr)
+			switch keyv.Kind {
+			// For value expression, use the key value, not the corresponding expression if the key is a scalar.
+			case reflect.Bool, reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128,
+				reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
+				reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+				valexpr = fmt.Sprintf("%s[%s]", v.fullyQualifiedNameOrExpr, api.VariableValueAsString(keyv))
+			case reflect.String:
+				if key := constant.StringVal(keyv.Value); keyv.Len == int64(len(key)) { // fully loaded
+					valexpr = fmt.Sprintf("%s[%q]", v.fullyQualifiedNameOrExpr, key)
+				}
+				// TODO(polina): use nil for nil keys of different types
+			}
+			key, keyref := s.convertVariable(keyv, keyexpr)
+			val, valref := s.convertVariable(valv, valexpr)
 			// If key or value or both are scalars, we can use
 			// a single variable to represet key:value format.
 			// Otherwise, we must return separate variables for both.
 			if keyref > 0 && valref > 0 { // Both are not scalars
 				keyvar := dap.Variable{
 					Name:               fmt.Sprintf("[key %d]", kvIndex),
+					EvaluateName:       keyexpr,
 					Value:              key,
 					VariablesReference: keyref,
 				}
 				valvar := dap.Variable{
 					Name:               fmt.Sprintf("[val %d]", kvIndex),
+					EvaluateName:       valexpr,
 					Value:              val,
 					VariablesReference: valref,
 				}
 				children = append(children, keyvar, valvar)
 			} else { // At least one is a scalar
 				kvvar := dap.Variable{
-					Name:  key,
-					Value: val,
+					Name:         key,
+					EvaluateName: valexpr,
+					Value:        val,
 				}
 				if keyref != 0 { // key is a type to be expanded
-					kvvar.Name = fmt.Sprintf("%s[%d]", kvvar.Name, kvIndex) // Make the name unique
+					kvvar.Name = fmt.Sprintf("%s(%#x)", kvvar.Name, keyv.Addr) // Make the name unique
 					kvvar.VariablesReference = keyref
 				} else if valref != 0 { // val is a type to be expanded
 					kvvar.VariablesReference = valref
@@ -906,35 +967,52 @@ func (s *Server) onVariablesRequest(request *dap.VariablesRequest) {
 	case reflect.Slice, reflect.Array:
 		children = make([]dap.Variable, len(v.Children))
 		for i := range v.Children {
-			c := &v.Children[i]
-			value, varref := s.convertVariable(c)
+			cfqname := fmt.Sprintf("%s[%d]", v.fullyQualifiedNameOrExpr, i)
+			cvalue, cvarref := s.convertVariable(&v.Children[i], cfqname)
 			children[i] = dap.Variable{
 				Name:               fmt.Sprintf("[%d]", i),
-				Value:              value,
-				VariablesReference: varref,
+				EvaluateName:       cfqname,
+				Value:              cvalue,
+				VariablesReference: cvarref,
 			}
 		}
 	default:
 		children = make([]dap.Variable, len(v.Children))
 		for i := range v.Children {
 			c := &v.Children[i]
-			value, variablesReference := s.convertVariable(c)
+			cfqname := fmt.Sprintf("%s.%s", v.fullyQualifiedNameOrExpr, c.Name)
+
+			if strings.HasPrefix(c.Name, "~") || strings.HasPrefix(c.Name, ".") {
+				cfqname = ""
+			} else if v.isScope && v.fullyQualifiedNameOrExpr == "" {
+				cfqname = c.Name
+			} else if v.fullyQualifiedNameOrExpr == "" {
+				cfqname = ""
+			} else if v.Kind == reflect.Interface {
+				cfqname = fmt.Sprintf("%s.(%s)", v.fullyQualifiedNameOrExpr, c.Name) // c is data
+			} else if v.Kind == reflect.Ptr {
+				cfqname = fmt.Sprintf("(*%v)", v.fullyQualifiedNameOrExpr) // c is the nameless pointer value
+			} else if v.Kind == reflect.Complex64 || v.Kind == reflect.Complex128 {
+				cfqname = "" // complex children are not struct fields and can't be accessed directly
+			}
+			cvalue, cvarref := s.convertVariable(c, cfqname)
 			children[i] = dap.Variable{
 				Name:               c.Name,
-				Value:              value,
-				VariablesReference: variablesReference,
+				EvaluateName:       cfqname,
+				Value:              cvalue,
+				VariablesReference: cvarref,
 			}
 		}
 	}
 	response := &dap.VariablesResponse{
 		Response: *newResponse(request.Request),
 		Body:     dap.VariablesResponseBody{Variables: children},
-		// TODO(polina): support evaluateName field
 	}
 	s.send(response)
 }
 
-// convertVariable converts proc.Variable to dap.Variable value and reference.
+// convertVariable converts proc.Variable to dap.Variable value and reference
+// while keeping track of the full qualified name or load expression.
 // Variable reference is used to keep track of the children associated with each
 // variable. It is shared with the host via scopes or evaluate response and is an index
 // into the s.variableHandles map, used to look up variables and their children on
@@ -942,23 +1020,23 @@ func (s *Server) onVariablesRequest(request *dap.VariablesRequest) {
 // variables request can be issued to get the elements of the compound variable. As a
 // custom, a zero reference, reminiscent of a zero pointer, is used to indicate that
 // a scalar variable cannot be "dereferenced" to get its elements (as there are none).
-func (s *Server) convertVariable(v *proc.Variable) (value string, variablesReference int) {
-	return s.convertVariableWithOpts(v, false)
+func (s *Server) convertVariable(v *proc.Variable, qualifiedNameOrExpr string) (value string, variablesReference int) {
+	return s.convertVariableWithOpts(v, qualifiedNameOrExpr, false)
 }
 
 func (s *Server) convertVariableToString(v *proc.Variable) string {
-	val, _ := s.convertVariableWithOpts(v, true)
+	val, _ := s.convertVariableWithOpts(v, "", true)
 	return val
 }
 
-// convertVarialbeWithOpts allows to skip reference generation in case all we need is
+// convertVariableWithOpts allows to skip reference generation in case all we need is
 // a string representation of the variable.
-func (s *Server) convertVariableWithOpts(v *proc.Variable, skipRef bool) (value string, variablesReference int) {
+func (s *Server) convertVariableWithOpts(v *proc.Variable, qualifiedNameOrExpr string, skipRef bool) (value string, variablesReference int) {
 	maybeCreateVariableHandle := func(v *proc.Variable) int {
 		if skipRef {
 			return 0
 		}
-		return s.variableHandles.create(v)
+		return s.variableHandles.create(&fullyQualifiedVariable{v, qualifiedNameOrExpr, false /*not a scope*/})
 	}
 	if v.Unreadable != nil {
 		value = fmt.Sprintf("unreadable <%v>", v.Unreadable)
@@ -1180,7 +1258,7 @@ func (s *Server) onEvaluateRequest(request *dap.EvaluateRequest) {
 			}
 			response.Body = dap.EvaluateResponseBody{
 				Result:             strings.TrimRight(retVarsAsStr, ", "),
-				VariablesReference: s.variableHandles.create(retVarsAsVar),
+				VariablesReference: s.variableHandles.create(&fullyQualifiedVariable{retVarsAsVar, "", false /*not a scope*/}),
 			}
 		}
 	} else { // {expression}
@@ -1189,7 +1267,9 @@ func (s *Server) onEvaluateRequest(request *dap.EvaluateRequest) {
 			s.sendErrorResponseWithOpts(request.Request, UnableToEvaluateExpression, "Unable to evaluate expression", err.Error(), showErrorToUser)
 			return
 		}
-		exprVal, exprRef := s.convertVariable(exprVar)
+		// TODO(polina): as far as I can tell, evaluateName is ignored by vscode for expression variables.
+		// Should it be skipped alltogether for all levels?
+		exprVal, exprRef := s.convertVariable(exprVar, fmt.Sprintf("(%s)", request.Arguments.Expression))
 		response.Body = dap.EvaluateResponseBody{Result: exprVal, VariablesReference: exprRef}
 	}
 	s.send(response)


Test patch:
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index ba12c1d2..1e94da3b 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -10,10 +10,6 @@ jobs:
         include:
           - go: 1.15
             os: macos-latest
-          - go: 1.14
-            os: ubuntu-latest
-          - go: 1.13
-            os: ubuntu-latest
     steps:
       - uses: actions/checkout@v2
       - uses: actions/setup-go@v1
diff --git a/Documentation/usage/dlv_test.md b/Documentation/usage/dlv_test.md
index ab484ff6..e86207cd 100644
--- a/Documentation/usage/dlv_test.md
+++ b/Documentation/usage/dlv_test.md
@@ -29,7 +29,7 @@ dlv test [package]
       --allow-non-terminal-interactive   Allows interactive sessions of Delve that don't have a terminal as stdin, stdout and stderr
       --api-version int                  Selects API version when headless. New clients should use v2. Can be reset via RPCServer.SetApiVersion. See Documentation/api/json-rpc/README.md. (default 1)
       --backend string                   Backend selection (see 'dlv help backend'). (default "default")
-      --build-flags string               Build flags, to be passed to the compiler.
+      --build-flags string               Build flags, to be passed to the compiler. For example: --build-flags="-tags=integration -mod=vendor -cover -v"
       --check-go-version                 Checks that the version of Go in use is compatible with Delve. (default true)
       --disable-aslr                     Disables address space randomization
       --headless                         Run debug server only, in headless mode.
diff --git a/_scripts/test_linux.sh b/_scripts/test_linux.sh
new file mode 100755
index 00000000..387a1345
--- /dev/null
+++ b/_scripts/test_linux.sh
@@ -0,0 +1,50 @@
+#!/bin/bash
+set -e
+set -x
+
+apt-get -qq update
+apt-get install -y dwz wget make git gcc curl jq
+dwz --version
+
+version=$1
+arch=$2
+
+function getgo {
+	export GOROOT=/usr/local/go/$1
+	if [ ! -d "$GOROOT" ]; then
+		wget -q https://dl.google.com/go/"$1".linux-"${arch}".tar.gz
+		mkdir -p /usr/local/go
+		tar -C /usr/local/go -xzf "$1".linux-"${arch}".tar.gz
+		mv -f /usr/local/go/go "$GOROOT"
+	fi
+}
+
+if [ "$version" = "gotip" ]; then
+	exit 0
+	echo Building Go from tip
+	getgo $(curl https://golang.org/VERSION?m=text)
+	export GOROOT_BOOTSTRAP=$GOROOT
+	export GOROOT=/usr/local/go/go-tip
+	git clone https://go.googlesource.com/go /usr/local/go/go-tip
+	cd /usr/local/go/go-tip/src
+	./make.bash
+	cd -
+else
+	echo Finding latest patch version for $version
+	version=$(curl 'https://golang.org/dl/?mode=json&include=all' | jq '.[].version' --raw-output | egrep ^$version'($|\.|beta|rc)' | head -1)
+	echo "Go $version on $arch"
+	getgo $version
+fi
+
+
+GOPATH=$(pwd)/go
+export GOPATH
+export PATH=$PATH:$GOROOT/bin:$GOPATH/bin
+go version
+
+uname -a
+echo "$PATH"
+echo "$GOROOT"
+echo "$GOPATH"
+cd delve
+make test
diff --git a/_scripts/test_windows.ps1 b/_scripts/test_windows.ps1
new file mode 100644
index 00000000..2011a705
--- /dev/null
+++ b/_scripts/test_windows.ps1
@@ -0,0 +1,69 @@
+param (
+    [Parameter(Mandatory = $true)][string]$version,
+    [Parameter(Mandatory = $true)][string]$arch
+)
+
+# Install MinGW.
+if (-Not(Test-Path "C:\mingw64"))
+{
+    $file = "x86_64-4.9.2-release-win32-seh-rt_v4-rev3.7z"
+    $url = "https://bintray.com/artifact/download/drewwells/generic/$file"
+    Invoke-WebRequest -UserAgent wget -Uri $url -OutFile $file
+    &7z x -oC:\ $file > $null
+}
+
+# Install Procdump
+if (-Not(Test-Path "C:\procdump"))
+{
+    mkdir C:\procdump
+    Invoke-WebRequest -UserAgent wget -Uri https://download.sysinternals.com/files/Procdump.zip -OutFile C:\procdump\procdump.zip
+    &7z x -oC:\procdump\ C:\procdump\procdump.zip > $null
+}
+
+$env:PATH += ";C:\procdump;C:\mingw64\bin"
+
+function GetGo($version) {
+    $env:GOROOT = "C:\go\$version"
+    if (-Not(Test-Path $env:GOROOT))
+    {
+        $file = "$version.windows-$arch.zip"
+        $url = "https://dl.google.com/go/$file"
+        Invoke-WebRequest -UserAgent wget -Uri $url -OutFile $file
+        &7z x -oC:\go $file > $null
+        Move-Item -Path C:\go\go -Destination $env:GOROOT -force
+    }
+}
+
+if ($version -eq "gotip") {
+    Exit 0
+    $latest = Invoke-WebRequest -Uri https://golang.org/VERSION?m=text -UseBasicParsing | Select-Object -ExpandProperty Content
+    GetGo $latest
+    $env:GOROOT_BOOTSTRAP = $env:GOROOT
+    $env:GOROOT = "C:\go\go-tip"
+    Write-Host "Building Go with GOROOT_BOOTSTRAP $env:GOROOT_BOOTSTRAP"
+    if (-Not(Test-Path $env:GOROOT)) {
+        git clone https://go.googlesource.com/go C:\go\go-tip
+        Push-Location -Path C:\go\go-tip\src
+    } else {
+        Push-Location -Path C:\go\go-tip\src
+        git pull
+    }
+    .\make.bat
+    Pop-Location
+} else {
+    # Install Go
+    Write-Host "Finding latest patch version for $version"
+    $version = Invoke-WebRequest -Uri 'https://golang.org/dl/?mode=json&include=all' -UseBasicParsing | foreach {$_.Content} | ConvertFrom-Json | foreach {$_.version} | Select-String -Pattern "^$version($|\.|beta|rc)" | Select-Object -First 1 | foreach {$_.Line}
+    Write-Host "Go $version on $arch"
+    GetGo $version
+}
+
+$env:GOPATH = "C:\gopath"
+$env:PATH += ";$env:GOROOT\bin;$env:GOPATH\bin"
+Write-Host $env:PATH
+Write-Host $env:GOROOT
+Write-Host $env:GOPATH
+
+go version
+go env
+mingw32-make test
diff --git a/cmd/dlv/dlv_test.go b/cmd/dlv/dlv_test.go
index 52ab06b5..8a9874a6 100644
--- a/cmd/dlv/dlv_test.go
+++ b/cmd/dlv/dlv_test.go
@@ -322,21 +322,24 @@ func TestRedirect(t *testing.T) {
 
 	// and detach from and kill the headless instance
 	client := rpc2.NewClient(listenAddr)
-	if err := client.Detach(true); err != nil {
-		t.Fatalf("error detaching from headless instance: %v", err)
-	}
+	_ = client.Detach(true)
 	cmd.Wait()
 }
 
 func checkAutogenDoc(t *testing.T, filename, gencommand string, generated []byte) {
 	saved := slurpFile(t, filepath.Join(projectRoot(), filename))
 
+	saved = bytes.ReplaceAll(saved, []byte("\r\n"), []byte{'\n'})
+	generated = bytes.ReplaceAll(generated, []byte("\r\n"), []byte{'\n'})
+
 	if len(saved) != len(generated) {
+		t.Logf("generated %q saved %q\n", generated, saved)
 		t.Fatalf("%s: needs to be regenerated; run %s", filename, gencommand)
 	}
 
 	for i := range saved {
 		if saved[i] != generated[i] {
+			t.Logf("generated %q saved %q\n", generated, saved)
 			t.Fatalf("%s: needs to be regenerated; run %s", filename, gencommand)
 		}
 	}
diff --git a/pkg/dwarf/line/line_parser_test.go b/pkg/dwarf/line/line_parser_test.go
index f5d0901e..cbb5b71c 100644
--- a/pkg/dwarf/line/line_parser_test.go
+++ b/pkg/dwarf/line/line_parser_test.go
@@ -11,12 +11,14 @@ import (
 	"os"
 	"os/exec"
 	"path/filepath"
+	"runtime"
 	"strings"
 	"testing"
 	"time"
 	"unsafe"
 
 	"github.com/go-delve/delve/pkg/dwarf/godwarf"
+	"github.com/go-delve/delve/pkg/goversion"
 )
 
 var userTestFile string
@@ -113,8 +115,10 @@ func testDebugLinePrologueParser(p string, t *testing.T) {
 			}
 		}
 
-		if len(dbl.IncludeDirs) != 1 {
-			t.Fatal("Include dirs not parsed correctly")
+		if !goversion.VersionAfterOrEqual(runtime.Version(), 1, 16) {
+			if len(dbl.IncludeDirs) != 1 {
+				t.Fatal("Include dirs not parsed correctly")
+			}
 		}
 
 		for _, ln := range dbl.Lookup {
@@ -127,6 +131,7 @@ func testDebugLinePrologueParser(p string, t *testing.T) {
 		}
 
 		for _, n := range dbl.FileNames {
+			t.Logf("file %s\n", n.Path)
 			if strings.Contains(n.Path, "/_fixtures/testnextprog.go") {
 				mainFileFound = true
 				break
diff --git a/pkg/proc/proc_test.go b/pkg/proc/proc_test.go
index 8a410165..3ea5c6e7 100644
--- a/pkg/proc/proc_test.go
+++ b/pkg/proc/proc_test.go
@@ -1302,23 +1302,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)
 			}
 		}
 
diff --git a/pkg/proc/proc_unix_test.go b/pkg/proc/proc_unix_test.go
index f19dd3ba..40034920 100644
--- a/pkg/proc/proc_unix_test.go
+++ b/pkg/proc/proc_unix_test.go
@@ -54,6 +54,8 @@ func TestIssue419(t *testing.T) {
 	for i := 0; i < 2; i++ {
 		err := <-errChan
 
+		t.Logf("error %T %#v\n", err, err)
+
 		if v, ok := err.(errIssue419); ok {
 			assertNoError(v.err, t, "syscall.Kill")
 			continue
diff --git a/service/dap/daptest/client.go b/service/dap/daptest/client.go
index 0f3f8a55..70c1d22e 100644
--- a/service/dap/daptest/client.go
+++ b/service/dap/daptest/client.go
@@ -145,6 +145,11 @@ func (c *Client) ExpectLaunchResponse(t *testing.T) *dap.LaunchResponse {
 	return c.expectReadProtocolMessage(t).(*dap.LaunchResponse)
 }
 
+func (c *Client) ExpectAttachResponse(t *testing.T) *dap.AttachResponse {
+	t.Helper()
+	return c.expectReadProtocolMessage(t).(*dap.AttachResponse)
+}
+
 func (c *Client) ExpectSetExceptionBreakpointsResponse(t *testing.T) *dap.SetExceptionBreakpointsResponse {
 	t.Helper()
 	return c.expectReadProtocolMessage(t).(*dap.SetExceptionBreakpointsResponse)
@@ -332,10 +337,11 @@ func (c *Client) LaunchRequestWithArgs(arguments map[string]interface{}) {
 	c.send(request)
 }
 
-// AttachRequest sends an 'attach' request.
-func (c *Client) AttachRequest() {
+// AttachRequest sends an 'attach' request with the specified
+// arguments.
+func (c *Client) AttachRequest(arguments map[string]interface{}) {
 	request := &dap.AttachRequest{Request: *c.newRequest("attach")}
-	// TODO(polina): populate meaningful arguments
+	request.Arguments = arguments
 	c.send(request)
 }
 
@@ -345,6 +351,14 @@ func (c *Client) DisconnectRequest() {
 	c.send(request)
 }
 
+// DisconnectRequest sends a 'disconnect' request with an option to specify
+// `terminateDebuggee`.
+func (c *Client) DisconnectRequestWithKillOption(kill bool) {
+	request := &dap.DisconnectRequest{Request: *c.newRequest("disconnect")}
+	request.Arguments.TerminateDebuggee = kill
+	c.send(request)
+}
+
 // SetBreakpointsRequest sends a 'setBreakpoints' request.
 func (c *Client) SetBreakpointsRequest(file string, lines []int) {
 	c.SetConditionalBreakpointsRequest(file, lines, nil)
diff --git a/service/dap/server_test.go b/service/dap/server_test.go
index e8d6ad96..8c71bc63 100644
--- a/service/dap/server_test.go
+++ b/service/dap/server_test.go
@@ -1,10 +1,12 @@
 package dap
 
 import (
+	"bufio"
 	"flag"
 	"io"
 	"net"
 	"os"
+	"os/exec"
 	"path/filepath"
 	"regexp"
 	"runtime"
@@ -39,7 +41,7 @@ func TestMain(m *testing.M) {
 
 // name is for _fixtures/<name>.go
 func runTest(t *testing.T, name string, test func(c *daptest.Client, f protest.Fixture)) {
-	var buildFlags protest.BuildFlags
+	var buildFlags protest.BuildFlags = protest.AllNonOptimized
 	fixture := protest.BuildFixture(name, buildFlags)
 
 	// Start the DAP server.
@@ -78,8 +80,8 @@ func runTest(t *testing.T, name string, test func(c *daptest.Client, f protest.F
 	test(client, fixture)
 }
 
-// TestStopOnEntry emulates the message exchange that can be observed with
-// VS Code for the most basic debug session with "stopOnEntry" enabled:
+// TestLaunchStopOnEntry emulates the message exchange that can be observed with
+// VS Code for the most basic launch debug session with "stopOnEntry" enabled:
 // - User selects "Start Debugging":  1 >> initialize
 //                                 :  1 << initialize
 //                                 :  2 >> launch
@@ -111,7 +113,7 @@ func runTest(t *testing.T, name string, test func(c *daptest.Client, f protest.F
 //                                 : 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.
-func TestStopOnEntry(t *testing.T) {
+func TestLaunchStopOnEntry(t *testing.T) {
 	runTest(t, "increment", func(client *daptest.Client, fixture protest.Fixture) {
 		// 1 >> initialize, << initialize
 		client.InitializeRequest()
@@ -224,6 +226,128 @@ func TestStopOnEntry(t *testing.T) {
 	})
 }
 
+// TestAttachStopOnEntry is like TestLaunchStopOnEntry, but with attach request.
+func TestAttachStopOnEntry(t *testing.T) {
+	if runtime.GOOS == "freebsd" {
+		t.SkipNow()
+	}
+	runTest(t, "loopprog", func(client *daptest.Client, fixture protest.Fixture) {
+		// Start the program to attach to
+		cmd := exec.Command(fixture.Path)
+		stdout, err := cmd.StdoutPipe()
+		if err != nil {
+			t.Fatal(err)
+		}
+		cmd.Stderr = os.Stderr
+		if err := cmd.Start(); err != nil {
+			t.Fatal(err)
+		}
+		// Wait for output.
+		// This will give the target process time to initialize the runtime before we attach,
+		// so we can rely on having goroutines when they are requested on attach.
+		scanOut := bufio.NewScanner(stdout)
+		scanOut.Scan()
+		if scanOut.Text() != "past main" {
+			t.Errorf("expected loopprog.go to output \"past main\"")
+		}
+
+		// 1 >> initialize, << initialize
+		client.InitializeRequest()
+		initResp := client.ExpectInitializeResponse(t)
+		if initResp.Seq != 0 || initResp.RequestSeq != 1 {
+			t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=1", initResp)
+		}
+
+		// 2 >> attach, << initialized, << attach
+		client.AttachRequest(
+			map[string]interface{}{"mode": "local", "processId": cmd.Process.Pid, "stopOnEntry": true})
+		initEvent := client.ExpectInitializedEvent(t)
+		if initEvent.Seq != 0 {
+			t.Errorf("\ngot %#v\nwant Seq=0", initEvent)
+		}
+		attachResp := client.ExpectAttachResponse(t)
+		if attachResp.Seq != 0 || attachResp.RequestSeq != 2 {
+			t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=2", attachResp)
+		}
+
+		// 3 >> setBreakpoints, << setBreakpoints
+		client.SetBreakpointsRequest(fixture.Source, nil)
+		sbpResp := client.ExpectSetBreakpointsResponse(t)
+		if sbpResp.Seq != 0 || sbpResp.RequestSeq != 3 || len(sbpResp.Body.Breakpoints) != 0 {
+			t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=3, len(Breakpoints)=0", sbpResp)
+		}
+
+		// 4 >> setExceptionBreakpoints, << setExceptionBreakpoints
+		client.SetExceptionBreakpointsRequest()
+		sebpResp := client.ExpectSetExceptionBreakpointsResponse(t)
+		if sebpResp.Seq != 0 || sebpResp.RequestSeq != 4 {
+			t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=4", sebpResp)
+		}
+
+		// 5 >> configurationDone, << stopped, << configurationDone
+		client.ConfigurationDoneRequest()
+		stopEvent := client.ExpectStoppedEvent(t)
+		if stopEvent.Seq != 0 ||
+			stopEvent.Body.Reason != "entry" ||
+			stopEvent.Body.ThreadId != 1 ||
+			!stopEvent.Body.AllThreadsStopped {
+			t.Errorf("\ngot %#v\nwant Seq=0, Body={Reason=\"entry\", ThreadId=1, AllThreadsStopped=true}", stopEvent)
+		}
+		cdResp := client.ExpectConfigurationDoneResponse(t)
+		if cdResp.Seq != 0 || cdResp.RequestSeq != 5 {
+			t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=5", cdResp)
+		}
+
+		// 6 >> threads, << threads
+		client.ThreadsRequest()
+		tResp := client.ExpectThreadsResponse(t)
+		// Expect main goroutine plus runtime at this point.
+		if tResp.Seq != 0 || tResp.RequestSeq != 6 || len(tResp.Body.Threads) < 2 {
+			t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=6 len(Threads)>1", tResp)
+		}
+
+		// 7 >> threads, << threads
+		client.ThreadsRequest()
+		client.ExpectThreadsResponse(t)
+
+		// 8 >> stackTrace, << response
+		client.StackTraceRequest(1, 0, 20)
+		client.ExpectStackTraceResponse(t)
+
+		// 9 >> stackTrace, << response
+		client.StackTraceRequest(1, 0, 20)
+		client.ExpectStackTraceResponse(t)
+
+		// 10 >> evaluate, << error
+		client.EvaluateRequest("foo", 0 /*no frame specified*/, "repl")
+		erResp := client.ExpectVisibleErrorResponse(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)
+		}
+
+		// 11 >> evaluate, << evaluate
+		client.EvaluateRequest("1+1", 0 /*no frame specified*/, "repl")
+		evResp := client.ExpectEvaluateResponse(t)
+		if evResp.Seq != 0 || evResp.RequestSeq != 11 || evResp.Body.Result != "2" {
+			t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=10 Result=2", evResp)
+		}
+
+		// We cannot contiue here like in the launch case becase the program
+		// will never terminate. Since we won't receive a terminate event and
+		// there are no breakpoints to stop on, we just detach.
+
+		// TODO(polina): once https://github.com/go-delve/delve/issues/2259 is
+		// fixed, test with kill=false.
+
+		// 12 >> disconnect, << disconnect
+		client.DisconnectRequestWithKillOption(true)
+		dResp := client.ExpectDisconnectResponse(t)
+		if dResp.Seq != 0 || dResp.RequestSeq != 12 {
+			t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=12", dResp)
+		}
+	})
+}
+
 // Like the test above, except the program is configured to continue on entry.
 func TestContinueOnEntry(t *testing.T) {
 	runTest(t, "increment", func(client *daptest.Client, fixture protest.Fixture) {
@@ -368,8 +492,8 @@ func TestPreSetBreakpoint(t *testing.T) {
 		client.VariablesRequest(1000) // Arguments
 		args := client.ExpectVariablesResponse(t)
 		expectChildren(t, args, "Arguments", 2)
-		expectVarExact(t, args, 0, "y", "0", noChildren)
-		expectVarExact(t, args, 1, "~r1", "0", noChildren)
+		expectVarExact(t, args, 0, "y", "y", "0", noChildren)
+		expectVarExact(t, args, 1, "~r1", "", "0", noChildren)
 
 		client.VariablesRequest(1001) // Locals
 		locals := client.ExpectVariablesResponse(t)
@@ -456,11 +580,12 @@ func expectChildren(t *testing.T, got *dap.VariablesResponse, parentName string,
 // expectVar is a helper for verifying the values within a VariablesResponse.
 //     i - index of the variable within VariablesRespose.Body.Variables array (-1 will search all vars for a match)
 //     name - name of the variable
+//     evalName - fully qualified variable name or alternative expression to load this variable
 //     value - the value of the variable
-//     useExactMatch - true if value is to be compared to exactly, false if to be used as regex
+//     useExactMatch - true if name, evalName and value are to be compared to exactly, false if to be used as regex
 //     hasRef - true if the variable should have children and therefore a non-0 variable reference
 //     ref - reference to retrieve children of this variable (0 if none)
-func expectVar(t *testing.T, got *dap.VariablesResponse, i int, name, value string, useExactMatch, hasRef bool) (ref int) {
+func expectVar(t *testing.T, got *dap.VariablesResponse, i int, name, evalName, value string, useExactMatch, hasRef bool) (ref int) {
 	t.Helper()
 	if len(got.Body.Variables) <= i {
 		t.Errorf("\ngot  len=%d (children=%#v)\nwant len>%d", len(got.Body.Variables), got.Body.Variables, i)
@@ -480,31 +605,62 @@ func expectVar(t *testing.T, got *dap.VariablesResponse, i int, name, value stri
 	}
 
 	goti := got.Body.Variables[i]
-	if goti.Name != name || (goti.VariablesReference > 0) != hasRef {
+	matchedName := false
+	if useExactMatch {
+		matchedName = (goti.Name == name)
+	} else {
+		matchedName, _ = regexp.MatchString(name, goti.Name)
+	}
+	if !matchedName || (goti.VariablesReference > 0) != hasRef {
 		t.Errorf("\ngot  %#v\nwant Name=%q hasRef=%t", goti, name, hasRef)
 	}
-	matched := false
+	matchedEvalName := false
 	if useExactMatch {
-		matched = (goti.Value == value)
+		matchedEvalName = (goti.EvaluateName == evalName)
 	} else {
-		matched, _ = regexp.MatchString(value, goti.Value)
+		matchedEvalName, _ = regexp.MatchString(evalName, goti.EvaluateName)
 	}
-	if !matched {
+	if !matchedEvalName {
+		t.Errorf("\ngot  %q\nwant EvaluateName=%q", goti.EvaluateName, evalName)
+	}
+	matchedValue := false
+	if useExactMatch {
+		matchedValue = (goti.Value == value)
+	} else {
+		matchedValue, _ = regexp.MatchString(value, goti.Value)
+	}
+	if !matchedValue {
 		t.Errorf("\ngot  %s=%q\nwant %q", name, goti.Value, value)
 	}
 	return goti.VariablesReference
 }
 
 // expectVarExact is a helper like expectVar that matches value exactly.
-func expectVarExact(t *testing.T, got *dap.VariablesResponse, i int, name, value string, hasRef bool) (ref int) {
+func expectVarExact(t *testing.T, got *dap.VariablesResponse, i int, name, evalName, value string, hasRef bool) (ref int) {
 	t.Helper()
-	return expectVar(t, got, i, name, value, true, hasRef)
+	return expectVar(t, got, i, name, evalName, value, true, hasRef)
 }
 
-// expectVarRegex is a helper like expectVar that treats value as a regex.
-func expectVarRegex(t *testing.T, got *dap.VariablesResponse, i int, name, value string, hasRef bool) (ref int) {
+// expectVarRegex is a helper like expectVar that treats value, evalName or name as a regex.
+func expectVarRegex(t *testing.T, got *dap.VariablesResponse, i int, name, evalName, value string, hasRef bool) (ref int) {
 	t.Helper()
-	return expectVar(t, got, i, name, value, false, hasRef)
+	return expectVar(t, got, i, name, evalName, value, false, hasRef)
+}
+
+// validateEvaluateName issues an evaluate request with evaluateName of a variable and
+// confirms that it succeeds and returns the same variable record as the original.
+func validateEvaluateName(t *testing.T, client *daptest.Client, got *dap.VariablesResponse, i int) {
+	t.Helper()
+	original := got.Body.Variables[i]
+	client.EvaluateRequest(original.EvaluateName, 1000, "this context will be ignored")
+	validated := client.ExpectEvaluateResponse(t)
+	if original.VariablesReference == 0 && validated.Body.VariablesReference != 0 ||
+		original.VariablesReference != 0 && validated.Body.VariablesReference == 0 {
+		t.Errorf("\ngot  varref=%d\nwant %d", validated.Body.VariablesReference, original.VariablesReference)
+	}
+	if original.Value != validated.Body.Result {
+		t.Errorf("\ngot  value=%q\nwant %q", validated.Body.Result, original.Value)
+	}
 }
 
 // TestStackTraceRequest executes to a breakpoint and tests different
@@ -513,7 +669,7 @@ 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,
+		runDebugSessionWithBPs(t, client, "launch",
 			// Launch
 			func() {
 				client.LaunchRequest("exec", fixture.Path, !stopOnEntry)
@@ -579,7 +735,7 @@ func TestStackTraceRequest(t *testing.T) {
 // configurations of 'scopes' and 'variables' requests.
 func TestScopesAndVariablesRequests(t *testing.T) {
 	runTest(t, "testvariables", func(client *daptest.Client, fixture protest.Fixture) {
-		runDebugSessionWithBPs(t, client,
+		runDebugSessionWithBPs(t, client, "launch",
 			// Launch
 			func() {
 				client.LaunchRequestWithArgs(map[string]interface{}{
@@ -615,21 +771,23 @@ func TestScopesAndVariablesRequests(t *testing.T) {
 					client.VariablesRequest(1000)
 					args := client.ExpectVariablesResponse(t)
 					expectChildren(t, args, "Arguments", 2)
-					expectVarExact(t, args, 0, "baz", `"bazburzum"`, noChildren)
-					ref := expectVarExact(t, args, 1, "bar", `<main.FooBar>`, hasChildren)
+					expectVarExact(t, args, 0, "baz", "baz", `"bazburzum"`, noChildren)
+					ref := expectVarExact(t, args, 1, "bar", "bar", `<main.FooBar>`, hasChildren)
 					if ref > 0 {
 						client.VariablesRequest(ref)
 						bar := client.ExpectVariablesResponse(t)
 						expectChildren(t, bar, "bar", 2)
-						expectVarExact(t, bar, 0, "Baz", "10", noChildren)
-						expectVarExact(t, bar, 1, "Bur", `"lorem"`, noChildren)
+						expectVarExact(t, bar, 0, "Baz", "bar.Baz", "10", noChildren)
+						expectVarExact(t, bar, 1, "Bur", "bar.Bur", `"lorem"`, noChildren)
+						validateEvaluateName(t, client, bar, 0)
+						validateEvaluateName(t, client, bar, 1)
 					}
 
 					// Globals
 
 					client.VariablesRequest(1002)
 					globals := client.ExpectVariablesResponse(t)
-					expectVarExact(t, globals, 0, "p1", "10", noChildren)
+					expectVarExact(t, globals, 0, "p1", "main.p1", "10", noChildren)
 
 					// Locals
 
@@ -638,74 +796,75 @@ func TestScopesAndVariablesRequests(t *testing.T) {
 					expectChildren(t, locals, "Locals", 30)
 
 					// reflect.Kind == Bool
-					expectVarExact(t, locals, -1, "b1", "true", noChildren)
-					expectVarExact(t, locals, -1, "b2", "false", noChildren)
+					expectVarExact(t, locals, -1, "b1", "b1", "true", noChildren)
+					expectVarExact(t, locals, -1, "b2", "b2", "false", noChildren)
 					// reflect.Kind == Int
-					expectVarExact(t, locals, -1, "a2", "6", noChildren)
-					expectVarExact(t, locals, -1, "neg", "-1", noChildren)
+					expectVarExact(t, locals, -1, "a2", "a2", "6", noChildren)
+					expectVarExact(t, locals, -1, "neg", "neg", "-1", noChildren)
 					// reflect.Kind == Int8
-					expectVarExact(t, locals, -1, "i8", "1", noChildren)
+					expectVarExact(t, locals, -1, "i8", "i8", "1", noChildren)
 					// reflect.Kind == Int16 - see testvariables2
 					// reflect.Kind == Int32 - see testvariables2
 					// reflect.Kind == Int64 - see testvariables2
 					// reflect.Kind == Uint
 					// reflect.Kind == Uint8
-					expectVarExact(t, locals, -1, "u8", "255", noChildren)
+					expectVarExact(t, locals, -1, "u8", "u8", "255", noChildren)
 					// reflect.Kind == Uint16
-					expectVarExact(t, locals, -1, "u16", "65535", noChildren)
+					expectVarExact(t, locals, -1, "u16", "u16", "65535", noChildren)
 					// reflect.Kind == Uint32
-					expectVarExact(t, locals, -1, "u32", "4294967295", noChildren)
+					expectVarExact(t, locals, -1, "u32", "u32", "4294967295", noChildren)
 					// reflect.Kind == Uint64
-					expectVarExact(t, locals, -1, "u64", "18446744073709551615", noChildren)
+					expectVarExact(t, locals, -1, "u64", "u64", "18446744073709551615", noChildren)
 					// reflect.Kind == Uintptr
-					expectVarExact(t, locals, -1, "up", "5", noChildren)
+					expectVarExact(t, locals, -1, "up", "up", "5", noChildren)
 					// reflect.Kind == Float32
-					expectVarExact(t, locals, -1, "f32", "1.2", noChildren)
+					expectVarExact(t, locals, -1, "f32", "f32", "1.2", noChildren)
 					// reflect.Kind == Float64
-					expectVarExact(t, locals, -1, "a3", "7.23", noChildren)
+					expectVarExact(t, locals, -1, "a3", "a3", "7.23", noChildren)
 					// reflect.Kind == Complex64
-					ref = expectVarExact(t, locals, -1, "c64", "(1 + 2i)", hasChildren)
+					ref = expectVarExact(t, locals, -1, "c64", "c64", "(1 + 2i)", hasChildren)
 					if ref > 0 {
 						client.VariablesRequest(ref)
 						c64 := client.ExpectVariablesResponse(t)
 						expectChildren(t, c64, "c64", 2)
-						expectVarExact(t, c64, 0, "real", "1", noChildren)
-						expectVarExact(t, c64, 1, "imaginary", "2", noChildren)
+						expectVarExact(t, c64, 0, "real", "", "1", noChildren)
+						expectVarExact(t, c64, 1, "imaginary", "", "2", noChildren)
 					}
 					// reflect.Kind == Complex128
-					ref = expectVarExact(t, locals, -1, "c128", "(2 + 3i)", hasChildren)
+					ref = expectVarExact(t, locals, -1, "c128", "c128", "(2 + 3i)", hasChildren)
 					if ref > 0 {
 						client.VariablesRequest(ref)
 						c128 := client.ExpectVariablesResponse(t)
 						expectChildren(t, c128, "c128", 2)
-						expectVarExact(t, c128, 0, "real", "2", noChildren)
-						expectVarExact(t, c128, 1, "imaginary", "3", noChildren)
+						expectVarExact(t, c128, 0, "real", "", "2", noChildren)
+						expectVarExact(t, c128, 1, "imaginary", "", "3", noChildren)
 					}
 					// reflect.Kind == Array
-					ref = expectVarExact(t, locals, -1, "a4", "<[2]int>", hasChildren)
+					ref = expectVarExact(t, locals, -1, "a4", "a4", "<[2]int>", hasChildren)
 					if ref > 0 {
 						client.VariablesRequest(ref)
 						a4 := client.ExpectVariablesResponse(t)
 						expectChildren(t, a4, "a4", 2)
-						expectVarExact(t, a4, 0, "[0]", "1", noChildren)
-						expectVarExact(t, a4, 1, "[1]", "2", noChildren)
+						expectVarExact(t, a4, 0, "[0]", "a4[0]", "1", noChildren)
+						expectVarExact(t, a4, 1, "[1]", "a4[1]", "2", noChildren)
 					}
-					ref = expectVarExact(t, locals, -1, "a11", "<[3]main.FooBar>", hasChildren)
+					ref = expectVarExact(t, locals, -1, "a11", "a11", "<[3]main.FooBar>", hasChildren)
 					if ref > 0 {
 						client.VariablesRequest(ref)
 						a11 := client.ExpectVariablesResponse(t)
 						expectChildren(t, a11, "a11", 3)
-						expectVarExact(t, a11, 0, "[0]", "<main.FooBar>", hasChildren)
-						ref = expectVarExact(t, a11, 1, "[1]", "<main.FooBar>", hasChildren)
+						expectVarExact(t, a11, 0, "[0]", "a11[0]", "<main.FooBar>", hasChildren)
+						ref = expectVarExact(t, a11, 1, "[1]", "a11[1]", "<main.FooBar>", hasChildren)
 						if ref > 0 {
 							client.VariablesRequest(ref)
 							a11_1 := client.ExpectVariablesResponse(t)
 							expectChildren(t, a11_1, "a11[1]", 2)
-							expectVarExact(t, a11_1, 0, "Baz", "2", noChildren)
-							expectVarExact(t, a11_1, 1, "Bur", `"b"`, noChildren)
-
+							expectVarExact(t, a11_1, 0, "Baz", "a11[1].Baz", "2", noChildren)
+							expectVarExact(t, a11_1, 1, "Bur", "a11[1].Bur", `"b"`, noChildren)
+							validateEvaluateName(t, client, a11_1, 0)
+							validateEvaluateName(t, client, a11_1, 1)
 						}
-						expectVarExact(t, a11, 2, "[2]", "<main.FooBar>", hasChildren)
+						expectVarExact(t, a11, 2, "[2]", "a11[2]", "<main.FooBar>", hasChildren)
 					}
 
 					// reflect.Kind == Chan - see testvariables2
@@ -713,87 +872,96 @@ func TestScopesAndVariablesRequests(t *testing.T) {
 					// reflect.Kind == Interface - see testvariables2
 					// reflect.Kind == Map - see testvariables2
 					// reflect.Kind == Ptr
-					ref = expectVarRegex(t, locals, -1, "a7", "<\\*main\\.FooBar>\\(0x[0-9a-f]+\\)", hasChildren)
+					ref = expectVarRegex(t, locals, -1, "a7", "a7", `<\*main\.FooBar>\(0x[0-9a-f]+\)`, hasChildren)
 					if ref > 0 {
 						client.VariablesRequest(ref)
 						a7 := client.ExpectVariablesResponse(t)
 						expectChildren(t, a7, "a7", 1)
-						ref = expectVarExact(t, a7, 0, "", "<main.FooBar>", hasChildren)
+						ref = expectVarExact(t, a7, 0, "", "(*a7)", "<main.FooBar>", hasChildren)
 						if ref > 0 {
 							client.VariablesRequest(ref)
 							a7val := client.ExpectVariablesResponse(t)
 							expectChildren(t, a7val, "*a7", 2)
-							expectVarExact(t, a7val, 0, "Baz", "5", noChildren)
-							expectVarExact(t, a7val, 1, "Bur", `"strum"`, noChildren)
+							expectVarExact(t, a7val, 0, "Baz", "(*a7).Baz", "5", noChildren)
+							expectVarExact(t, a7val, 1, "Bur", "(*a7).Bur", `"strum"`, noChildren)
+							validateEvaluateName(t, client, a7val, 0)
+							validateEvaluateName(t, client, a7val, 1)
 						}
 					}
 					// TODO(polina): how to test for "nil" (without type) and "void"?
-					expectVarExact(t, locals, -1, "a9", "nil <*main.FooBar>", noChildren)
+					expectVarExact(t, locals, -1, "a9", "a9", "nil <*main.FooBar>", noChildren)
 					// reflect.Kind == Slice
-					ref = expectVarExact(t, locals, -1, "a5", "<[]int> (length: 5, cap: 5)", hasChildren)
+					ref = expectVarExact(t, locals, -1, "a5", "a5", "<[]int> (length: 5, cap: 5)", hasChildren)
 					if ref > 0 {
 						client.VariablesRequest(ref)
 						a5 := client.ExpectVariablesResponse(t)
 						expectChildren(t, a5, "a5", 5)
-						expectVarExact(t, a5, 0, "[0]", "1", noChildren)
-						expectVarExact(t, a5, 4, "[4]", "5", noChildren)
+						expectVarExact(t, a5, 0, "[0]", "a5[0]", "1", noChildren)
+						expectVarExact(t, a5, 4, "[4]", "a5[4]", "5", noChildren)
+						validateEvaluateName(t, client, a5, 0)
+						validateEvaluateName(t, client, a5, 1)
 					}
-					ref = expectVarExact(t, locals, -1, "a12", "<[]main.FooBar> (length: 2, cap: 2)", hasChildren)
+					ref = expectVarExact(t, locals, -1, "a12", "a12", "<[]main.FooBar> (length: 2, cap: 2)", hasChildren)
 					if ref > 0 {
 						client.VariablesRequest(ref)
 						a12 := client.ExpectVariablesResponse(t)
 						expectChildren(t, a12, "a12", 2)
-						expectVarExact(t, a12, 0, "[0]", "<main.FooBar>", hasChildren)
-						ref = expectVarExact(t, a12, 1, "[1]", "<main.FooBar>", hasChildren)
+						expectVarExact(t, a12, 0, "[0]", "a12[0]", "<main.FooBar>", hasChildren)
+						ref = expectVarExact(t, a12, 1, "[1]", "a12[1]", "<main.FooBar>", hasChildren)
 						if ref > 0 {
 							client.VariablesRequest(ref)
 							a12_1 := client.ExpectVariablesResponse(t)
 							expectChildren(t, a12_1, "a12[1]", 2)
-							expectVarExact(t, a12_1, 0, "Baz", "5", noChildren)
-							expectVarExact(t, a12_1, 1, "Bur", `"e"`, noChildren)
+							expectVarExact(t, a12_1, 0, "Baz", "a12[1].Baz", "5", noChildren)
+							expectVarExact(t, a12_1, 1, "Bur", "a12[1].Bur", `"e"`, noChildren)
+							validateEvaluateName(t, client, a12_1, 0)
+							validateEvaluateName(t, client, a12_1, 1)
 						}
 					}
-					ref = expectVarExact(t, locals, -1, "a13", "<[]*main.FooBar> (length: 3, cap: 3)", hasChildren)
+					ref = expectVarExact(t, locals, -1, "a13", "a13", "<[]*main.FooBar> (length: 3, cap: 3)", hasChildren)
 					if ref > 0 {
 						client.VariablesRequest(ref)
 						a13 := client.ExpectVariablesResponse(t)
 						expectChildren(t, a13, "a13", 3)
-						expectVarRegex(t, a13, 0, "[0]", "<\\*main\\.FooBar>\\(0x[0-9a-f]+\\)", hasChildren)
-						expectVarRegex(t, a13, 1, "[1]", "<\\*main\\.FooBar>\\(0x[0-9a-f]+\\)", hasChildren)
-						ref = expectVarRegex(t, a13, 2, "[2]", "<\\*main\\.FooBar>\\(0x[0-9a-f]+\\)", hasChildren)
+						expectVarRegex(t, a13, 0, `\[0\]`, `a13\[0\]`, `<\*main\.FooBar>\(0x[0-9a-f]+\)`, hasChildren)
+						expectVarRegex(t, a13, 1, `\[1\]`, `a13\[1\]`, `<\*main\.FooBar>\(0x[0-9a-f]+\)`, hasChildren)
+						ref = expectVarRegex(t, a13, 2, `\[2\]`, `a13\[2\]`, `<\*main\.FooBar>\(0x[0-9a-f]+\)`, hasChildren)
 						if ref > 0 {
 							client.VariablesRequest(ref)
 							a13_2 := client.ExpectVariablesResponse(t)
 							expectChildren(t, a13_2, "a13[2]", 1)
-							ref = expectVarExact(t, a13_2, 0, "", "<main.FooBar>", hasChildren)
+							ref = expectVarExact(t, a13_2, 0, "", "(*a13[2])", "<main.FooBar>", hasChildren)
+							validateEvaluateName(t, client, a13_2, 0)
 							if ref > 0 {
 								client.VariablesRequest(ref)
 								val := client.ExpectVariablesResponse(t)
 								expectChildren(t, val, "*a13[2]", 2)
-								expectVarExact(t, val, 0, "Baz", "8", noChildren)
-								expectVarExact(t, val, 1, "Bur", `"h"`, noChildren)
+								expectVarExact(t, val, 0, "Baz", "(*a13[2]).Baz", "8", noChildren)
+								expectVarExact(t, val, 1, "Bur", "(*a13[2]).Bur", `"h"`, noChildren)
+								validateEvaluateName(t, client, val, 0)
+								validateEvaluateName(t, client, val, 1)
 							}
 						}
 					}
 					// reflect.Kind == String
-					expectVarExact(t, locals, -1, "a1", `"foofoofoofoofoofoo"`, noChildren)
-					expectVarExact(t, locals, -1, "a10", `"ofo"`, noChildren)
+					expectVarExact(t, locals, -1, "a1", "a1", `"foofoofoofoofoofoo"`, noChildren)
+					expectVarExact(t, locals, -1, "a10", "a10", `"ofo"`, noChildren)
 					// reflect.Kind == Struct
-					ref = expectVarExact(t, locals, -1, "a6", "<main.FooBar>", hasChildren)
+					ref = expectVarExact(t, locals, -1, "a6", "a6", "<main.FooBar>", hasChildren)
 					if ref > 0 {
 						client.VariablesRequest(ref)
 						a6 := client.ExpectVariablesResponse(t)
 						expectChildren(t, a6, "a6", 2)
-						expectVarExact(t, a6, 0, "Baz", "8", noChildren)
-						expectVarExact(t, a6, 1, "Bur", `"word"`, noChildren)
+						expectVarExact(t, a6, 0, "Baz", "a6.Baz", "8", noChildren)
+						expectVarExact(t, a6, 1, "Bur", "a6.Bur", `"word"`, noChildren)
 					}
-					ref = expectVarExact(t, locals, -1, "a8", "<main.FooBar2>", hasChildren)
+					ref = expectVarExact(t, locals, -1, "a8", "a8", "<main.FooBar2>", hasChildren)
 					if ref > 0 {
 						client.VariablesRequest(ref)
 						a8 := client.ExpectVariablesResponse(t)
 						expectChildren(t, a8, "a8", 2)
-						expectVarExact(t, a8, 0, "Bur", "10", noChildren)
-						expectVarExact(t, a8, 1, "Baz", `"feh"`, noChildren)
+						expectVarExact(t, a8, 0, "Bur", "a8.Bur", "10", noChildren)
+						expectVarExact(t, a8, 1, "Baz", "a8.Baz", `"feh"`, noChildren)
 					}
 					// reflect.Kind == UnsafePointer - see testvariables2
 				},
@@ -825,11 +993,11 @@ func TestScopesAndVariablesRequests(t *testing.T) {
 					client.VariablesRequest(1001) // Locals
 					locals := client.ExpectVariablesResponse(t)
 					expectChildren(t, locals, "Locals", 1)
-					expectVarExact(t, locals, -1, "a1", `"bur"`, noChildren)
+					expectVarExact(t, locals, -1, "a1", "a1", `"bur"`, noChildren)
 
 					client.VariablesRequest(1002) // Globals
 					globals := client.ExpectVariablesResponse(t)
-					expectVarExact(t, globals, 0, "p1", "10", noChildren)
+					expectVarExact(t, globals, 0, "p1", "main.p1", "10", noChildren)
 
 					client.VariablesRequest(7777)
 					erres = client.ExpectErrorResponse(t)
@@ -846,7 +1014,7 @@ func TestScopesAndVariablesRequests(t *testing.T) {
 // configurations of 'scopes' and 'variables' requests.
 func TestScopesAndVariablesRequests2(t *testing.T) {
 	runTest(t, "testvariables2", func(client *daptest.Client, fixture protest.Fixture) {
-		runDebugSessionWithBPs(t, client,
+		runDebugSessionWithBPs(t, client, "launch",
 			// Launch
 			func() {
 				client.LaunchRequest("exec", fixture.Path, !stopOnEntry)
@@ -895,13 +1063,13 @@ func TestScopesAndVariablesRequests2(t *testing.T) {
 					// reflect.Kind == Bool - see testvariables
 					// reflect.Kind == Int - see testvariables
 					// reflect.Kind == Int8
-					expectVarExact(t, locals, -1, "ni8", "-5", noChildren)
+					expectVarExact(t, locals, -1, "ni8", "ni8", "-5", noChildren)
 					// reflect.Kind == Int16
-					expectVarExact(t, locals, -1, "ni16", "-5", noChildren)
+					expectVarExact(t, locals, -1, "ni16", "ni16", "-5", noChildren)
 					// reflect.Kind == Int32
-					expectVarExact(t, locals, -1, "ni32", "-5", noChildren)
+					expectVarExact(t, locals, -1, "ni32", "ni32", "-5", noChildren)
 					// reflect.Kind == Int64
-					expectVarExact(t, locals, -1, "ni64", "-5", noChildren)
+					expectVarExact(t, locals, -1, "ni64", "ni64", "-5", noChildren)
 					// reflect.Kind == Uint
 					// reflect.Kind == Uint8 - see testvariables
 					// reflect.Kind == Uint16 - see testvariables
@@ -910,150 +1078,178 @@ func TestScopesAndVariablesRequests2(t *testing.T) {
 					// reflect.Kind == Uintptr - see testvariables
 					// reflect.Kind == Float32 - see testvariables
 					// reflect.Kind == Float64
-					expectVarExact(t, locals, -1, "pinf", "+Inf", noChildren)
-					expectVarExact(t, locals, -1, "ninf", "-Inf", noChildren)
-					expectVarExact(t, locals, -1, "nan", "NaN", noChildren)
+					expectVarExact(t, locals, -1, "pinf", "pinf", "+Inf", noChildren)
+					expectVarExact(t, locals, -1, "ninf", "ninf", "-Inf", noChildren)
+					expectVarExact(t, locals, -1, "nan", "nan", "NaN", noChildren)
 					// reflect.Kind == Complex64 - see testvariables
 					// reflect.Kind == Complex128 - see testvariables
 					// reflect.Kind == Array
-					expectVarExact(t, locals, -1, "a0", "<[0]int>", noChildren)
+					expectVarExact(t, locals, -1, "a0", "a0", "<[0]int>", noChildren)
 					// reflect.Kind == Chan
-					ref := expectVarExact(t, locals, -1, "ch1", "<chan int>", hasChildren)
+					ref := expectVarExact(t, locals, -1, "ch1", "ch1", "<chan int>", hasChildren)
 					if ref > 0 {
 						client.VariablesRequest(ref)
 						ch1 := client.ExpectVariablesResponse(t)
 						expectChildren(t, ch1, "ch1", 11)
-						expectVarExact(t, ch1, 0, "qcount", "4", noChildren)
-						expectVarExact(t, ch1, 10, "lock", "<runtime.mutex>", hasChildren)
+						expectVarExact(t, ch1, 0, "qcount", "ch1.qcount", "4", noChildren)
+						expectVarExact(t, ch1, 10, "lock", "ch1.lock", "<runtime.mutex>", hasChildren)
+						validateEvaluateName(t, client, ch1, 0)
+						validateEvaluateName(t, client, ch1, 10)
 					}
-					expectVarExact(t, locals, -1, "chnil", "nil <chan int>", noChildren)
+					expectVarExact(t, locals, -1, "chnil", "chnil", "nil <chan int>", noChildren)
 					// reflect.Kind == Func
-					expectVarExact(t, locals, -1, "fn1", "main.afunc", noChildren)
-					expectVarExact(t, locals, -1, "fn2", "<main.functype>", noChildren)
+					expectVarExact(t, locals, -1, "fn1", "fn1", "main.afunc", noChildren)
+					expectVarExact(t, locals, -1, "fn2", "fn2", "<main.functype>", noChildren)
 					// reflect.Kind == Interface
-					expectVarExact(t, locals, -1, "ifacenil", "nil <interface {}>", noChildren)
-					ref = expectVarExact(t, locals, -1, "iface2", "<interface {}(string)>", hasChildren)
+					expectVarExact(t, locals, -1, "ifacenil", "ifacenil", "nil <interface {}>", noChildren)
+					ref = expectVarExact(t, locals, -1, "iface2", "iface2", "<interface {}(string)>", hasChildren)
 					if ref > 0 {
 						client.VariablesRequest(ref)
 						iface2 := client.ExpectVariablesResponse(t)
 						expectChildren(t, iface2, "iface2", 1)
-						expectVarExact(t, iface2, 0, "data", `"test"`, noChildren)
+						expectVarExact(t, iface2, 0, "data", "iface2.(data)", `"test"`, noChildren)
+						validateEvaluateName(t, client, iface2, 0)
 					}
-					ref = expectVarExact(t, locals, -1, "iface4", "<interface {}([]go/constant.Value)>", hasChildren)
+					ref = expectVarExact(t, locals, -1, "iface4", "iface4", "<interface {}([]go/constant.Value)>", hasChildren)
 					if ref > 0 {
 						client.VariablesRequest(ref)
 						iface4 := client.ExpectVariablesResponse(t)
 						expectChildren(t, iface4, "iface4", 1)
-						ref = expectVarExact(t, iface4, 0, "data", "<[]go/constant.Value> (length: 1, cap: 1)", hasChildren)
+						ref = expectVarExact(t, iface4, 0, "data", "iface4.(data)", "<[]go/constant.Value> (length: 1, cap: 1)", hasChildren)
 						if ref > 0 {
 							client.VariablesRequest(ref)
 							iface4data := client.ExpectVariablesResponse(t)
 							expectChildren(t, iface4data, "iface4.data", 1)
-							expectVarExact(t, iface4data, 0, "[0]", "<go/constant.Value(go/constant.int64Val)>", hasChildren)
-
+							ref = expectVarExact(t, iface4data, 0, "[0]", "iface4.(data)[0]", "<go/constant.Value(go/constant.int64Val)>", hasChildren)
+							if ref > 0 {
+								client.VariablesRequest(ref)
+								iface4data0 := client.ExpectVariablesResponse(t)
+								expectChildren(t, iface4data0, "iface4.data[0]", 1)
+								expectVarExact(t, iface4data0, 0, "data", "iface4.(data)[0].(data)", "4", noChildren)
+								validateEvaluateName(t, client, iface4data0, 0)
+							}
 						}
 					}
-					expectVarExact(t, locals, -1, "errnil", "nil <error>", noChildren)
-					expectVarExact(t, locals, -1, "err1", "<error(*main.astruct)>", hasChildren)
+					expectVarExact(t, locals, -1, "errnil", "errnil", "nil <error>", noChildren)
+					ref = expectVarExact(t, locals, -1, "err1", "err1", "<error(*main.astruct)>", hasChildren)
+					if ref > 0 {
+						client.VariablesRequest(ref)
+						err1 := client.ExpectVariablesResponse(t)
+						expectChildren(t, err1, "err1", 1)
+						expectVarRegex(t, err1, 0, "data", `err1\.\(data\)`, `<\*main\.astruct>\(0x[0-9a-f]+\)`, hasChildren)
+						validateEvaluateName(t, client, err1, 0)
+					}
 					// reflect.Kind == Map
-					expectVarExact(t, locals, -1, "mnil", "nil <map[string]main.astruct>", noChildren)
-					ref = expectVarExact(t, locals, -1, "m2", "<map[int]*main.astruct> (length: 1)", hasChildren)
+					expectVarExact(t, locals, -1, "mnil", "mnil", "nil <map[string]main.astruct>", noChildren)
+					// key - scalar, value - compound
+					ref = expectVarExact(t, locals, -1, "m2", "m2", "<map[int]*main.astruct> (length: 1)", hasChildren)
 					if ref > 0 {
 						client.VariablesRequest(ref)
 						m2 := client.ExpectVariablesResponse(t)
-						expectChildren(t, m2, "m2", 1)
-						ref = expectVarRegex(t, m2, 0, "1", "<\\*main\\.astruct>\\(0x[0-9a-f]+\\)", hasChildren)
+						expectChildren(t, m2, "m2", 1) // each key-value represented by a single child
+						ref = expectVarRegex(t, m2, 0, "1", `m2\[1\]`, `<\*main\.astruct>\(0x[0-9a-f]+\)`, hasChildren)
 						if ref > 0 {
 							client.VariablesRequest(ref)
-							m2_1 := client.ExpectVariablesResponse(t)
-							expectChildren(t, m2_1, "m2[1]", 1)
-							ref = expectVarExact(t, m2_1, 0, "", "<main.astruct>", hasChildren)
+							m2kv1 := client.ExpectVariablesResponse(t)
+							expectChildren(t, m2kv1, "m2[1]", 1)
+							ref = expectVarExact(t, m2kv1, 0, "", "(*m2[1])", "<main.astruct>", hasChildren)
 							if ref > 0 {
 								client.VariablesRequest(ref)
-								m2_1val := client.ExpectVariablesResponse(t)
-								expectChildren(t, m2_1val, "*m2[1]", 2)
-								expectVarExact(t, m2_1val, 0, "A", "10", noChildren)
-								expectVarExact(t, m2_1val, 1, "B", "11", noChildren)
+								m2kv1deref := client.ExpectVariablesResponse(t)
+								expectChildren(t, m2kv1deref, "*m2[1]", 2)
+								expectVarExact(t, m2kv1deref, 0, "A", "(*m2[1]).A", "10", noChildren)
+								expectVarExact(t, m2kv1deref, 1, "B", "(*m2[1]).B", "11", noChildren)
+								validateEvaluateName(t, client, m2kv1deref, 0)
+								validateEvaluateName(t, client, m2kv1deref, 1)
 							}
 						}
 					}
-					ref = expectVarExact(t, locals, -1, "m3", "<map[main.astruct]int> (length: 2)", hasChildren)
+					// key - compound, value - scalar
+					ref = expectVarExact(t, locals, -1, "m3", "m3", "<map[main.astruct]int> (length: 2)", hasChildren)
 					if ref > 0 {
 						client.VariablesRequest(ref)
 						m3 := client.ExpectVariablesResponse(t)
-						expectChildren(t, m3, "m3", 2)
-						ref = expectVarExact(t, m3, 0, "<main.astruct>[0]", "42", hasChildren)
+						expectChildren(t, m3, "m3", 2) // each key-value represented by a single child
+						ref = expectVarRegex(t, m3, 0, `<main\.astruct>\(0x[0-9a-f]+\)`, `m3\[\(\*\(\*"main.astruct"\)\(0x[0-9a-f]+\)\)\]`, "42", hasChildren)
 						if ref > 0 {
 							client.VariablesRequest(ref)
-							m3_0 := client.ExpectVariablesResponse(t)
-							expectChildren(t, m3_0, "m3[0]", 2)
-							expectVarExact(t, m3_0, 0, "A", "1", noChildren)
-							expectVarExact(t, m3_0, 1, "B", "1", noChildren)
+							m3kv0 := client.ExpectVariablesResponse(t)
+							expectChildren(t, m3kv0, "m3[0]", 2)
+							expectVarRegex(t, m3kv0, 0, "A", `\(*\(*"main\.astruct"\)\(0x[0-9a-f]+\)\)\.A`, "1", noChildren)
+							validateEvaluateName(t, client, m3kv0, 0)
 						}
-						ref = expectVarExact(t, m3, 1, "<main.astruct>[1]", "43", hasChildren)
-						if ref > 0 {
+						ref = expectVarRegex(t, m3, 1, `<main\.astruct>\(0x[0-9a-f]+\)`, `m3\[\(\*\(\*"main.astruct"\)\(0x[0-9a-f]+\)\)\]`, "43", hasChildren)
+						if ref > 0 { // inspect another key from another key-value child
 							client.VariablesRequest(ref)
-							m3_1 := client.ExpectVariablesResponse(t)
-							expectChildren(t, m3_1, "m3[1]", 2)
-							expectVarExact(t, m3_1, 0, "A", "2", noChildren)
-							expectVarExact(t, m3_1, 1, "B", "2", noChildren)
+							m3kv1 := client.ExpectVariablesResponse(t)
+							expectChildren(t, m3kv1, "m3[1]", 2)
+							expectVarRegex(t, m3kv1, 1, "B", `\(*\(*"main\.astruct"\)\(0x[0-9a-f]+\)\)\.B`, "2", noChildren)
+							validateEvaluateName(t, client, m3kv1, 1)
 						}
 					}
-					ref = expectVarExact(t, locals, -1, "m4", "<map[main.astruct]main.astruct> (length: 2)", hasChildren)
+					// key - compound, value - compound
+					ref = expectVarExact(t, locals, -1, "m4", "m4", "<map[main.astruct]main.astruct> (length: 2)", hasChildren)
 					if ref > 0 {
 						client.VariablesRequest(ref)
 						m4 := client.ExpectVariablesResponse(t)
-						expectChildren(t, m4, "m4", 4)
-						expectVarExact(t, m4, 0, "[key 0]", "<main.astruct>", hasChildren)
-						expectVarExact(t, m4, 1, "[val 0]", "<main.astruct>", hasChildren)
-						ref = expectVarExact(t, m4, 2, "[key 1]", "<main.astruct>", hasChildren)
+						expectChildren(t, m4, "m4", 4) // each key and value represented by a child, so double the key-value count
+						expectVarRegex(t, m4, 0, `\[key 0\]`, `\(\*\(\*"main\.astruct"\)\(0x[0-9a-f]+\)\)`, `<main\.astruct>`, hasChildren)
+						expectVarRegex(t, m4, 1, `\[val 0\]`, `m4\[\(\*\(\*"main\.astruct"\)\(0x[0-9a-f]+\)\)\]`, `<main\.astruct>`, hasChildren)
+						ref = expectVarRegex(t, m4, 2, `\[key 1\]`, `\(\*\(\*"main\.astruct"\)\(0x[0-9a-f]+\)\)`, `<main\.astruct>`, hasChildren)
 						if ref > 0 {
 							client.VariablesRequest(ref)
 							m4Key1 := client.ExpectVariablesResponse(t)
 							expectChildren(t, m4Key1, "m4Key1", 2)
-							expectVarExact(t, m4Key1, 0, "A", "2", noChildren)
-							expectVarExact(t, m4Key1, 1, "B", "2", noChildren)
+							expectVarRegex(t, m4Key1, 0, "A", `\(\*\(\*"main\.astruct"\)\(0x[0-9a-f]+\)\)\.A`, "2", noChildren)
+							expectVarRegex(t, m4Key1, 1, "B", `\(\*\(\*"main\.astruct"\)\(0x[0-9a-f]+\)\)\.B`, "2", noChildren)
+							validateEvaluateName(t, client, m4Key1, 0)
+							validateEvaluateName(t, client, m4Key1, 1)
 						}
-						ref = expectVarExact(t, m4, 3, "[val 1]", "<main.astruct>", hasChildren)
+						ref = expectVarRegex(t, m4, 3, `\[val 1\]`, `m4\[\(\*\(\*"main\.astruct"\)\(0x[0-9a-f]+\)\)\]`, `<main\.astruct>`, hasChildren)
 						if ref > 0 {
 							client.VariablesRequest(ref)
 							m4Val1 := client.ExpectVariablesResponse(t)
 							expectChildren(t, m4Val1, "m4Val1", 2)
-							expectVarExact(t, m4Val1, 0, "A", "22", noChildren)
-							expectVarExact(t, m4Val1, 1, "B", "22", noChildren)
+							expectVarRegex(t, m4Val1, 0, "A", `m4\[\(\*\(\*"main\.astruct"\)\(0x[0-9a-f]+\)\)\]\.A`, "22", noChildren)
+							expectVarRegex(t, m4Val1, 1, "B", `m4\[\(\*\(\*"main\.astruct"\)\(0x[0-9a-f]+\)\)\]\.B`, "22", noChildren)
+							validateEvaluateName(t, client, m4Val1, 0)
+							validateEvaluateName(t, client, m4Val1, 1)
 						}
 					}
-					expectVarExact(t, locals, -1, "emptymap", "<map[string]string> (length: 0)", noChildren)
+					expectVarExact(t, locals, -1, "emptymap", "emptymap", "<map[string]string> (length: 0)", noChildren)
 					// reflect.Kind == Ptr - see testvariables
 					// reflect.Kind == Slice
-					ref = expectVarExact(t, locals, -1, "zsslice", "<[]struct {}> (length: 3, cap: 3)", hasChildren)
+					ref = expectVarExact(t, locals, -1, "zsslice", "zsslice", "<[]struct {}> (length: 3, cap: 3)", hasChildren)
 					if ref > 0 {
 						client.VariablesRequest(ref)
 						zsslice := client.ExpectVariablesResponse(t)
 						expectChildren(t, zsslice, "zsslice", 3)
+						expectVarExact(t, zsslice, 2, "[2]", "zsslice[2]", "<struct {}>", noChildren)
+						validateEvaluateName(t, client, zsslice, 2)
 					}
-					expectVarExact(t, locals, -1, "emptyslice", "<[]string> (length: 0, cap: 0)", noChildren)
-					expectVarExact(t, locals, -1, "nilslice", "nil <[]int>", noChildren)
+					expectVarExact(t, locals, -1, "emptyslice", "emptyslice", "<[]string> (length: 0, cap: 0)", noChildren)
+					expectVarExact(t, locals, -1, "nilslice", "nilslice", "nil <[]int>", noChildren)
 					// reflect.Kind == String
-					expectVarExact(t, locals, -1, "longstr", "\"very long string 0123456789a0123456789b0123456789c0123456789d012...+73 more\"", noChildren)
+					expectVarExact(t, locals, -1, "longstr", "longstr", "\"very long string 0123456789a0123456789b0123456789c0123456789d012...+73 more\"", noChildren)
 					// reflect.Kind == Struct
-					expectVarExact(t, locals, -1, "zsvar", "<struct {}>", noChildren)
+					expectVarExact(t, locals, -1, "zsvar", "zsvar", "<struct {}>", noChildren)
 					// reflect.Kind == UnsafePointer
 					// TODO(polina): how do I test for unsafe.Pointer(nil)?
-					expectVarRegex(t, locals, -1, "upnil", "unsafe\\.Pointer\\(0x0\\)", noChildren)
-					expectVarRegex(t, locals, -1, "up1", "unsafe\\.Pointer\\(0x[0-9a-f]+\\)", noChildren)
+					expectVarRegex(t, locals, -1, "upnil", "upnil", `unsafe\.Pointer\(0x0\)`, noChildren)
+					expectVarRegex(t, locals, -1, "up1", "up1", `unsafe\.Pointer\(0x[0-9a-f]+\)`, noChildren)
 
 					// Test unreadable variable
-					ref = expectVarExact(t, locals, -1, "unread", "<*int>(0x3039)", hasChildren)
+					ref = expectVarExact(t, locals, -1, "unread", "unread", "<*int>(0x3039)", hasChildren)
 					if ref > 0 {
 						client.VariablesRequest(ref)
 						val := client.ExpectVariablesResponse(t)
 						expectChildren(t, val, "*unread", 1)
-						expectVarRegex(t, val, 0, "", "unreadable <.+>", noChildren)
+						expectVarRegex(t, val, 0, "^$", `\(\*unread\)`, "unreadable <.+>", noChildren)
+						validateEvaluateName(t, client, val, 0)
 					}
 
 					// Test that variables are not yet loaded completely.
-					ref = expectVarExact(t, locals, -1, "m1", "<map[string]main.astruct> (length: 66)", hasChildren)
+					ref = expectVarExact(t, locals, -1, "m1", "m1", "<map[string]main.astruct> (length: 66)", hasChildren)
 					if ref > 0 {
 						client.VariablesRequest(ref)
 						m1 := client.ExpectVariablesResponse(t)
@@ -1071,7 +1267,7 @@ func TestScopesAndVariablesRequests2(t *testing.T) {
 // in another package and tests that globals scope got updated to those vars.
 func TestGlobalScopeAndVariables(t *testing.T) {
 	runTest(t, "consts", func(client *daptest.Client, fixture protest.Fixture) {
-		runDebugSessionWithBPs(t, client,
+		runDebugSessionWithBPs(t, client, "launch",
 			// Launch
 			func() {
 				client.LaunchRequestWithArgs(map[string]interface{}{
@@ -1107,7 +1303,7 @@ func TestGlobalScopeAndVariables(t *testing.T) {
 
 					client.StackTraceRequest(1, 0, 20)
 					stack = client.ExpectStackTraceResponse(t)
-					expectStackFrames(t, stack, "", 14, 1000, 4, 4)
+					expectStackFrames(t, stack, "", 13, 1000, 4, 4)
 
 					client.ScopesRequest(1000)
 					scopes = client.ExpectScopesResponse(t)
@@ -1118,13 +1314,14 @@ func TestGlobalScopeAndVariables(t *testing.T) {
 					client.VariablesRequest(1002)
 					globals := client.ExpectVariablesResponse(t)
 					expectChildren(t, globals, "Globals", 1)
-					ref := expectVarExact(t, globals, 0, "SomeVar", "<github.com/go-delve/delve/_fixtures/internal/dir0/pkg.SomeType>", hasChildren)
+					ref := expectVarExact(t, globals, 0, "SomeVar", "github.com/go-delve/delve/_fixtures/internal/dir0/pkg.SomeVar", "<github.com/go-delve/delve/_fixtures/internal/dir0/pkg.SomeType>", hasChildren)
 
 					if ref > 0 {
 						client.VariablesRequest(ref)
 						somevar := client.ExpectVariablesResponse(t)
 						expectChildren(t, somevar, "SomeVar", 1)
-						expectVarExact(t, somevar, 0, "X", "0", noChildren)
+						// TODO(polina): unlike main.p, this prefix won't work
+						expectVarExact(t, somevar, 0, "X", "github.com/go-delve/delve/_fixtures/internal/dir0/pkg.SomeVar.X", "0", noChildren)
 					}
 				},
 				disconnect: false,
@@ -1137,7 +1334,7 @@ func TestGlobalScopeAndVariables(t *testing.T) {
 func TestLaunchRequestWithStackTraceDepth(t *testing.T) {
 	runTest(t, "increment", func(client *daptest.Client, fixture protest.Fixture) {
 		var stResp *dap.StackTraceResponse
-		runDebugSessionWithBPs(t, client,
+		runDebugSessionWithBPs(t, client, "launch",
 			// Launch
 			func() {
 				client.LaunchRequestWithArgs(map[string]interface{}{
@@ -1161,7 +1358,7 @@ func TestLaunchRequestWithStackTraceDepth(t *testing.T) {
 // configurations of setBreakpoint requests.
 func TestSetBreakpoint(t *testing.T) {
 	runTest(t, "loopprog", func(client *daptest.Client, fixture protest.Fixture) {
-		runDebugSessionWithBPs(t, client,
+		runDebugSessionWithBPs(t, client, "launch",
 			// Launch
 			func() {
 				client.LaunchRequest("exec", fixture.Path, !stopOnEntry)
@@ -1217,7 +1414,7 @@ func TestSetBreakpoint(t *testing.T) {
 					handleStop(t, client, 1, "main.loop", 8)
 					client.VariablesRequest(1001) // Locals
 					locals := client.ExpectVariablesResponse(t)
-					expectVarExact(t, locals, 0, "i", "0", noChildren) // i == 0
+					expectVarExact(t, locals, 0, "i", "i", "0", noChildren) // i == 0
 
 					// Edit the breakpoint to add a condition
 					client.SetConditionalBreakpointsRequest(fixture.Source, []int{8}, map[int]string{8: "i == 3"})
@@ -1230,7 +1427,7 @@ func TestSetBreakpoint(t *testing.T) {
 					handleStop(t, client, 1, "main.loop", 8)
 					client.VariablesRequest(1001) // Locals
 					locals = client.ExpectVariablesResponse(t)
-					expectVarExact(t, locals, 0, "i", "3", noChildren) // i == 3
+					expectVarExact(t, locals, 0, "i", "i", "3", noChildren) // i == 3
 
 					// Edit the breakpoint to remove a condition
 					client.SetConditionalBreakpointsRequest(fixture.Source, []int{8}, map[int]string{8: ""})
@@ -1243,7 +1440,7 @@ func TestSetBreakpoint(t *testing.T) {
 					handleStop(t, client, 1, "main.loop", 8)
 					client.VariablesRequest(1001) // Locals
 					locals = client.ExpectVariablesResponse(t)
-					expectVarExact(t, locals, 0, "i", "4", noChildren) // i == 4
+					expectVarExact(t, locals, 0, "i", "i", "4", noChildren) // i == 4
 
 					// Set at a line without a statement
 					client.SetBreakpointsRequest(fixture.Source, []int{1000})
@@ -1269,7 +1466,7 @@ func expectEval(t *testing.T, got *dap.EvaluateResponse, value string, hasRef bo
 
 func TestEvaluateRequest(t *testing.T) {
 	runTest(t, "testvariables", func(client *daptest.Client, fixture protest.Fixture) {
-		runDebugSessionWithBPs(t, client,
+		runDebugSessionWithBPs(t, client, "launch",
 			// Launch
 			func() {
 				client.LaunchRequest("exec", fixture.Path, !stopOnEntry)
@@ -1291,8 +1488,10 @@ func TestEvaluateRequest(t *testing.T) {
 						client.VariablesRequest(ref)
 						a5 := client.ExpectVariablesResponse(t)
 						expectChildren(t, a5, "a5", 5)
-						expectVarExact(t, a5, 0, "[0]", "1", noChildren)
-						expectVarExact(t, a5, 4, "[4]", "5", noChildren)
+						expectVarExact(t, a5, 0, "[0]", "(a5)[0]", "1", noChildren)
+						expectVarExact(t, a5, 4, "[4]", "(a5)[4]", "5", noChildren)
+						validateEvaluateName(t, client, a5, 0)
+						validateEvaluateName(t, client, a5, 4)
 					}
 
 					// All (binary and unary) on basic types except <-, ++ and --
@@ -1319,7 +1518,7 @@ func TestEvaluateRequest(t *testing.T) {
 						expr := client.ExpectVariablesResponse(t)
 						expectChildren(t, expr, "(*int)(2)", 1)
 						// TODO(polina): should this be printed as (unknown int) instead?
-						expectVarExact(t, expr, 0, "", "<int>", noChildren)
+						expectVarExact(t, expr, 0, "", "(*((*int)(2)))", "<int>", noChildren)
 					}
 					// Type casts between string, []byte and []rune
 					client.EvaluateRequest("[]byte(\"ABC€\")", 1000, "this context will be ignored")
@@ -1346,7 +1545,8 @@ func TestEvaluateRequest(t *testing.T) {
 						client.VariablesRequest(ref)
 						expr := client.ExpectVariablesResponse(t)
 						expectChildren(t, expr, "mp[1]", 1)
-						expectVarExact(t, expr, 0, "data", "42", noChildren)
+						expectVarExact(t, expr, 0, "data", "(mp[1]).(data)", "42", noChildren)
+						validateEvaluateName(t, client, expr, 0)
 					}
 
 					// Pointer dereference
@@ -1357,7 +1557,8 @@ func TestEvaluateRequest(t *testing.T) {
 						client.VariablesRequest(ref)
 						expr := client.ExpectVariablesResponse(t)
 						expectChildren(t, expr, "*ms.Nest", 2)
-						expectVarExact(t, expr, 0, "Level", "1", noChildren)
+						expectVarExact(t, expr, 0, "Level", "(*ms.Nest).Level", "1", noChildren)
+						validateEvaluateName(t, client, expr, 0)
 					}
 
 					// Calls to builtin functions: cap, len, complex, imag and real
@@ -1407,7 +1608,7 @@ func TestEvaluateRequest(t *testing.T) {
 func TestEvaluateCallRequest(t *testing.T) {
 	protest.MustSupportFunctionCalls(t, testBackend)
 	runTest(t, "fncall", func(client *daptest.Client, fixture protest.Fixture) {
-		runDebugSessionWithBPs(t, client,
+		runDebugSessionWithBPs(t, client, "launch",
 			// Launch
 			func() {
 				client.LaunchRequest("exec", fixture.Path, !stopOnEntry)
@@ -1492,7 +1693,7 @@ func TestEvaluateCallRequest(t *testing.T) {
 						client.VariablesRequest(ref)
 						rv := client.ExpectVariablesResponse(t)
 						expectChildren(t, rv, "rv", 1)
-						expectVarExact(t, rv, 0, "~r2", "3", noChildren)
+						expectVarExact(t, rv, 0, "~r2", "", "3", noChildren)
 					}
 					// One named return value
 					// Panic doesn't panic, but instead returns the error as a named return variable
@@ -1503,12 +1704,12 @@ func TestEvaluateCallRequest(t *testing.T) {
 						client.VariablesRequest(ref)
 						rv := client.ExpectVariablesResponse(t)
 						expectChildren(t, rv, "rv", 1)
-						ref = expectVarExact(t, rv, 0, "~panic", "<interface {}(string)>", hasChildren)
+						ref = expectVarExact(t, rv, 0, "~panic", "", "<interface {}(string)>", hasChildren)
 						if ref > 0 {
 							client.VariablesRequest(ref)
 							p := client.ExpectVariablesResponse(t)
 							expectChildren(t, p, "~panic", 1)
-							expectVarExact(t, p, 0, "data", "\"callpanic panicked\"", noChildren)
+							expectVarExact(t, p, 0, "data", "", "\"callpanic panicked\"", noChildren)
 						}
 					}
 					// Multiple return values
@@ -1519,8 +1720,8 @@ func TestEvaluateCallRequest(t *testing.T) {
 						client.VariablesRequest(ref)
 						rvs := client.ExpectVariablesResponse(t)
 						expectChildren(t, rvs, "rvs", 2)
-						expectVarExact(t, rvs, 0, "~r2", "1", noChildren)
-						expectVarExact(t, rvs, 1, "~r3", "2", noChildren)
+						expectVarExact(t, rvs, 0, "~r2", "", "1", noChildren)
+						expectVarExact(t, rvs, 1, "~r3", "", "2", noChildren)
 					}
 					// No frame defaults to top-most frame
 					client.EvaluateRequest("call call1(one, two)", 0, "this context will be ignored")
@@ -1551,7 +1752,7 @@ func TestEvaluateCallRequest(t *testing.T) {
 
 func TestNextAndStep(t *testing.T) {
 	runTest(t, "testinline", func(client *daptest.Client, fixture protest.Fixture) {
-		runDebugSessionWithBPs(t, client,
+		runDebugSessionWithBPs(t, client, "launch",
 			// Launch
 			func() {
 				client.LaunchRequest("exec", fixture.Path, !stopOnEntry)
@@ -1597,7 +1798,7 @@ func TestBadAccess(t *testing.T) {
 		t.Skip("not applicable")
 	}
 	runTest(t, "issue2078", func(client *daptest.Client, fixture protest.Fixture) {
-		runDebugSessionWithBPs(t, client,
+		runDebugSessionWithBPs(t, client, "launch",
 			// Launch
 			func() {
 				client.LaunchRequest("exec", fixture.Path, !stopOnEntry)
@@ -1647,7 +1848,7 @@ func TestBadAccess(t *testing.T) {
 
 func TestPanicBreakpointOnContinue(t *testing.T) {
 	runTest(t, "panic", func(client *daptest.Client, fixture protest.Fixture) {
-		runDebugSessionWithBPs(t, client,
+		runDebugSessionWithBPs(t, client, "launch",
 			// Launch
 			func() {
 				client.LaunchRequest("exec", fixture.Path, !stopOnEntry)
@@ -1679,7 +1880,7 @@ func TestPanicBreakpointOnNext(t *testing.T) {
 	}
 
 	runTest(t, "panic", func(client *daptest.Client, fixture protest.Fixture) {
-		runDebugSessionWithBPs(t, client,
+		runDebugSessionWithBPs(t, client, "launch",
 			// Launch
 			func() {
 				client.LaunchRequest("exec", fixture.Path, !stopOnEntry)
@@ -1706,7 +1907,7 @@ func TestPanicBreakpointOnNext(t *testing.T) {
 
 func TestFatalThrowBreakpoint(t *testing.T) {
 	runTest(t, "testdeadlock", func(client *daptest.Client, fixture protest.Fixture) {
-		runDebugSessionWithBPs(t, client,
+		runDebugSessionWithBPs(t, client, "launch",
 			// Launch
 			func() {
 				client.LaunchRequest("exec", fixture.Path, !stopOnEntry)
@@ -1775,19 +1976,27 @@ type onBreakpoint struct {
 
 // runDebugSessionWithBPs is a helper for executing the common init and shutdown
 // sequences for a program that does not stop on entry
-// while specifying breakpoints and unique launch criteria via parameters.
-//     launchRequest - a function that sends a launch request, so the test author
-//                     has full control of its arguments. Note that he rest of
-//                     the test sequence assumes that stopOneEntry is false.
+// while specifying breakpoints and unique launch/attach criteria via parameters.
+//    cmd            - "launch" or "attach"
+//    cmdRequest     - a function that sends a launch or attach request,
+//                     so the test author has full control of its arguments.
+//                     Note that he rest of the test sequence assumes that
+//                     stopOnEntry is false.
 //     breakpoints   - list of lines, where breakpoints are to be set
 //     onBreakpoints - list of test sequences to execute at each of the set breakpoints.
-func runDebugSessionWithBPs(t *testing.T, client *daptest.Client, launchRequest func(), source string, breakpoints []int, onBPs []onBreakpoint) {
+func runDebugSessionWithBPs(t *testing.T, client *daptest.Client, cmd string, cmdRequest func(), source string, breakpoints []int, onBPs []onBreakpoint) {
 	client.InitializeRequest()
 	client.ExpectInitializeResponse(t)
 
-	launchRequest()
+	cmdRequest()
 	client.ExpectInitializedEvent(t)
-	client.ExpectLaunchResponse(t)
+	if cmd == "launch" {
+		client.ExpectLaunchResponse(t)
+	} else if cmd == "attach" {
+		client.ExpectAttachResponse(t)
+	} else {
+		panic("expected launch or attach command")
+	}
 
 	client.SetBreakpointsRequest(source, breakpoints)
 	client.ExpectSetBreakpointsResponse(t)
@@ -1807,7 +2016,7 @@ func runDebugSessionWithBPs(t *testing.T, client *daptest.Client, launchRequest
 		client.ExpectStoppedEvent(t)
 		onBP.execute()
 		if onBP.disconnect {
-			client.DisconnectRequest()
+			client.DisconnectRequestWithKillOption(true)
 			client.ExpectDisconnectResponse(t)
 			return
 		}
@@ -1816,23 +2025,25 @@ func runDebugSessionWithBPs(t *testing.T, client *daptest.Client, launchRequest
 		// "Continue" is triggered after the response is sent
 	}
 
-	client.ExpectTerminatedEvent(t)
-	client.DisconnectRequest()
+	if cmd == "launch" { // Let the program run to completion
+		client.ExpectTerminatedEvent(t)
+	}
+	client.DisconnectRequestWithKillOption(true)
 	client.ExpectDisconnectResponse(t)
 }
 
 // runDebugSession is a helper for executing the standard init and shutdown
 // sequences for a program that does not stop on entry
 // while specifying unique launch criteria via parameters.
-func runDebugSession(t *testing.T, client *daptest.Client, launchRequest func(), source string) {
-	runDebugSessionWithBPs(t, client, launchRequest, source, nil, nil)
+func runDebugSession(t *testing.T, client *daptest.Client, cmd string, cmdRequest func(), source string) {
+	runDebugSessionWithBPs(t, client, cmd, cmdRequest, source, nil, nil)
 }
 
 func TestLaunchDebugRequest(t *testing.T) {
 	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, func() {
+		runDebugSession(t, client, "launch", func() {
 			// Use the default output directory.
 			client.LaunchRequestWithArgs(map[string]interface{}{
 				"mode": "debug", "program": fixture.Source})
@@ -1842,7 +2053,7 @@ func TestLaunchDebugRequest(t *testing.T) {
 
 func TestLaunchTestRequest(t *testing.T) {
 	runTest(t, "increment", func(client *daptest.Client, fixture protest.Fixture) {
-		runDebugSession(t, client, func() {
+		runDebugSession(t, client, "launch", func() {
 			// We reuse the harness that builds, but ignore the built binary,
 			// only relying on the source to be built in response to LaunchRequest.
 			fixtures := protest.FindFixturesDir()
@@ -1859,7 +2070,7 @@ func TestLaunchTestRequest(t *testing.T) {
 // Terminated Event.
 func TestLaunchRequestWithArgs(t *testing.T) {
 	runTest(t, "testargs", func(client *daptest.Client, fixture protest.Fixture) {
-		runDebugSession(t, client, func() {
+		runDebugSession(t, client, "launch", func() {
 			client.LaunchRequestWithArgs(map[string]interface{}{
 				"mode": "exec", "program": fixture.Path,
 				"args": []string{"test", "pass flag"}})
@@ -1873,7 +2084,7 @@ func TestLaunchRequestWithArgs(t *testing.T) {
 // TerminatedEvent.
 func TestLaunchRequestWithBuildFlags(t *testing.T) {
 	runTest(t, "buildflagtest", func(client *daptest.Client, fixture protest.Fixture) {
-		runDebugSession(t, client, func() {
+		runDebugSession(t, client, "launch", func() {
 			// We reuse the harness that builds, but ignore the built binary,
 			// only relying on the source to be built in response to LaunchRequest.
 			client.LaunchRequestWithArgs(map[string]interface{}{
@@ -1883,6 +2094,44 @@ func TestLaunchRequestWithBuildFlags(t *testing.T) {
 	})
 }
 
+func TestAttachRequest(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) {
+		// 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)
+		}
+
+		runDebugSessionWithBPs(t, client, "attach",
+			// Attach
+			func() {
+				client.AttachRequest(map[string]interface{}{"mode": "local", "processId": cmd.Process.Pid, "stopOnEntry": false})
+			},
+			// Set breakpoints
+			fixture.Source, []int{8},
+			[]onBreakpoint{{
+				// Stop at line 8
+				execute: func() {
+					handleStop(t, client, 1, "main.loop", 8)
+					client.VariablesRequest(1001) // Locals
+					locals := client.ExpectVariablesResponse(t)
+					expectChildren(t, locals, "Locals", 1)
+					expectVarRegex(t, locals, 0, "i", "i", "[0-9]+", noChildren)
+				},
+				disconnect: true,
+			}})
+	})
+}
+
 func TestUnupportedCommandResponses(t *testing.T) {
 	var got *dap.ErrorResponse
 	runTest(t, "increment", func(client *daptest.Client, fixture protest.Fixture) {
@@ -1947,9 +2196,6 @@ func TestRequiredNotYetImplementedResponses(t *testing.T) {
 			seqCnt++
 		}
 
-		client.AttachRequest()
-		expectNotYetImplemented("attach")
-
 		client.PauseRequest()
 		expectNotYetImplemented("pause")
 	})
@@ -2098,6 +2344,92 @@ func TestBadLaunchRequests(t *testing.T) {
 	})
 }
 
+func TestBadAttachRequest(t *testing.T) {
+	runTest(t, "loopprog", func(client *daptest.Client, fixture protest.Fixture) {
+		seqCnt := 1
+		expectFailedToAttach := func(response *dap.ErrorResponse) {
+			t.Helper()
+			if response.RequestSeq != seqCnt {
+				t.Errorf("RequestSeq got %d, want %d", seqCnt, response.RequestSeq)
+			}
+			if response.Command != "attach" {
+				t.Errorf("Command got %q, want \"attach\"", response.Command)
+			}
+			if response.Message != "Failed to attach" {
+				t.Errorf("Message got %q, want \"Failed to attach\"", response.Message)
+			}
+			if response.Body.Error.Id != 3001 {
+				t.Errorf("Id got %d, want 3001", response.Body.Error.Id)
+			}
+			seqCnt++
+		}
+
+		expectFailedToAttachWithMessage := func(response *dap.ErrorResponse, errmsg string) {
+			t.Helper()
+			expectFailedToAttach(response)
+			if response.Body.Error.Format != errmsg {
+				t.Errorf("\ngot  %q\nwant %q", response.Body.Error.Format, errmsg)
+			}
+		}
+
+		client.AttachRequest(map[string]interface{}{"mode": "remote"})
+		expectFailedToAttachWithMessage(client.ExpectErrorResponse(t),
+			"Failed to attach: Unsupported 'mode' value \"remote\" in debug configuration")
+
+		client.AttachRequest(map[string]interface{}{"mode": "blah blah blah"})
+		expectFailedToAttachWithMessage(client.ExpectErrorResponse(t),
+			"Failed to attach: Unsupported 'mode' value \"blah blah blah\" in debug configuration")
+
+		client.AttachRequest(map[string]interface{}{"mode": 123})
+		expectFailedToAttachWithMessage(client.ExpectErrorResponse(t),
+			"Failed to attach: Unsupported 'mode' value %!q(float64=123) in debug configuration")
+
+		client.AttachRequest(map[string]interface{}{"mode": ""}) // defaults to "local"
+		expectFailedToAttachWithMessage(client.ExpectErrorResponse(t),
+			"Failed to attach: The 'processId' attribute is missing in debug configuration")
+
+		client.AttachRequest(map[string]interface{}{}) // defaults to "local"
+		expectFailedToAttachWithMessage(client.ExpectErrorResponse(t),
+			"Failed to attach: The 'processId' attribute is missing in debug configuration")
+
+		client.AttachRequest(map[string]interface{}{"processId": 0}) // defaults to "local"
+		expectFailedToAttachWithMessage(client.ExpectErrorResponse(t),
+			"Failed to attach: The 'processId' attribute is missing in debug configuration")
+
+		client.AttachRequest(map[string]interface{}{"processId": "1"}) // defaults to "local"
+		expectFailedToAttachWithMessage(client.ExpectErrorResponse(t),
+			"Failed to attach: The 'processId' attribute is missing in debug configuration")
+
+		client.AttachRequest(map[string]interface{}{"processId": 1}) // defaults to "local"
+		// The exact message varies on different systems, so skip that check
+		expectFailedToAttach(client.ExpectErrorResponse(t)) // could not attach to pid 1
+
+		// This will make debugger.(*Debugger) panic, which we will catch as an internal error.
+		client.AttachRequest(map[string]interface{}{"processId": -1}) // defaults to "local"
+		er := client.ExpectErrorResponse(t)
+		if er.RequestSeq != seqCnt {
+			t.Errorf("RequestSeq got %d, want %d", seqCnt, er.RequestSeq)
+		}
+		seqCnt++
+		if er.Command != "" {
+			t.Errorf("Command got %q, want \"attach\"", er.Command)
+		}
+		if er.Body.Error.Format != "Internal Error: runtime error: index out of range [0] with length 0" {
+			t.Errorf("Message got %q, want \"Internal Error: runtime error: index out of range [0] with length 0\"", er.Message)
+		}
+		if er.Body.Error.Id != 8888 {
+			t.Errorf("Id got %d, want 8888", er.Body.Error.Id)
+		}
+
+		// We failed to launch the program. Make sure shutdown still works.
+		client.DisconnectRequest()
+		dresp := client.ExpectDisconnectResponse(t)
+		if dresp.RequestSeq != seqCnt {
+			t.Errorf("got %#v, want RequestSeq=%d", dresp, seqCnt)
+		}
+	})
+}
+
 func TestBadlyFormattedMessageToServer(t *testing.T) {
 	runTest(t, "increment", func(client *daptest.Client, fixture protest.Fixture) {
 		// Send a badly formatted message to the server, and expect it to close the
diff --git a/service/test/integration1_test.go b/service/test/integration1_test.go
index 08966c52..8b5afdab 100644
--- a/service/test/integration1_test.go
+++ b/service/test/integration1_test.go
@@ -730,19 +730,20 @@ func Test1ClientServer_FullStacktrace(t *testing.T) {
 		for _, g := range gs {
 			frames, err := c.Stacktrace(g.ID, 10, true)
 			assertNoError(err, t, fmt.Sprintf("Stacktrace(%d)", g.ID))
+			t.Logf("goroutine %d", g.ID)
 			for i, frame := range frames {
+				t.Logf("\tframe %d off=%#x bpoff=%#x pc=%#x %s:%d %s", i, frame.FrameOffset, frame.FramePointerOffset, frame.PC, frame.File, frame.Line, frame.Function.Name())
 				if frame.Function == nil {
 					continue
 				}
 				if frame.Function.Name() != "main.agoroutine" {
 					continue
 				}
-				t.Logf("frame %d: %v", i, frame)
 				for _, arg := range frame.Arguments {
 					if arg.Name != "i" {
 						continue
 					}
-					t.Logf("frame %d, variable i is %v\n", i, arg)
+					t.Logf("\tvariable i is %+v\n", arg)
 					argn, err := strconv.Atoi(arg.Value)
 					if err == nil {
 						found[argn] = true
@@ -757,6 +758,8 @@ func Test1ClientServer_FullStacktrace(t *testing.T) {
 			}
 		}
 
+		t.Logf("continue")
+
 		state = <-c.Continue()
 		if state.Err != nil {
 			t.Fatalf("Continue(): %v\n", state.Err)
@@ -767,10 +770,10 @@ func Test1ClientServer_FullStacktrace(t *testing.T) {
 
 		cur := 3
 		for i, frame := range frames {
+			t.Logf("\tframe %d off=%#x bpoff=%#x pc=%#x %s:%d %s", i, frame.FrameOffset, frame.FramePointerOffset, frame.PC, frame.File, frame.Line, frame.Function.Name())
 			if i == 0 {
 				continue
 			}
-			t.Logf("frame %d: %v", i, frame)
 			v := frame.Var("n")
 			if v == nil {
 				t.Fatalf("Could not find value of variable n in frame %d", i)
diff --git a/service/test/integration2_test.go b/service/test/integration2_test.go
index be66223b..e0468a6b 100644
--- a/service/test/integration2_test.go
+++ b/service/test/integration2_test.go
@@ -925,19 +925,20 @@ func TestClientServer_FullStacktrace(t *testing.T) {
 		for _, g := range gs {
 			frames, err := c.Stacktrace(g.ID, 10, 0, &normalLoadConfig)
 			assertNoError(err, t, fmt.Sprintf("Stacktrace(%d)", g.ID))
+			t.Logf("goroutine %d", g.ID)
 			for i, frame := range frames {
+				t.Logf("\tframe %d off=%#x bpoff=%#x pc=%#x %s:%d %s", i, frame.FrameOffset, frame.FramePointerOffset, frame.PC, frame.File, frame.Line, frame.Function.Name())
 				if frame.Function == nil {
 					continue
 				}
 				if frame.Function.Name() != "main.agoroutine" {
 					continue
 				}
-				t.Logf("frame %d: %v", i, frame)
 				for _, arg := range frame.Arguments {
 					if arg.Name != "i" {
 						continue
 					}
-					t.Logf("frame %v, variable i is %v\n", frame, arg)
+					t.Logf("\tvariable i is %+v\n", arg)
 					argn, err := strconv.Atoi(arg.Value)
 					if err == nil {
 						found[argn] = true
@@ -952,6 +953,8 @@ func TestClientServer_FullStacktrace(t *testing.T) {
 			}
 		}
 
+		t.Logf("continue")
+
 		state = <-c.Continue()
 		if state.Err != nil {
 			t.Fatalf("Continue(): %v\n", state.Err)
@@ -962,10 +965,10 @@ func TestClientServer_FullStacktrace(t *testing.T) {
 
 		cur := 3
 		for i, frame := range frames {
+			t.Logf("\tframe %d off=%#x bpoff=%#x pc=%#x %s:%d %s", i, frame.FrameOffset, frame.FramePointerOffset, frame.PC, frame.File, frame.Line, frame.Function.Name())
 			if i == 0 {
 				continue
 			}
-			t.Logf("frame %d: %v", i, frame)
 			v := frame.Var("n")
 			if v == nil {
 				t.Fatalf("Could not find value of variable n in frame %d", i)
@@ -1467,7 +1470,7 @@ func TestClientServer_FpRegisters(t *testing.T) {
 
 func TestClientServer_RestartBreakpointPosition(t *testing.T) {
 	protest.AllowRecording(t)
-	if buildMode == "pie" {
+	if buildMode == "pie" || (runtime.GOOS == "darwin" && runtime.GOARCH == "arm64") {
 		t.Skip("not meaningful in PIE mode")
 	}
 	withTestClient2("locationsprog2", t, func(c service.Client) {
@@ -1709,7 +1712,7 @@ func TestAcceptMulticlient(t *testing.T) {
 			},
 		})
 		if err := server.Run(); err != nil {
-			t.Fatal(err)
+			panic(err)
 		}
 		<-disconnectChan
 		server.Stop()