Commit 54260c57 authored by Russ Cox's avatar Russ Cox

cmd/go: add more information to -debug-actiongraph

Add information about action and build IDs,
commands run, time spent in commands,
and overall time to -debug-actiongraph output.

Also avoid a terrible outcome in:

	go build -debug-actiongraph x.go

Change-Id: I7a3a638f4848d75f2bdc997517f4ab23656da4f1
Reviewed-on: https://go-review.googlesource.com/c/go/+/177138
Run-TryBot: Russ Cox <rsc@golang.org>
Reviewed-by: default avatarBryan C. Mills <bcmills@google.com>
parent 86463c15
...@@ -91,9 +91,10 @@ type Action struct { ...@@ -91,9 +91,10 @@ type Action struct {
output []byte // output redirect buffer (nil means use b.Print) output []byte // output redirect buffer (nil means use b.Print)
// Execution state. // Execution state.
pending int // number of deps yet to complete pending int // number of deps yet to complete
priority int // relative execution priority priority int // relative execution priority
Failed bool // whether the action failed Failed bool // whether the action failed
json *actionJSON // action graph information
} }
// BuildActionID returns the action ID section of a's build ID. // BuildActionID returns the action ID section of a's build ID.
...@@ -125,6 +126,9 @@ func (q *actionQueue) Pop() interface{} { ...@@ -125,6 +126,9 @@ func (q *actionQueue) Pop() interface{} {
} }
func (q *actionQueue) push(a *Action) { func (q *actionQueue) push(a *Action) {
if a.json != nil {
a.json.TimeReady = time.Now()
}
heap.Push(q, a) heap.Push(q, a)
} }
...@@ -136,16 +140,28 @@ type actionJSON struct { ...@@ -136,16 +140,28 @@ type actionJSON struct {
ID int ID int
Mode string Mode string
Package string Package string
Deps []int `json:",omitempty"` Deps []int `json:",omitempty"`
IgnoreFail bool `json:",omitempty"` IgnoreFail bool `json:",omitempty"`
Args []string `json:",omitempty"` Args []string `json:",omitempty"`
Link bool `json:",omitempty"` Link bool `json:",omitempty"`
Objdir string `json:",omitempty"` Objdir string `json:",omitempty"`
Target string `json:",omitempty"` Target string `json:",omitempty"`
Priority int `json:",omitempty"` Priority int `json:",omitempty"`
Failed bool `json:",omitempty"` Failed bool `json:",omitempty"`
Built string `json:",omitempty"` Built string `json:",omitempty"`
VetxOnly bool `json:",omitempty"` VetxOnly bool `json:",omitempty"`
NeedVet bool `json:",omitempty"`
NeedBuild bool `json:",omitempty"`
ActionID string `json:",omitempty"`
BuildID string `json:",omitempty"`
TimeReady time.Time `json:",omitempty"`
TimeStart time.Time `json:",omitempty"`
TimeDone time.Time `json:",omitempty"`
Cmd []string // `json:",omitempty"`
CmdReal time.Duration `json:",omitempty"`
CmdUser time.Duration `json:",omitempty"`
CmdSys time.Duration `json:",omitempty"`
} }
// cacheKey is the key for the action cache. // cacheKey is the key for the action cache.
...@@ -175,26 +191,30 @@ func actionGraphJSON(a *Action) string { ...@@ -175,26 +191,30 @@ func actionGraphJSON(a *Action) string {
var list []*actionJSON var list []*actionJSON
for id, a := range workq { for id, a := range workq {
aj := &actionJSON{ if a.json == nil {
Mode: a.Mode, a.json = &actionJSON{
ID: id, Mode: a.Mode,
IgnoreFail: a.IgnoreFail, ID: id,
Args: a.Args, IgnoreFail: a.IgnoreFail,
Objdir: a.Objdir, Args: a.Args,
Target: a.Target, Objdir: a.Objdir,
Failed: a.Failed, Target: a.Target,
Priority: a.priority, Failed: a.Failed,
Built: a.built, Priority: a.priority,
VetxOnly: a.VetxOnly, Built: a.built,
} VetxOnly: a.VetxOnly,
if a.Package != nil { NeedBuild: a.needBuild,
// TODO(rsc): Make this a unique key for a.Package somehow. NeedVet: a.needVet,
aj.Package = a.Package.ImportPath }
} if a.Package != nil {
for _, a1 := range a.Deps { // TODO(rsc): Make this a unique key for a.Package somehow.
aj.Deps = append(aj.Deps, inWorkq[a1]) a.json.Package = a.Package.ImportPath
}
for _, a1 := range a.Deps {
a.json.Deps = append(a.json.Deps, inWorkq[a1])
}
} }
list = append(list, aj) list = append(list, a.json)
} }
js, err := json.MarshalIndent(list, "", "\t") js, err := json.MarshalIndent(list, "", "\t")
......
...@@ -423,6 +423,9 @@ func (b *Builder) useCache(a *Action, p *load.Package, actionHash cache.ActionID ...@@ -423,6 +423,9 @@ func (b *Builder) useCache(a *Action, p *load.Package, actionHash cache.ActionID
// engineered 96-bit partial SHA256 collision. // engineered 96-bit partial SHA256 collision.
a.actionID = actionHash a.actionID = actionHash
actionID := hashToString(actionHash) actionID := hashToString(actionHash)
if a.json != nil {
a.json.ActionID = actionID
}
contentID := actionID // temporary placeholder, likely unique contentID := actionID // temporary placeholder, likely unique
a.buildID = actionID + buildIDSeparator + contentID a.buildID = actionID + buildIDSeparator + contentID
...@@ -440,6 +443,9 @@ func (b *Builder) useCache(a *Action, p *load.Package, actionHash cache.ActionID ...@@ -440,6 +443,9 @@ func (b *Builder) useCache(a *Action, p *load.Package, actionHash cache.ActionID
buildID, _ = buildid.ReadFile(target) buildID, _ = buildid.ReadFile(target)
if strings.HasPrefix(buildID, actionID+buildIDSeparator) { if strings.HasPrefix(buildID, actionID+buildIDSeparator) {
a.buildID = buildID a.buildID = buildID
if a.json != nil {
a.json.BuildID = a.buildID
}
a.built = target a.built = target
// Poison a.Target to catch uses later in the build. // Poison a.Target to catch uses later in the build.
a.Target = "DO NOT USE - " + a.Mode a.Target = "DO NOT USE - " + a.Mode
...@@ -482,6 +488,9 @@ func (b *Builder) useCache(a *Action, p *load.Package, actionHash cache.ActionID ...@@ -482,6 +488,9 @@ func (b *Builder) useCache(a *Action, p *load.Package, actionHash cache.ActionID
// Poison a.Target to catch uses later in the build. // Poison a.Target to catch uses later in the build.
a.Target = "DO NOT USE - main build pseudo-cache Target" a.Target = "DO NOT USE - main build pseudo-cache Target"
a.built = "DO NOT USE - main build pseudo-cache built" a.built = "DO NOT USE - main build pseudo-cache built"
if a.json != nil {
a.json.BuildID = a.buildID
}
return true return true
} }
// Otherwise restore old build ID for main build. // Otherwise restore old build ID for main build.
...@@ -549,6 +558,9 @@ func (b *Builder) useCache(a *Action, p *load.Package, actionHash cache.ActionID ...@@ -549,6 +558,9 @@ func (b *Builder) useCache(a *Action, p *load.Package, actionHash cache.ActionID
a.built = file a.built = file
a.Target = "DO NOT USE - using cache" a.Target = "DO NOT USE - using cache"
a.buildID = buildID a.buildID = buildID
if a.json != nil {
a.json.BuildID = a.buildID
}
if p := a.Package; p != nil { if p := a.Package; p != nil {
// Clearer than explaining that something else is stale. // Clearer than explaining that something else is stale.
p.StaleReason = "not installed but available in build cache" p.StaleReason = "not installed but available in build cache"
...@@ -644,6 +656,9 @@ func (b *Builder) updateBuildID(a *Action, target string, rewrite bool) error { ...@@ -644,6 +656,9 @@ func (b *Builder) updateBuildID(a *Action, target string, rewrite bool) error {
// Replace with new content-based ID. // Replace with new content-based ID.
a.buildID = newID a.buildID = newID
if a.json != nil {
a.json.BuildID = a.buildID
}
if len(matches) == 0 { if len(matches) == 0 {
// Assume the user specified -buildid= to override what we were going to choose. // Assume the user specified -buildid= to override what we were going to choose.
return nil return nil
......
...@@ -75,13 +75,22 @@ func (b *Builder) Do(root *Action) { ...@@ -75,13 +75,22 @@ func (b *Builder) Do(root *Action) {
a.priority = i a.priority = i
} }
if cfg.DebugActiongraph != "" { // Write action graph, without timing information, in case we fail and exit early.
js := actionGraphJSON(root) writeActionGraph := func() {
if err := ioutil.WriteFile(cfg.DebugActiongraph, []byte(js), 0666); err != nil { if file := cfg.DebugActiongraph; file != "" {
fmt.Fprintf(os.Stderr, "go: writing action graph: %v\n", err) if strings.HasSuffix(file, ".go") {
base.SetExitStatus(1) // Do not overwrite Go source code in:
// go build -debug-actiongraph x.go
base.Fatalf("go: refusing to write action graph to %v\n", file)
}
js := actionGraphJSON(root)
if err := ioutil.WriteFile(file, []byte(js), 0666); err != nil {
fmt.Fprintf(os.Stderr, "go: writing action graph: %v\n", err)
base.SetExitStatus(1)
}
} }
} }
writeActionGraph()
b.readySema = make(chan bool, len(all)) b.readySema = make(chan bool, len(all))
...@@ -100,11 +109,16 @@ func (b *Builder) Do(root *Action) { ...@@ -100,11 +109,16 @@ func (b *Builder) Do(root *Action) {
// Handle runs a single action and takes care of triggering // Handle runs a single action and takes care of triggering
// any actions that are runnable as a result. // any actions that are runnable as a result.
handle := func(a *Action) { handle := func(a *Action) {
if a.json != nil {
a.json.TimeStart = time.Now()
}
var err error var err error
if a.Func != nil && (!a.Failed || a.IgnoreFail) { if a.Func != nil && (!a.Failed || a.IgnoreFail) {
err = a.Func(b, a) err = a.Func(b, a)
} }
if a.json != nil {
a.json.TimeDone = time.Now()
}
// The actions run in parallel but all the updates to the // The actions run in parallel but all the updates to the
// shared work state are serialized through b.exec. // shared work state are serialized through b.exec.
...@@ -170,6 +184,9 @@ func (b *Builder) Do(root *Action) { ...@@ -170,6 +184,9 @@ func (b *Builder) Do(root *Action) {
} }
wg.Wait() wg.Wait()
// Write action graph again, this time with timing information.
writeActionGraph()
} }
// buildActionID computes the action ID for a build action. // buildActionID computes the action ID for a build action.
...@@ -1322,7 +1339,7 @@ func (b *Builder) getPkgConfigFlags(p *load.Package) (cflags, ldflags []string, ...@@ -1322,7 +1339,7 @@ func (b *Builder) getPkgConfigFlags(p *load.Package) (cflags, ldflags []string,
} }
} }
var out []byte var out []byte
out, err = b.runOut(p.Dir, nil, b.PkgconfigCmd(), "--cflags", pcflags, "--", pkgs) out, err = b.runOut(nil, p.Dir, nil, b.PkgconfigCmd(), "--cflags", pcflags, "--", pkgs)
if err != nil { if err != nil {
b.showOutput(nil, p.Dir, b.PkgconfigCmd()+" --cflags "+strings.Join(pcflags, " ")+" -- "+strings.Join(pkgs, " "), string(out)) b.showOutput(nil, p.Dir, b.PkgconfigCmd()+" --cflags "+strings.Join(pcflags, " ")+" -- "+strings.Join(pkgs, " "), string(out))
b.Print(err.Error() + "\n") b.Print(err.Error() + "\n")
...@@ -1337,7 +1354,7 @@ func (b *Builder) getPkgConfigFlags(p *load.Package) (cflags, ldflags []string, ...@@ -1337,7 +1354,7 @@ func (b *Builder) getPkgConfigFlags(p *load.Package) (cflags, ldflags []string,
return nil, nil, err return nil, nil, err
} }
} }
out, err = b.runOut(p.Dir, nil, b.PkgconfigCmd(), "--libs", pcflags, "--", pkgs) out, err = b.runOut(nil, p.Dir, nil, b.PkgconfigCmd(), "--libs", pcflags, "--", pkgs)
if err != nil { if err != nil {
b.showOutput(nil, p.Dir, b.PkgconfigCmd()+" --libs "+strings.Join(pcflags, " ")+" -- "+strings.Join(pkgs, " "), string(out)) b.showOutput(nil, p.Dir, b.PkgconfigCmd()+" --libs "+strings.Join(pcflags, " ")+" -- "+strings.Join(pkgs, " "), string(out))
b.Print(err.Error() + "\n") b.Print(err.Error() + "\n")
...@@ -1437,6 +1454,9 @@ func BuildInstallFunc(b *Builder, a *Action) (err error) { ...@@ -1437,6 +1454,9 @@ func BuildInstallFunc(b *Builder, a *Action) (err error) {
a1 := a.Deps[0] a1 := a.Deps[0]
a.buildID = a1.buildID a.buildID = a1.buildID
if a.json != nil {
a.json.BuildID = a.buildID
}
// If we are using the eventual install target as an up-to-date // If we are using the eventual install target as an up-to-date
// cached copy of the thing we built, then there's no need to // cached copy of the thing we built, then there's no need to
...@@ -1853,7 +1873,7 @@ var cgoTypeSigRe = lazyregexp.New(`\b_C2?(type|func|var|macro)_\B`) ...@@ -1853,7 +1873,7 @@ var cgoTypeSigRe = lazyregexp.New(`\b_C2?(type|func|var|macro)_\B`)
// If the command fails, run prints information about the failure // If the command fails, run prints information about the failure
// and returns a non-nil error. // and returns a non-nil error.
func (b *Builder) run(a *Action, dir string, desc string, env []string, cmdargs ...interface{}) error { func (b *Builder) run(a *Action, dir string, desc string, env []string, cmdargs ...interface{}) error {
out, err := b.runOut(dir, env, cmdargs...) out, err := b.runOut(a, dir, env, cmdargs...)
if len(out) > 0 { if len(out) > 0 {
if desc == "" { if desc == "" {
desc = b.fmtcmd(dir, "%s", strings.Join(str.StringList(cmdargs...), " ")) desc = b.fmtcmd(dir, "%s", strings.Join(str.StringList(cmdargs...), " "))
...@@ -1885,7 +1905,8 @@ func (b *Builder) processOutput(out []byte) string { ...@@ -1885,7 +1905,8 @@ func (b *Builder) processOutput(out []byte) string {
// runOut runs the command given by cmdline in the directory dir. // runOut runs the command given by cmdline in the directory dir.
// It returns the command output and any errors that occurred. // It returns the command output and any errors that occurred.
func (b *Builder) runOut(dir string, env []string, cmdargs ...interface{}) ([]byte, error) { // It accumulates execution time in a.
func (b *Builder) runOut(a *Action, dir string, env []string, cmdargs ...interface{}) ([]byte, error) {
cmdline := str.StringList(cmdargs...) cmdline := str.StringList(cmdargs...)
for _, arg := range cmdline { for _, arg := range cmdline {
...@@ -1926,7 +1947,17 @@ func (b *Builder) runOut(dir string, env []string, cmdargs ...interface{}) ([]by ...@@ -1926,7 +1947,17 @@ func (b *Builder) runOut(dir string, env []string, cmdargs ...interface{}) ([]by
cmd.Dir = dir cmd.Dir = dir
cmd.Env = base.EnvForDir(cmd.Dir, os.Environ()) cmd.Env = base.EnvForDir(cmd.Dir, os.Environ())
cmd.Env = append(cmd.Env, env...) cmd.Env = append(cmd.Env, env...)
start := time.Now()
err := cmd.Run() err := cmd.Run()
if a != nil && a.json != nil {
aj := a.json
aj.Cmd = append(aj.Cmd, joinUnambiguously(cmdline))
aj.CmdReal += time.Since(start)
if ps := cmd.ProcessState; ps != nil {
aj.CmdUser += ps.UserTime()
aj.CmdSys += ps.SystemTime()
}
}
// err can be something like 'exit status 1'. // err can be something like 'exit status 1'.
// Add information about what program was running. // Add information about what program was running.
...@@ -2124,7 +2155,7 @@ func (b *Builder) ccompile(a *Action, p *load.Package, outfile string, flags []s ...@@ -2124,7 +2155,7 @@ func (b *Builder) ccompile(a *Action, p *load.Package, outfile string, flags []s
if !filepath.IsAbs(outfile) { if !filepath.IsAbs(outfile) {
outfile = filepath.Join(p.Dir, outfile) outfile = filepath.Join(p.Dir, outfile)
} }
output, err := b.runOut(filepath.Dir(file), b.cCompilerEnv(), compiler, flags, "-o", outfile, "-c", filepath.Base(file)) output, err := b.runOut(a, filepath.Dir(file), b.cCompilerEnv(), compiler, flags, "-o", outfile, "-c", filepath.Base(file))
if len(output) > 0 { if len(output) > 0 {
// On FreeBSD 11, when we pass -g to clang 3.8 it // On FreeBSD 11, when we pass -g to clang 3.8 it
// invokes its internal assembler with -dwarf-version=2. // invokes its internal assembler with -dwarf-version=2.
...@@ -2157,7 +2188,7 @@ func (b *Builder) ccompile(a *Action, p *load.Package, outfile string, flags []s ...@@ -2157,7 +2188,7 @@ func (b *Builder) ccompile(a *Action, p *load.Package, outfile string, flags []s
} }
// gccld runs the gcc linker to create an executable from a set of object files. // gccld runs the gcc linker to create an executable from a set of object files.
func (b *Builder) gccld(p *load.Package, objdir, outfile string, flags []string, objs []string) error { func (b *Builder) gccld(a *Action, p *load.Package, objdir, outfile string, flags []string, objs []string) error {
var cmd []string var cmd []string
if len(p.CXXFiles) > 0 || len(p.SwigCXXFiles) > 0 { if len(p.CXXFiles) > 0 || len(p.SwigCXXFiles) > 0 {
cmd = b.GxxCmd(p.Dir, objdir) cmd = b.GxxCmd(p.Dir, objdir)
...@@ -2167,7 +2198,7 @@ func (b *Builder) gccld(p *load.Package, objdir, outfile string, flags []string, ...@@ -2167,7 +2198,7 @@ func (b *Builder) gccld(p *load.Package, objdir, outfile string, flags []string,
cmdargs := []interface{}{cmd, "-o", outfile, objs, flags} cmdargs := []interface{}{cmd, "-o", outfile, objs, flags}
dir := p.Dir dir := p.Dir
out, err := b.runOut(dir, b.cCompilerEnv(), cmdargs...) out, err := b.runOut(a, dir, b.cCompilerEnv(), cmdargs...)
if len(out) > 0 { if len(out) > 0 {
// Filter out useless linker warnings caused by bugs outside Go. // Filter out useless linker warnings caused by bugs outside Go.
// See also cmd/link/internal/ld's hostlink method. // See also cmd/link/internal/ld's hostlink method.
...@@ -2651,7 +2682,7 @@ func (b *Builder) dynimport(a *Action, p *load.Package, objdir, importGo, cgoExe ...@@ -2651,7 +2682,7 @@ func (b *Builder) dynimport(a *Action, p *load.Package, objdir, importGo, cgoExe
} }
ldflags = append(n, "-pie") ldflags = append(n, "-pie")
} }
if err := b.gccld(p, objdir, dynobj, ldflags, linkobj); err != nil { if err := b.gccld(a, p, objdir, dynobj, ldflags, linkobj); err != nil {
return err return err
} }
...@@ -2710,7 +2741,7 @@ var ( ...@@ -2710,7 +2741,7 @@ var (
) )
func (b *Builder) swigDoVersionCheck() error { func (b *Builder) swigDoVersionCheck() error {
out, err := b.runOut("", nil, "swig", "-version") out, err := b.runOut(nil, "", nil, "swig", "-version")
if err != nil { if err != nil {
return err return err
} }
...@@ -2865,7 +2896,7 @@ func (b *Builder) swigOne(a *Action, p *load.Package, file, objdir string, pcCFL ...@@ -2865,7 +2896,7 @@ func (b *Builder) swigOne(a *Action, p *load.Package, file, objdir string, pcCFL
args = append(args, "-c++") args = append(args, "-c++")
} }
out, err := b.runOut(p.Dir, nil, "swig", args, file) out, err := b.runOut(a, p.Dir, nil, "swig", args, file)
if err != nil { if err != nil {
if len(out) > 0 { if len(out) > 0 {
if bytes.Contains(out, []byte("-intgosize")) || bytes.Contains(out, []byte("-cgo")) { if bytes.Contains(out, []byte("-intgosize")) || bytes.Contains(out, []byte("-cgo")) {
......
...@@ -140,7 +140,7 @@ func (gcToolchain) gc(b *Builder, a *Action, archive string, importcfg []byte, s ...@@ -140,7 +140,7 @@ func (gcToolchain) gc(b *Builder, a *Action, archive string, importcfg []byte, s
args = append(args, mkAbs(p.Dir, f)) args = append(args, mkAbs(p.Dir, f))
} }
output, err = b.runOut(p.Dir, nil, args...) output, err = b.runOut(a, p.Dir, nil, args...)
return ofile, output, err return ofile, output, err
} }
......
...@@ -94,7 +94,7 @@ func (tools gccgoToolchain) gc(b *Builder, a *Action, archive string, importcfg ...@@ -94,7 +94,7 @@ func (tools gccgoToolchain) gc(b *Builder, a *Action, archive string, importcfg
args = append(args, mkAbs(p.Dir, f)) args = append(args, mkAbs(p.Dir, f))
} }
output, err = b.runOut(p.Dir, nil, args) output, err = b.runOut(a, p.Dir, nil, args)
return ofile, output, err return ofile, output, err
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment