mirror of
https://github.com/golang/go.git
synced 2025-05-29 11:25:43 +00:00
cmd/compile: enable flag-specified dump of specific phase+function
For very large input files, use of GOSSAFUNC to obtain a dump after compilation steps can lead to both unwieldy large output files and unwieldy larger processes (because the output is buffered in a string). This flag -d=ssa/<phase>/dump:<function name> provides finer control of what is dumped, into a smaller file, and with less memory overhead in the running compiler. The special phase name "build" is added to allow printing of the just-built ssa before any transformations are applied. This was helpful in making sense of the gogo/protobuf problems. The output format was tweaked to remove gratuitous spaces, and a crude -d=ssa/help help text was added. Change-Id: If7516e22203420eb6ed3614f7cee44cb9260f43e Reviewed-on: https://go-review.googlesource.com/23044 Run-TryBot: David Chase <drchase@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Keith Randall <khr@golang.org>
This commit is contained in:
parent
10560afb54
commit
a190f3c8a3
@ -246,6 +246,7 @@ func Main() {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
val := 1
|
val := 1
|
||||||
|
valstring := ""
|
||||||
if i := strings.Index(name, "="); i >= 0 {
|
if i := strings.Index(name, "="); i >= 0 {
|
||||||
var err error
|
var err error
|
||||||
val, err = strconv.Atoi(name[i+1:])
|
val, err = strconv.Atoi(name[i+1:])
|
||||||
@ -253,6 +254,9 @@ func Main() {
|
|||||||
log.Fatalf("invalid debug value %v", name)
|
log.Fatalf("invalid debug value %v", name)
|
||||||
}
|
}
|
||||||
name = name[:i]
|
name = name[:i]
|
||||||
|
} else if i := strings.Index(name, ":"); i >= 0 {
|
||||||
|
valstring = name[i+1:]
|
||||||
|
name = name[:i]
|
||||||
}
|
}
|
||||||
for _, t := range debugtab {
|
for _, t := range debugtab {
|
||||||
if t.name == name {
|
if t.name == name {
|
||||||
@ -273,7 +277,7 @@ func Main() {
|
|||||||
flag = phase[i+1:]
|
flag = phase[i+1:]
|
||||||
phase = phase[:i]
|
phase = phase[:i]
|
||||||
}
|
}
|
||||||
err := ssa.PhaseOption(phase, flag, val)
|
err := ssa.PhaseOption(phase, flag, val, valstring)
|
||||||
if err != "" {
|
if err != "" {
|
||||||
log.Fatalf(err)
|
log.Fatalf(err)
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ package ssa
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
"os"
|
||||||
"regexp"
|
"regexp"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
@ -41,6 +42,9 @@ func Compile(f *Func) {
|
|||||||
// Run all the passes
|
// Run all the passes
|
||||||
printFunc(f)
|
printFunc(f)
|
||||||
f.Config.HTML.WriteFunc("start", f)
|
f.Config.HTML.WriteFunc("start", f)
|
||||||
|
if BuildDump != "" && BuildDump == f.Name {
|
||||||
|
f.dumpFile("build")
|
||||||
|
}
|
||||||
if checkEnabled {
|
if checkEnabled {
|
||||||
checkFunc(f)
|
checkFunc(f)
|
||||||
}
|
}
|
||||||
@ -96,6 +100,10 @@ func Compile(f *Func) {
|
|||||||
f.LogStat("TIME(ns):BYTES:ALLOCS", time, nBytes, nAllocs)
|
f.LogStat("TIME(ns):BYTES:ALLOCS", time, nBytes, nAllocs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if p.dump != nil && p.dump[f.Name] {
|
||||||
|
// Dump function to appropriately named file
|
||||||
|
f.dumpFile(phaseName)
|
||||||
|
}
|
||||||
if checkEnabled {
|
if checkEnabled {
|
||||||
checkFunc(f)
|
checkFunc(f)
|
||||||
}
|
}
|
||||||
@ -105,16 +113,48 @@ func Compile(f *Func) {
|
|||||||
phaseName = ""
|
phaseName = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: should be a config field
|
||||||
|
var dumpFileSeq int
|
||||||
|
|
||||||
|
// dumpFile creates a file from the phase name and function name
|
||||||
|
// Dumping is done to files to avoid buffering huge strings before
|
||||||
|
// output.
|
||||||
|
func (f *Func) dumpFile(phaseName string) {
|
||||||
|
dumpFileSeq++
|
||||||
|
fname := fmt.Sprintf("%s__%s_%d.dump", phaseName, f.Name, dumpFileSeq)
|
||||||
|
fname = strings.Replace(fname, " ", "_", -1)
|
||||||
|
fname = strings.Replace(fname, "/", "_", -1)
|
||||||
|
fname = strings.Replace(fname, ":", "_", -1)
|
||||||
|
|
||||||
|
fi, err := os.Create(fname)
|
||||||
|
if err != nil {
|
||||||
|
f.Config.Warnl(0, "Unable to create after-phase dump file %s", fname)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
p := stringFuncPrinter{w: fi}
|
||||||
|
fprintFunc(p, f)
|
||||||
|
fi.Close()
|
||||||
|
}
|
||||||
|
|
||||||
type pass struct {
|
type pass struct {
|
||||||
name string
|
name string
|
||||||
fn func(*Func)
|
fn func(*Func)
|
||||||
required bool
|
required bool
|
||||||
disabled bool
|
disabled bool
|
||||||
time bool // report time to run pass
|
time bool // report time to run pass
|
||||||
mem bool // report mem stats to run pass
|
mem bool // report mem stats to run pass
|
||||||
stats int // pass reports own "stats" (e.g., branches removed)
|
stats int // pass reports own "stats" (e.g., branches removed)
|
||||||
debug int // pass performs some debugging. =1 should be in error-testing-friendly Warnl format.
|
debug int // pass performs some debugging. =1 should be in error-testing-friendly Warnl format.
|
||||||
test int // pass-specific ad-hoc option, perhaps useful in development
|
test int // pass-specific ad-hoc option, perhaps useful in development
|
||||||
|
dump map[string]bool // dump if function name matches
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *pass) addDump(s string) {
|
||||||
|
if p.dump == nil {
|
||||||
|
p.dump = make(map[string]bool)
|
||||||
|
}
|
||||||
|
p.dump[s] = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run consistency checker between each phase
|
// Run consistency checker between each phase
|
||||||
@ -127,6 +167,7 @@ var IntrinsicsDisable bool
|
|||||||
var BuildDebug int
|
var BuildDebug int
|
||||||
var BuildTest int
|
var BuildTest int
|
||||||
var BuildStats int
|
var BuildStats int
|
||||||
|
var BuildDump string // name of function to dump after initial build of ssa
|
||||||
|
|
||||||
// PhaseOption sets the specified flag in the specified ssa phase,
|
// PhaseOption sets the specified flag in the specified ssa phase,
|
||||||
// returning empty string if this was successful or a string explaining
|
// returning empty string if this was successful or a string explaining
|
||||||
@ -146,7 +187,35 @@ var BuildStats int
|
|||||||
//
|
//
|
||||||
// BOOT_GO_GCFLAGS=-d='ssa/~^.*scc$/off' GO_GCFLAGS='-d=ssa/~^.*scc$/off' ./make.bash
|
// BOOT_GO_GCFLAGS=-d='ssa/~^.*scc$/off' GO_GCFLAGS='-d=ssa/~^.*scc$/off' ./make.bash
|
||||||
//
|
//
|
||||||
func PhaseOption(phase, flag string, val int) string {
|
func PhaseOption(phase, flag string, val int, valString string) string {
|
||||||
|
if phase == "help" {
|
||||||
|
lastcr := 0
|
||||||
|
phasenames := "check, all, build, intrinsics"
|
||||||
|
for _, p := range passes {
|
||||||
|
pn := strings.Replace(p.name, " ", "_", -1)
|
||||||
|
if len(pn)+len(phasenames)-lastcr > 70 {
|
||||||
|
phasenames += "\n"
|
||||||
|
lastcr = len(phasenames)
|
||||||
|
phasenames += pn
|
||||||
|
} else {
|
||||||
|
phasenames += ", " + pn
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "" +
|
||||||
|
`GcFlag -d=ssa/<phase>/<flag>[=<value>]|[:<function_name>]
|
||||||
|
<phase> is one of:
|
||||||
|
` + phasenames + `
|
||||||
|
<flag> is one of on, off, debug, mem, time, test, stats, dump
|
||||||
|
<value> defaults to 1
|
||||||
|
<function_name> is required for "dump", specifies name of function to dump after <phase>
|
||||||
|
Except for dump, output is directed to standard out; dump appears in a file.
|
||||||
|
Phase "all" supports flags "time", "mem", and "dump".
|
||||||
|
Phases "intrinsics" supports flags "on", "off", and "debug".
|
||||||
|
Interpretation of the "debug" value depends on the phase.
|
||||||
|
Dump files are named <phase>__<function_name>_<seq>.dump.
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
if phase == "check" && flag == "on" {
|
if phase == "check" && flag == "on" {
|
||||||
checkEnabled = val != 0
|
checkEnabled = val != 0
|
||||||
return ""
|
return ""
|
||||||
@ -157,9 +226,18 @@ func PhaseOption(phase, flag string, val int) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
alltime := false
|
alltime := false
|
||||||
|
allmem := false
|
||||||
|
alldump := false
|
||||||
if phase == "all" {
|
if phase == "all" {
|
||||||
if flag == "time" {
|
if flag == "time" {
|
||||||
alltime = val != 0
|
alltime = val != 0
|
||||||
|
} else if flag == "mem" {
|
||||||
|
allmem = val != 0
|
||||||
|
} else if flag == "dump" {
|
||||||
|
alldump = val != 0
|
||||||
|
if alldump {
|
||||||
|
BuildDump = valString
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return fmt.Sprintf("Did not find a flag matching %s in -d=ssa/%s debug option", flag, phase)
|
return fmt.Sprintf("Did not find a flag matching %s in -d=ssa/%s debug option", flag, phase)
|
||||||
}
|
}
|
||||||
@ -186,6 +264,8 @@ func PhaseOption(phase, flag string, val int) string {
|
|||||||
BuildTest = val
|
BuildTest = val
|
||||||
case "stats":
|
case "stats":
|
||||||
BuildStats = val
|
BuildStats = val
|
||||||
|
case "dump":
|
||||||
|
BuildDump = valString
|
||||||
default:
|
default:
|
||||||
return fmt.Sprintf("Did not find a flag matching %s in -d=ssa/%s debug option", flag, phase)
|
return fmt.Sprintf("Did not find a flag matching %s in -d=ssa/%s debug option", flag, phase)
|
||||||
}
|
}
|
||||||
@ -205,6 +285,10 @@ func PhaseOption(phase, flag string, val int) string {
|
|||||||
for i, p := range passes {
|
for i, p := range passes {
|
||||||
if phase == "all" {
|
if phase == "all" {
|
||||||
p.time = alltime
|
p.time = alltime
|
||||||
|
p.mem = allmem
|
||||||
|
if alldump {
|
||||||
|
p.addDump(valString)
|
||||||
|
}
|
||||||
passes[i] = p
|
passes[i] = p
|
||||||
matchedOne = true
|
matchedOne = true
|
||||||
} else if p.name == phase || p.name == underphase || re != nil && re.MatchString(p.name) {
|
} else if p.name == phase || p.name == underphase || re != nil && re.MatchString(p.name) {
|
||||||
@ -223,6 +307,8 @@ func PhaseOption(phase, flag string, val int) string {
|
|||||||
p.stats = val
|
p.stats = val
|
||||||
case "test":
|
case "test":
|
||||||
p.test = val
|
p.test = val
|
||||||
|
case "dump":
|
||||||
|
p.addDump(valString)
|
||||||
default:
|
default:
|
||||||
return fmt.Sprintf("Did not find a flag matching %s in -d=ssa/%s debug option", flag, phase)
|
return fmt.Sprintf("Did not find a flag matching %s in -d=ssa/%s debug option", flag, phase)
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ package ssa
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// A Func represents a Go func declaration (or function literal) and
|
// A Func represents a Go func declaration (or function literal) and
|
||||||
@ -113,7 +114,7 @@ func (f *Func) LogStat(key string, args ...interface{}) {
|
|||||||
}
|
}
|
||||||
n := "missing_pass"
|
n := "missing_pass"
|
||||||
if f.pass != nil {
|
if f.pass != nil {
|
||||||
n = f.pass.name
|
n = strings.Replace(f.pass.name, " ", "_", -1)
|
||||||
}
|
}
|
||||||
f.Config.Warnl(f.Entry.Line, "\t%s\t%s%s\t%s", n, key, value, f.Name)
|
f.Config.Warnl(f.Entry.Line, "\t%s\t%s%s\t%s", n, key, value, f.Name)
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user