]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile,cmd/internal/objabi: move -d flag parser to objabi
authorAustin Clements <austin@google.com>
Fri, 17 Sep 2021 14:07:41 +0000 (10:07 -0400)
committerAustin Clements <austin@google.com>
Fri, 5 Nov 2021 00:52:04 +0000 (00:52 +0000)
This moves and slightly generalizes the -d debug flag parser from
cmd/compile/internal/base to cmd/internal/objabi so that we can use
the same debug flag syntax in other tools.

This makes a few minor tweaks to implementation details. The flag
itself is now just a flag.Value that gets constructed explicitly,
rather than at init time, and we've cleaned up the implementation a
little (e.g., using a map instead of a linear search of a slice). The
help text is now automatically alphabetized. Rather than describing
the values of some flags in the help text footer, we simply include it
in the flags' help text and make sure multi-line help text renders
sensibly.

For #48297.

Change-Id: Id373ee3b767e456be483fb28c110d025149be532
Reviewed-on: https://go-review.googlesource.com/c/go/+/359956
Trust: Austin Clements <austin@google.com>
Run-TryBot: Austin Clements <austin@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: David Chase <drchase@google.com>
Reviewed-by: Cherry Mui <cherryyz@google.com>
src/cmd/compile/internal/base/debug.go
src/cmd/compile/internal/base/flag.go
src/cmd/internal/objabi/flag.go

index e2245e1c26ea64cb01cd4a63315f68fe70c5f949..37e345bd7fb64098a90b7f1a932913913ec8b782 100644 (file)
@@ -6,15 +6,6 @@
 
 package base
 
-import (
-       "fmt"
-       "log"
-       "os"
-       "reflect"
-       "strconv"
-       "strings"
-)
-
 // Debug holds the parsed debugging configuration values.
 var Debug DebugFlags
 
@@ -26,7 +17,7 @@ var Debug DebugFlags
 // Each setting is name=value; for ints, name is short for name=1.
 type DebugFlags struct {
        Append               int    `help:"print information about append compilation"`
-       Checkptr             int    `help:"instrument unsafe pointer conversions"`
+       Checkptr             int    `help:"instrument unsafe pointer conversions\n0: instrumentation disabled\n1: conversions involving unsafe.Pointer are instrumented\n2: conversions to unsafe.Pointer force heap allocation"`
        Closure              int    `help:"print information about closure compilation"`
        DclStack             int    `help:"run internal dclstack check"`
        Defer                int    `help:"print information about defer compilation"`
@@ -40,7 +31,7 @@ type DebugFlags struct {
        LocationLists        int    `help:"print information about DWARF location list creation"`
        Nil                  int    `help:"print information about nil checks"`
        NoOpenDefer          int    `help:"disable open-coded defers"`
-       PCTab                string `help:"print named pc-value table"`
+       PCTab                string `help:"print named pc-value table\nOne of: pctospadj, pctofile, pctoline, pctoinline, pctopcdata"`
        Panic                int    `help:"show all compiler panics"`
        Slice                int    `help:"print information about slice compilation"`
        SoftFloat            int    `help:"force compiler to emit soft-float code"`
@@ -52,141 +43,10 @@ type DebugFlags struct {
        WB                   int    `help:"print information about write barriers"`
        ABIWrap              int    `help:"print information about ABI wrapper generation"`
 
-       any bool // set when any of the values have been set
-}
-
-// Any reports whether any of the debug flags have been set.
-func (d *DebugFlags) Any() bool { return d.any }
-
-type debugField struct {
-       name string
-       help string
-       val  interface{} // *int or *string
-}
-
-var debugTab []debugField
-
-func init() {
-       v := reflect.ValueOf(&Debug).Elem()
-       t := v.Type()
-       for i := 0; i < t.NumField(); i++ {
-               f := t.Field(i)
-               if f.Name == "any" {
-                       continue
-               }
-               name := strings.ToLower(f.Name)
-               help := f.Tag.Get("help")
-               if help == "" {
-                       panic(fmt.Sprintf("base.Debug.%s is missing help text", f.Name))
-               }
-               ptr := v.Field(i).Addr().Interface()
-               switch ptr.(type) {
-               default:
-                       panic(fmt.Sprintf("base.Debug.%s has invalid type %v (must be int or string)", f.Name, f.Type))
-               case *int, *string:
-                       // ok
-               }
-               debugTab = append(debugTab, debugField{name, help, ptr})
-       }
+       Any bool // set when any of the debug flags have been set
 }
 
 // DebugSSA is called to set a -d ssa/... option.
 // If nil, those options are reported as invalid options.
 // If DebugSSA returns a non-empty string, that text is reported as a compiler error.
 var DebugSSA func(phase, flag string, val int, valString string) string
-
-// parseDebug parses the -d debug string argument.
-func parseDebug(debugstr string) {
-       // parse -d argument
-       if debugstr == "" {
-               return
-       }
-       Debug.any = true
-Split:
-       for _, name := range strings.Split(debugstr, ",") {
-               if name == "" {
-                       continue
-               }
-               // display help about the -d option itself and quit
-               if name == "help" {
-                       fmt.Print(debugHelpHeader)
-                       maxLen := len("ssa/help")
-                       for _, t := range debugTab {
-                               if len(t.name) > maxLen {
-                                       maxLen = len(t.name)
-                               }
-                       }
-                       for _, t := range debugTab {
-                               fmt.Printf("\t%-*s\t%s\n", maxLen, t.name, t.help)
-                       }
-                       // ssa options have their own help
-                       fmt.Printf("\t%-*s\t%s\n", maxLen, "ssa/help", "print help about SSA debugging")
-                       fmt.Print(debugHelpFooter)
-                       os.Exit(0)
-               }
-               val, valstring, haveInt := 1, "", true
-               if i := strings.IndexAny(name, "=:"); i >= 0 {
-                       var err error
-                       name, valstring = name[:i], name[i+1:]
-                       val, err = strconv.Atoi(valstring)
-                       if err != nil {
-                               val, haveInt = 1, false
-                       }
-               }
-               for _, t := range debugTab {
-                       if t.name != name {
-                               continue
-                       }
-                       switch vp := t.val.(type) {
-                       case nil:
-                               // Ignore
-                       case *string:
-                               *vp = valstring
-                       case *int:
-                               if !haveInt {
-                                       log.Fatalf("invalid debug value %v", name)
-                               }
-                               *vp = val
-                       default:
-                               panic("bad debugtab type")
-                       }
-                       continue Split
-               }
-               // special case for ssa for now
-               if DebugSSA != nil && strings.HasPrefix(name, "ssa/") {
-                       // expect form ssa/phase/flag
-                       // e.g. -d=ssa/generic_cse/time
-                       // _ in phase name also matches space
-                       phase := name[4:]
-                       flag := "debug" // default flag is debug
-                       if i := strings.Index(phase, "/"); i >= 0 {
-                               flag = phase[i+1:]
-                               phase = phase[:i]
-                       }
-                       err := DebugSSA(phase, flag, val, valstring)
-                       if err != "" {
-                               log.Fatalf(err)
-                       }
-                       continue Split
-               }
-               log.Fatalf("unknown debug key -d %s\n", name)
-       }
-}
-
-const debugHelpHeader = `usage: -d arg[,arg]* and arg is <key>[=<value>]
-
-<key> is one of:
-
-`
-
-const debugHelpFooter = `
-<value> is key-specific.
-
-Key "checkptr" supports values:
-       "0": instrumentation disabled
-       "1": conversions involving unsafe.Pointer are instrumented
-       "2": conversions to unsafe.Pointer force heap allocation
-
-Key "pctab" supports values:
-       "pctospadj", "pctofile", "pctoline", "pctoinline", "pctopcdata"
-`
index 9d630ce97acb1bef8e86ce2181a87304444d72e3..f38eaa91c0cc15cdd1caf5e96bff9a5e4fb1e12f 100644 (file)
@@ -64,19 +64,19 @@ type CmdFlags struct {
        // V is added by objabi.AddVersionFlag
        W CountFlag "help:\"debug parse tree after type checking\""
 
-       LowerC int          "help:\"concurrency during compilation (1 means no concurrency)\""
-       LowerD func(string) "help:\"enable debugging settings; try -d help\""
-       LowerE CountFlag    "help:\"no limit on number of errors reported\""
-       LowerH CountFlag    "help:\"halt on error\""
-       LowerJ CountFlag    "help:\"debug runtime-initialized variables\""
-       LowerL CountFlag    "help:\"disable inlining\""
-       LowerM CountFlag    "help:\"print optimization decisions\""
-       LowerO string       "help:\"write output to `file`\""
-       LowerP *string      "help:\"set expected package import `path`\"" // &Ctxt.Pkgpath, set below
-       LowerR CountFlag    "help:\"debug generated wrappers\""
-       LowerT bool         "help:\"enable tracing for debugging the compiler\""
-       LowerW CountFlag    "help:\"debug type checking\""
-       LowerV *bool        "help:\"increase debug verbosity\""
+       LowerC int        "help:\"concurrency during compilation (1 means no concurrency)\""
+       LowerD flag.Value "help:\"enable debugging settings; try -d help\""
+       LowerE CountFlag  "help:\"no limit on number of errors reported\""
+       LowerH CountFlag  "help:\"halt on error\""
+       LowerJ CountFlag  "help:\"debug runtime-initialized variables\""
+       LowerL CountFlag  "help:\"disable inlining\""
+       LowerM CountFlag  "help:\"print optimization decisions\""
+       LowerO string     "help:\"write output to `file`\""
+       LowerP *string    "help:\"set expected package import `path`\"" // &Ctxt.Pkgpath, set below
+       LowerR CountFlag  "help:\"debug generated wrappers\""
+       LowerT bool       "help:\"enable tracing for debugging the compiler\""
+       LowerW CountFlag  "help:\"debug type checking\""
+       LowerV *bool      "help:\"increase debug verbosity\""
 
        // Special characters
        Percent          int  "flag:\"%\" help:\"debug non-static initializers\""
@@ -145,7 +145,7 @@ func ParseFlags() {
        Flag.I = addImportDir
 
        Flag.LowerC = 1
-       Flag.LowerD = parseDebug
+       Flag.LowerD = objabi.NewDebugFlag(&Debug, DebugSSA)
        Flag.LowerP = &Ctxt.Pkgpath
        Flag.LowerV = &Ctxt.Debugvlog
 
@@ -331,7 +331,11 @@ func registerFlags() {
                        f := v.Field(i).Interface().(func(string))
                        objabi.Flagfn1(name, help, f)
                default:
-                       panic(fmt.Sprintf("base.Flag.%s has unexpected type %s", f.Name, f.Type))
+                       if val, ok := v.Field(i).Interface().(flag.Value); ok {
+                               flag.Var(val, name, help)
+                       } else {
+                               panic(fmt.Sprintf("base.Flag.%s has unexpected type %s", f.Name, f.Type))
+                       }
                }
        }
 }
@@ -359,7 +363,7 @@ func concurrentBackendAllowed() bool {
        // while writing the object file, and that is non-concurrent.
        // Adding Debug_vlog, however, causes Debug.S to also print
        // while flushing the plist, which happens concurrently.
-       if Ctxt.Debugvlog || Debug.Any() || Flag.Live > 0 {
+       if Ctxt.Debugvlog || Debug.Any || Flag.Live > 0 {
                return false
        }
        // TODO: Test and delete this condition.
index e41fc570b0894926a29ccd0736fb4199f53b0c6c..f75c054fcb6110c332dfd12d5ec9fdd66d47b5fe 100644 (file)
@@ -13,6 +13,8 @@ import (
        "io/ioutil"
        "log"
        "os"
+       "reflect"
+       "sort"
        "strconv"
        "strings"
 )
@@ -202,3 +204,165 @@ func DecodeArg(arg string) string {
        }
        return b.String()
 }
+
+type debugField struct {
+       name string
+       help string
+       val  interface{} // *int or *string
+}
+
+type DebugFlag struct {
+       tab map[string]debugField
+       any *bool
+
+       debugSSA DebugSSA
+}
+
+// A DebugSSA function is called to set a -d ssa/... option.
+// If nil, those options are reported as invalid options.
+// If DebugSSA returns a non-empty string, that text is reported as a compiler error.
+// If phase is "help", it should print usage information and terminate the process.
+type DebugSSA func(phase, flag string, val int, valString string) string
+
+// NewDebugFlag constructs a DebugFlag for the fields of debug, which
+// must be a pointer to a struct.
+//
+// Each field of *debug is a different value, named for the lower-case of the field name.
+// Each field must be an int or string and must have a `help` struct tag.
+// There may be an "Any bool" field, which will be set if any debug flags are set.
+//
+// The returned flag takes a comma-separated list of settings.
+// Each setting is name=value; for ints, name is short for name=1.
+//
+// If debugSSA is non-nil, any debug flags of the form ssa/... will be
+// passed to debugSSA for processing.
+func NewDebugFlag(debug interface{}, debugSSA DebugSSA) *DebugFlag {
+       flag := &DebugFlag{
+               tab:      make(map[string]debugField),
+               debugSSA: debugSSA,
+       }
+
+       v := reflect.ValueOf(debug).Elem()
+       t := v.Type()
+       for i := 0; i < t.NumField(); i++ {
+               f := t.Field(i)
+               ptr := v.Field(i).Addr().Interface()
+               if f.Name == "Any" {
+                       switch ptr := ptr.(type) {
+                       default:
+                               panic("debug.Any must have type bool")
+                       case *bool:
+                               flag.any = ptr
+                       }
+                       continue
+               }
+               name := strings.ToLower(f.Name)
+               help := f.Tag.Get("help")
+               if help == "" {
+                       panic(fmt.Sprintf("debug.%s is missing help text", f.Name))
+               }
+               switch ptr.(type) {
+               default:
+                       panic(fmt.Sprintf("debug.%s has invalid type %v (must be int or string)", f.Name, f.Type))
+               case *int, *string:
+                       // ok
+               }
+               flag.tab[name] = debugField{name, help, ptr}
+       }
+
+       return flag
+}
+
+func (f *DebugFlag) Set(debugstr string) error {
+       if debugstr == "" {
+               return nil
+       }
+       if f.any != nil {
+               *f.any = true
+       }
+       for _, name := range strings.Split(debugstr, ",") {
+               if name == "" {
+                       continue
+               }
+               // display help about the debug option itself and quit
+               if name == "help" {
+                       fmt.Print(debugHelpHeader)
+                       maxLen, names := 0, []string{}
+                       if f.debugSSA != nil {
+                               maxLen = len("ssa/help")
+                       }
+                       for name := range f.tab {
+                               if len(name) > maxLen {
+                                       maxLen = len(name)
+                               }
+                               names = append(names, name)
+                       }
+                       sort.Strings(names)
+                       // Indent multi-line help messages.
+                       nl := fmt.Sprintf("\n\t%-*s\t", maxLen, "")
+                       for _, name := range names {
+                               help := f.tab[name].help
+                               fmt.Printf("\t%-*s\t%s\n", maxLen, name, strings.Replace(help, "\n", nl, -1))
+                       }
+                       if f.debugSSA != nil {
+                               // ssa options have their own help
+                               fmt.Printf("\t%-*s\t%s\n", maxLen, "ssa/help", "print help about SSA debugging")
+                       }
+                       os.Exit(0)
+               }
+
+               val, valstring, haveInt := 1, "", true
+               if i := strings.IndexAny(name, "=:"); i >= 0 {
+                       var err error
+                       name, valstring = name[:i], name[i+1:]
+                       val, err = strconv.Atoi(valstring)
+                       if err != nil {
+                               val, haveInt = 1, false
+                       }
+               }
+
+               if t, ok := f.tab[name]; ok {
+                       switch vp := t.val.(type) {
+                       case nil:
+                               // Ignore
+                       case *string:
+                               *vp = valstring
+                       case *int:
+                               if !haveInt {
+                                       log.Fatalf("invalid debug value %v", name)
+                               }
+                               *vp = val
+                       default:
+                               panic("bad debugtab type")
+                       }
+               } else if f.debugSSA != nil && strings.HasPrefix(name, "ssa/") {
+                       // expect form ssa/phase/flag
+                       // e.g. -d=ssa/generic_cse/time
+                       // _ in phase name also matches space
+                       phase := name[4:]
+                       flag := "debug" // default flag is debug
+                       if i := strings.Index(phase, "/"); i >= 0 {
+                               flag = phase[i+1:]
+                               phase = phase[:i]
+                       }
+                       err := f.debugSSA(phase, flag, val, valstring)
+                       if err != "" {
+                               log.Fatalf(err)
+                       }
+               } else {
+                       return fmt.Errorf("unknown debug key %s\n", name)
+               }
+       }
+
+       return nil
+}
+
+const debugHelpHeader = `usage: -d arg[,arg]* and arg is <key>[=<value>]
+
+<key> is one of:
+
+`
+
+func (f *DebugFlag) String() string {
+       return ""
+}