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 LiteralAllocHash *HashDebug
60 var MergeLocalsHash *HashDebug
61 var VariableMakeHash *HashDebug
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
127
128 func DebugHashMatchPkgFunc(pkg, fn string) bool {
129 return hashDebug.MatchPkgFunc(pkg, fn, nil)
130 }
131
132 func DebugHashMatchPos(pos src.XPos) bool {
133 return hashDebug.MatchPos(pos, nil)
134 }
135
136
137
138
139 func HasDebugHash() bool {
140 return hashDebug != nil
141 }
142
143
144 func toHashAndMask(s, varname string) hashAndMask {
145 l := len(s)
146 if l > 64 {
147 s = s[l-64:]
148 l = 64
149 }
150 m := ^(^uint64(0) << l)
151 h, err := strconv.ParseUint(s, 2, 64)
152 if err != nil {
153 Fatalf("Could not parse %s (=%s) as a binary number", varname, s)
154 }
155
156 return hashAndMask{name: varname, hash: h, mask: m}
157 }
158
159
160
161
162 func NewHashDebug(ev, s string, file io.Writer) *HashDebug {
163 if s == "" {
164 return nil
165 }
166
167 hd := &HashDebug{name: ev, logfile: file}
168 if !strings.Contains(s, "/") {
169 m, err := bisect.New(s)
170 if err != nil {
171 Fatalf("%s: %v", ev, err)
172 }
173 hd.bisect = m
174 return hd
175 }
176
177
178 ss := strings.Split(s, "/")
179
180 i := 0
181 for len(ss) > 0 {
182 s := ss[0]
183 if len(s) == 0 || len(s) > 0 && s[0] != '-' {
184 break
185 }
186 ss = ss[1:]
187 hd.excludes = append(hd.excludes, toHashAndMask(s[1:], fmt.Sprintf("%s%d", "HASH_EXCLUDE", i)))
188 i++
189 }
190
191 i = 0
192 for _, s := range ss {
193 if s == "" {
194 if i != 0 || len(ss) > 1 && ss[1] != "" || len(ss) > 2 {
195 Fatalf("Empty hash match string for %s should be first (and only) one", ev)
196 }
197
198 hd.matches = append(hd.matches, toHashAndMask("0", fmt.Sprintf("%s0", ev)))
199 hd.matches = append(hd.matches, toHashAndMask("1", fmt.Sprintf("%s1", ev)))
200 break
201 }
202 if i == 0 {
203 hd.matches = append(hd.matches, toHashAndMask(s, ev))
204 } else {
205 hd.matches = append(hd.matches, toHashAndMask(s, fmt.Sprintf("%s%d", ev, i-1)))
206 }
207 i++
208 }
209 return hd
210 }
211
212
213 func (d *HashDebug) excluded(hash uint64) bool {
214 for _, m := range d.excludes {
215 if (m.hash^hash)&m.mask == 0 {
216 return true
217 }
218 }
219 return false
220 }
221
222
223 func hashString(hash uint64) string {
224 hstr := ""
225 if hash == 0 {
226 hstr = "0"
227 } else {
228 for ; hash != 0; hash = hash >> 1 {
229 hstr = string('0'+byte(hash&1)) + hstr
230 }
231 }
232 if len(hstr) > 24 {
233 hstr = hstr[len(hstr)-24:]
234 }
235 return hstr
236 }
237
238
239 func (d *HashDebug) match(hash uint64) *hashAndMask {
240 for i, m := range d.matches {
241 if (m.hash^hash)&m.mask == 0 {
242 return &d.matches[i]
243 }
244 }
245 return nil
246 }
247
248
249
250
251
252
253 func (d *HashDebug) MatchPkgFunc(pkg, fn string, note func() string) bool {
254 if d == nil {
255 return true
256 }
257
258 return d.matchPkgFunc(pkg, fn, note)
259 }
260
261 func (d *HashDebug) matchPkgFunc(pkg, fn string, note func() string) bool {
262 hash := bisect.Hash(pkg, fn)
263 return d.matchAndLog(hash, func() string { return pkg + "." + fn }, note)
264 }
265
266
267
268
269
270
271 func (d *HashDebug) MatchPos(pos src.XPos, desc func() string) bool {
272 if d == nil {
273 return true
274 }
275
276 return d.matchPos(Ctxt, pos, desc)
277 }
278
279 func (d *HashDebug) matchPos(ctxt *obj.Link, pos src.XPos, note func() string) bool {
280 return d.matchPosWithInfo(ctxt, pos, nil, note)
281 }
282
283 func (d *HashDebug) matchPosWithInfo(ctxt *obj.Link, pos src.XPos, info any, note func() string) bool {
284 hash := d.hashPos(ctxt, pos)
285 if info != nil {
286 hash = bisect.Hash(hash, info)
287 }
288 return d.matchAndLog(hash,
289 func() string {
290 r := d.fmtPos(ctxt, pos)
291 if info != nil {
292 r += fmt.Sprintf(" (%v)", info)
293 }
294 return r
295 },
296 note)
297 }
298
299
300
301
302
303
304 func (d *HashDebug) MatchPosWithInfo(pos src.XPos, info any, desc func() string) bool {
305 if d == nil {
306 return true
307 }
308
309 return d.matchPosWithInfo(Ctxt, pos, info, desc)
310 }
311
312
313
314
315
316
317
318 func (d *HashDebug) matchAndLog(hash uint64, text, note func() string) bool {
319 if d.bisect != nil {
320 enabled := d.bisect.ShouldEnable(hash)
321 if d.bisect.ShouldPrint(hash) {
322 disabled := ""
323 if !enabled {
324 disabled = " [DISABLED]"
325 }
326 var t string
327 if !d.bisect.MarkerOnly() {
328 t = text()
329 if note != nil {
330 if n := note(); n != "" {
331 t += ": " + n + disabled
332 disabled = ""
333 }
334 }
335 }
336 d.log(d.name, hash, strings.TrimSpace(t+disabled))
337 }
338 return enabled
339 }
340
341
342 if d.excluded(hash) {
343 return false
344 }
345 if m := d.match(hash); m != nil {
346 d.log(m.name, hash, text())
347 return true
348 }
349 return false
350 }
351
352
353
354
355 func (d *HashDebug) short(name string) string {
356 if d.fileSuffixOnly {
357 return filepath.Base(name)
358 }
359 return name
360 }
361
362
363
364 func (d *HashDebug) hashPos(ctxt *obj.Link, pos src.XPos) uint64 {
365 if d.inlineSuffixOnly {
366 p := ctxt.InnermostPos(pos)
367 return bisect.Hash(d.short(p.Filename()), p.Line(), p.Col())
368 }
369 h := bisect.Hash()
370 ctxt.AllPos(pos, func(p src.Pos) {
371 h = bisect.Hash(h, d.short(p.Filename()), p.Line(), p.Col())
372 })
373 return h
374 }
375
376
377
378 func (d *HashDebug) fmtPos(ctxt *obj.Link, pos src.XPos) string {
379 format := func(p src.Pos) string {
380 return fmt.Sprintf("%s:%d:%d", d.short(p.Filename()), p.Line(), p.Col())
381 }
382 if d.inlineSuffixOnly {
383 return format(ctxt.InnermostPos(pos))
384 }
385 var stk []string
386 ctxt.AllPos(pos, func(p src.Pos) {
387 stk = append(stk, format(p))
388 })
389 return strings.Join(stk, "; ")
390 }
391
392
393
394 func (d *HashDebug) log(varname string, hash uint64, text string) {
395 d.mu.Lock()
396 defer d.mu.Unlock()
397
398 file := d.logfile
399 if file == nil {
400 if tmpfile := os.Getenv("GSHS_LOGFILE"); tmpfile != "" {
401 var err error
402 file, err = os.OpenFile(tmpfile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
403 if err != nil {
404 Fatalf("could not open hash-testing logfile %s", tmpfile)
405 return
406 }
407 }
408 if file == nil {
409 file = os.Stdout
410 }
411 d.logfile = file
412 }
413
414
415 fmt.Fprintf(file, "%s %s\n", text, bisect.Marker(hash))
416
417
418
419 fmt.Fprintf(file, "%s triggered %s %s\n", varname, text, hashString(hash))
420 }
421
View as plain text