Repository: go-delve/delve (go)

Issue:
[feature request] Make the backtrace print colored
The BT command prints something like this 

```
 0  0x00000000006594ce in k8s.io/kubernetes/vendor/k8s.io/apimachinery/pkg/api/meta.(*DefaultRESTMapper).RESTMappings
    at /home/vagrant/gopath/src/k8s.io/kubernetes/vendor/k8s.io/apimachinery/pkg/api/meta/restmapper.go:493
 1  0x000000000064e07c in k8s.io/kubernetes/vendor/k8s.io/apimachinery/pkg/api/meta.MultiRESTMapper.RESTMappings
    at /home/vagrant/gopath/src/k8s.io/kubernetes/vendor/k8s.io/apimachinery/pkg/api/meta/multirestmapper.go:193
 2  0x0000000000000000 in k8s.io/kubernetes/vendor/k8s.io/apimachinery/pkg/api/meta.(*MultiRESTMapper).RESTMappings
    at <autogenerated>:132
 3  0x000000000065083a in k8s.io/kubernetes/vendor/k8s.io/apimachinery/pkg/api/meta.PriorityRESTMapper.RESTMapping
    at /home/vagrant/gopath/src/k8s.io/kubernetes/vendor/k8s.io/apimachinery/pkg/api/meta/priority.go:156
 4  0x0000000000000000 in k8s.io/kubernetes/vendor/k8s.io/apimachinery/pkg/api/meta.(*PriorityRESTMapper).RESTMapping
    at <autogenerated>:214
 5  0x00000000007e4615 in k8s.io/kubernetes/pkg/kubectl/cmd/util.ShortcutExpander.RESTMapping
    at ./util/shortcut_restmapper.go:94
 6  0x0000000000000000 in k8s.io/kubernetes/pkg/kubectl/cmd/util.(*ShortcutExpander).RESTMapping
    at <autogenerated>:209
 7  0x00000000007fba2b in k8s.io/kubernetes/pkg/kubectl/resource.(*Mapper).InfoForData
    at /home/vagrant/gopath/src/k8s.io/kubernetes/pkg/kubectl/resource/mapper.go:58
 8  0x0000000000805154 in k8s.io/kubernetes/pkg/kubectl/resource.(*StreamVisitor).Visit
    at /home/vagrant/gopath/src/k8s.io/kubernetes/pkg/kubectl/resource/visitor.go:545
 9  0x00000000008049b5 in k8s.io/kubernetes/pkg/kubectl/resource.(*FileVisitor).Visit
    at /home/vagrant/gopath/src/k8s.io/kubernetes/pkg/kubectl/resource/visitor.go:501
10  0x0000000000802bae in k8s.io/kubernetes/pkg/kubectl/resource.EagerVisitorList.Visit
    at /home/vagrant/gopath/src/k8s.io/kubernetes/pkg/kubectl/resource/visitor.go:211

```

It feels pretty difficult to parse this visually. 
It would be great if the address, function, file and line would be colored differently. 

I use dlv from a iterm2 command like in Mac and am not very familiar with other use cases. I believe it is also supported in Windows, maybe in visual studio. 
Not sure how all the coloring would work in windows. 


Pull request:
terminal: add color highlighting to stacktraces
Adds configurable highlighting for function names and filenames in stack traces.

Fixes #720


Patch:
diff --git a/Documentation/usage/dlv_attach.md b/Documentation/usage/dlv_attach.md
index a86973cd..58e4ae9d 100644
--- a/Documentation/usage/dlv_attach.md
+++ b/Documentation/usage/dlv_attach.md
@@ -35,7 +35,7 @@ dlv attach pid [executable] [flags]
       --check-go-version                 Exits if the version of Go in use is not compatible (too old or too new) with the version of Delve. (default true)
       --headless                         Run debug server only, in headless mode. Server will accept both JSON-RPC or DAP client connections.
       --init string                      Init file, executed by the terminal client.
-  -l, --listen string                    Debugging server listen address. (default "127.0.0.1:0")
+  -l, --listen string                    Debugging server listen address. Prefix with 'unix:' to use a unix domain socket. (default "127.0.0.1:0")
       --log                              Enable debugging server logging.
       --log-dest string                  Writes logs to the specified file or file descriptor (see 'dlv help log').
       --log-output string                Comma separated list of components that should produce debug output (see 'dlv help log')
diff --git a/Documentation/usage/dlv_backend.md b/Documentation/usage/dlv_backend.md
index 3ccc9ba9..d4c2bcbb 100644
--- a/Documentation/usage/dlv_backend.md
+++ b/Documentation/usage/dlv_backend.md
@@ -32,7 +32,7 @@ are:
       --disable-aslr                     Disables address space randomization
       --headless                         Run debug server only, in headless mode. Server will accept both JSON-RPC or DAP client connections.
       --init string                      Init file, executed by the terminal client.
-  -l, --listen string                    Debugging server listen address. (default "127.0.0.1:0")
+  -l, --listen string                    Debugging server listen address. Prefix with 'unix:' to use a unix domain socket. (default "127.0.0.1:0")
       --log                              Enable debugging server logging.
       --log-dest string                  Writes logs to the specified file or file descriptor (see 'dlv help log').
       --log-output string                Comma separated list of components that should produce debug output (see 'dlv help log')
diff --git a/Documentation/usage/dlv_connect.md b/Documentation/usage/dlv_connect.md
index c83e94d0..ea06d67e 100644
--- a/Documentation/usage/dlv_connect.md
+++ b/Documentation/usage/dlv_connect.md
@@ -4,7 +4,7 @@ Connect to a headless debug server with a terminal client.
 
 ### Synopsis
 
-Connect to a running headless debug server with a terminal client.
+Connect to a running headless debug server with a terminal client. Prefix with 'unix:' to use a unix domain socket.
 
 ```
 dlv connect addr [flags]
diff --git a/Documentation/usage/dlv_core.md b/Documentation/usage/dlv_core.md
index 456e908c..d582cc1f 100644
--- a/Documentation/usage/dlv_core.md
+++ b/Documentation/usage/dlv_core.md
@@ -31,7 +31,7 @@ dlv core <executable> <core> [flags]
       --check-go-version                 Exits if the version of Go in use is not compatible (too old or too new) with the version of Delve. (default true)
       --headless                         Run debug server only, in headless mode. Server will accept both JSON-RPC or DAP client connections.
       --init string                      Init file, executed by the terminal client.
-  -l, --listen string                    Debugging server listen address. (default "127.0.0.1:0")
+  -l, --listen string                    Debugging server listen address. Prefix with 'unix:' to use a unix domain socket. (default "127.0.0.1:0")
       --log                              Enable debugging server logging.
       --log-dest string                  Writes logs to the specified file or file descriptor (see 'dlv help log').
       --log-output string                Comma separated list of components that should produce debug output (see 'dlv help log')
diff --git a/Documentation/usage/dlv_dap.md b/Documentation/usage/dlv_dap.md
index 3f5f7db8..fafb1e7d 100644
--- a/Documentation/usage/dlv_dap.md
+++ b/Documentation/usage/dlv_dap.md
@@ -42,7 +42,7 @@ dlv dap [flags]
 ```
       --check-go-version    Exits if the version of Go in use is not compatible (too old or too new) with the version of Delve. (default true)
       --disable-aslr        Disables address space randomization
-  -l, --listen string       Debugging server listen address. (default "127.0.0.1:0")
+  -l, --listen string       Debugging server listen address. Prefix with 'unix:' to use a unix domain socket. (default "127.0.0.1:0")
       --log                 Enable debugging server logging.
       --log-dest string     Writes logs to the specified file or file descriptor (see 'dlv help log').
       --log-output string   Comma separated list of components that should produce debug output (see 'dlv help log')
diff --git a/Documentation/usage/dlv_debug.md b/Documentation/usage/dlv_debug.md
index 23c75ea8..572b65f6 100644
--- a/Documentation/usage/dlv_debug.md
+++ b/Documentation/usage/dlv_debug.md
@@ -36,7 +36,7 @@ dlv debug [package] [flags]
       --disable-aslr                     Disables address space randomization
       --headless                         Run debug server only, in headless mode. Server will accept both JSON-RPC or DAP client connections.
       --init string                      Init file, executed by the terminal client.
-  -l, --listen string                    Debugging server listen address. (default "127.0.0.1:0")
+  -l, --listen string                    Debugging server listen address. Prefix with 'unix:' to use a unix domain socket. (default "127.0.0.1:0")
       --log                              Enable debugging server logging.
       --log-dest string                  Writes logs to the specified file or file descriptor (see 'dlv help log').
       --log-output string                Comma separated list of components that should produce debug output (see 'dlv help log')
diff --git a/Documentation/usage/dlv_exec.md b/Documentation/usage/dlv_exec.md
index 70d96536..10099651 100644
--- a/Documentation/usage/dlv_exec.md
+++ b/Documentation/usage/dlv_exec.md
@@ -35,7 +35,7 @@ dlv exec <path/to/binary> [flags]
       --disable-aslr                     Disables address space randomization
       --headless                         Run debug server only, in headless mode. Server will accept both JSON-RPC or DAP client connections.
       --init string                      Init file, executed by the terminal client.
-  -l, --listen string                    Debugging server listen address. (default "127.0.0.1:0")
+  -l, --listen string                    Debugging server listen address. Prefix with 'unix:' to use a unix domain socket. (default "127.0.0.1:0")
       --log                              Enable debugging server logging.
       --log-dest string                  Writes logs to the specified file or file descriptor (see 'dlv help log').
       --log-output string                Comma separated list of components that should produce debug output (see 'dlv help log')
diff --git a/Documentation/usage/dlv_log.md b/Documentation/usage/dlv_log.md
index 7dc6ed2c..b1d4eab5 100644
--- a/Documentation/usage/dlv_log.md
+++ b/Documentation/usage/dlv_log.md
@@ -48,7 +48,7 @@ and dap modes.
       --disable-aslr                     Disables address space randomization
       --headless                         Run debug server only, in headless mode. Server will accept both JSON-RPC or DAP client connections.
       --init string                      Init file, executed by the terminal client.
-  -l, --listen string                    Debugging server listen address. (default "127.0.0.1:0")
+  -l, --listen string                    Debugging server listen address. Prefix with 'unix:' to use a unix domain socket. (default "127.0.0.1:0")
       --log                              Enable debugging server logging.
       --log-dest string                  Writes logs to the specified file or file descriptor (see 'dlv help log').
       --log-output string                Comma separated list of components that should produce debug output (see 'dlv help log')
diff --git a/Documentation/usage/dlv_redirect.md b/Documentation/usage/dlv_redirect.md
index 940b1359..f4bb0689 100644
--- a/Documentation/usage/dlv_redirect.md
+++ b/Documentation/usage/dlv_redirect.md
@@ -35,7 +35,7 @@ File redirects can also be changed using the 'restart' command.
       --disable-aslr                     Disables address space randomization
       --headless                         Run debug server only, in headless mode. Server will accept both JSON-RPC or DAP client connections.
       --init string                      Init file, executed by the terminal client.
-  -l, --listen string                    Debugging server listen address. (default "127.0.0.1:0")
+  -l, --listen string                    Debugging server listen address. Prefix with 'unix:' to use a unix domain socket. (default "127.0.0.1:0")
       --log                              Enable debugging server logging.
       --log-dest string                  Writes logs to the specified file or file descriptor (see 'dlv help log').
       --log-output string                Comma separated list of components that should produce debug output (see 'dlv help log')
diff --git a/Documentation/usage/dlv_replay.md b/Documentation/usage/dlv_replay.md
index 6f783563..a080f586 100644
--- a/Documentation/usage/dlv_replay.md
+++ b/Documentation/usage/dlv_replay.md
@@ -30,7 +30,7 @@ dlv replay [trace directory] [flags]
       --check-go-version                 Exits if the version of Go in use is not compatible (too old or too new) with the version of Delve. (default true)
       --headless                         Run debug server only, in headless mode. Server will accept both JSON-RPC or DAP client connections.
       --init string                      Init file, executed by the terminal client.
-  -l, --listen string                    Debugging server listen address. (default "127.0.0.1:0")
+  -l, --listen string                    Debugging server listen address. Prefix with 'unix:' to use a unix domain socket. (default "127.0.0.1:0")
       --log                              Enable debugging server logging.
       --log-dest string                  Writes logs to the specified file or file descriptor (see 'dlv help log').
       --log-output string                Comma separated list of components that should produce debug output (see 'dlv help log')
diff --git a/Documentation/usage/dlv_run.md b/Documentation/usage/dlv_run.md
index 6ab4b674..02b7fe73 100644
--- a/Documentation/usage/dlv_run.md
+++ b/Documentation/usage/dlv_run.md
@@ -24,7 +24,7 @@ dlv run [flags]
       --disable-aslr                     Disables address space randomization
       --headless                         Run debug server only, in headless mode. Server will accept both JSON-RPC or DAP client connections.
       --init string                      Init file, executed by the terminal client.
-  -l, --listen string                    Debugging server listen address. (default "127.0.0.1:0")
+  -l, --listen string                    Debugging server listen address. Prefix with 'unix:' to use a unix domain socket. (default "127.0.0.1:0")
       --log                              Enable debugging server logging.
       --log-dest string                  Writes logs to the specified file or file descriptor (see 'dlv help log').
       --log-output string                Comma separated list of components that should produce debug output (see 'dlv help log')
diff --git a/Documentation/usage/dlv_version.md b/Documentation/usage/dlv_version.md
index b04dd76e..92f26b8b 100644
--- a/Documentation/usage/dlv_version.md
+++ b/Documentation/usage/dlv_version.md
@@ -24,7 +24,7 @@ dlv version [flags]
       --disable-aslr                     Disables address space randomization
       --headless                         Run debug server only, in headless mode. Server will accept both JSON-RPC or DAP client connections.
       --init string                      Init file, executed by the terminal client.
-  -l, --listen string                    Debugging server listen address. (default "127.0.0.1:0")
+  -l, --listen string                    Debugging server listen address. Prefix with 'unix:' to use a unix domain socket. (default "127.0.0.1:0")
       --log                              Enable debugging server logging.
       --log-dest string                  Writes logs to the specified file or file descriptor (see 'dlv help log').
       --log-output string                Comma separated list of components that should produce debug output (see 'dlv help log')
diff --git a/cmd/dlv/cmds/commands.go b/cmd/dlv/cmds/commands.go
index 2cda26e0..63b2b118 100644
--- a/cmd/dlv/cmds/commands.go
+++ b/cmd/dlv/cmds/commands.go
@@ -137,7 +137,7 @@ func New(docCall bool) *cobra.Command {
 		Long:  dlvCommandLongDesc,
 	}
 
-	rootCommand.PersistentFlags().StringVarP(&addr, "listen", "l", "127.0.0.1:0", "Debugging server listen address.")
+	rootCommand.PersistentFlags().StringVarP(&addr, "listen", "l", "127.0.0.1:0", "Debugging server listen address. Prefix with 'unix:' to use a unix domain socket.")
 
 	rootCommand.PersistentFlags().BoolVarP(&log, "log", "", false, "Enable debugging server logging.")
 	rootCommand.PersistentFlags().StringVarP(&logOutput, "log-output", "", "", `Comma separated list of components that should produce debug output (see 'dlv help log')`)
@@ -184,7 +184,7 @@ option to let the process continue or kill it.
 	connectCommand := &cobra.Command{
 		Use:   "connect addr",
 		Short: "Connect to a headless debug server with a terminal client.",
-		Long:  "Connect to a running headless debug server with a terminal client.",
+		Long:  "Connect to a running headless debug server with a terminal client. Prefix with 'unix:' to use a unix domain socket.",
 		PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
 			if len(args) == 0 {
 				return errors.New("you must provide an address as the first argument")
@@ -514,7 +514,7 @@ func dapCmd(cmd *cobra.Command, args []string) {
 		}
 		var conn net.Conn
 		if dapClientAddr == "" {
-			listener, err := net.Listen("tcp", addr)
+			listener, err := netListen(addr)
 			if err != nil {
 				fmt.Printf("couldn't start listener: %s\n", err)
 				return 1
@@ -912,11 +912,10 @@ func splitArgs(cmd *cobra.Command, args []string) ([]string, []string) {
 func connect(addr string, clientConn net.Conn, conf *config.Config) int {
 	// Create and start a terminal - attach to running instance
 	var client *rpc2.RPCClient
-	if clientConn != nil {
-		client = rpc2.NewClientFromConn(clientConn)
-	} else {
-		client = rpc2.NewClient(addr)
+	if clientConn == nil {
+		clientConn = netDial(addr)
 	}
+	client = rpc2.NewClientFromConn(clientConn)
 	if client.IsMulticlient() {
 		state, _ := client.GetStateNonBlocking()
 		// The error return of GetState will usually be the ErrProcessExited,
@@ -1002,7 +1001,7 @@ func execute(attachPid int, processArgs []string, conf *config.Config, coreFile
 
 	// Make a TCP listener
 	if headless {
-		listener, err = net.Listen("tcp", addr)
+		listener, err = netListen(addr)
 	} else {
 		listener, clientConn = service.ListenerPipe()
 	}
@@ -1128,3 +1127,26 @@ func configUsageFunc(cmd *cobra.Command) {
 		return usage(cmd)
 	})
 }
+
+const unixAddrPrefix = "unix:"
+
+func netListen(addr string) (net.Listener, error) {
+	if strings.HasPrefix(addr, unixAddrPrefix) {
+		return net.Listen("unix", addr[len(unixAddrPrefix):])
+	}
+	return net.Listen("tcp", addr)
+}
+
+func netDial(addr string) net.Conn {
+	var conn net.Conn
+	var err error
+	if strings.HasPrefix(addr, unixAddrPrefix) {
+		conn, err = net.Dial("unix", addr[len(unixAddrPrefix):])
+	} else {
+		conn, err = net.Dial("tcp", addr)
+	}
+	if err != nil {
+		logflags.RPCLogger().Errorf("error dialing %s: %v", addr, err)
+	}
+	return conn
+}
diff --git a/pkg/config/config.go b/pkg/config/config.go
index ac85f532..029fb906 100644
--- a/pkg/config/config.go
+++ b/pkg/config/config.go
@@ -79,6 +79,12 @@ type Config struct {
 	// Source list tab color, as a terminal escape sequence.
 	SourceListTabColor string `yaml:"source-list-tab-color"`
 
+	// Color for function names in the stack trace.
+	StackTraceFunctionColor string `yaml:"stacktrace-function-color"`
+
+	// Color for the base name in paths in the stack trace.
+	StackTraceBasenameColor string `yaml:"stacktrace-basename-color"`
+
 	// number of lines to list above and below cursor when printfile() is
 	// called (i.e. when execution stops, listCommand is used, etc)
 	SourceListLineCount *int `yaml:"source-list-line-count,omitempty"`
diff --git a/pkg/terminal/command.go b/pkg/terminal/command.go
index 49522e4e..da6bc522 100644
--- a/pkg/terminal/command.go
+++ b/pkg/terminal/command.go
@@ -2639,7 +2639,7 @@ func digits(n int) int {
 }
 
 func printStack(t *Term, out io.Writer, stack []api.Stackframe, ind string, offsets bool) {
-	api.PrintStack(t.formatPath, out, stack, ind, offsets, func(api.Stackframe) bool { return true })
+	api.PrintStack(t.formatPath, out, stack, ind, offsets, t.stackTraceColors, func(api.Stackframe) bool { return true })
 }
 
 func printcontext(t *Term, state *api.DebuggerState) {
diff --git a/pkg/terminal/terminal.go b/pkg/terminal/terminal.go
index b9a4bfd2..4a28af46 100644
--- a/pkg/terminal/terminal.go
+++ b/pkg/terminal/terminal.go
@@ -60,6 +60,8 @@ type Term struct {
 	displays []displayEntry
 	oldPid   int
 
+	stackTraceColors api.StackTraceColors
+
 	historyFile *os.File
 
 	starlarkEnv *starbind.Env
@@ -160,6 +162,17 @@ func (t *Term) updateColorScheme() {
 	case nil:
 		t.stdout.colorEscapes[colorize.LineNoStyle] = fmt.Sprintf(terminalHighlightEscapeCode, ansiBlue)
 	}
+
+	wd2 := func(s, defaultStr string) string {
+		if s == "" {
+			return defaultStr
+		}
+		return s
+	}
+
+	t.stackTraceColors.FunctionColor = wd2(conf.StackTraceFunctionColor, "\033[1m")
+	t.stackTraceColors.BasenameColor = wd2(conf.StackTraceBasenameColor, "\033[1m")
+	t.stackTraceColors.NormalColor = terminalResetEscapeCode
 }
 
 func (t *Term) updateTab() {
diff --git a/service/api/prettyprint.go b/service/api/prettyprint.go
index 123dab34..2f6cc68e 100644
--- a/service/api/prettyprint.go
+++ b/service/api/prettyprint.go
@@ -564,7 +564,13 @@ func digits(n int) int {
 	return int(math.Floor(math.Log10(float64(n)))) + 1
 }
 
-func PrintStack(formatPath func(string) string, out io.Writer, stack []Stackframe, ind string, offsets bool, include func(Stackframe) bool) {
+type StackTraceColors struct {
+	FunctionColor string
+	BasenameColor string
+	NormalColor   string
+}
+
+func PrintStack(formatPath func(string) string, out io.Writer, stack []Stackframe, ind string, offsets bool, stc StackTraceColors, include func(Stackframe) bool) {
 	if len(stack) == 0 {
 		return
 	}
@@ -577,8 +583,22 @@ func PrintStack(formatPath func(string) string, out io.Writer, stack []Stackfram
 		extranl = extranl || (len(stack[i].Defers) > 0) || (len(stack[i].Arguments) > 0) || (len(stack[i].Locals) > 0)
 	}
 
+	fileLine := func(file string, line int) string {
+		file = formatPath(file)
+		if stc.BasenameColor == "" {
+			return fmt.Sprintf("%s:%d", file, line)
+		}
+		slash := strings.LastIndex(file, "/")
+		if slash < 0 {
+			slash = 0
+		} else {
+			slash++
+		}
+		return fmt.Sprintf("%s%s%s:%d%s", file[:slash], stc.BasenameColor, file[slash:], line, stc.NormalColor)
+	}
+
 	d := digits(len(stack) - 1)
-	fmtstr := "%s%" + strconv.Itoa(d) + "d  0x%016x in %s\n"
+	fmtstr := "%s%" + strconv.Itoa(d) + "d  0x%016x in " + stc.FunctionColor + "%s" + stc.NormalColor + "\n"
 	s := ind + strings.Repeat(" ", d+2+len(ind))
 
 	for i := range stack {
@@ -590,7 +610,7 @@ func PrintStack(formatPath func(string) string, out io.Writer, stack []Stackfram
 			continue
 		}
 		fmt.Fprintf(out, fmtstr, ind, i, stack[i].PC, stack[i].Function.Name())
-		fmt.Fprintf(out, "%sat %s:%d\n", s, formatPath(stack[i].File), stack[i].Line)
+		fmt.Fprintf(out, "%sat %s\n", s, fileLine(stack[i].File, stack[i].Line))
 
 		if offsets {
 			fmt.Fprintf(out, "%sframe: %+#x frame pointer %+#x\n", s, stack[i].FrameOffset, stack[i].FramePointerOffset)
@@ -603,9 +623,9 @@ func PrintStack(formatPath func(string) string, out io.Writer, stack []Stackfram
 				fmt.Fprintf(out, "%s(unreadable defer: %s)\n", deferHeader, d.Unreadable)
 				continue
 			}
-			fmt.Fprintf(out, "%s%#016x in %s\n", deferHeader, d.DeferredLoc.PC, d.DeferredLoc.Function.Name())
+			fmt.Fprintf(out, "%s%#016x in %s%s%s\n", deferHeader, d.DeferredLoc.PC, stc.FunctionColor, d.DeferredLoc.Function.Name(), stc.NormalColor)
 			fmt.Fprintf(out, "%sat %s:%d\n", s2, formatPath(d.DeferredLoc.File), d.DeferredLoc.Line)
-			fmt.Fprintf(out, "%sdeferred by %s at %s:%d\n", s2, d.DeferLoc.Function.Name(), formatPath(d.DeferLoc.File), d.DeferLoc.Line)
+			fmt.Fprintf(out, "%sdeferred by %s at %s\n", s2, d.DeferLoc.Function.Name(), fileLine(d.DeferLoc.File, d.DeferLoc.Line))
 		}
 
 		for j := range stack[i].Arguments {
diff --git a/service/dap/server.go b/service/dap/server.go
index 800639f2..5e0956c9 100644
--- a/service/dap/server.go
+++ b/service/dap/server.go
@@ -1892,17 +1892,33 @@ func (s *Session) onAttachRequest(request *dap.AttachRequest) {
 				fmt.Sprintf("debug session already in progress at %s - use remote mode to connect to a server with an active debug session", s.address()))
 			return
 		}
-		if args.ProcessID == 0 {
-			s.sendShowUserErrorResponse(request.Request, FailedToAttach, "Failed to attach",
-				"The 'processId' attribute is missing in debug configuration")
+		if args.AttachWaitFor != "" && args.ProcessID != 0 {
+			s.sendShowUserErrorResponse(
+				request.Request,
+				FailedToAttach,
+				"Failed to attach",
+				"'processId' and 'waitFor' are mutually exclusive, and can't be specified at the same time")
+			return
+		} else if args.AttachWaitFor != "" {
+			s.config.Debugger.AttachWaitFor = args.AttachWaitFor
+			// Keep the default value the same as waitfor-interval.
+			s.config.Debugger.AttachWaitForInterval = 1
+			s.config.log.Debugf("Waiting for a process with a name beginning with this prefix: %s", args.AttachWaitFor)
+		} else if args.ProcessID != 0 {
+			s.config.Debugger.AttachPid = args.ProcessID
+			s.config.log.Debugf("Attaching to pid %d", args.ProcessID)
+		} else {
+			s.sendShowUserErrorResponse(
+				request.Request,
+				FailedToAttach,
+				"Failed to attach",
+				"The 'processId' or 'waitFor' attribute is missing in debug configuration")
 			return
 		}
-		s.config.Debugger.AttachPid = args.ProcessID
 		if args.Backend == "" {
 			args.Backend = "default"
 		}
 		s.config.Debugger.Backend = args.Backend
-		s.config.log.Debugf("attaching to pid %d", args.ProcessID)
 		var err error
 		func() {
 			s.mu.Lock()
@@ -3458,7 +3474,7 @@ func (s *Session) stacktrace(goroutineID int64, g *proc.G) (string, error) {
 	fmt.Fprintln(&buf, "Stack:")
 	userLoc := g.UserCurrent()
 	userFuncPkg := fnPackageName(&userLoc)
-	api.PrintStack(s.toClientPath, &buf, apiFrames, "\t", false, func(s api.Stackframe) bool {
+	api.PrintStack(s.toClientPath, &buf, apiFrames, "\t", false, api.StackTraceColors{}, func(s api.Stackframe) bool {
 		// Include all stack frames if the stack trace is for a system goroutine,
 		// otherwise, skip runtime stack frames.
 		if userFuncPkg == "runtime" {
diff --git a/service/dap/types.go b/service/dap/types.go
index 1ab685f9..59f06065 100644
--- a/service/dap/types.go
+++ b/service/dap/types.go
@@ -237,6 +237,7 @@ func (m *SubstitutePath) UnmarshalJSON(data []byte) error {
 }
 
 // AttachConfig is the collection of attach request attributes recognized by DAP implementation.
+// 'processId' and 'waitFor' are mutually exclusive, and can't be specified at the same time.
 type AttachConfig struct {
 	// Acceptable values are:
 	//   "local": attaches to the local process with the given ProcessID.
@@ -245,9 +246,12 @@ type AttachConfig struct {
 	// Default is "local".
 	Mode string `json:"mode"`
 
-	// The numeric ID of the process to be debugged. Required and must not be 0.
+	// The numeric ID of the process to be debugged.
 	ProcessID int `json:"processId,omitempty"`
 
+	// Wait for a process with a name beginning with this prefix.
+	AttachWaitFor string `json:"waitFor,omitempty"`
+
 	LaunchAttachCommonConfig
 }
 


Test patch:
diff --git a/Documentation/usage/dlv_test.md b/Documentation/usage/dlv_test.md
index 37a9fe0f..d849d1da 100644
--- a/Documentation/usage/dlv_test.md
+++ b/Documentation/usage/dlv_test.md
@@ -38,7 +38,7 @@ dlv test [package] [flags]
       --disable-aslr                     Disables address space randomization
       --headless                         Run debug server only, in headless mode. Server will accept both JSON-RPC or DAP client connections.
       --init string                      Init file, executed by the terminal client.
-  -l, --listen string                    Debugging server listen address. (default "127.0.0.1:0")
+  -l, --listen string                    Debugging server listen address. Prefix with 'unix:' to use a unix domain socket. (default "127.0.0.1:0")
       --log                              Enable debugging server logging.
       --log-dest string                  Writes logs to the specified file or file descriptor (see 'dlv help log').
       --log-output string                Comma separated list of components that should produce debug output (see 'dlv help log')
diff --git a/cmd/dlv/dlv_test.go b/cmd/dlv/dlv_test.go
index 2b057c22..c4e903ca 100644
--- a/cmd/dlv/dlv_test.go
+++ b/cmd/dlv/dlv_test.go
@@ -1359,3 +1359,65 @@ func TestDefaultBinary(t *testing.T) {
 		t.Errorf("outputs match")
 	}
 }
+
+func TestUnixDomainSocket(t *testing.T) {
+	tmpdir := os.TempDir()
+	if tmpdir == "" {
+		return
+	}
+
+	listenPath := filepath.Join(tmpdir, "delve_test")
+
+	var err error
+
+	cmd := exec.Command("go", "run", "_scripts/make.go", "build")
+	cmd.Dir = projectRoot()
+	out, err := cmd.CombinedOutput()
+	if err != nil {
+		t.Fatalf("makefile error: %v\noutput %s\n", err, string(out))
+	}
+
+	dlvbin := filepath.Join(cmd.Dir, "dlv")
+	defer os.Remove(dlvbin)
+
+	fixtures := protest.FindFixturesDir()
+
+	buildtestdir := filepath.Join(fixtures, "buildtest")
+
+	cmd = exec.Command(dlvbin, "debug", "--headless=true", "--listen=unix:"+listenPath, "--api-version=2", "--backend="+testBackend, "--log", "--log-output=debugger,rpc")
+	cmd.Dir = buildtestdir
+	stderr, err := cmd.StderrPipe()
+	assertNoError(err, t, "stderr pipe")
+	defer stderr.Close()
+
+	assertNoError(cmd.Start(), t, "dlv debug")
+
+	scan := bufio.NewScanner(stderr)
+	// wait for the debugger to start
+	for scan.Scan() {
+		text := scan.Text()
+		t.Log(text)
+		if strings.Contains(text, "API server pid = ") {
+			break
+		}
+	}
+	go func() {
+		for scan.Scan() {
+			t.Log(scan.Text())
+			// keep pipe empty
+		}
+	}()
+
+	conn, err := net.Dial("unix", listenPath)
+	assertNoError(err, t, "dialing")
+
+	client := rpc2.NewClientFromConn(conn)
+	state := <-client.Continue()
+
+	if !state.Exited {
+		t.Fatal("Program did not exit")
+	}
+
+	client.Detach(true)
+	cmd.Wait()
+}
diff --git a/service/dap/server_test.go b/service/dap/server_test.go
index c2feba4d..f82fc767 100644
--- a/service/dap/server_test.go
+++ b/service/dap/server_test.go
@@ -5901,6 +5901,51 @@ func TestAttachRequest(t *testing.T) {
 	})
 }
 
+func TestAttachWaitForRequest(t *testing.T) {
+	if runtime.GOOS == "freebsd" {
+		// The value of /proc/[pid]/cmdline might be wrong on FreeBSD: zero or the same as the parent process.
+		t.Skip("test skipped on freebsd")
+	}
+	runTest(t, "loopprog", func(client *daptest.Client, fixture protest.Fixture) {
+		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\"")
+		}
+
+		t.Logf("The process id of program %q is %d", cmd.Path, cmd.Process.Pid)
+
+		client.InitializeRequest()
+		client.ExpectInitializeResponseAndCapabilities(t)
+		client.AttachRequest(map[string]interface{}{
+			"mode":        "local",
+			"waitFor":     fixture.Path,
+			"stopOnEntry": true,
+		})
+		client.ExpectCapabilitiesEventSupportTerminateDebuggee(t)
+		client.ExpectInitializedEvent(t)
+		client.ExpectAttachResponse(t)
+		client.DisconnectRequestWithKillOption(true)
+		client.ExpectOutputEvent(t)
+		client.ExpectDisconnectResponse(t)
+		client.ExpectTerminatedEvent(t)
+
+		runtime.KeepAlive(stdout)
+	})
+}
+
 // Since we are in async mode while running, we might receive thee messages after pause request
 // in either order.
 func expectPauseResponseAndStoppedEvent(t *testing.T, client *daptest.Client) {
@@ -6621,24 +6666,28 @@ func TestBadAttachRequest(t *testing.T) {
 
 		client.AttachRequest(map[string]interface{}{"mode": ""}) // empty mode defaults to "local" (not an error)
 		checkFailedToAttachWithMessage(client.ExpectVisibleErrorResponse(t),
-			"Failed to attach: The 'processId' attribute is missing in debug configuration")
+			"Failed to attach: The 'processId' or 'waitFor' attribute is missing in debug configuration")
 
 		client.AttachRequest(map[string]interface{}{}) // no mode defaults to "local" (not an error)
 		checkFailedToAttachWithMessage(client.ExpectVisibleErrorResponse(t),
-			"Failed to attach: The 'processId' attribute is missing in debug configuration")
+			"Failed to attach: The 'processId' or 'waitFor' attribute is missing in debug configuration")
 
 		// Bad "processId"
 		client.AttachRequest(map[string]interface{}{"mode": "local"})
 		checkFailedToAttachWithMessage(client.ExpectVisibleErrorResponse(t),
-			"Failed to attach: The 'processId' attribute is missing in debug configuration")
+			"Failed to attach: The 'processId' or 'waitFor' attribute is missing in debug configuration")
 
 		client.AttachRequest(map[string]interface{}{"mode": "local", "processId": nil})
 		checkFailedToAttachWithMessage(client.ExpectVisibleErrorResponse(t),
-			"Failed to attach: The 'processId' attribute is missing in debug configuration")
+			"Failed to attach: The 'processId' or 'waitFor' attribute is missing in debug configuration")
 
 		client.AttachRequest(map[string]interface{}{"mode": "local", "processId": 0})
 		checkFailedToAttachWithMessage(client.ExpectVisibleErrorResponse(t),
-			"Failed to attach: The 'processId' attribute is missing in debug configuration")
+			"Failed to attach: The 'processId' or 'waitFor' attribute is missing in debug configuration")
+
+		client.AttachRequest(map[string]interface{}{"mode": "local", "processId": 1, "waitFor": "loopprog"})
+		checkFailedToAttachWithMessage(client.ExpectVisibleErrorResponse(t),
+			"Failed to attach: 'processId' and 'waitFor' are mutually exclusive, and can't be specified at the same time")
 
 		client.AttachRequest(map[string]interface{}{"mode": "local", "processId": "1"})
 		checkFailedToAttachWithMessage(client.ExpectVisibleErrorResponse(t),