cmd/go: add a debug-trace flag to generate traces

If cmd/go is provided with a -debug-trace=<file> option, cmd/go will write an
execution trace to that file.

Updates #38714

Change-Id: I3e6521343902c08266a0292f4280298a3bf8b725
Reviewed-on: https://go-review.googlesource.com/c/go/+/237683
Run-TryBot: Michael Matloob <matloob@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Jay Conrod <jayconrod@google.com>
Reviewed-by: Bryan C. Mills <bcmills@google.com>
diff --git a/src/cmd/go/internal/cfg/cfg.go b/src/cmd/go/internal/cfg/cfg.go
index 7f8f8e9..f9bbcd9 100644
--- a/src/cmd/go/internal/cfg/cfg.go
+++ b/src/cmd/go/internal/cfg/cfg.go
@@ -51,6 +51,7 @@
 	CmdName string // "build", "install", "list", "mod tidy", etc.
 
 	DebugActiongraph string // -debug-actiongraph flag (undocumented, unstable)
+	DebugTrace       string // -debug-trace flag
 )
 
 func defaultContext() build.Context {
diff --git a/src/cmd/go/internal/work/build.go b/src/cmd/go/internal/work/build.go
index 7146c9c..fbd49b4 100644
--- a/src/cmd/go/internal/work/build.go
+++ b/src/cmd/go/internal/work/build.go
@@ -270,6 +270,7 @@
 
 	// Undocumented, unstable debugging flags.
 	cmd.Flag.StringVar(&cfg.DebugActiongraph, "debug-actiongraph", "", "")
+	cmd.Flag.StringVar(&cfg.DebugTrace, "debug-trace", "", "")
 }
 
 // AddModCommonFlags adds the module-related flags common to build commands
diff --git a/src/cmd/go/main.go b/src/cmd/go/main.go
index fdf49b7..3512866 100644
--- a/src/cmd/go/main.go
+++ b/src/cmd/go/main.go
@@ -7,6 +7,7 @@
 package main
 
 import (
+	"context"
 	"flag"
 	"fmt"
 	"log"
@@ -34,6 +35,7 @@
 	"cmd/go/internal/run"
 	"cmd/go/internal/test"
 	"cmd/go/internal/tool"
+	"cmd/go/internal/trace"
 	"cmd/go/internal/version"
 	"cmd/go/internal/vet"
 	"cmd/go/internal/work"
@@ -187,7 +189,11 @@
 				cmd.Flag.Parse(args[1:])
 				args = cmd.Flag.Args()
 			}
+			ctx := maybeStartTrace(context.Background())
+			ctx, span := trace.StartSpan(ctx, fmt.Sprint("Running ", cmd.Name(), " command"))
+			_ = ctx
 			cmd.Run(cmd, args)
+			span.Done()
 			base.Exit()
 			return
 		}
@@ -209,3 +215,21 @@
 	help.PrintUsage(os.Stderr, base.Go)
 	os.Exit(2)
 }
+
+func maybeStartTrace(pctx context.Context) context.Context {
+	if cfg.DebugTrace == "" {
+		return pctx
+	}
+
+	ctx, close, err := trace.Start(pctx, cfg.DebugTrace)
+	if err != nil {
+		base.Fatalf("failed to start trace: %v", err)
+	}
+	base.AtExit(func() {
+		if err := close(); err != nil {
+			base.Fatalf("failed to stop trace: %v", err)
+		}
+	})
+
+	return ctx
+}