1
2
3
4
5 package trace_test
6
7 import (
8 "internal/trace"
9 "internal/trace/testtrace"
10 "io"
11 "math"
12 "testing"
13 "time"
14 )
15
16
17
18 func aeq(x, y float64) bool {
19 if x < 0 && y < 0 {
20 x, y = -x, -y
21 }
22 const digits = 8
23 factor := 1 - math.Pow(10, -digits+1)
24 return x*factor <= y && y*factor <= x
25 }
26
27 func TestMMU(t *testing.T) {
28 t.Parallel()
29
30
31
32
33
34
35 util := [][]trace.MutatorUtil{{
36 {0e9, 1},
37 {1e9, 0},
38 {2e9, 1},
39 {3e9, 0},
40 {4e9, 1},
41 {5e9, 0},
42 }}
43 mmuCurve := trace.NewMMUCurve(util)
44
45 for _, test := range []struct {
46 window time.Duration
47 want float64
48 worst []float64
49 }{
50 {0, 0, []float64{}},
51 {time.Millisecond, 0, []float64{0, 0}},
52 {time.Second, 0, []float64{0, 0}},
53 {2 * time.Second, 0.5, []float64{0.5, 0.5}},
54 {3 * time.Second, 1 / 3.0, []float64{1 / 3.0}},
55 {4 * time.Second, 0.5, []float64{0.5}},
56 {5 * time.Second, 3 / 5.0, []float64{3 / 5.0}},
57 {6 * time.Second, 3 / 5.0, []float64{3 / 5.0}},
58 } {
59 if got := mmuCurve.MMU(test.window); !aeq(test.want, got) {
60 t.Errorf("for %s window, want mu = %f, got %f", test.window, test.want, got)
61 }
62 worst := mmuCurve.Examples(test.window, 2)
63
64
65
66
67 if len(worst) != len(test.worst) {
68 t.Errorf("for %s window, want worst %v, got %v", test.window, test.worst, worst)
69 } else {
70 for i := range worst {
71 if worst[i].MutatorUtil != test.worst[i] {
72 t.Errorf("for %s window, want worst %v, got %v", test.window, test.worst, worst)
73 break
74 }
75 }
76 }
77 }
78 }
79
80 func TestMMUTrace(t *testing.T) {
81
82
83 if testing.Short() {
84
85 t.Skip("skipping in -short mode")
86 }
87 check := func(t *testing.T, mu [][]trace.MutatorUtil) {
88 mmuCurve := trace.NewMMUCurve(mu)
89
90
91
92 for window := time.Nanosecond; window < 10*time.Second; window *= 10 {
93 want := mmuSlow(mu[0], window)
94 got := mmuCurve.MMU(window)
95 if !aeq(want, got) {
96 t.Errorf("want %f, got %f mutator utilization in window %s", want, got, window)
97 }
98 }
99
100
101
102
103
104 defer func(old int) { trace.BandsPerSeries = old }(trace.BandsPerSeries)
105 trace.BandsPerSeries = 1
106 mmuCurve2 := trace.NewMMUCurve(mu)
107 quantiles := []float64{0, 1 - .999, 1 - .99}
108 for window := time.Microsecond; window < time.Second; window *= 10 {
109 mud1 := mmuCurve.MUD(window, quantiles)
110 mud2 := mmuCurve2.MUD(window, quantiles)
111 for i := range mud1 {
112 if !aeq(mud1[i], mud2[i]) {
113 t.Errorf("for quantiles %v at window %v, want %v, got %v", quantiles, window, mud2, mud1)
114 break
115 }
116 }
117 }
118 }
119 t.Run("V2", func(t *testing.T) {
120 testPath := "testdata/tests/go122-gc-stress.test"
121 r, _, err := testtrace.ParseFile(testPath)
122 if err != nil {
123 t.Fatalf("malformed test %s: bad trace file: %v", testPath, err)
124 }
125 var events []trace.Event
126 tr, err := trace.NewReader(r)
127 if err != nil {
128 t.Fatalf("malformed test %s: bad trace file: %v", testPath, err)
129 }
130 for {
131 ev, err := tr.ReadEvent()
132 if err == io.EOF {
133 break
134 }
135 if err != nil {
136 t.Fatalf("malformed test %s: bad trace file: %v", testPath, err)
137 }
138 events = append(events, ev)
139 }
140
141 check(t, trace.MutatorUtilizationV2(events, trace.UtilSTW|trace.UtilBackground|trace.UtilAssist))
142 })
143 }
144
145 func mmuSlow(util []trace.MutatorUtil, window time.Duration) (mmu float64) {
146 if max := time.Duration(util[len(util)-1].Time - util[0].Time); window > max {
147 window = max
148 }
149
150 mmu = 1.0
151
152
153
154 muInWindow := func(util []trace.MutatorUtil, end int64) float64 {
155 total := 0.0
156 var prevU trace.MutatorUtil
157 for _, u := range util {
158 if u.Time > end {
159 total += prevU.Util * float64(end-prevU.Time)
160 break
161 }
162 total += prevU.Util * float64(u.Time-prevU.Time)
163 prevU = u
164 }
165 return total / float64(end-util[0].Time)
166 }
167 update := func() {
168 for i, u := range util {
169 if u.Time+int64(window) > util[len(util)-1].Time {
170 break
171 }
172 mmu = math.Min(mmu, muInWindow(util[i:], u.Time+int64(window)))
173 }
174 }
175
176
177 update()
178
179
180 rutil := make([]trace.MutatorUtil, len(util))
181 if util[len(util)-1].Util != 0 {
182 panic("irreversible trace")
183 }
184 for i, u := range util {
185 util1 := 0.0
186 if i != 0 {
187 util1 = util[i-1].Util
188 }
189 rutil[len(rutil)-i-1] = trace.MutatorUtil{Time: -u.Time, Util: util1}
190 }
191 util = rutil
192
193 update()
194 return
195 }
196
View as plain text