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