Commit 0174858c authored by Russ Cox's avatar Russ Cox

cmd/go: add undocumented -debug-actiongraph flag to dump action graph

This will be useful for debugging but is intentionally
undocumented and not guaranteed to persist in any
particular form.

Change-Id: I60710a1e94cfc2ce31fe91fc268c51985060f8df
Reviewed-on: https://go-review.googlesource.com/69330
Run-TryBot: Russ Cox <rsc@golang.org>
Reviewed-by: 's avatarDavid Crawshaw <crawshaw@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
parent 966c459f
......@@ -37,6 +37,8 @@ var (
BuildV bool // -v flag
BuildWork bool // -work flag
BuildX bool // -x flag
DebugActiongraph string // -debug-actiongraph flag (undocumented, unstable)
)
func init() {
......
......@@ -111,7 +111,7 @@ func runRun(cmd *base.Command, args []string) {
}
p.Internal.ExeName = src[:len(src)-len(".go")] // name temporary executable for first go file
a1 := b.Action(work.ModeBuild, work.ModeBuild, p)
a := &work.Action{Func: buildRunProgram, Args: cmdArgs, Deps: []*work.Action{a1}}
a := &work.Action{Mode: "go run", Func: buildRunProgram, Args: cmdArgs, Deps: []*work.Action{a1}}
b.Do(a)
}
......
......@@ -522,7 +522,7 @@ func runTest(cmd *base.Command, args []string) {
}
sort.Strings(all)
a := &work.Action{}
a := &work.Action{Mode: "go test -i"}
for _, p := range load.PackagesForBuild(all) {
a.Deps = append(a.Deps, b.Action(work.ModeInstall, work.ModeInstall, p))
}
......@@ -599,7 +599,7 @@ func runTest(cmd *base.Command, args []string) {
}
// Ultimately the goal is to print the output.
root := &work.Action{Deps: prints}
root := &work.Action{Mode: "go test", Deps: prints}
// Force the printing of results to happen in order,
// one at a time.
......@@ -652,8 +652,8 @@ var windowsBadWords = []string{
func builderTest(b *work.Builder, p *load.Package) (buildAction, runAction, printAction *work.Action, err error) {
if len(p.TestGoFiles)+len(p.XTestGoFiles) == 0 {
build := b.Action(work.ModeBuild, work.ModeBuild, p)
run := &work.Action{Package: p, Deps: []*work.Action{build}}
print := &work.Action{Func: builderNoTest, Package: p, Deps: []*work.Action{run}}
run := &work.Action{Mode: "test run", Package: p, Deps: []*work.Action{build}}
print := &work.Action{Mode: "test print", Func: builderNoTest, Package: p, Deps: []*work.Action{run}}
return build, run, print, nil
}
......@@ -945,6 +945,7 @@ func builderTest(b *work.Builder, p *load.Package) (buildAction, runAction, prin
}
}
buildAction = &work.Action{
Mode: "test build",
Func: work.BuildInstallFunc,
Deps: []*work.Action{buildAction},
Package: pmain,
......@@ -953,22 +954,25 @@ func builderTest(b *work.Builder, p *load.Package) (buildAction, runAction, prin
runAction = buildAction // make sure runAction != nil even if not running test
}
if testC {
printAction = &work.Action{Package: p, Deps: []*work.Action{runAction}} // nop
printAction = &work.Action{Mode: "test print (nop)", Package: p, Deps: []*work.Action{runAction}} // nop
} else {
// run test
runAction = &work.Action{
Mode: "test run",
Func: builderRunTest,
Deps: []*work.Action{buildAction},
Package: p,
IgnoreFail: true,
}
cleanAction := &work.Action{
Mode: "test clean",
Func: builderCleanTest,
Deps: []*work.Action{runAction},
Package: p,
Objdir: testDir,
}
printAction = &work.Action{
Mode: "test print",
Func: builderPrintTest,
Deps: []*work.Action{cleanAction},
Package: p,
......
......@@ -9,6 +9,7 @@ import (
"bytes"
"container/heap"
"debug/elf"
"encoding/json"
"errors"
"flag"
"fmt"
......@@ -223,6 +224,9 @@ func AddBuildFlags(cmd *base.Command) {
cmd.Flag.Var((*base.StringsFlag)(&cfg.BuildContext.BuildTags), "tags", "")
cmd.Flag.Var((*base.StringsFlag)(&cfg.BuildToolexec), "toolexec", "")
cmd.Flag.BoolVar(&cfg.BuildWork, "work", false, "")
// Undocumented, unstable debugging flags.
cmd.Flag.StringVar(&cfg.DebugActiongraph, "debug-actiongraph", "", "")
}
// fileExtSplit expects a filename and returns the name
......@@ -471,7 +475,7 @@ func runBuild(cmd *base.Command, args []string) {
a = b.libaction(libName, pkgs, ModeBuild, depMode)
}
} else {
a = &Action{}
a = &Action{Mode: "go build"}
for _, p := range pkgs {
a.Deps = append(a.Deps, b.Action(ModeBuild, depMode, p))
}
......@@ -589,7 +593,7 @@ func InstallPackages(args []string, forGet bool) {
a = b.libaction(libName, pkgs, ModeInstall, ModeInstall)
}
} else {
a = &Action{}
a = &Action{Mode: "go install"}
var tools []*Action
for _, p := range pkgs {
// During 'go get', don't attempt (and fail) to install packages with only tests.
......@@ -611,6 +615,7 @@ func InstallPackages(args []string, forGet bool) {
}
if len(tools) > 0 {
a = &Action{
Mode: "go install (tools)",
Deps: tools,
}
}
......@@ -672,6 +677,7 @@ type Builder struct {
// An Action represents a single action in the action graph.
type Action struct {
Mode string // description of action operation
Package *load.Package // the package this action works on
Deps []*Action // actions that must happen before this one
Func func(*Builder, *Action) error // the action itself (nil = no-op)
......@@ -692,6 +698,21 @@ type Action struct {
Failed bool // whether the action failed
}
type actionJSON struct {
ID int
Mode string
Package string
Deps []int `json:",omitempty"`
IgnoreFail bool `json:",omitempty"`
Args []string `json:",omitempty"`
Link bool `json:",omitempty"`
Objdir string `json:",omitempty"`
Target string `json:",omitempty"`
Priority int `json:",omitempty"`
Failed bool `json:",omitempty"`
Pkgfile string `json:",omitempty"`
}
// cacheKey is the key for the action cache.
type cacheKey struct {
mode BuildMode
......@@ -699,6 +720,57 @@ type cacheKey struct {
shlib string
}
func actionGraphJSON(a *Action) string {
var workq []*Action
var inWorkq = make(map[*Action]int)
add := func(a *Action) {
if _, ok := inWorkq[a]; ok {
return
}
inWorkq[a] = len(workq)
workq = append(workq, a)
}
add(a)
for i := 0; i < len(workq); i++ {
for _, dep := range workq[i].Deps {
add(dep)
}
}
var list []*actionJSON
for id, a := range workq {
aj := &actionJSON{
Mode: a.Mode,
ID: id,
IgnoreFail: a.IgnoreFail,
Args: a.Args,
Link: a.Link,
Objdir: a.Objdir,
Target: a.Target,
Failed: a.Failed,
Priority: a.priority,
}
if a.Package != nil {
// TODO(rsc): Make this a unique key for a.Package somehow.
aj.Package = a.Package.ImportPath
aj.Pkgfile = a.Package.Internal.Pkgfile
}
for _, a1 := range a.Deps {
aj.Deps = append(aj.Deps, inWorkq[a1])
}
list = append(list, aj)
}
js, err := json.MarshalIndent(list, "", "\t")
if err != nil {
fmt.Fprintf(os.Stderr, "go: writing debug action graph: %v\n", err)
return ""
}
return string(js)
}
// BuildMode specifies the build mode:
// are we just building things or also installing the results?
type BuildMode int
......@@ -827,7 +899,7 @@ func (b *Builder) action1(mode BuildMode, depMode BuildMode, p *load.Package, lo
return a
}
a = &Action{Package: p}
a = &Action{Mode: "???", Package: p}
b.actionCache[key] = a
for _, p1 := range p.Internal.Imports {
......@@ -854,11 +926,13 @@ func (b *Builder) action1(mode BuildMode, depMode BuildMode, p *load.Package, lo
switch p.ImportPath {
case "builtin", "unsafe":
// Fake packages - nothing to build.
a.Mode = "built-in package"
return a
}
// gccgo standard library is "fake" too.
if cfg.BuildToolchainName == "gccgo" {
// the target name is needed for cgo.
a.Mode = "gccgo stdlib"
a.Target = p.Internal.Target
return a
}
......@@ -867,6 +941,7 @@ func (b *Builder) action1(mode BuildMode, depMode BuildMode, p *load.Package, lo
if !p.Stale && p.Internal.Target != "" {
// p.Stale==false implies that p.Internal.Target is up-to-date.
// Record target name for use by actions depending on this one.
a.Mode = "use installed"
a.Target = p.Internal.Target
p.Internal.Pkgfile = a.Target
return a
......@@ -938,7 +1013,7 @@ func (b *Builder) action1(mode BuildMode, depMode BuildMode, p *load.Package, lo
}
func (b *Builder) libaction(libname string, pkgs []*load.Package, mode, depMode BuildMode) *Action {
a := &Action{}
a := &Action{Mode: "libaction???"}
switch mode {
default:
base.Fatalf("unrecognized mode %v", mode)
......@@ -1046,7 +1121,7 @@ func (b *Builder) libaction(libname string, pkgs []*load.Package, mode, depMode
if p.Internal.Target == "" {
continue
}
shlibnameaction := &Action{}
shlibnameaction := &Action{Mode: "shlibname"}
shlibnameaction.Func = (*Builder).installShlibname
shlibnameaction.Target = p.Internal.Target[:len(p.Internal.Target)-2] + ".shlibname"
a.Deps = append(a.Deps, shlibnameaction)
......@@ -1095,6 +1170,14 @@ func (b *Builder) Do(root *Action) {
a.priority = i
}
if cfg.DebugActiongraph != "" {
js := actionGraphJSON(root)
if err := ioutil.WriteFile(cfg.DebugActiongraph, []byte(js), 0666); err != nil {
fmt.Fprintf(os.Stderr, "go: writing action graph: %v\n", err)
base.SetExitStatus(1)
}
}
b.readySema = make(chan bool, len(all))
// Initialize per-action execution state.
......@@ -3040,7 +3123,7 @@ func (tools gccgoToolchain) ld(b *Builder, root *Action, out, importcfg string,
}
func (tools gccgoToolchain) ldShared(b *Builder, toplevelactions []*Action, out, importcfg string, allactions []*Action) error {
fakeRoot := &Action{}
fakeRoot := &Action{Mode: "gccgo ldshared"}
fakeRoot.Deps = toplevelactions
return tools.link(b, fakeRoot, out, importcfg, allactions, "", nil, "shared", out)
}
......@@ -3673,7 +3756,7 @@ func (b *Builder) swigDoIntSize(objdir string) (intsize string, err error) {
p := load.GoFilesPackage(srcs)
if _, _, e := BuildToolchain.gc(b, &Action{Package: p, Objdir: objdir}, "", nil, false, srcs); e != nil {
if _, _, e := BuildToolchain.gc(b, &Action{Mode: "swigDoIntSize", Package: p, Objdir: objdir}, "", nil, false, srcs); e != nil {
return "32", nil
}
return "64", nil
......
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