github.com/shogo82148/std@v1.22.1-0.20240327122250-4e474527810c/cmd/compile/internal/base/hashdebug.go (about)

     1  // Copyright 2022 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package base
     6  
     7  import (
     8  	"github.com/shogo82148/std/bytes"
     9  	"github.com/shogo82148/std/cmd/internal/src"
    10  	"github.com/shogo82148/std/internal/bisect"
    11  	"github.com/shogo82148/std/io"
    12  	"github.com/shogo82148/std/sync"
    13  )
    14  
    15  type HashDebug struct {
    16  	mu   sync.Mutex
    17  	name string
    18  	// what file (if any) receives the yes/no logging?
    19  	// default is os.Stdout
    20  	logfile          io.Writer
    21  	posTmp           []src.Pos
    22  	bytesTmp         bytes.Buffer
    23  	matches          []hashAndMask
    24  	excludes         []hashAndMask
    25  	bisect           *bisect.Matcher
    26  	fileSuffixOnly   bool
    27  	inlineSuffixOnly bool
    28  }
    29  
    30  // SetInlineSuffixOnly controls whether hashing and reporting use the entire
    31  // inline position, or just the most-inline suffix.  Compiler debugging tends
    32  // to want the whole inlining, debugging user problems (loopvarhash, e.g.)
    33  // typically does not need to see the entire inline tree, there is just one
    34  // copy of the source code.
    35  func (d *HashDebug) SetInlineSuffixOnly(b bool) *HashDebug
    36  
    37  var FmaHash *HashDebug
    38  var LoopVarHash *HashDebug
    39  var PGOHash *HashDebug
    40  
    41  // DebugHashMatchPkgFunc reports whether debug variable Gossahash
    42  //
    43  //  1. is empty (returns true; this is a special more-quickly implemented case of 4 below)
    44  //
    45  //  2. is "y" or "Y" (returns true)
    46  //
    47  //  3. is "n" or "N" (returns false)
    48  //
    49  //  4. does not explicitly exclude the sha1 hash of pkgAndName (see step 6)
    50  //
    51  //  5. is a suffix of the sha1 hash of pkgAndName (returns true)
    52  //
    53  //  6. OR
    54  //     if the (non-empty) value is in the regular language
    55  //     "(-[01]+/)+?([01]+(/[01]+)+?"
    56  //     (exclude..)(....include...)
    57  //     test the [01]+ exclude substrings, if any suffix-match, return false (4 above)
    58  //     test the [01]+ include substrings, if any suffix-match, return true
    59  //     The include substrings AFTER the first slash are numbered 0,1, etc and
    60  //     are named fmt.Sprintf("%s%d", varname, number)
    61  //     As an extra-special case for multiple failure search,
    62  //     an excludes-only string ending in a slash (terminated, not separated)
    63  //     implicitly specifies the include string "0/1", that is, match everything.
    64  //     (Exclude strings are used for automated search for multiple failures.)
    65  //     Clause 6 is not really intended for human use and only
    66  //     matters for failures that require multiple triggers.
    67  //
    68  // Otherwise it returns false.
    69  //
    70  // Unless Flags.Gossahash is empty, when DebugHashMatchPkgFunc returns true the message
    71  //
    72  //	"%s triggered %s\n", varname, pkgAndName
    73  //
    74  // is printed on the file named in environment variable GSHS_LOGFILE,
    75  // or standard out if that is empty.  "Varname" is either the name of
    76  // the variable or the name of the substring, depending on which matched.
    77  //
    78  // Typical use:
    79  //
    80  //  1. you make a change to the compiler, say, adding a new phase
    81  //
    82  //  2. it is broken in some mystifying way, for example, make.bash builds a broken
    83  //     compiler that almost works, but crashes compiling a test in run.bash.
    84  //
    85  //  3. add this guard to the code, which by default leaves it broken, but does not
    86  //     run the broken new code if Flags.Gossahash is non-empty and non-matching:
    87  //
    88  //     if !base.DebugHashMatch(ir.PkgFuncName(fn)) {
    89  //     return nil // early exit, do nothing
    90  //     }
    91  //
    92  //  4. rebuild w/o the bad code,
    93  //     GOCOMPILEDEBUG=gossahash=n ./all.bash
    94  //     to verify that you put the guard in the right place with the right sense of the test.
    95  //
    96  //  5. use github.com/dr2chase/gossahash to search for the error:
    97  //
    98  //     go install github.com/dr2chase/gossahash@latest
    99  //
   100  //     gossahash -- <the thing that fails>
   101  //
   102  //     for example: GOMAXPROCS=1 gossahash -- ./all.bash
   103  //
   104  //  6. gossahash should return a single function whose miscompilation
   105  //     causes the problem, and you can focus on that.
   106  func DebugHashMatchPkgFunc(pkg, fn string) bool
   107  
   108  func DebugHashMatchPos(pos src.XPos) bool
   109  
   110  // HasDebugHash returns true if Flags.Gossahash is non-empty, which
   111  // results in hashDebug being not-nil.  I.e., if !HasDebugHash(),
   112  // there is no need to create the string for hashing and testing.
   113  func HasDebugHash() bool
   114  
   115  // NewHashDebug returns a new hash-debug tester for the
   116  // environment variable ev.  If ev is not set, it returns
   117  // nil, allowing a lightweight check for normal-case behavior.
   118  func NewHashDebug(ev, s string, file io.Writer) *HashDebug
   119  
   120  // MatchPkgFunc returns true if either the variable used to create d is
   121  // unset, or if its value is y, or if it is a suffix of the base-two
   122  // representation of the hash of pkg and fn.  If the variable is not nil,
   123  // then a true result is accompanied by stylized output to d.logfile, which
   124  // is used for automated bug search.
   125  func (d *HashDebug) MatchPkgFunc(pkg, fn string, note func() string) bool
   126  
   127  // MatchPos is similar to MatchPkgFunc, but for hash computation
   128  // it uses the source position including all inlining information instead of
   129  // package name and path.
   130  // Note that the default answer for no environment variable (d == nil)
   131  // is "yes", do the thing.
   132  func (d *HashDebug) MatchPos(pos src.XPos, desc func() string) bool
   133  
   134  // MatchPosWithInfo is similar to MatchPos, but with additional information
   135  // that is included for hash computation, so it can distinguish multiple
   136  // matches on the same source location.
   137  // Note that the default answer for no environment variable (d == nil)
   138  // is "yes", do the thing.
   139  func (d *HashDebug) MatchPosWithInfo(pos src.XPos, info any, desc func() string) bool