github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/time/calibrated_clock_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 "testing" 19 "time" 20 ) 21 22 // newTestCalibratedClock returns a CalibratedClock that collects samples from 23 // the given sample list and cycle counts from the given cycle list. 24 func newTestCalibratedClock(samples []sample, cycles []TSCValue) *CalibratedClock { 25 return &CalibratedClock{ 26 ref: newTestSampler(samples, cycles), 27 } 28 } 29 30 func TestConstantFrequency(t *testing.T) { 31 // Perfectly constant frequency. 32 samples := []sample{ 33 {before: 100000, after: 100000 + defaultOverheadCycles, ref: 100}, 34 {before: 200000, after: 200000 + defaultOverheadCycles, ref: 200}, 35 {before: 300000, after: 300000 + defaultOverheadCycles, ref: 300}, 36 {before: 400000, after: 400000 + defaultOverheadCycles, ref: 400}, 37 {before: 500000, after: 500000 + defaultOverheadCycles, ref: 500}, 38 {before: 600000, after: 600000 + defaultOverheadCycles, ref: 600}, 39 {before: 700000, after: 700000 + defaultOverheadCycles, ref: 700}, 40 } 41 42 c := newTestCalibratedClock(samples, nil) 43 44 // Update from all samples. 45 for range samples { 46 c.Update() 47 } 48 49 c.mu.RLock() 50 if !c.ready { 51 c.mu.RUnlock() 52 t.Fatalf("clock not ready") 53 return // For checklocks consistency. 54 } 55 // A bit after the last sample. 56 now, ok := c.params.ComputeTime(750000) 57 c.mu.RUnlock() 58 if !ok { 59 t.Fatalf("ComputeTime ok got %v want true", ok) 60 } 61 62 t.Logf("now: %v", now) 63 64 // Time should be between the current sample and where we'd expect the 65 // next sample. 66 if now < 700 || now > 800 { 67 t.Errorf("now got %v want > 700 && < 800", now) 68 } 69 } 70 71 func TestErrorCorrection(t *testing.T) { 72 testCases := []struct { 73 name string 74 samples [5]sample 75 projectedTimeStart int64 76 projectedTimeEnd int64 77 }{ 78 // Initial calibration should be ~1MHz for each of these, and 79 // the reference clock changes in samples[2]. 80 { 81 name: "slow-down", 82 samples: [5]sample{ 83 {before: 1000000, after: 1000001, ref: ReferenceNS(1 * ApproxUpdateInterval.Nanoseconds())}, 84 {before: 2000000, after: 2000001, ref: ReferenceNS(2 * ApproxUpdateInterval.Nanoseconds())}, 85 // Reference clock has slowed down, causing 100ms of error. 86 {before: 3010000, after: 3010001, ref: ReferenceNS(3 * ApproxUpdateInterval.Nanoseconds())}, 87 {before: 4020000, after: 4020001, ref: ReferenceNS(4 * ApproxUpdateInterval.Nanoseconds())}, 88 {before: 5030000, after: 5030001, ref: ReferenceNS(5 * ApproxUpdateInterval.Nanoseconds())}, 89 }, 90 projectedTimeStart: 3005 * time.Millisecond.Nanoseconds(), 91 projectedTimeEnd: 3015 * time.Millisecond.Nanoseconds(), 92 }, 93 { 94 name: "speed-up", 95 samples: [5]sample{ 96 {before: 1000000, after: 1000001, ref: ReferenceNS(1 * ApproxUpdateInterval.Nanoseconds())}, 97 {before: 2000000, after: 2000001, ref: ReferenceNS(2 * ApproxUpdateInterval.Nanoseconds())}, 98 // Reference clock has sped up, causing 100ms of error. 99 {before: 2990000, after: 2990001, ref: ReferenceNS(3 * ApproxUpdateInterval.Nanoseconds())}, 100 {before: 3980000, after: 3980001, ref: ReferenceNS(4 * ApproxUpdateInterval.Nanoseconds())}, 101 {before: 4970000, after: 4970001, ref: ReferenceNS(5 * ApproxUpdateInterval.Nanoseconds())}, 102 }, 103 projectedTimeStart: 2985 * time.Millisecond.Nanoseconds(), 104 projectedTimeEnd: 2995 * time.Millisecond.Nanoseconds(), 105 }, 106 } 107 for _, tc := range testCases { 108 t.Run(tc.name, func(t *testing.T) { 109 c := newTestCalibratedClock(tc.samples[:], nil) 110 111 // Initial calibration takes two updates. 112 _, ok := c.Update() 113 if ok { 114 t.Fatalf("Update ready too early") 115 } 116 117 params, ok := c.Update() 118 if !ok { 119 t.Fatalf("Update not ready") 120 } 121 122 // Initial calibration is ~1MHz. 123 hz := params.Frequency 124 if hz < 990000 || hz > 1010000 { 125 t.Fatalf("Frequency got %v want > 990kHz && < 1010kHz", hz) 126 } 127 128 // Project time at the next update. Given the 1MHz 129 // calibration, it is expected to be ~3.1s/2.9s, not 130 // the actual 3s. 131 // 132 // N.B. the next update time is the "after" time above. 133 projected, ok := params.ComputeTime(tc.samples[2].after) 134 if !ok { 135 t.Fatalf("ComputeTime ok got %v want true", ok) 136 } 137 if projected < tc.projectedTimeStart || projected > tc.projectedTimeEnd { 138 t.Fatalf("ComputeTime(%v) got %v want > %v && < %v", tc.samples[2].after, projected, tc.projectedTimeStart, tc.projectedTimeEnd) 139 } 140 141 // Update again to see the changed reference clock. 142 params, ok = c.Update() 143 if !ok { 144 t.Fatalf("Update not ready") 145 } 146 147 // We now know that TSC = tc.samples[2].after -> 3s, 148 // but with the previous params indicated that TSC 149 // tc.samples[2].after -> 3.5s/2.5s. We can't allow the 150 // clock to go backwards, and having the clock jump 151 // forwards is undesirable. There should be a smooth 152 // transition that corrects the clock error over time. 153 // Check that the clock is continuous at TSC = 154 // tc.samples[2].after. 155 newProjected, ok := params.ComputeTime(tc.samples[2].after) 156 if !ok { 157 t.Fatalf("ComputeTime ok got %v want true", ok) 158 } 159 if newProjected != projected { 160 t.Errorf("Discontinuous time; ComputeTime(%v) got %v want %v", tc.samples[2].after, newProjected, projected) 161 } 162 163 // As the reference clock stablizes, ensure that the clock error 164 // decreases. 165 initialErr := c.errorNS 166 t.Logf("initial error: %v ns", initialErr) 167 168 _, ok = c.Update() 169 if !ok { 170 t.Fatalf("Update not ready") 171 } 172 if c.errorNS.Magnitude() > initialErr.Magnitude() { 173 t.Errorf("errorNS increased, got %v want |%v| <= |%v|", c.errorNS, c.errorNS, initialErr) 174 } 175 176 _, ok = c.Update() 177 if !ok { 178 t.Fatalf("Update not ready") 179 } 180 if c.errorNS.Magnitude() > initialErr.Magnitude() { 181 t.Errorf("errorNS increased, got %v want |%v| <= |%v|", c.errorNS, c.errorNS, initialErr) 182 } 183 184 t.Logf("final error: %v ns", c.errorNS) 185 }) 186 } 187 }