From: Russ Cox Date: Mon, 9 Oct 2017 18:37:11 +0000 (-0400) Subject: cmd/go: add undocumented -debug-actiongraph flag to dump action graph X-Git-Tag: go1.10beta1~812 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=0174858c71789365dc3550916916d43490a900fc;p=gostls13.git 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 Reviewed-by: David Crawshaw TryBot-Result: Gobot Gobot --- diff --git a/src/cmd/go/internal/cfg/cfg.go b/src/cmd/go/internal/cfg/cfg.go index d45043e6dc..290757fdb4 100644 --- a/src/cmd/go/internal/cfg/cfg.go +++ b/src/cmd/go/internal/cfg/cfg.go @@ -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() { diff --git a/src/cmd/go/internal/run/run.go b/src/cmd/go/internal/run/run.go index 6e276c28ec..72af80fac2 100644 --- a/src/cmd/go/internal/run/run.go +++ b/src/cmd/go/internal/run/run.go @@ -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) } diff --git a/src/cmd/go/internal/test/test.go b/src/cmd/go/internal/test/test.go index 90fa1b1ce1..0d21194287 100644 --- a/src/cmd/go/internal/test/test.go +++ b/src/cmd/go/internal/test/test.go @@ -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, diff --git a/src/cmd/go/internal/work/build.go b/src/cmd/go/internal/work/build.go index 21d3f8f680..92e2a3750d 100644 --- a/src/cmd/go/internal/work/build.go +++ b/src/cmd/go/internal/work/build.go @@ -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