github.com/cockroachdb/tools@v0.0.0-20230222021103-a6d27438930d/cmd/signature-fuzzer/internal/fuzz-generator/wraprand.go (about) 1 // Copyright 2021 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package generator 6 7 import ( 8 "fmt" 9 "math/rand" 10 "os" 11 "runtime" 12 "strings" 13 ) 14 15 const ( 16 RandCtlNochecks = 0 17 RandCtlChecks = 1 << iota 18 RandCtlCapture 19 RandCtlPanic 20 ) 21 22 func NewWrapRand(seed int64, ctl int) *wraprand { 23 rand.Seed(seed) 24 return &wraprand{seed: seed, ctl: ctl} 25 } 26 27 type wraprand struct { 28 f32calls int 29 f64calls int 30 intncalls int 31 seed int64 32 tag string 33 calls []string 34 ctl int 35 } 36 37 func (w *wraprand) captureCall(tag string, val string) { 38 call := tag + ": " + val + "\n" 39 pc := make([]uintptr, 10) 40 n := runtime.Callers(1, pc) 41 if n == 0 { 42 panic("why?") 43 } 44 pc = pc[:n] // pass only valid pcs to runtime.CallersFrames 45 frames := runtime.CallersFrames(pc) 46 for { 47 frame, more := frames.Next() 48 if strings.Contains(frame.File, "testing.") { 49 break 50 } 51 call += fmt.Sprintf("%s %s:%d\n", frame.Function, frame.File, frame.Line) 52 if !more { 53 break 54 } 55 56 } 57 w.calls = append(w.calls, call) 58 } 59 60 func (w *wraprand) Intn(n int64) int64 { 61 w.intncalls++ 62 rv := rand.Int63n(n) 63 if w.ctl&RandCtlCapture != 0 { 64 w.captureCall("Intn", fmt.Sprintf("%d", rv)) 65 } 66 return rv 67 } 68 69 func (w *wraprand) Float32() float32 { 70 w.f32calls++ 71 rv := rand.Float32() 72 if w.ctl&RandCtlCapture != 0 { 73 w.captureCall("Float32", fmt.Sprintf("%f", rv)) 74 } 75 return rv 76 } 77 78 func (w *wraprand) NormFloat64() float64 { 79 w.f64calls++ 80 rv := rand.NormFloat64() 81 if w.ctl&RandCtlCapture != 0 { 82 w.captureCall("NormFloat64", fmt.Sprintf("%f", rv)) 83 } 84 return rv 85 } 86 87 func (w *wraprand) emitCalls(fn string) { 88 outf, err := os.OpenFile(fn, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666) 89 if err != nil { 90 panic(err) 91 } 92 for _, c := range w.calls { 93 fmt.Fprint(outf, c) 94 } 95 outf.Close() 96 } 97 98 func (w *wraprand) Equal(w2 *wraprand) bool { 99 return w.f32calls == w2.f32calls && 100 w.f64calls == w2.f64calls && 101 w.intncalls == w2.intncalls 102 } 103 104 func (w *wraprand) Check(w2 *wraprand) { 105 if w.ctl != 0 && !w.Equal(w2) { 106 fmt.Fprintf(os.Stderr, "wraprand consistency check failed:\n") 107 t := "w" 108 if w.tag != "" { 109 t = w.tag 110 } 111 t2 := "w2" 112 if w2.tag != "" { 113 t2 = w2.tag 114 } 115 fmt.Fprintf(os.Stderr, " %s: {f32:%d f64:%d i:%d}\n", t, 116 w.f32calls, w.f64calls, w.intncalls) 117 fmt.Fprintf(os.Stderr, " %s: {f32:%d f64:%d i:%d}\n", t2, 118 w2.f32calls, w2.f64calls, w2.intncalls) 119 if w.ctl&RandCtlCapture != 0 { 120 f := fmt.Sprintf("/tmp/%s.txt", t) 121 f2 := fmt.Sprintf("/tmp/%s.txt", t2) 122 w.emitCalls(f) 123 w2.emitCalls(f2) 124 fmt.Fprintf(os.Stderr, "=-= emitted calls to %s, %s\n", f, f2) 125 } 126 if w.ctl&RandCtlPanic != 0 { 127 panic("bad") 128 } 129 } 130 } 131 132 func (w *wraprand) Checkpoint(tag string) { 133 if w.ctl&RandCtlCapture != 0 { 134 w.calls = append(w.calls, "=-=\n"+tag+"\n=-=\n") 135 } 136 }