From d02b74a5dcfed4bfc8f2f8e545bca4d2afabb296 Mon Sep 17 00:00:00 2001 From: Googler Date: Fri, 27 Apr 2018 10:37:02 -0700 Subject: Check in gVisor. PiperOrigin-RevId: 194583126 Change-Id: Ica1d8821a90f74e7e745962d71801c598c652463 --- pkg/sentry/time/sampler_test.go | 183 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 183 insertions(+) create mode 100644 pkg/sentry/time/sampler_test.go (limited to 'pkg/sentry/time/sampler_test.go') diff --git a/pkg/sentry/time/sampler_test.go b/pkg/sentry/time/sampler_test.go new file mode 100644 index 000000000..caf7e5c53 --- /dev/null +++ b/pkg/sentry/time/sampler_test.go @@ -0,0 +1,183 @@ +// Copyright 2018 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package time + +import ( + "errors" + "testing" +) + +// errNoSamples is returned when testReferenceClocks runs out of samples. +var errNoSamples = errors.New("no samples available") + +// testReferenceClocks returns a preset list of samples and cycle counts. +type testReferenceClocks struct { + samples []sample + cycles []TSCValue +} + +// Sample implements referenceClocks.Sample, returning the next sample in the list. +func (t *testReferenceClocks) Sample(_ ClockID) (sample, error) { + if len(t.samples) == 0 { + return sample{}, errNoSamples + } + + s := t.samples[0] + if len(t.samples) == 1 { + t.samples = nil + } else { + t.samples = t.samples[1:] + } + + return s, nil +} + +// Cycles implements referenceClocks.Cycles, returning the next TSCValue in the list. +func (t *testReferenceClocks) Cycles() TSCValue { + if len(t.cycles) == 0 { + return 0 + } + + c := t.cycles[0] + if len(t.cycles) == 1 { + t.cycles = nil + } else { + t.cycles = t.cycles[1:] + } + + return c +} + +// newTestSampler returns a sampler that collects samples from +// the given sample list and cycle counts from the given cycle list. +func newTestSampler(samples []sample, cycles []TSCValue) *sampler { + return &sampler{ + clocks: &testReferenceClocks{ + samples: samples, + cycles: cycles, + }, + overhead: defaultOverheadCycles, + } +} + +// generateSamples generates n samples with the given overhead. +func generateSamples(n int, overhead TSCValue) []sample { + samples := []sample{{before: 1000000, after: 1000000 + overhead, ref: 100}} + for i := 0; i < n-1; i++ { + prev := samples[len(samples)-1] + samples = append(samples, sample{ + before: prev.before + 1000000, + after: prev.after + 1000000, + ref: prev.ref + 100, + }) + } + return samples +} + +// TestSample ensures that samples can be collected. +func TestSample(t *testing.T) { + testCases := []struct { + name string + samples []sample + err error + }{ + { + name: "basic", + samples: []sample{ + {before: 100000, after: 100000 + defaultOverheadCycles, ref: 100}, + }, + err: nil, + }, + { + // Sample with backwards TSC ignored. + // referenceClock should retry and get errNoSamples. + name: "backwards-tsc-ignored", + samples: []sample{ + {before: 100000, after: 90000, ref: 100}, + }, + err: errNoSamples, + }, + { + // Sample far above overhead skipped. + // referenceClock should retry and get errNoSamples. + name: "reject-overhead", + samples: []sample{ + {before: 100000, after: 100000 + 5*defaultOverheadCycles, ref: 100}, + }, + err: errNoSamples, + }, + { + // Maximum overhead allowed is bounded. + name: "over-max-overhead", + // Generate a bunch of samples. The reference clock + // needs a while to ramp up its expected overhead. + samples: generateSamples(100, 2*maxOverheadCycles), + err: errOverheadTooHigh, + }, + { + // Overhead at maximum overhead is allowed. + name: "max-overhead", + // Generate a bunch of samples. The reference clock + // needs a while to ramp up its expected overhead. + samples: generateSamples(100, maxOverheadCycles), + err: nil, + }, + } + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + s := newTestSampler(tc.samples, nil) + err := s.Sample() + if err != tc.err { + t.Errorf("Sample err got %v want %v", err, tc.err) + } + }) + } +} + +// TestOutliersIgnored tests that referenceClock ignores samples with very high +// overhead. +func TestOutliersIgnored(t *testing.T) { + s := newTestSampler([]sample{ + {before: 100000, after: 100000 + defaultOverheadCycles, ref: 100}, + {before: 200000, after: 200000 + defaultOverheadCycles, ref: 200}, + {before: 300000, after: 300000 + defaultOverheadCycles, ref: 300}, + {before: 400000, after: 400000 + defaultOverheadCycles, ref: 400}, + {before: 500000, after: 500000 + 5*defaultOverheadCycles, ref: 500}, // Ignored + {before: 600000, after: 600000 + defaultOverheadCycles, ref: 600}, + {before: 700000, after: 700000 + defaultOverheadCycles, ref: 700}, + }, nil) + + // Collect 5 samples. + for i := 0; i < 5; i++ { + err := s.Sample() + if err != nil { + t.Fatalf("Unexpected error while sampling: %v", err) + } + } + + oldest, newest, ok := s.Range() + if !ok { + t.Fatalf("Range not ok") + } + + if oldest.ref != 100 { + t.Errorf("oldest.ref got %v want %v", oldest.ref, 100) + } + + // We skipped the high-overhead sample. + if newest.ref != 600 { + t.Errorf("newest.ref got %v want %v", newest.ref, 600) + } +} -- cgit v1.2.3