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