Repository: go-delve/delve (go)

Issue:
Feature req: color for the input prompt
1. What version of Delve are you using (`dlv version`)?

Delve Debugger
Version: 1.21.1
Build: $Id: a358c02f24aa7047ecc562b0587dc2d08330b2cf $

2. What version of Go are you using? (`go version`)?

go version go1.21.3 linux/amd64

4. What operating system and processor architecture are you using?

linux amd64

6. What did you do?

Ran dlv with lots of output

7. What did you expect to see?

More color

9. What did you see instead?

Too little color!


Specifically, when I have a lot of output, I sometimes need to scroll-back a lot.  It's hard to visually find the "edges" between commands.  If the `(dlv)` prompt and typed input could have a color config, I would set that to something bright, so I could scrollback more easily.

Pull request description:
terminal: add prompt color option
Add option to configure prompt color.

Fixes #3603


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..4ceacfb4 100644
--- a/pkg/config/config.go
+++ b/pkg/config/config.go
@@ -76,9 +76,18 @@ type Config struct {
 	// Source list comment color, as a terminal escape sequence.
 	SourceListCommentColor string `yaml:"source-list-comment-color"`
 
+	// Color for the prompt line.
+	PromptColor string `yaml:"prompt-color"`
+
 	// 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..aa1d75af 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() {
@@ -423,6 +436,10 @@ func (t *Term) formatPath(path string) string {
 }
 
 func (t *Term) promptForInput() (string, error) {
+	if t.stdout.colorEscapes != nil && t.conf.PromptColor != "" {
+		fmt.Fprint(os.Stdout, t.conf.PromptColor)
+		defer fmt.Fprint(os.Stdout, terminalResetEscapeCode)
+	}
 	l, err := t.line.Prompt(t.prompt)
 	if err != nil {
 		return "", err
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),