gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/pkg/sentry/time/parameters_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 "math" 19 "testing" 20 "time" 21 ) 22 23 func TestParametersComputeTime(t *testing.T) { 24 testCases := []struct { 25 name string 26 params Parameters 27 now TSCValue 28 want int64 29 }{ 30 { 31 // Now is the same as the base cycles. 32 name: "base-cycles", 33 params: Parameters{ 34 BaseCycles: 10000, 35 BaseRef: ReferenceNS(5000 * time.Millisecond.Nanoseconds()), 36 Frequency: 10000, 37 }, 38 now: 10000, 39 want: 5000 * time.Millisecond.Nanoseconds(), 40 }, 41 { 42 // Now is the behind the base cycles. Time is frozen. 43 name: "backwards", 44 params: Parameters{ 45 BaseCycles: 10000, 46 BaseRef: ReferenceNS(5000 * time.Millisecond.Nanoseconds()), 47 Frequency: 10000, 48 }, 49 now: 9000, 50 want: 5000 * time.Millisecond.Nanoseconds(), 51 }, 52 { 53 // Now is ahead of the base cycles. 54 name: "ahead", 55 params: Parameters{ 56 BaseCycles: 10000, 57 BaseRef: ReferenceNS(5000 * time.Millisecond.Nanoseconds()), 58 Frequency: 10000, 59 }, 60 now: 15000, 61 want: 5500 * time.Millisecond.Nanoseconds(), 62 }, 63 } 64 for _, tc := range testCases { 65 t.Run(tc.name, func(t *testing.T) { 66 got, ok := tc.params.ComputeTime(tc.now) 67 if !ok { 68 t.Errorf("ComputeTime ok got %v want true", got) 69 } 70 if got != tc.want { 71 t.Errorf("ComputeTime got %+v want %+v", got, tc.want) 72 } 73 }) 74 } 75 } 76 77 func TestParametersErrorAdjust(t *testing.T) { 78 testCases := []struct { 79 name string 80 oldParams Parameters 81 now TSCValue 82 newParams Parameters 83 want Parameters 84 errorNS ReferenceNS 85 wantErr bool 86 }{ 87 { 88 // newParams are perfectly continuous with oldParams 89 // and don't need adjustment. 90 name: "continuous", 91 oldParams: Parameters{ 92 BaseCycles: 0, 93 BaseRef: 0, 94 Frequency: 10000, 95 }, 96 now: 50000, 97 newParams: Parameters{ 98 BaseCycles: 50000, 99 BaseRef: ReferenceNS(5000 * time.Millisecond.Nanoseconds()), 100 Frequency: 10000, 101 }, 102 want: Parameters{ 103 BaseCycles: 50000, 104 BaseRef: ReferenceNS(5000 * time.Millisecond.Nanoseconds()), 105 Frequency: 10000, 106 }, 107 }, 108 { 109 // Same as "continuous", but with now ahead of 110 // newParams.BaseCycles. The result is the same as 111 // there is no error to correct. 112 name: "continuous-nowdiff", 113 oldParams: Parameters{ 114 BaseCycles: 0, 115 BaseRef: 0, 116 Frequency: 10000, 117 }, 118 now: 60000, 119 newParams: Parameters{ 120 BaseCycles: 50000, 121 BaseRef: ReferenceNS(5000 * time.Millisecond.Nanoseconds()), 122 Frequency: 10000, 123 }, 124 want: Parameters{ 125 BaseCycles: 50000, 126 BaseRef: ReferenceNS(5000 * time.Millisecond.Nanoseconds()), 127 Frequency: 10000, 128 }, 129 }, 130 { 131 // errorAdjust bails out if the TSC goes backwards. 132 name: "tsc-backwards", 133 oldParams: Parameters{ 134 BaseCycles: 10000, 135 BaseRef: ReferenceNS(1000 * time.Millisecond.Nanoseconds()), 136 Frequency: 10000, 137 }, 138 now: 9000, 139 newParams: Parameters{ 140 BaseCycles: 9000, 141 BaseRef: ReferenceNS(1100 * time.Millisecond.Nanoseconds()), 142 Frequency: 10000, 143 }, 144 wantErr: true, 145 }, 146 { 147 // errorAdjust bails out if new params are from after now. 148 name: "params-after-now", 149 oldParams: Parameters{ 150 BaseCycles: 10000, 151 BaseRef: ReferenceNS(1000 * time.Millisecond.Nanoseconds()), 152 Frequency: 10000, 153 }, 154 now: 11000, 155 newParams: Parameters{ 156 BaseCycles: 12000, 157 BaseRef: ReferenceNS(1200 * time.Millisecond.Nanoseconds()), 158 Frequency: 10000, 159 }, 160 wantErr: true, 161 }, 162 { 163 // Host clock sped up. 164 name: "speed-up", 165 oldParams: Parameters{ 166 BaseCycles: 0, 167 BaseRef: 0, 168 Frequency: 10000, 169 }, 170 now: 45000, 171 // Host frequency changed to 9000 immediately after 172 // oldParams was returned. 173 newParams: Parameters{ 174 BaseCycles: 45000, 175 // From oldParams, we think ref = 4.5s at cycles = 45000. 176 BaseRef: ReferenceNS(5000 * time.Millisecond.Nanoseconds()), 177 Frequency: 9000, 178 }, 179 want: Parameters{ 180 BaseCycles: 45000, 181 BaseRef: ReferenceNS(4500 * time.Millisecond.Nanoseconds()), 182 // We must decrease the new frequency by 50% to 183 // correct 0.5s of error in 1s 184 // (ApproxUpdateInterval). 185 Frequency: 4500, 186 }, 187 errorNS: ReferenceNS(-500 * time.Millisecond.Nanoseconds()), 188 }, 189 { 190 // Host clock sped up, with now ahead of newParams. 191 name: "speed-up-nowdiff", 192 oldParams: Parameters{ 193 BaseCycles: 0, 194 BaseRef: 0, 195 Frequency: 10000, 196 }, 197 now: 50000, 198 // Host frequency changed to 9000 immediately after 199 // oldParams was returned. 200 newParams: Parameters{ 201 BaseCycles: 45000, 202 BaseRef: ReferenceNS(5000 * time.Millisecond.Nanoseconds()), 203 Frequency: 9000, 204 }, 205 // nextRef = 6000ms 206 // nextCycles = 9000 * (6000ms - 5000ms) + 45000 207 // nextCycles = 9000 * (1s) + 45000 208 // nextCycles = 54000 209 // f = (54000 - 50000) / 1s = 4000 210 // 211 // ref = 5000ms - (50000 - 45000) / 4000 212 // ref = 3.75s 213 want: Parameters{ 214 BaseCycles: 45000, 215 BaseRef: ReferenceNS(3750 * time.Millisecond.Nanoseconds()), 216 Frequency: 4000, 217 }, 218 // oldNow = 50000 * 10000 = 5s 219 // newNow = (50000 - 45000) / 9000 + 5s = 5.555s 220 errorNS: ReferenceNS((5000*time.Millisecond - 5555555555).Nanoseconds()), 221 }, 222 { 223 // Host clock sped up. The new parameters are so far 224 // ahead that the next update time already passed. 225 name: "speed-up-uncorrectable-baseref", 226 oldParams: Parameters{ 227 BaseCycles: 0, 228 BaseRef: 0, 229 Frequency: 10000, 230 }, 231 now: 50000, 232 // Host frequency changed to 5000 immediately after 233 // oldParams was returned. 234 newParams: Parameters{ 235 BaseCycles: 45000, 236 BaseRef: ReferenceNS(9000 * time.Millisecond.Nanoseconds()), 237 Frequency: 5000, 238 }, 239 // The next update should be at 10s, but newParams 240 // already passed 6s. Thus it is impossible to correct 241 // the clock by then. 242 wantErr: true, 243 }, 244 { 245 // Host clock sped up. The new parameters are moving so 246 // fast that the next update should be before now. 247 name: "speed-up-uncorrectable-frequency", 248 oldParams: Parameters{ 249 BaseCycles: 0, 250 BaseRef: 0, 251 Frequency: 10000, 252 }, 253 now: 55000, 254 // Host frequency changed to 7500 immediately after 255 // oldParams was returned. 256 newParams: Parameters{ 257 BaseCycles: 45000, 258 BaseRef: ReferenceNS(6000 * time.Millisecond.Nanoseconds()), 259 Frequency: 7500, 260 }, 261 // The next update should be at 6.5s, but newParams are 262 // so far ahead and fast that they reach 6.5s at cycle 263 // 48750, which before now! Thus it is impossible to 264 // correct the clock by then. 265 wantErr: true, 266 }, 267 { 268 // Host clock slowed down. 269 name: "slow-down", 270 oldParams: Parameters{ 271 BaseCycles: 0, 272 BaseRef: 0, 273 Frequency: 10000, 274 }, 275 now: 55000, 276 // Host frequency changed to 11000 immediately after 277 // oldParams was returned. 278 newParams: Parameters{ 279 BaseCycles: 55000, 280 // From oldParams, we think ref = 5.5s at cycles = 55000. 281 BaseRef: ReferenceNS(5000 * time.Millisecond.Nanoseconds()), 282 Frequency: 11000, 283 }, 284 want: Parameters{ 285 BaseCycles: 55000, 286 BaseRef: ReferenceNS(5500 * time.Millisecond.Nanoseconds()), 287 // We must increase the new frequency by 50% to 288 // correct 0.5s of error in 1s 289 // (ApproxUpdateInterval). 290 Frequency: 16500, 291 }, 292 errorNS: ReferenceNS(500 * time.Millisecond.Nanoseconds()), 293 }, 294 { 295 // Host clock slowed down, with now ahead of newParams. 296 name: "slow-down-nowdiff", 297 oldParams: Parameters{ 298 BaseCycles: 0, 299 BaseRef: 0, 300 Frequency: 10000, 301 }, 302 now: 60000, 303 // Host frequency changed to 11000 immediately after 304 // oldParams was returned. 305 newParams: Parameters{ 306 BaseCycles: 55000, 307 BaseRef: ReferenceNS(5000 * time.Millisecond.Nanoseconds()), 308 Frequency: 11000, 309 }, 310 // nextRef = 7000ms 311 // nextCycles = 11000 * (7000ms - 5000ms) + 55000 312 // nextCycles = 11000 * (2000ms) + 55000 313 // nextCycles = 77000 314 // f = (77000 - 60000) / 1s = 17000 315 // 316 // ref = 6000ms - (60000 - 55000) / 17000 317 // ref = 5705882353ns 318 want: Parameters{ 319 BaseCycles: 55000, 320 BaseRef: ReferenceNS(5705882353), 321 Frequency: 17000, 322 }, 323 // oldNow = 60000 * 10000 = 6s 324 // newNow = (60000 - 55000) / 11000 + 5s = 5.4545s 325 errorNS: ReferenceNS((6*time.Second - 5454545454).Nanoseconds()), 326 }, 327 { 328 // Host time went backwards. 329 name: "time-backwards", 330 oldParams: Parameters{ 331 BaseCycles: 50000, 332 BaseRef: ReferenceNS(5000 * time.Millisecond.Nanoseconds()), 333 Frequency: 10000, 334 }, 335 now: 60000, 336 newParams: Parameters{ 337 BaseCycles: 60000, 338 // From oldParams, we think ref = 6s at cycles = 60000. 339 BaseRef: ReferenceNS(4000 * time.Millisecond.Nanoseconds()), 340 Frequency: 10000, 341 }, 342 want: Parameters{ 343 BaseCycles: 60000, 344 BaseRef: ReferenceNS(6000 * time.Millisecond.Nanoseconds()), 345 // We must increase the frequency by 200% to 346 // correct 2s of error in 1s 347 // (ApproxUpdateInterval). 348 Frequency: 30000, 349 }, 350 errorNS: ReferenceNS(2000 * time.Millisecond.Nanoseconds()), 351 }, 352 { 353 // Host time went backwards, with now ahead of newParams. 354 name: "time-backwards-nowdiff", 355 oldParams: Parameters{ 356 BaseCycles: 50000, 357 BaseRef: ReferenceNS(5000 * time.Millisecond.Nanoseconds()), 358 Frequency: 10000, 359 }, 360 now: 65000, 361 // nextRef = 7500ms 362 // nextCycles = 10000 * (7500ms - 4000ms) + 60000 363 // nextCycles = 10000 * (3500ms) + 60000 364 // nextCycles = 95000 365 // f = (95000 - 65000) / 1s = 30000 366 // 367 // ref = 6500ms - (65000 - 60000) / 30000 368 // ref = 6333333333ns 369 newParams: Parameters{ 370 BaseCycles: 60000, 371 BaseRef: ReferenceNS(4000 * time.Millisecond.Nanoseconds()), 372 Frequency: 10000, 373 }, 374 want: Parameters{ 375 BaseCycles: 60000, 376 BaseRef: ReferenceNS(6333333334), 377 Frequency: 30000, 378 }, 379 // oldNow = 65000 * 10000 = 6.5s 380 // newNow = (65000 - 60000) / 10000 + 4s = 4.5s 381 errorNS: ReferenceNS(2000 * time.Millisecond.Nanoseconds()), 382 }, 383 } 384 for _, tc := range testCases { 385 t.Run(tc.name, func(t *testing.T) { 386 got, errorNS, err := errorAdjust(tc.oldParams, tc.newParams, tc.now) 387 if err != nil && !tc.wantErr { 388 t.Errorf("err got %v want nil", err) 389 } else if err == nil && tc.wantErr { 390 t.Errorf("err got nil want non-nil") 391 } 392 393 if got != tc.want { 394 t.Errorf("Parameters got %+v want %+v", got, tc.want) 395 } 396 if errorNS != tc.errorNS { 397 t.Errorf("errorNS got %v want %v", errorNS, tc.errorNS) 398 } 399 }) 400 } 401 } 402 403 func testMuldiv(t *testing.T, v uint64) { 404 for i := uint64(1); i <= 1000000; i++ { 405 mult := uint64(1000000000) 406 div := i * mult 407 res, ok := muldiv64(v, mult, div) 408 if !ok { 409 t.Errorf("Result of %v * %v / %v ok got false want true", v, mult, div) 410 } 411 if want := v / i; res != want { 412 t.Errorf("Bad result of %v * %v / %v: got %v, want %v", v, mult, div, res, want) 413 } 414 } 415 } 416 417 func TestMulDiv(t *testing.T) { 418 testMuldiv(t, math.MaxUint64) 419 for i := int64(-10); i <= 10; i++ { 420 testMuldiv(t, uint64(i)) 421 } 422 } 423 424 func TestMulDivZero(t *testing.T) { 425 if r, ok := muldiv64(2, 4, 0); ok { 426 t.Errorf("muldiv64(2, 4, 0) got %d, ok want !ok", r) 427 } 428 429 if r, ok := muldiv64(0, 0, 0); ok { 430 t.Errorf("muldiv64(0, 0, 0) got %d, ok want !ok", r) 431 } 432 } 433 434 func TestMulDivOverflow(t *testing.T) { 435 testCases := []struct { 436 name string 437 val uint64 438 mult uint64 439 div uint64 440 ok bool 441 ret uint64 442 }{ 443 { 444 name: "2^62", 445 val: 1 << 63, 446 mult: 4, 447 div: 8, 448 ok: true, 449 ret: 1 << 62, 450 }, 451 { 452 name: "2^64-1", 453 val: 0xffffffffffffffff, 454 mult: 1, 455 div: 1, 456 ok: true, 457 ret: 0xffffffffffffffff, 458 }, 459 { 460 name: "2^64", 461 val: 1 << 63, 462 mult: 4, 463 div: 2, 464 ok: false, 465 }, 466 { 467 name: "2^125", 468 val: 1 << 63, 469 mult: 1 << 63, 470 div: 2, 471 ok: false, 472 }, 473 } 474 475 for _, tc := range testCases { 476 t.Run(tc.name, func(t *testing.T) { 477 r, ok := muldiv64(tc.val, tc.mult, tc.div) 478 if ok != tc.ok { 479 t.Errorf("ok got %v want %v", ok, tc.ok) 480 } 481 if tc.ok && r != tc.ret { 482 t.Errorf("ret got %v want %v", r, tc.ret) 483 } 484 }) 485 } 486 } 487 488 func BenchmarkMuldiv64(b *testing.B) { 489 var v uint64 = math.MaxUint64 490 for i := uint64(1); i <= 1000000; i++ { 491 mult := uint64(1000000000) 492 div := i * mult 493 res, ok := muldiv64(v, mult, div) 494 if !ok { 495 b.Errorf("Result of %v * %v / %v ok got false want true", v, mult, div) 496 } 497 if want := v / i; res != want { 498 b.Errorf("Bad result of %v * %v / %v: got %v, want %v", v, mult, div, res, want) 499 } 500 } 501 }