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