Call Delve using exec() instead of "dlv attach".

"dlv attach":

- Spams the terminal
- Requires sysctl -w kernel.yama.ptrace_scope=0
- Apparently, does not allow the debugger to inspect variables

Test: Manual.
Change-Id: I625369effaf5abda2b5d884c8ce5bde7247774eb
diff --git a/android/env.go b/android/env.go
index 46bd3d6..c7c96d5 100644
--- a/android/env.go
+++ b/android/env.go
@@ -15,9 +15,11 @@
 package android
 
 import (
+	"fmt"
 	"os"
 	"os/exec"
 	"strings"
+	"syscall"
 
 	"android/soong/env"
 )
@@ -30,28 +32,59 @@
 // a manifest regeneration.
 
 var originalEnv map[string]string
-var SoongDelveListen string
-var SoongDelvePath string
+var soongDelveListen string
+var soongDelvePath string
+var soongDelveEnv []string
 
 func init() {
 	// Delve support needs to read this environment variable very early, before NewConfig has created a way to
 	// access originalEnv with dependencies.  Store the value where soong_build can find it, it will manually
 	// ensure the dependencies are created.
-	SoongDelveListen = os.Getenv("SOONG_DELVE")
-	SoongDelvePath, _ = exec.LookPath("dlv")
+	soongDelveListen = os.Getenv("SOONG_DELVE")
+	soongDelvePath, _ = exec.LookPath("dlv")
 
 	originalEnv = make(map[string]string)
+	soongDelveEnv = []string{}
 	for _, env := range os.Environ() {
 		idx := strings.IndexRune(env, '=')
 		if idx != -1 {
 			originalEnv[env[:idx]] = env[idx+1:]
+			if env[:idx] != "SOONG_DELVE" {
+				soongDelveEnv = append(soongDelveEnv, env)
+			}
 		}
 	}
+
 	// Clear the environment to prevent use of os.Getenv(), which would not provide dependencies on environment
 	// variable values.  The environment is available through ctx.Config().Getenv, ctx.Config().IsEnvTrue, etc.
 	os.Clearenv()
 }
 
+func ReexecWithDelveMaybe() {
+	if soongDelveListen == "" {
+		return
+	}
+
+	if soongDelvePath == "" {
+		fmt.Fprintln(os.Stderr, "SOONG_DELVE is set but failed to find dlv")
+		os.Exit(1)
+	}
+	dlvArgv := []string{
+		soongDelvePath,
+		"--listen=:" + soongDelveListen,
+		"--headless=true",
+		"--api-version=2",
+		"exec",
+		os.Args[0],
+		"--",
+	}
+	dlvArgv = append(dlvArgv, os.Args[1:]...)
+	os.Chdir(absSrcDir)
+	syscall.Exec(soongDelvePath, dlvArgv, soongDelveEnv)
+	fmt.Fprintln(os.Stderr, "exec() failed while trying to reexec with Delve")
+	os.Exit(1)
+}
+
 // getenv checks either os.Getenv or originalEnv so that it works before or after the init()
 // function above.  It doesn't add any dependencies on the environment variable, so it should
 // only be used for values that won't change.  For values that might change use ctx.Config().Getenv.
diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go
index bd1a9ed..905f206 100644
--- a/cmd/soong_build/main.go
+++ b/cmd/soong_build/main.go
@@ -18,12 +18,7 @@
 	"flag"
 	"fmt"
 	"os"
-	"os/exec"
 	"path/filepath"
-	"strconv"
-	"strings"
-	"syscall"
-	"time"
 
 	"github.com/google/blueprint/bootstrap"
 
@@ -55,42 +50,7 @@
 }
 
 func main() {
-	if android.SoongDelveListen != "" {
-		if android.SoongDelvePath == "" {
-			fmt.Fprintln(os.Stderr, "SOONG_DELVE is set but failed to find dlv")
-			os.Exit(1)
-		}
-		pid := strconv.Itoa(os.Getpid())
-		cmd := []string{android.SoongDelvePath,
-			"attach", pid,
-			"--headless",
-			"-l", android.SoongDelveListen,
-			"--api-version=2",
-			"--accept-multiclient",
-			"--log",
-		}
-
-		fmt.Println("Starting", strings.Join(cmd, " "))
-		dlv := exec.Command(cmd[0], cmd[1:]...)
-		dlv.Stdout = os.Stdout
-		dlv.Stderr = os.Stderr
-		dlv.Stdin = nil
-
-		// Put dlv into its own process group so we can kill it and the child process it starts.
-		dlv.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
-
-		err := dlv.Start()
-		if err != nil {
-			// Print the error starting dlv and continue.
-			fmt.Println(err)
-		} else {
-			// Kill the process group for dlv when soong_build exits.
-			defer syscall.Kill(-dlv.Process.Pid, syscall.SIGKILL)
-			// Wait to give dlv a chance to connect and pause the process.
-			time.Sleep(time.Second)
-		}
-	}
-
+	android.ReexecWithDelveMaybe()
 	flag.Parse()
 
 	// The top-level Blueprints file is passed as the first argument.