Allow debugging with SOONG_DELVE=<listen addr>

Allow running Soong in a headless delve debugger by passing
SOONG_DELVE=<listen addr> in the environment.

Bug: 80165685
Test: SOONG_DELVE=:1234 m nothing
Change-Id: Icfc893c8a8354a9bbc99112d9c83259cb41906d1
This commit is contained in:
Colin Cross 2019-06-19 13:33:24 -07:00
parent 7b97ecd1f5
commit aa812d122c
5 changed files with 81 additions and 1 deletions

View File

@ -337,6 +337,19 @@ build/soong/scripts/setup_go_workspace_for_soong.sh
This will bind mount the Soong source directories into the directory in the layout expected by
the IDE.
### Running Soong in a debugger
To run the soong_build process in a debugger, install `dlv` and then start the build with
`SOONG_DELVE=<listen addr>` in the environment.
For examle:
```bash
SOONG_DELVE=:1234 m nothing
```
and then in another terminal:
```
dlv connect :1234
```
## Contact
Email android-building@googlegroups.com (external) for any questions, or see

View File

@ -16,6 +16,7 @@ package android
import (
"os"
"os/exec"
"strings"
"android/soong/env"
@ -29,8 +30,16 @@ import (
// a manifest regeneration.
var originalEnv map[string]string
var SoongDelveListen string
var SoongDelvePath 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")
originalEnv = make(map[string]string)
for _, env := range os.Environ() {
idx := strings.IndexRune(env, '=')
@ -38,6 +47,8 @@ func init() {
originalEnv[env[:idx]] = env[idx+1:]
}
}
// 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()
}

View File

@ -18,7 +18,12 @@ import (
"flag"
"fmt"
"os"
"os/exec"
"path/filepath"
"strconv"
"strings"
"syscall"
"time"
"github.com/google/blueprint/bootstrap"
@ -50,6 +55,42 @@ func newNameResolver(config android.Config) *android.NameResolver {
}
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)
}
}
flag.Parse()
// The top-level Blueprints file is passed as the first argument.
@ -72,7 +113,17 @@ func main() {
ctx.SetAllowMissingDependencies(configuration.AllowMissingDependencies())
bootstrap.Main(ctx.Context, configuration, configuration.ConfigFileName, configuration.ProductVariablesFileName)
extraNinjaDeps := []string{configuration.ConfigFileName, configuration.ProductVariablesFileName}
// Read the SOONG_DELVE again through configuration so that there is a dependency on the environment variable
// and soong_build will rerun when it is set for the first time.
if listen := configuration.Getenv("SOONG_DELVE"); listen != "" {
// Add a non-existent file to the dependencies so that soong_build will rerun when the debugger is
// enabled even if it completed successfully.
extraNinjaDeps = append(extraNinjaDeps, filepath.Join(configuration.BuildDir(), "always_rerun_for_delve"))
}
bootstrap.Main(ctx.Context, configuration, extraNinjaDeps...)
if docFile != "" {
if err := writeDocs(ctx, docFile); err != nil {

View File

@ -81,6 +81,7 @@ var Configuration = map[string]PathConfig{
"bzip2": Allowed,
"dd": Allowed,
"diff": Allowed,
"dlv": Allowed,
"egrep": Allowed,
"expr": Allowed,
"find": Allowed,

View File

@ -162,6 +162,10 @@ func (c *Cmd) wrapSandbox() {
c.ctx.Printf("AllowBuildBrokenUsesNetwork: %v", c.Sandbox.AllowBuildBrokenUsesNetwork)
c.ctx.Printf("BuildBrokenUsesNetwork: %v", c.config.BuildBrokenUsesNetwork())
sandboxArgs = append(sandboxArgs, "-N")
} else if dlv, _ := c.config.Environment().Get("SOONG_DELVE"); dlv != "" {
// The debugger is enabled and soong_build will pause until a remote delve process connects, allow
// network connections.
sandboxArgs = append(sandboxArgs, "-N")
}
// Stop nsjail from parsing arguments