Files
gvisor/pkg/sentry/time/parameters_test.go
T
Bin Lu 6acd185bd3 avoid runtime fails with missing stack maps in race mode on Arm64
In race mode, when calling the go function in asm code,
there will be an missing stack maps issue.

The root cause is:
The function of 'muldiv64' has a non-empty frame,
so it needs stack maps for locals, for which the macro NO_LOCAL_POINTERS will do.
Also, the macro GO_ARGS can covers arguments.

Signed-off-by: Bin Lu <bin.lu@arm.com>
2020-06-04 21:48:21 -04:00

502 lines
13 KiB
Go

// Copyright 2018 The gVisor Authors.
//
// 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 (
"math"
"testing"
"time"
)
func TestParametersComputeTime(t *testing.T) {
testCases := []struct {
name string
params Parameters
now TSCValue
want int64
}{
{
// Now is the same as the base cycles.
name: "base-cycles",
params: Parameters{
BaseCycles: 10000,
BaseRef: ReferenceNS(5000 * time.Millisecond.Nanoseconds()),
Frequency: 10000,
},
now: 10000,
want: 5000 * time.Millisecond.Nanoseconds(),
},
{
// Now is the behind the base cycles. Time is frozen.
name: "backwards",
params: Parameters{
BaseCycles: 10000,
BaseRef: ReferenceNS(5000 * time.Millisecond.Nanoseconds()),
Frequency: 10000,
},
now: 9000,
want: 5000 * time.Millisecond.Nanoseconds(),
},
{
// Now is ahead of the base cycles.
name: "ahead",
params: Parameters{
BaseCycles: 10000,
BaseRef: ReferenceNS(5000 * time.Millisecond.Nanoseconds()),
Frequency: 10000,
},
now: 15000,
want: 5500 * time.Millisecond.Nanoseconds(),
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
got, ok := tc.params.ComputeTime(tc.now)
if !ok {
t.Errorf("ComputeTime ok got %v want true", got)
}
if got != tc.want {
t.Errorf("ComputeTime got %+v want %+v", got, tc.want)
}
})
}
}
func TestParametersErrorAdjust(t *testing.T) {
testCases := []struct {
name string
oldParams Parameters
now TSCValue
newParams Parameters
want Parameters
errorNS ReferenceNS
wantErr bool
}{
{
// newParams are perfectly continuous with oldParams
// and don't need adjustment.
name: "continuous",
oldParams: Parameters{
BaseCycles: 0,
BaseRef: 0,
Frequency: 10000,
},
now: 50000,
newParams: Parameters{
BaseCycles: 50000,
BaseRef: ReferenceNS(5000 * time.Millisecond.Nanoseconds()),
Frequency: 10000,
},
want: Parameters{
BaseCycles: 50000,
BaseRef: ReferenceNS(5000 * time.Millisecond.Nanoseconds()),
Frequency: 10000,
},
},
{
// Same as "continuous", but with now ahead of
// newParams.BaseCycles. The result is the same as
// there is no error to correct.
name: "continuous-nowdiff",
oldParams: Parameters{
BaseCycles: 0,
BaseRef: 0,
Frequency: 10000,
},
now: 60000,
newParams: Parameters{
BaseCycles: 50000,
BaseRef: ReferenceNS(5000 * time.Millisecond.Nanoseconds()),
Frequency: 10000,
},
want: Parameters{
BaseCycles: 50000,
BaseRef: ReferenceNS(5000 * time.Millisecond.Nanoseconds()),
Frequency: 10000,
},
},
{
// errorAdjust bails out if the TSC goes backwards.
name: "tsc-backwards",
oldParams: Parameters{
BaseCycles: 10000,
BaseRef: ReferenceNS(1000 * time.Millisecond.Nanoseconds()),
Frequency: 10000,
},
now: 9000,
newParams: Parameters{
BaseCycles: 9000,
BaseRef: ReferenceNS(1100 * time.Millisecond.Nanoseconds()),
Frequency: 10000,
},
wantErr: true,
},
{
// errorAdjust bails out if new params are from after now.
name: "params-after-now",
oldParams: Parameters{
BaseCycles: 10000,
BaseRef: ReferenceNS(1000 * time.Millisecond.Nanoseconds()),
Frequency: 10000,
},
now: 11000,
newParams: Parameters{
BaseCycles: 12000,
BaseRef: ReferenceNS(1200 * time.Millisecond.Nanoseconds()),
Frequency: 10000,
},
wantErr: true,
},
{
// Host clock sped up.
name: "speed-up",
oldParams: Parameters{
BaseCycles: 0,
BaseRef: 0,
Frequency: 10000,
},
now: 45000,
// Host frequency changed to 9000 immediately after
// oldParams was returned.
newParams: Parameters{
BaseCycles: 45000,
// From oldParams, we think ref = 4.5s at cycles = 45000.
BaseRef: ReferenceNS(5000 * time.Millisecond.Nanoseconds()),
Frequency: 9000,
},
want: Parameters{
BaseCycles: 45000,
BaseRef: ReferenceNS(4500 * time.Millisecond.Nanoseconds()),
// We must decrease the new frequency by 50% to
// correct 0.5s of error in 1s
// (ApproxUpdateInterval).
Frequency: 4500,
},
errorNS: ReferenceNS(-500 * time.Millisecond.Nanoseconds()),
},
{
// Host clock sped up, with now ahead of newParams.
name: "speed-up-nowdiff",
oldParams: Parameters{
BaseCycles: 0,
BaseRef: 0,
Frequency: 10000,
},
now: 50000,
// Host frequency changed to 9000 immediately after
// oldParams was returned.
newParams: Parameters{
BaseCycles: 45000,
BaseRef: ReferenceNS(5000 * time.Millisecond.Nanoseconds()),
Frequency: 9000,
},
// nextRef = 6000ms
// nextCycles = 9000 * (6000ms - 5000ms) + 45000
// nextCycles = 9000 * (1s) + 45000
// nextCycles = 54000
// f = (54000 - 50000) / 1s = 4000
//
// ref = 5000ms - (50000 - 45000) / 4000
// ref = 3.75s
want: Parameters{
BaseCycles: 45000,
BaseRef: ReferenceNS(3750 * time.Millisecond.Nanoseconds()),
Frequency: 4000,
},
// oldNow = 50000 * 10000 = 5s
// newNow = (50000 - 45000) / 9000 + 5s = 5.555s
errorNS: ReferenceNS((5000*time.Millisecond - 5555555555).Nanoseconds()),
},
{
// Host clock sped up. The new parameters are so far
// ahead that the next update time already passed.
name: "speed-up-uncorrectable-baseref",
oldParams: Parameters{
BaseCycles: 0,
BaseRef: 0,
Frequency: 10000,
},
now: 50000,
// Host frequency changed to 5000 immediately after
// oldParams was returned.
newParams: Parameters{
BaseCycles: 45000,
BaseRef: ReferenceNS(9000 * time.Millisecond.Nanoseconds()),
Frequency: 5000,
},
// The next update should be at 10s, but newParams
// already passed 6s. Thus it is impossible to correct
// the clock by then.
wantErr: true,
},
{
// Host clock sped up. The new parameters are moving so
// fast that the next update should be before now.
name: "speed-up-uncorrectable-frequency",
oldParams: Parameters{
BaseCycles: 0,
BaseRef: 0,
Frequency: 10000,
},
now: 55000,
// Host frequency changed to 7500 immediately after
// oldParams was returned.
newParams: Parameters{
BaseCycles: 45000,
BaseRef: ReferenceNS(6000 * time.Millisecond.Nanoseconds()),
Frequency: 7500,
},
// The next update should be at 6.5s, but newParams are
// so far ahead and fast that they reach 6.5s at cycle
// 48750, which before now! Thus it is impossible to
// correct the clock by then.
wantErr: true,
},
{
// Host clock slowed down.
name: "slow-down",
oldParams: Parameters{
BaseCycles: 0,
BaseRef: 0,
Frequency: 10000,
},
now: 55000,
// Host frequency changed to 11000 immediately after
// oldParams was returned.
newParams: Parameters{
BaseCycles: 55000,
// From oldParams, we think ref = 5.5s at cycles = 55000.
BaseRef: ReferenceNS(5000 * time.Millisecond.Nanoseconds()),
Frequency: 11000,
},
want: Parameters{
BaseCycles: 55000,
BaseRef: ReferenceNS(5500 * time.Millisecond.Nanoseconds()),
// We must increase the new frequency by 50% to
// correct 0.5s of error in 1s
// (ApproxUpdateInterval).
Frequency: 16500,
},
errorNS: ReferenceNS(500 * time.Millisecond.Nanoseconds()),
},
{
// Host clock slowed down, with now ahead of newParams.
name: "slow-down-nowdiff",
oldParams: Parameters{
BaseCycles: 0,
BaseRef: 0,
Frequency: 10000,
},
now: 60000,
// Host frequency changed to 11000 immediately after
// oldParams was returned.
newParams: Parameters{
BaseCycles: 55000,
BaseRef: ReferenceNS(5000 * time.Millisecond.Nanoseconds()),
Frequency: 11000,
},
// nextRef = 7000ms
// nextCycles = 11000 * (7000ms - 5000ms) + 55000
// nextCycles = 11000 * (2000ms) + 55000
// nextCycles = 77000
// f = (77000 - 60000) / 1s = 17000
//
// ref = 6000ms - (60000 - 55000) / 17000
// ref = 5705882353ns
want: Parameters{
BaseCycles: 55000,
BaseRef: ReferenceNS(5705882353),
Frequency: 17000,
},
// oldNow = 60000 * 10000 = 6s
// newNow = (60000 - 55000) / 11000 + 5s = 5.4545s
errorNS: ReferenceNS((6*time.Second - 5454545454).Nanoseconds()),
},
{
// Host time went backwards.
name: "time-backwards",
oldParams: Parameters{
BaseCycles: 50000,
BaseRef: ReferenceNS(5000 * time.Millisecond.Nanoseconds()),
Frequency: 10000,
},
now: 60000,
newParams: Parameters{
BaseCycles: 60000,
// From oldParams, we think ref = 6s at cycles = 60000.
BaseRef: ReferenceNS(4000 * time.Millisecond.Nanoseconds()),
Frequency: 10000,
},
want: Parameters{
BaseCycles: 60000,
BaseRef: ReferenceNS(6000 * time.Millisecond.Nanoseconds()),
// We must increase the frequency by 200% to
// correct 2s of error in 1s
// (ApproxUpdateInterval).
Frequency: 30000,
},
errorNS: ReferenceNS(2000 * time.Millisecond.Nanoseconds()),
},
{
// Host time went backwards, with now ahead of newParams.
name: "time-backwards-nowdiff",
oldParams: Parameters{
BaseCycles: 50000,
BaseRef: ReferenceNS(5000 * time.Millisecond.Nanoseconds()),
Frequency: 10000,
},
now: 65000,
// nextRef = 7500ms
// nextCycles = 10000 * (7500ms - 4000ms) + 60000
// nextCycles = 10000 * (3500ms) + 60000
// nextCycles = 95000
// f = (95000 - 65000) / 1s = 30000
//
// ref = 6500ms - (65000 - 60000) / 30000
// ref = 6333333333ns
newParams: Parameters{
BaseCycles: 60000,
BaseRef: ReferenceNS(4000 * time.Millisecond.Nanoseconds()),
Frequency: 10000,
},
want: Parameters{
BaseCycles: 60000,
BaseRef: ReferenceNS(6333333334),
Frequency: 30000,
},
// oldNow = 65000 * 10000 = 6.5s
// newNow = (65000 - 60000) / 10000 + 4s = 4.5s
errorNS: ReferenceNS(2000 * time.Millisecond.Nanoseconds()),
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
got, errorNS, err := errorAdjust(tc.oldParams, tc.newParams, tc.now)
if err != nil && !tc.wantErr {
t.Errorf("err got %v want nil", err)
} else if err == nil && tc.wantErr {
t.Errorf("err got nil want non-nil")
}
if got != tc.want {
t.Errorf("Parameters got %+v want %+v", got, tc.want)
}
if errorNS != tc.errorNS {
t.Errorf("errorNS got %v want %v", errorNS, tc.errorNS)
}
})
}
}
func testMuldiv(t *testing.T, v uint64) {
for i := uint64(1); i <= 1000000; i++ {
mult := uint64(1000000000)
div := i * mult
res, ok := muldiv64(v, mult, div)
if !ok {
t.Errorf("Result of %v * %v / %v ok got false want true", v, mult, div)
}
if want := v / i; res != want {
t.Errorf("Bad result of %v * %v / %v: got %v, want %v", v, mult, div, res, want)
}
}
}
func TestMulDiv(t *testing.T) {
testMuldiv(t, math.MaxUint64)
for i := int64(-10); i <= 10; i++ {
testMuldiv(t, uint64(i))
}
}
func TestMulDivZero(t *testing.T) {
if r, ok := muldiv64(2, 4, 0); ok {
t.Errorf("muldiv64(2, 4, 0) got %d, ok want !ok", r)
}
if r, ok := muldiv64(0, 0, 0); ok {
t.Errorf("muldiv64(0, 0, 0) got %d, ok want !ok", r)
}
}
func TestMulDivOverflow(t *testing.T) {
testCases := []struct {
name string
val uint64
mult uint64
div uint64
ok bool
ret uint64
}{
{
name: "2^62",
val: 1 << 63,
mult: 4,
div: 8,
ok: true,
ret: 1 << 62,
},
{
name: "2^64-1",
val: 0xffffffffffffffff,
mult: 1,
div: 1,
ok: true,
ret: 0xffffffffffffffff,
},
{
name: "2^64",
val: 1 << 63,
mult: 4,
div: 2,
ok: false,
},
{
name: "2^125",
val: 1 << 63,
mult: 1 << 63,
div: 2,
ok: false,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
r, ok := muldiv64(tc.val, tc.mult, tc.div)
if ok != tc.ok {
t.Errorf("ok got %v want %v", ok, tc.ok)
}
if tc.ok && r != tc.ret {
t.Errorf("ret got %v want %v", r, tc.ret)
}
})
}
}
func BenchmarkMuldiv64(b *testing.B) {
var v uint64 = math.MaxUint64
for i := uint64(1); i <= 1000000; i++ {
mult := uint64(1000000000)
div := i * mult
res, ok := muldiv64(v, mult, div)
if !ok {
b.Errorf("Result of %v * %v / %v ok got false want true", v, mult, div)
}
if want := v / i; res != want {
b.Errorf("Bad result of %v * %v / %v: got %v, want %v", v, mult, div, res, want)
}
}
}