github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/time/sampler_test.go (about) 1 // Copyright 2018 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package time 16 17 import ( 18 "errors" 19 "testing" 20 ) 21 22 // errNoSamples is returned when testReferenceClocks runs out of samples. 23 var errNoSamples = errors.New("no samples available") 24 25 // testReferenceClocks returns a preset list of samples and cycle counts. 26 type testReferenceClocks struct { 27 samples []sample 28 cycles []TSCValue 29 } 30 31 // Sample implements referenceClocks.Sample, returning the next sample in the list. 32 func (t *testReferenceClocks) Sample(_ ClockID) (sample, error) { 33 if len(t.samples) == 0 { 34 return sample{}, errNoSamples 35 } 36 37 s := t.samples[0] 38 if len(t.samples) == 1 { 39 t.samples = nil 40 } else { 41 t.samples = t.samples[1:] 42 } 43 44 return s, nil 45 } 46 47 // Cycles implements referenceClocks.Cycles, returning the next TSCValue in the list. 48 func (t *testReferenceClocks) Cycles() TSCValue { 49 if len(t.cycles) == 0 { 50 return 0 51 } 52 53 c := t.cycles[0] 54 if len(t.cycles) == 1 { 55 t.cycles = nil 56 } else { 57 t.cycles = t.cycles[1:] 58 } 59 60 return c 61 } 62 63 // newTestSampler returns a sampler that collects samples from 64 // the given sample list and cycle counts from the given cycle list. 65 func newTestSampler(samples []sample, cycles []TSCValue) *sampler { 66 return &sampler{ 67 clocks: &testReferenceClocks{ 68 samples: samples, 69 cycles: cycles, 70 }, 71 overhead: defaultOverheadCycles, 72 } 73 } 74 75 // generateSamples generates n samples with the given overhead. 76 func generateSamples(n int, overhead TSCValue) []sample { 77 samples := []sample{{before: 1000000, after: 1000000 + overhead, ref: 100}} 78 for i := 0; i < n-1; i++ { 79 prev := samples[len(samples)-1] 80 samples = append(samples, sample{ 81 before: prev.before + 1000000, 82 after: prev.after + 1000000, 83 ref: prev.ref + 100, 84 }) 85 } 86 return samples 87 } 88 89 // TestSample ensures that samples can be collected. 90 func TestSample(t *testing.T) { 91 testCases := []struct { 92 name string 93 samples []sample 94 err error 95 }{ 96 { 97 name: "basic", 98 samples: []sample{ 99 {before: 100000, after: 100000 + defaultOverheadCycles, ref: 100}, 100 }, 101 err: nil, 102 }, 103 { 104 // Sample with backwards TSC ignored. 105 // referenceClock should retry and get errNoSamples. 106 name: "backwards-tsc-ignored", 107 samples: []sample{ 108 {before: 100000, after: 90000, ref: 100}, 109 }, 110 err: errNoSamples, 111 }, 112 { 113 // Sample far above overhead skipped. 114 // referenceClock should retry and get errNoSamples. 115 name: "reject-overhead", 116 samples: []sample{ 117 {before: 100000, after: 100000 + 5*defaultOverheadCycles, ref: 100}, 118 }, 119 err: errNoSamples, 120 }, 121 { 122 // Maximum overhead allowed is bounded. 123 name: "over-max-overhead", 124 // Generate a bunch of samples. The reference clock 125 // needs a while to ramp up its expected overhead. 126 samples: generateSamples(100, 2*maxOverheadCycles), 127 err: errOverheadTooHigh, 128 }, 129 { 130 // Overhead at maximum overhead is allowed. 131 name: "max-overhead", 132 // Generate a bunch of samples. The reference clock 133 // needs a while to ramp up its expected overhead. 134 samples: generateSamples(100, maxOverheadCycles), 135 err: nil, 136 }, 137 } 138 for _, tc := range testCases { 139 t.Run(tc.name, func(t *testing.T) { 140 s := newTestSampler(tc.samples, nil) 141 err := s.Sample() 142 if err != tc.err { 143 t.Errorf("Sample err got %v want %v", err, tc.err) 144 } 145 }) 146 } 147 } 148 149 // TestOutliersIgnored tests that referenceClock ignores samples with very high 150 // overhead. 151 func TestOutliersIgnored(t *testing.T) { 152 s := newTestSampler([]sample{ 153 {before: 100000, after: 100000 + defaultOverheadCycles, ref: 100}, 154 {before: 200000, after: 200000 + defaultOverheadCycles, ref: 200}, 155 {before: 300000, after: 300000 + defaultOverheadCycles, ref: 300}, 156 {before: 400000, after: 400000 + defaultOverheadCycles, ref: 400}, 157 {before: 500000, after: 500000 + 5*defaultOverheadCycles, ref: 500}, // Ignored 158 {before: 600000, after: 600000 + defaultOverheadCycles, ref: 600}, 159 {before: 700000, after: 700000 + defaultOverheadCycles, ref: 700}, 160 }, nil) 161 162 // Collect 5 samples. 163 for i := 0; i < 5; i++ { 164 err := s.Sample() 165 if err != nil { 166 t.Fatalf("Unexpected error while sampling: %v", err) 167 } 168 } 169 170 oldest, newest, ok := s.Range() 171 if !ok { 172 t.Fatalf("Range not ok") 173 } 174 175 if oldest.ref != 100 { 176 t.Errorf("oldest.ref got %v want %v", oldest.ref, 100) 177 } 178 179 // We skipped the high-overhead sample. 180 if newest.ref != 600 { 181 t.Errorf("newest.ref got %v want %v", newest.ref, 600) 182 } 183 }