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  }