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 ConvertHash *HashDebug
57 var FmaHash *HashDebug
58 var LoopVarHash *HashDebug
59 var PGOHash *HashDebug
60 var LiteralAllocHash *HashDebug
61 var MergeLocalsHash *HashDebug
62 var VariableMakeHash *HashDebug
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
129 func DebugHashMatchPkgFunc(pkg, fn string) bool {
130 return hashDebug.MatchPkgFunc(pkg, fn, nil)
131 }
132
133 func DebugHashMatchPos(pos src.XPos) bool {
134 return hashDebug.MatchPos(pos, nil)
135 }
136
137
138
139
140 func HasDebugHash() bool {
141 return hashDebug != nil
142 }
143
144
145 func toHashAndMask(s, varname string) hashAndMask {
146 l := len(s)
147 if l > 64 {
148 s = s[l-64:]
149 l = 64
150 }
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 }
156
157 return hashAndMask{name: varname, hash: h, mask: m}
158 }
159
160
161
162
163 func NewHashDebug(ev, s string, file io.Writer) *HashDebug {
164 if s == "" {
165 return nil
166 }
167
168 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 = m
175 return hd
176 }
177
178
179 ss := strings.Split(s, "/")
180
181 i := 0
182 for len(ss) > 0 {
183 s := ss[0]
184 if len(s) == 0 || len(s) > 0 && s[0] != '-' {
185 break
186 }
187 ss = ss[1:]
188 hd.excludes = append(hd.excludes, toHashAndMask(s[1:], fmt.Sprintf("%s%d", "HASH_EXCLUDE", i)))
189 i++
190 }
191
192 i = 0
193 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
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 break
202 }
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 hd
211 }
212
213
214 func (d *HashDebug) excluded(hash uint64) bool {
215 for _, m := range d.excludes {
216 if (m.hash^hash)&m.mask == 0 {
217 return true
218 }
219 }
220 return false
221 }
222
223
224 func 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)) + hstr
231 }
232 }
233 if len(hstr) > 24 {
234 hstr = hstr[len(hstr)-24:]
235 }
236 return hstr
237 }
238
239
240 func (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 nil
247 }
248
249
250
251
252
253
254 func (d *HashDebug) MatchPkgFunc(pkg, fn string, note func() string) bool {
255 if d == nil {
256 return true
257 }
258
259 return d.matchPkgFunc(pkg, fn, note)
260 }
261
262 func (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 }
266
267
268
269
270
271
272 func (d *HashDebug) MatchPos(pos src.XPos, desc func() string) bool {
273 if d == nil {
274 return true
275 }
276
277 return d.matchPos(Ctxt, pos, desc)
278 }
279
280 func (d *HashDebug) matchPos(ctxt *obj.Link, pos src.XPos, note func() string) bool {
281 return d.matchPosWithInfo(ctxt, pos, nil, note)
282 }
283
284 func (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 r
296 },
297 note)
298 }
299
300
301
302
303
304
305 func (d *HashDebug) MatchPosWithInfo(pos src.XPos, info any, desc func() string) bool {
306 if d == nil {
307 return true
308 }
309
310 return d.matchPosWithInfo(Ctxt, pos, info, desc)
311 }
312
313
314
315
316
317
318
319 func (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 string
328 if !d.bisect.MarkerOnly() {
329 t = text()
330 if note != nil {
331 if n := note(); n != "" {
332 t += ": " + n + disabled
333 disabled = ""
334 }
335 }
336 }
337 d.log(d.name, hash, strings.TrimSpace(t+disabled))
338 }
339 return enabled
340 }
341
342
343 if d.excluded(hash) {
344 return false
345 }
346 if m := d.match(hash); m != nil {
347 d.log(m.name, hash, text())
348 return true
349 }
350 return false
351 }
352
353
354
355
356 func (d *HashDebug) short(name string) string {
357 if d.fileSuffixOnly {
358 return filepath.Base(name)
359 }
360 return name
361 }
362
363
364
365 func (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 h
375 }
376
377
378
379 func (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 []string
387 ctxt.AllPos(pos, func(p src.Pos) {
388 stk = append(stk, format(p))
389 })
390 return strings.Join(stk, "; ")
391 }
392
393
394
395 func (d *HashDebug) log(varname string, hash uint64, text string) {
396 d.mu.Lock()
397 defer d.mu.Unlock()
398
399 file := d.logfile
400 if file == nil {
401 if tmpfile := os.Getenv("GSHS_LOGFILE"); tmpfile != "" {
402 var err error
403 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 return
407 }
408 }
409 if file == nil {
410 file = os.Stdout
411 }
412 d.logfile = file
413 }
414
415
416 fmt.Fprintf(file, "%s %s\n", text, bisect.Marker(hash))
417
418
419
420 fmt.Fprintf(file, "%s triggered %s %s\n", varname, text, hashString(hash))
421 }
422
View as plain text