1
2
3
4
5 package base
6
7 import (
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 )
20
21 type hashAndMask struct {
22
23 hash uint64
24 mask uint64
25 name string
26 }
27
28 type HashDebug struct {
29 mu sync.Mutex
30 name string
31
32
33 logfile io.Writer
34 posTmp []src.Pos
35 bytesTmp bytes.Buffer
36 matches []hashAndMask
37 excludes []hashAndMask
38 bisect *bisect.Matcher
39 fileSuffixOnly bool
40 inlineSuffixOnly bool
41 }
42
43
44
45
46
47
48 func (d *HashDebug) SetInlineSuffixOnly(b bool) *HashDebug {
49 d.inlineSuffixOnly = b
50 return d
51 }
52
53
54 var hashDebug *HashDebug
55
56 var FmaHash *HashDebug
57 var LoopVarHash *HashDebug
58 var PGOHash *HashDebug
59 var MergeLocalsHash *HashDebug
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126 func DebugHashMatchPkgFunc(pkg, fn string) bool {
127 return hashDebug.MatchPkgFunc(pkg, fn, nil)
128 }
129
130 func DebugHashMatchPos(pos src.XPos) bool {
131 return hashDebug.MatchPos(pos, nil)
132 }
133
134
135
136
137 func HasDebugHash() bool {
138 return hashDebug != nil
139 }
140
141
142 func toHashAndMask(s, varname string) hashAndMask {
143 l := len(s)
144 if l > 64 {
145 s = s[l-64:]
146 l = 64
147 }
148 m := ^(^uint64(0) << l)
149 h, err := strconv.ParseUint(s, 2, 64)
150 if err != nil {
151 Fatalf("Could not parse %s (=%s) as a binary number", varname, s)
152 }
153
154 return hashAndMask{name: varname, hash: h, mask: m}
155 }
156
157
158
159
160 func NewHashDebug(ev, s string, file io.Writer) *HashDebug {
161 if s == "" {
162 return nil
163 }
164
165 hd := &HashDebug{name: ev, logfile: file}
166 if !strings.Contains(s, "/") {
167 m, err := bisect.New(s)
168 if err != nil {
169 Fatalf("%s: %v", ev, err)
170 }
171 hd.bisect = m
172 return hd
173 }
174
175
176 ss := strings.Split(s, "/")
177
178 i := 0
179 for len(ss) > 0 {
180 s := ss[0]
181 if len(s) == 0 || len(s) > 0 && s[0] != '-' {
182 break
183 }
184 ss = ss[1:]
185 hd.excludes = append(hd.excludes, toHashAndMask(s[1:], fmt.Sprintf("%s%d", "HASH_EXCLUDE", i)))
186 i++
187 }
188
189 i = 0
190 for _, s := range ss {
191 if s == "" {
192 if i != 0 || len(ss) > 1 && ss[1] != "" || len(ss) > 2 {
193 Fatalf("Empty hash match string for %s should be first (and only) one", ev)
194 }
195
196 hd.matches = append(hd.matches, toHashAndMask("0", fmt.Sprintf("%s0", ev)))
197 hd.matches = append(hd.matches, toHashAndMask("1", fmt.Sprintf("%s1", ev)))
198 break
199 }
200 if i == 0 {
201 hd.matches = append(hd.matches, toHashAndMask(s, ev))
202 } else {
203 hd.matches = append(hd.matches, toHashAndMask(s, fmt.Sprintf("%s%d", ev, i-1)))
204 }
205 i++
206 }
207 return hd
208 }
209
210
211 func (d *HashDebug) excluded(hash uint64) bool {
212 for _, m := range d.excludes {
213 if (m.hash^hash)&m.mask == 0 {
214 return true
215 }
216 }
217 return false
218 }
219
220
221 func hashString(hash uint64) string {
222 hstr := ""
223 if hash == 0 {
224 hstr = "0"
225 } else {
226 for ; hash != 0; hash = hash >> 1 {
227 hstr = string('0'+byte(hash&1)) + hstr
228 }
229 }
230 if len(hstr) > 24 {
231 hstr = hstr[len(hstr)-24:]
232 }
233 return hstr
234 }
235
236
237 func (d *HashDebug) match(hash uint64) *hashAndMask {
238 for i, m := range d.matches {
239 if (m.hash^hash)&m.mask == 0 {
240 return &d.matches[i]
241 }
242 }
243 return nil
244 }
245
246
247
248
249
250
251 func (d *HashDebug) MatchPkgFunc(pkg, fn string, note func() string) bool {
252 if d == nil {
253 return true
254 }
255
256 return d.matchPkgFunc(pkg, fn, note)
257 }
258
259 func (d *HashDebug) matchPkgFunc(pkg, fn string, note func() string) bool {
260 hash := bisect.Hash(pkg, fn)
261 return d.matchAndLog(hash, func() string { return pkg + "." + fn }, note)
262 }
263
264
265
266
267
268
269 func (d *HashDebug) MatchPos(pos src.XPos, desc func() string) bool {
270 if d == nil {
271 return true
272 }
273
274 return d.matchPos(Ctxt, pos, desc)
275 }
276
277 func (d *HashDebug) matchPos(ctxt *obj.Link, pos src.XPos, note func() string) bool {
278 return d.matchPosWithInfo(ctxt, pos, nil, note)
279 }
280
281 func (d *HashDebug) matchPosWithInfo(ctxt *obj.Link, pos src.XPos, info any, note func() string) bool {
282 hash := d.hashPos(ctxt, pos)
283 if info != nil {
284 hash = bisect.Hash(hash, info)
285 }
286 return d.matchAndLog(hash,
287 func() string {
288 r := d.fmtPos(ctxt, pos)
289 if info != nil {
290 r += fmt.Sprintf(" (%v)", info)
291 }
292 return r
293 },
294 note)
295 }
296
297
298
299
300
301
302 func (d *HashDebug) MatchPosWithInfo(pos src.XPos, info any, desc func() string) bool {
303 if d == nil {
304 return true
305 }
306
307 return d.matchPosWithInfo(Ctxt, pos, info, desc)
308 }
309
310
311
312
313
314
315
316 func (d *HashDebug) matchAndLog(hash uint64, text, note func() string) bool {
317 if d.bisect != nil {
318 enabled := d.bisect.ShouldEnable(hash)
319 if d.bisect.ShouldPrint(hash) {
320 disabled := ""
321 if !enabled {
322 disabled = " [DISABLED]"
323 }
324 var t string
325 if !d.bisect.MarkerOnly() {
326 t = text()
327 if note != nil {
328 if n := note(); n != "" {
329 t += ": " + n + disabled
330 disabled = ""
331 }
332 }
333 }
334 d.log(d.name, hash, strings.TrimSpace(t+disabled))
335 }
336 return enabled
337 }
338
339
340 if d.excluded(hash) {
341 return false
342 }
343 if m := d.match(hash); m != nil {
344 d.log(m.name, hash, text())
345 return true
346 }
347 return false
348 }
349
350
351
352
353 func (d *HashDebug) short(name string) string {
354 if d.fileSuffixOnly {
355 return filepath.Base(name)
356 }
357 return name
358 }
359
360
361
362 func (d *HashDebug) hashPos(ctxt *obj.Link, pos src.XPos) uint64 {
363 if d.inlineSuffixOnly {
364 p := ctxt.InnermostPos(pos)
365 return bisect.Hash(d.short(p.Filename()), p.Line(), p.Col())
366 }
367 h := bisect.Hash()
368 ctxt.AllPos(pos, func(p src.Pos) {
369 h = bisect.Hash(h, d.short(p.Filename()), p.Line(), p.Col())
370 })
371 return h
372 }
373
374
375
376 func (d *HashDebug) fmtPos(ctxt *obj.Link, pos src.XPos) string {
377 format := func(p src.Pos) string {
378 return fmt.Sprintf("%s:%d:%d", d.short(p.Filename()), p.Line(), p.Col())
379 }
380 if d.inlineSuffixOnly {
381 return format(ctxt.InnermostPos(pos))
382 }
383 var stk []string
384 ctxt.AllPos(pos, func(p src.Pos) {
385 stk = append(stk, format(p))
386 })
387 return strings.Join(stk, "; ")
388 }
389
390
391
392 func (d *HashDebug) log(varname string, hash uint64, text string) {
393 d.mu.Lock()
394 defer d.mu.Unlock()
395
396 file := d.logfile
397 if file == nil {
398 if tmpfile := os.Getenv("GSHS_LOGFILE"); tmpfile != "" {
399 var err error
400 file, err = os.OpenFile(tmpfile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
401 if err != nil {
402 Fatalf("could not open hash-testing logfile %s", tmpfile)
403 return
404 }
405 }
406 if file == nil {
407 file = os.Stdout
408 }
409 d.logfile = file
410 }
411
412
413 fmt.Fprintf(file, "%s %s\n", text, bisect.Marker(hash))
414
415
416
417 fmt.Fprintf(file, "%s triggered %s %s\n", varname, text, hashString(hash))
418 }
419
View as plain text