Ensure errors are handled or logged
if err != nil {
1// Copyright 2022 The Go Authors. All rights reserved.2// Use of this source code is governed by a BSD-style3// license that can be found in the LICENSE file.45package base67import (8 "bytes"9 "cmd/internal/obj"10 "cmd/internal/src"11 "fmt"12 "internal/bisect"13 "io"14 "os"15 "path/filepath"16 "strconv"17 "strings"18 "sync"19)2021type hashAndMask struct {22 // a hash h matches if (h^hash)&mask == 023 hash uint6424 mask uint6425 name string // base name, or base name + "0", "1", etc.26}2728type HashDebug struct {29 mu sync.Mutex // for logfile, posTmp, bytesTmp30 name string // base name of the flag/variable.31 // what file (if any) receives the yes/no logging?32 // default is os.Stdout33 logfile io.Writer34 posTmp []src.Pos35 bytesTmp bytes.Buffer36 matches []hashAndMask // A hash matches if one of these matches.37 excludes []hashAndMask // explicitly excluded hash suffixes38 bisect *bisect.Matcher39 fileSuffixOnly bool // for Pos hashes, remove the directory prefix.40 inlineSuffixOnly bool // for Pos hashes, remove all but the most inline position.41}4243// SetInlineSuffixOnly controls whether hashing and reporting use the entire44// inline position, or just the most-inline suffix. Compiler debugging tends45// to want the whole inlining, debugging user problems (loopvarhash, e.g.)46// typically does not need to see the entire inline tree, there is just one47// copy of the source code.48func (d *HashDebug) SetInlineSuffixOnly(b bool) *HashDebug {49 d.inlineSuffixOnly = b50 return d51}5253// The default compiler-debugging HashDebug, for "-d=gossahash=..."54var hashDebug *HashDebug5556var ConvertHash *HashDebug // for debugging float-to-[u]int conversion changes57var FmaHash *HashDebug // for debugging fused-multiply-add floating point changes58var LoopVarHash *HashDebug // for debugging shared/private loop variable changes59var PGOHash *HashDebug // for debugging PGO optimization decisions60var LiteralAllocHash *HashDebug // for debugging literal allocation optimizations61var MergeLocalsHash *HashDebug // for debugging local stack slot merging changes62var VariableMakeHash *HashDebug // for debugging variable-sized make optimizations6364// DebugHashMatchPkgFunc reports whether debug variable Gossahash65//66// 1. is empty (returns true; this is a special more-quickly implemented case of 4 below)67//68// 2. is "y" or "Y" (returns true)69//70// 3. is "n" or "N" (returns false)71//72// 4. does not explicitly exclude the sha1 hash of pkgAndName (see step 6)73//74// 5. is a suffix of the sha1 hash of pkgAndName (returns true)75//76// 6. OR77// if the (non-empty) value is in the regular language78// "(-[01]+/)+?([01]+(/[01]+)+?"79// (exclude..)(....include...)80// test the [01]+ exclude substrings, if any suffix-match, return false (4 above)81// test the [01]+ include substrings, if any suffix-match, return true82// The include substrings AFTER the first slash are numbered 0,1, etc and83// are named fmt.Sprintf("%s%d", varname, number)84// As an extra-special case for multiple failure search,85// an excludes-only string ending in a slash (terminated, not separated)86// implicitly specifies the include string "0/1", that is, match everything.87// (Exclude strings are used for automated search for multiple failures.)88// Clause 6 is not really intended for human use and only89// matters for failures that require multiple triggers.90//91// Otherwise it returns false.92//93// Unless Flags.Gossahash is empty, when DebugHashMatchPkgFunc returns true the message94//95// "%s triggered %s\n", varname, pkgAndName96//97// is printed on the file named in environment variable GSHS_LOGFILE,98// or standard out if that is empty. "Varname" is either the name of99// the variable or the name of the substring, depending on which matched.100//101// Typical use:102//103// 1. you make a change to the compiler, say, adding a new phase104//105// 2. it is broken in some mystifying way, for example, make.bash builds a broken106// compiler that almost works, but crashes compiling a test in run.bash.107//108// 3. add this guard to the code, which by default leaves it broken, but does not109// run the broken new code if Flags.Gossahash is non-empty and non-matching:110//111// if !base.DebugHashMatch(ir.PkgFuncName(fn)) {112// return nil // early exit, do nothing113// }114//115// 4. rebuild w/o the bad code,116// GOCOMPILEDEBUG=gossahash=n ./all.bash117// to verify that you put the guard in the right place with the right sense of the test.118//119// 5. use github.com/dr2chase/gossahash to search for the error:120//121// go install github.com/dr2chase/gossahash@latest122//123// gossahash -- <the thing that fails>124//125// for example: GOMAXPROCS=1 gossahash -- ./all.bash126//127// 6. gossahash should return a single function whose miscompilation128// causes the problem, and you can focus on that.129func DebugHashMatchPkgFunc(pkg, fn string) bool {130 return hashDebug.MatchPkgFunc(pkg, fn, nil)131}132133func DebugHashMatchPos(pos src.XPos) bool {134 return hashDebug.MatchPos(pos, nil)135}136137// HasDebugHash returns true if Flags.Gossahash is non-empty, which138// results in hashDebug being not-nil. I.e., if !HasDebugHash(),139// there is no need to create the string for hashing and testing.140func HasDebugHash() bool {141 return hashDebug != nil142}143144// TODO: Delete when we switch to bisect-only.145func toHashAndMask(s, varname string) hashAndMask {146 l := len(s)147 if l > 64 {148 s = s[l-64:]149 l = 64150 }151 m := ^(^uint64(0) << l)152 h, err := strconv.ParseUint(s, 2, 64)153 if err != nil {154 Fatalf("Could not parse %s (=%s) as a binary number", varname, s)155 }156157 return hashAndMask{name: varname, hash: h, mask: m}158}159160// NewHashDebug returns a new hash-debug tester for the161// environment variable ev. If ev is not set, it returns162// nil, allowing a lightweight check for normal-case behavior.163func NewHashDebug(ev, s string, file io.Writer) *HashDebug {164 if s == "" {165 return nil166 }167168 hd := &HashDebug{name: ev, logfile: file}169 if !strings.Contains(s, "/") {170 m, err := bisect.New(s)171 if err != nil {172 Fatalf("%s: %v", ev, err)173 }174 hd.bisect = m175 return hd176 }177178 // TODO: Delete remainder of function when we switch to bisect-only.179 ss := strings.Split(s, "/")180 // first remove any leading exclusions; these are preceded with "-"181 i := 0182 for len(ss) > 0 {183 s := ss[0]184 if len(s) == 0 || len(s) > 0 && s[0] != '-' {185 break186 }187 ss = ss[1:]188 hd.excludes = append(hd.excludes, toHashAndMask(s[1:], fmt.Sprintf("%s%d", "HASH_EXCLUDE", i)))189 i++190 }191 // hash searches may use additional EVs with 0, 1, 2, ... suffixes.192 i = 0193 for _, s := range ss {194 if s == "" {195 if i != 0 || len(ss) > 1 && ss[1] != "" || len(ss) > 2 {196 Fatalf("Empty hash match string for %s should be first (and only) one", ev)197 }198 // Special case of should match everything.199 hd.matches = append(hd.matches, toHashAndMask("0", fmt.Sprintf("%s0", ev)))200 hd.matches = append(hd.matches, toHashAndMask("1", fmt.Sprintf("%s1", ev)))201 break202 }203 if i == 0 {204 hd.matches = append(hd.matches, toHashAndMask(s, ev))205 } else {206 hd.matches = append(hd.matches, toHashAndMask(s, fmt.Sprintf("%s%d", ev, i-1)))207 }208 i++209 }210 return hd211}212213// TODO: Delete when we switch to bisect-only.214func (d *HashDebug) excluded(hash uint64) bool {215 for _, m := range d.excludes {216 if (m.hash^hash)&m.mask == 0 {217 return true218 }219 }220 return false221}222223// TODO: Delete when we switch to bisect-only.224func hashString(hash uint64) string {225 hstr := ""226 if hash == 0 {227 hstr = "0"228 } else {229 for ; hash != 0; hash = hash >> 1 {230 hstr = string('0'+byte(hash&1)) + hstr231 }232 }233 if len(hstr) > 24 {234 hstr = hstr[len(hstr)-24:]235 }236 return hstr237}238239// TODO: Delete when we switch to bisect-only.240func (d *HashDebug) match(hash uint64) *hashAndMask {241 for i, m := range d.matches {242 if (m.hash^hash)&m.mask == 0 {243 return &d.matches[i]244 }245 }246 return nil247}248249// MatchPkgFunc returns true if either the variable used to create d is250// unset, or if its value is y, or if it is a suffix of the base-two251// representation of the hash of pkg and fn. If the variable is not nil,252// then a true result is accompanied by stylized output to d.logfile, which253// is used for automated bug search.254func (d *HashDebug) MatchPkgFunc(pkg, fn string, note func() string) bool {255 if d == nil {256 return true257 }258 // Written this way to make inlining likely.259 return d.matchPkgFunc(pkg, fn, note)260}261262func (d *HashDebug) matchPkgFunc(pkg, fn string, note func() string) bool {263 hash := bisect.Hash(pkg, fn)264 return d.matchAndLog(hash, func() string { return pkg + "." + fn }, note)265}266267// MatchPos is similar to MatchPkgFunc, but for hash computation268// it uses the source position including all inlining information instead of269// package name and path.270// Note that the default answer for no environment variable (d == nil)271// is "yes", do the thing.272func (d *HashDebug) MatchPos(pos src.XPos, desc func() string) bool {273 if d == nil {274 return true275 }276 // Written this way to make inlining likely.277 return d.matchPos(Ctxt, pos, desc)278}279280func (d *HashDebug) matchPos(ctxt *obj.Link, pos src.XPos, note func() string) bool {281 return d.matchPosWithInfo(ctxt, pos, nil, note)282}283284func (d *HashDebug) matchPosWithInfo(ctxt *obj.Link, pos src.XPos, info any, note func() string) bool {285 hash := d.hashPos(ctxt, pos)286 if info != nil {287 hash = bisect.Hash(hash, info)288 }289 return d.matchAndLog(hash,290 func() string {291 r := d.fmtPos(ctxt, pos)292 if info != nil {293 r += fmt.Sprintf(" (%v)", info)294 }295 return r296 },297 note)298}299300// MatchPosWithInfo is similar to MatchPos, but with additional information301// that is included for hash computation, so it can distinguish multiple302// matches on the same source location.303// Note that the default answer for no environment variable (d == nil)304// is "yes", do the thing.305func (d *HashDebug) MatchPosWithInfo(pos src.XPos, info any, desc func() string) bool {306 if d == nil {307 return true308 }309 // Written this way to make inlining likely.310 return d.matchPosWithInfo(Ctxt, pos, info, desc)311}312313// matchAndLog is the core matcher. It reports whether the hash matches the pattern.314// If a report needs to be printed, match prints that report to the log file.315// The text func must be non-nil and should return a user-readable316// representation of what was hashed. The note func may be nil; if non-nil,317// it should return additional information to display to the user when this318// change is selected.319func (d *HashDebug) matchAndLog(hash uint64, text, note func() string) bool {320 if d.bisect != nil {321 enabled := d.bisect.ShouldEnable(hash)322 if d.bisect.ShouldPrint(hash) {323 disabled := ""324 if !enabled {325 disabled = " [DISABLED]"326 }327 var t string328 if !d.bisect.MarkerOnly() {329 t = text()330 if note != nil {331 if n := note(); n != "" {332 t += ": " + n + disabled333 disabled = ""334 }335 }336 }337 d.log(d.name, hash, strings.TrimSpace(t+disabled))338 }339 return enabled340 }341342 // TODO: Delete rest of function body when we switch to bisect-only.343 if d.excluded(hash) {344 return false345 }346 if m := d.match(hash); m != nil {347 d.log(m.name, hash, text())348 return true349 }350 return false351}352353// short returns the form of file name to use for d.354// The default is the full path, but fileSuffixOnly selects355// just the final path element.356func (d *HashDebug) short(name string) string {357 if d.fileSuffixOnly {358 return filepath.Base(name)359 }360 return name361}362363// hashPos returns a hash of the position pos, including its entire inline stack.364// If d.inlineSuffixOnly is true, hashPos only considers the innermost (leaf) position on the inline stack.365func (d *HashDebug) hashPos(ctxt *obj.Link, pos src.XPos) uint64 {366 if d.inlineSuffixOnly {367 p := ctxt.InnermostPos(pos)368 return bisect.Hash(d.short(p.Filename()), p.Line(), p.Col())369 }370 h := bisect.Hash()371 ctxt.AllPos(pos, func(p src.Pos) {372 h = bisect.Hash(h, d.short(p.Filename()), p.Line(), p.Col())373 })374 return h375}376377// fmtPos returns a textual formatting of the position pos, including its entire inline stack.378// If d.inlineSuffixOnly is true, fmtPos only considers the innermost (leaf) position on the inline stack.379func (d *HashDebug) fmtPos(ctxt *obj.Link, pos src.XPos) string {380 format := func(p src.Pos) string {381 return fmt.Sprintf("%s:%d:%d", d.short(p.Filename()), p.Line(), p.Col())382 }383 if d.inlineSuffixOnly {384 return format(ctxt.InnermostPos(pos))385 }386 var stk []string387 ctxt.AllPos(pos, func(p src.Pos) {388 stk = append(stk, format(p))389 })390 return strings.Join(stk, "; ")391}392393// log prints a match with the given hash and textual formatting.394// TODO: Delete varname parameter when we switch to bisect-only.395func (d *HashDebug) log(varname string, hash uint64, text string) {396 d.mu.Lock()397 defer d.mu.Unlock()398399 file := d.logfile400 if file == nil {401 if tmpfile := os.Getenv("GSHS_LOGFILE"); tmpfile != "" {402 var err error403 file, err = os.OpenFile(tmpfile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)404 if err != nil {405 Fatalf("could not open hash-testing logfile %s", tmpfile)406 return407 }408 }409 if file == nil {410 file = os.Stdout411 }412 d.logfile = file413 }414415 // Bisect output.416 fmt.Fprintf(file, "%s %s\n", text, bisect.Marker(hash))417418 // Gossahash output.419 // TODO: Delete rest of function when we switch to bisect-only.420 fmt.Fprintf(file, "%s triggered %s %s\n", varname, text, hashString(hash))421}
Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.