Skip to content

Commit

Permalink
cmd/go: add undocumented -debug-actiongraph flag to dump action graph
Browse files Browse the repository at this point in the history
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 <[email protected]>
Reviewed-by: David Crawshaw <[email protected]>
TryBot-Result: Gobot Gobot <[email protected]>
  • Loading branch information
rsc committed Oct 10, 2017
1 parent 966c459 commit 0174858
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 13 deletions.
2 changes: 2 additions & 0 deletions src/cmd/go/internal/cfg/cfg.go
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down
2 changes: 1 addition & 1 deletion src/cmd/go/internal/run/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}

Expand Down
14 changes: 9 additions & 5 deletions src/cmd/go/internal/test/test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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))
}
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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
}

Expand Down Expand Up @@ -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,
Expand All @@ -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,
Expand Down
97 changes: 90 additions & 7 deletions src/cmd/go/internal/work/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"bytes"
"container/heap"
"debug/elf"
"encoding/json"
"errors"
"flag"
"fmt"
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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))
}
Expand Down Expand Up @@ -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.
Expand All @@ -611,6 +615,7 @@ func InstallPackages(args []string, forGet bool) {
}
if len(tools) > 0 {
a = &Action{
Mode: "go install (tools)",
Deps: tools,
}
}
Expand Down Expand Up @@ -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)
Expand All @@ -692,13 +698,79 @@ 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
p *load.Package
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
Expand Down Expand Up @@ -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 {
Expand All @@ -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
}
Expand All @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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)
}
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit 0174858

Please sign in to comment.