mirror of
https://github.com/golang/go
synced 2024-11-23 05:10:09 -07:00
cmd/compile: modify debug-hash to support match exclusion
The goal here is to enable a search that will locate all the instances of a failure, not just the first one. This helps with searches for loopvar-change breakage, FP differences from fused-multiply-add, and allows certain semantics queries that can be implemented as compiler changes (for example, where does integer overflow routinely occur?) Change-Id: Ic28f1695d47e421c2089d1f3f7c4b40c56db970f Reviewed-on: https://go-review.googlesource.com/c/go/+/481195 Run-TryBot: David Chase <drchase@google.com> Reviewed-by: Matthew Dempsky <mdempsky@google.com> TryBot-Result: Gopher Robot <gobot@golang.org>
This commit is contained in:
parent
f9cf2c4d04
commit
ef9c211de8
@ -39,6 +39,7 @@ type HashDebug struct {
|
||||
posTmp []src.Pos
|
||||
bytesTmp bytes.Buffer
|
||||
matches []hashAndMask // A hash matches if one of these matches.
|
||||
excludes []hashAndMask // explicitly excluded hash suffixes
|
||||
yes, no bool
|
||||
fileSuffixOnly bool // for Pos hashes, remove the directory prefix.
|
||||
inlineSuffixOnly bool // for Pos hashes, remove all but the most inline position.
|
||||
@ -76,15 +77,23 @@ var LoopVarHash *HashDebug // for debugging shared/private loop variable changes
|
||||
//
|
||||
// 3. is "n" or "N" (returns false)
|
||||
//
|
||||
// 4. is a suffix of the sha1 hash of pkgAndName (returns true)
|
||||
// 4. does not explicitly exclude the sha1 hash of pkgAndName (see step 6)
|
||||
//
|
||||
// 5. OR
|
||||
// if the value is in the regular language "[01]+(/[01]+)+"
|
||||
// test the [01]+ substrings after in order returning true
|
||||
// for the first one that suffix-matches. The substrings AFTER
|
||||
// the first slash are numbered 0,1, etc and are named
|
||||
// fmt.Sprintf("%s%d", varname, number)
|
||||
// Clause 5 is not really intended for human use and only
|
||||
// 5. is a suffix of the sha1 hash of pkgAndName (returns true)
|
||||
//
|
||||
// 6. OR
|
||||
// if the (non-empty) value is in the regular language
|
||||
// "(-[01]+/)+?([01]+(/[01]+)+?"
|
||||
// (exclude..)(....include...)
|
||||
// test the [01]+ exclude substrings, if any suffix-match, return false (4 above)
|
||||
// test the [01]+ include substrings, if any suffix-match, return true
|
||||
// The include substrings AFTER the first slash are numbered 0,1, etc and
|
||||
// are named fmt.Sprintf("%s%d", varname, number)
|
||||
// As an extra-special case for multiple failure search,
|
||||
// an excludes-only string ending in a slash (terminated, not separated)
|
||||
// implicitly specifies the include string "0/1", that is, match everything.
|
||||
// (Exclude strings are used for automated search for multiple failures.)
|
||||
// Clause 6 is not really intended for human use and only
|
||||
// matters for failures that require multiple triggers.
|
||||
//
|
||||
// Otherwise it returns false.
|
||||
@ -169,11 +178,35 @@ func NewHashDebug(ev, s string, file writeSyncer) *HashDebug {
|
||||
return hd
|
||||
}
|
||||
ss := strings.Split(s, "/")
|
||||
hd.matches = append(hd.matches, toHashAndMask(ss[0], ev))
|
||||
// first remove any leading exclusions; these are preceded with "-"
|
||||
i := 0
|
||||
for len(ss) > 0 {
|
||||
s := ss[0]
|
||||
if len(s) == 0 || len(s) > 0 && s[0] != '-' {
|
||||
break
|
||||
}
|
||||
ss = ss[1:]
|
||||
hd.excludes = append(hd.excludes, toHashAndMask(s[1:], fmt.Sprintf("%s%d", "HASH_EXCLUDE", i)))
|
||||
i++
|
||||
}
|
||||
// hash searches may use additional EVs with 0, 1, 2, ... suffixes.
|
||||
for i := 1; i < len(ss); i++ {
|
||||
evi := fmt.Sprintf("%s%d", ev, i-1) // convention is extras begin indexing at zero
|
||||
hd.matches = append(hd.matches, toHashAndMask(ss[i], evi))
|
||||
i = 0
|
||||
for _, s := range ss {
|
||||
if s == "" {
|
||||
if i != 0 || len(ss) > 1 && ss[1] != "" || len(ss) > 2 {
|
||||
Fatalf("Empty hash match string for %s should be first (and only) one", ev)
|
||||
}
|
||||
// Special case of should match everything.
|
||||
hd.matches = append(hd.matches, toHashAndMask("0", fmt.Sprintf("%s0", ev)))
|
||||
hd.matches = append(hd.matches, toHashAndMask("1", fmt.Sprintf("%s1", ev)))
|
||||
break
|
||||
}
|
||||
if i == 0 {
|
||||
hd.matches = append(hd.matches, toHashAndMask(s, fmt.Sprintf("%s", ev)))
|
||||
} else {
|
||||
hd.matches = append(hd.matches, toHashAndMask(s, fmt.Sprintf("%s%d", ev, i-1)))
|
||||
}
|
||||
i++
|
||||
}
|
||||
return hd
|
||||
|
||||
@ -216,6 +249,36 @@ func (d *HashDebug) DebugHashMatch(pkgAndName string) bool {
|
||||
return d.DebugHashMatchParam(pkgAndName, 0)
|
||||
}
|
||||
|
||||
func (d *HashDebug) excluded(hash uint64) bool {
|
||||
for _, m := range d.excludes {
|
||||
if (m.hash^hash)&m.mask == 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func hashString(hash uint64) string {
|
||||
hstr := ""
|
||||
if hash == 0 {
|
||||
hstr = "0"
|
||||
} else {
|
||||
for ; hash != 0; hash = hash >> 1 {
|
||||
hstr = string('0'+byte(hash&1)) + hstr
|
||||
}
|
||||
}
|
||||
return hstr
|
||||
}
|
||||
|
||||
func (d *HashDebug) match(hash uint64) *hashAndMask {
|
||||
for i, m := range d.matches {
|
||||
if (m.hash^hash)&m.mask == 0 {
|
||||
return &d.matches[i]
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DebugHashMatchParam returns true if either the variable used to create d is
|
||||
// unset, or if its value is y, or if it is a suffix of the base-two
|
||||
// representation of the hash of pkgAndName and param. If the variable is not
|
||||
@ -236,20 +299,14 @@ func (d *HashDebug) DebugHashMatchParam(pkgAndName string, param uint64) bool {
|
||||
|
||||
hash := hashOf(pkgAndName, param)
|
||||
|
||||
for _, m := range d.matches {
|
||||
if (m.hash^hash)&m.mask == 0 {
|
||||
hstr := ""
|
||||
if hash == 0 {
|
||||
hstr = "0"
|
||||
} else {
|
||||
for ; hash != 0; hash = hash >> 1 {
|
||||
hstr = string('0'+byte(hash&1)) + hstr
|
||||
// Return false for explicitly excluded hashes
|
||||
if d.excluded(hash) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
d.logDebugHashMatch(m.name, pkgAndName, hstr, param)
|
||||
if m := d.match(hash); m != nil {
|
||||
d.logDebugHashMatch(m.name, pkgAndName, hashString(hash), param)
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@ -284,20 +341,14 @@ func (d *HashDebug) debugHashMatchPos(ctxt *obj.Link, pos src.XPos) bool {
|
||||
|
||||
hash := hashOfBytes(b, 0)
|
||||
|
||||
for _, m := range d.matches {
|
||||
if (m.hash^hash)&m.mask == 0 {
|
||||
hstr := ""
|
||||
if hash == 0 {
|
||||
hstr = "0"
|
||||
} else {
|
||||
for ; hash != 0; hash = hash >> 1 {
|
||||
hstr = string('0'+byte(hash&1)) + hstr
|
||||
// Return false for explicitly excluded hashes
|
||||
if d.excluded(hash) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
d.logDebugHashMatchLocked(m.name, "POS="+string(b), hstr, 0)
|
||||
if m := d.match(hash); m != nil {
|
||||
d.logDebugHashMatchLocked(m.name, "POS="+string(b), hashString(hash), 0)
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user