| // Copyright 2019 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <lib/affine/transform.h> |
| #include <lib/zx/clock.h> |
| #include <zircon/syscalls/clock.h> |
| |
| #include <array> |
| |
| #include <zxtest/zxtest.h> |
| |
| namespace { |
| |
| // Unpack a zx_clock_transformation_t from a syscall result and put it into an |
| // affine::Transform so we can call methods on it. |
| inline affine::Transform UnpackTransform(const zx_clock_transformation_t& ct) { |
| return affine::Transform{ |
| ct.reference_offset, ct.synthetic_offset, {ct.rate.synthetic_ticks, ct.rate.reference_ticks}}; |
| } |
| |
| // Unpack a zx_clock_rate_t from a syscall result and put it into an |
| // affine::Ratio so we can call methods on it. |
| inline affine::Ratio UnpackRatio(const zx_clock_rate_t& rate) { |
| return affine::Ratio{rate.synthetic_ticks, rate.reference_ticks}; |
| } |
| |
| inline zx_status_t CreateClock(uint64_t options, zx::time backstop, zx::clock* result) { |
| if (backstop.get() != 0) { |
| zx_clock_create_args_v1 args; |
| args.backstop_time = backstop.get(); |
| return zx::clock::create(options, &args, result); |
| } |
| |
| return zx::clock::create(options, nullptr, result); |
| } |
| |
| TEST(KernelClocksTestCase, Create) { |
| zx::clock clock; |
| |
| // Creating a clock with no special options should succeed. |
| ASSERT_OK(zx::clock::create(0, nullptr, &clock)); |
| |
| // Creating a monotonic clock should succeed. |
| ASSERT_OK(zx::clock::create(ZX_CLOCK_OPT_MONOTONIC, nullptr, &clock)); |
| |
| // Creating a monotonic + continuous clock should succeed. |
| ASSERT_OK(zx::clock::create(ZX_CLOCK_OPT_MONOTONIC | ZX_CLOCK_OPT_CONTINUOUS, nullptr, &clock)); |
| |
| // Creating a continuous clock, but failing to say that it is also |
| // monotonic, should fail. The arguments are invalid. |
| ASSERT_STATUS(zx::clock::create(ZX_CLOCK_OPT_CONTINUOUS, nullptr, &clock), ZX_ERR_INVALID_ARGS); |
| |
| // Attempting to create a clock with any currently undefined option flags |
| // should fail. The arguments are invalid. |
| constexpr uint64_t ILLEGAL_OPTION = static_cast<uint64_t>(1) << (ZX_CLOCK_ARGS_VERSION_SHIFT - 1); |
| static_assert((ZX_CLOCK_OPTS_ALL & ILLEGAL_OPTION) == 0, "Illegal option is actually legal!"); |
| ASSERT_STATUS(zx::clock::create(ILLEGAL_OPTION, nullptr, &clock), ZX_ERR_INVALID_ARGS); |
| |
| // Creating a clock with a defined, legal, backstop should work |
| zx_clock_create_args_v1 args; |
| args.backstop_time = 12345; |
| ASSERT_OK(zx::clock::create(0, &args, &clock)); |
| |
| // Passing a backstop time which is less than 0 is illegal |
| args.backstop_time = -12345; |
| ASSERT_STATUS(zx::clock::create(0, &args, &clock), ZX_ERR_INVALID_ARGS); |
| |
| // Note: the following tests require bypassing ulib/zx. zx::clock_create Will |
| // not allow us to make these mistakes. |
| |
| // Passing an args struct without specifying its version should fail. |
| ASSERT_STATUS(zx_clock_create(0, &args, clock.reset_and_get_address()), ZX_ERR_INVALID_ARGS); |
| |
| // Passing no args struct with a valid version should also fail. |
| ASSERT_STATUS(zx_clock_create(ZX_CLOCK_ARGS_VERSION(1), nullptr, clock.reset_and_get_address()), |
| ZX_ERR_INVALID_ARGS); |
| |
| // Passing an invalid args version should fail. |
| ASSERT_STATUS(zx_clock_create(ZX_CLOCK_ARGS_VERSION(7), &args, clock.reset_and_get_address()), |
| ZX_ERR_INVALID_ARGS); |
| } |
| |
| TEST(KernelClocksTestCase, Read) { |
| zx::clock the_clock; |
| int64_t read_val; |
| |
| constexpr std::array BACKSTOPS = {zx::time(0), zx::time(12345)}; |
| |
| for (const auto backstop : BACKSTOPS) { |
| // Create a basic clock, apply an explicit backstop value if needed. |
| ASSERT_OK(CreateClock(0, backstop, &the_clock)); |
| |
| // Attempt to read the clock. It has never been set before, so it should |
| // report the backstop time. |
| ASSERT_OK(the_clock.read(&read_val)); |
| ASSERT_EQ(backstop.get(), read_val); |
| |
| // Wait a bit and try again. It should still read zero; synthetic clocks do |
| // not start to tick until after their first update. |
| zx::nanosleep(zx::deadline_after(zx::msec(10))); |
| ASSERT_OK(the_clock.read(&read_val)); |
| ASSERT_EQ(backstop.get(), read_val); |
| |
| // Set the clock to a time. Record clock monotonic before and after we |
| // perform the initial update operation. While we cannot control the exact |
| // time at which the set operation will take place, we can bound the range |
| // of possible transformations and establish a min and max. |
| constexpr zx::time INITIAL_VALUE{1'000'000}; |
| zx::clock::update_args args; |
| args.set_value(INITIAL_VALUE); |
| |
| zx::time before_update = zx::clock::get_monotonic(); |
| ASSERT_OK(the_clock.update(args)); |
| zx::time after_update = zx::clock::get_monotonic(); |
| |
| // Now read the clock, and make sure that the value we read makes sense |
| // given our bounds. |
| zx::time before_read = zx::clock::get_monotonic(); |
| ASSERT_OK(the_clock.read(&read_val)); |
| zx::time after_read = zx::clock::get_monotonic(); |
| |
| // Compute the minimum and maximum values we should be able to get from our |
| // read operation based on the various bounds we have established. |
| affine::Transform min_function{after_update.get(), INITIAL_VALUE.get(), {}}; |
| affine::Transform max_function{before_update.get(), INITIAL_VALUE.get(), {}}; |
| int64_t min_expected = min_function.Apply(before_read.get()); |
| int64_t max_expected = max_function.Apply(after_read.get()); |
| |
| ASSERT_GE(read_val, min_expected); |
| ASSERT_LE(read_val, max_expected); |
| |
| // Remove the READ rights from the clock, then verify that we can no longer read the clock. |
| ASSERT_OK(the_clock.replace(ZX_DEFAULT_CLOCK_RIGHTS & ~ZX_RIGHT_READ, &the_clock)); |
| ASSERT_EQ(the_clock.read(&read_val), ZX_ERR_ACCESS_DENIED); |
| } |
| } |
| |
| TEST(KernelClocksTestCase, GetDetails) { |
| // Create clocks with the default backstop of zero, and an explicit backstop |
| constexpr std::array BACKSTOPS = {zx::time(0), zx::time(12345)}; |
| |
| // Create the 3 types of clocks (basic, monotonic, and monotonic + |
| // continuous), then make sure that get_details behaves properly for each |
| // clock type as we update the clocks. |
| std::array OPTIONS{ |
| static_cast<uint64_t>(0), |
| ZX_CLOCK_OPT_MONOTONIC, |
| ZX_CLOCK_OPT_MONOTONIC | ZX_CLOCK_OPT_CONTINUOUS, |
| }; |
| |
| for (const auto backstop : BACKSTOPS) { |
| for (const auto options : OPTIONS) { |
| // Create the clock |
| zx::clock the_clock; |
| ASSERT_OK(CreateClock(options, backstop, &the_clock)); |
| |
| //////////////////////////////////////////////////////////////////////// |
| // |
| // Phase 1: Fetch the initial details |
| // |
| //////////////////////////////////////////////////////////////////////// |
| zx_clock_details_v1_t details; |
| zx::ticks get_details_before = zx::ticks::now(); |
| ASSERT_OK(the_clock.get_details(&details)); |
| zx::ticks get_details_after = zx::ticks::now(); |
| |
| // Check the generation counter. It does not have a defined starting |
| // value, but it should always be even. An odd generation counter |
| // indicates a clock which is in the process of being updates (something |
| // we should never see when querying details) |
| ASSERT_TRUE((details.generation_counter & 0x1) == 0); |
| |
| // The options reported should match those used to create the clock. |
| ASSERT_EQ(options, details.options); |
| |
| // The backstop reported should match that used to create the clock (or be |
| // 0 if the defaults were used). |
| ASSERT_EQ(backstop.get(), details.backstop_time); |
| |
| // The |query_ticks| field of the details should indicate that this |
| // clock was queried sometime between the before and after times latched |
| // above. |
| ASSERT_GE(details.query_ticks, get_details_before.get()); |
| ASSERT_LE(details.query_ticks, get_details_after.get()); |
| |
| // The error bound should default to "unknown" |
| ASSERT_EQ(ZX_CLOCK_UNKNOWN_ERROR, details.error_bound); |
| |
| // None of the dynamic properties of the clock have ever been set. |
| // Their last update times should be 0. |
| ASSERT_EQ(0, details.last_value_update_ticks); |
| ASSERT_EQ(0, details.last_rate_adjust_update_ticks); |
| ASSERT_EQ(0, details.last_error_bounds_update_ticks); |
| |
| // Both initial transformations should indicate that the clock has never |
| // been set. This is done by setting the numerator of the |
| // transformation to 0, effectively stopping the synthetic clock. |
| ASSERT_EQ(0, details.ticks_to_synthetic.rate.synthetic_ticks); |
| ASSERT_EQ(0, details.mono_to_synthetic.rate.synthetic_ticks); |
| |
| // Record the details we just observed so we can observe how they change |
| // as we update. |
| zx_clock_details_v1_t last_details = details; |
| |
| //////////////////////////////////////////////////////////////////////// |
| // |
| // Phase 2: Set the initial value of the clock, then sanity check the |
| // details. |
| // |
| //////////////////////////////////////////////////////////////////////// |
| constexpr zx::time INITIAL_VALUE{1'000'000}; |
| zx::ticks update_before = zx::ticks::now(); |
| ASSERT_OK(the_clock.update(zx::clock::update_args{}.set_value(INITIAL_VALUE))); |
| zx::ticks update_after = zx::ticks::now(); |
| |
| get_details_before = zx::ticks::now(); |
| ASSERT_OK(the_clock.get_details(&details)); |
| get_details_after = zx::ticks::now(); |
| |
| // Sanity check the query time |
| ASSERT_GE(details.query_ticks, get_details_before.get()); |
| ASSERT_LE(details.query_ticks, get_details_after.get()); |
| |
| // The generation counter should have incremented by exactly 2. |
| ASSERT_EQ(last_details.generation_counter + 2, details.generation_counter); |
| |
| // The options should not have changed. |
| ASSERT_EQ(options, details.options); |
| |
| // The error bound should still be "unknown" |
| ASSERT_EQ(ZX_CLOCK_UNKNOWN_ERROR, details.error_bound); |
| |
| // The last value update time should be between the ticks that we |
| // latched above. Since this was the initial clock set operation, the |
| // last rate adjustment time should update as well. Even though we |
| // didn't request it explicitly, the rate did go from stopped to |
| // running. |
| ASSERT_GE(details.last_value_update_ticks, update_before.get()); |
| ASSERT_LE(details.last_value_update_ticks, update_after.get()); |
| ASSERT_EQ(details.last_value_update_ticks, details.last_rate_adjust_update_ticks); |
| ASSERT_EQ(details.last_value_update_ticks, details.last_rate_adjust_update_ticks); |
| ASSERT_EQ(last_details.last_error_bounds_update_ticks, |
| details.last_error_bounds_update_ticks); |
| |
| // The synthetic clock offset for both transformations should be the |
| // initial value we set for the clock. |
| ASSERT_EQ(INITIAL_VALUE.get(), details.ticks_to_synthetic.synthetic_offset); |
| ASSERT_EQ(INITIAL_VALUE.get(), details.mono_to_synthetic.synthetic_offset); |
| |
| // The rate of the mono <-> synthetic transformation should be 1:1. We |
| // have not adjusted its rate yet, and its nominal rate is the same as |
| // clock monotonic. |
| ASSERT_EQ(1, details.mono_to_synthetic.rate.synthetic_ticks); |
| ASSERT_EQ(1, details.mono_to_synthetic.rate.reference_ticks); |
| |
| // The expected ticks reference should be the update time. |
| // |
| // Note: this validation behavior assumes a particular behavior of the |
| // kernel's update implementation. Technically, there are many valid |
| // solutions for computing this equation; the two offsets allow us to |
| // write the equation for a line many different ways. Even so, we |
| // expect the kernel to be using the method we validate here because it |
| // is simple, cheap, and precise. |
| ASSERT_EQ(details.last_value_update_ticks, details.ticks_to_synthetic.reference_offset); |
| |
| // The rate of the ticks <-> synthetic should be equal to the ticks to |
| // clock monotonic ratio. Right now, however, we don't have a good way |
| // to query the VDSO constants in order to find this ratio. Instead, we |
| // take it on faith that this is correct, then use the ratio to compute |
| // and check the mono <-> synthetic reference offset. |
| // |
| // TODO(johngro): consider exposing this ratio from a VDSO based |
| // syscall. |
| int64_t expected_mono_reference; |
| affine::Ratio ticks_to_mono = UnpackRatio(details.ticks_to_synthetic.rate); |
| expected_mono_reference = ticks_to_mono.Scale(details.ticks_to_synthetic.reference_offset); |
| ASSERT_EQ(expected_mono_reference, details.mono_to_synthetic.reference_offset); |
| |
| // Update the last_details and move on to the next phase. |
| last_details = details; |
| |
| //////////////////////////////////////////////////////////////////////// |
| // |
| // Phase 3: Change the rate of the clock, then sanity check the details. |
| // |
| //////////////////////////////////////////////////////////////////////// |
| constexpr int32_t PPM_ADJ = 65; |
| update_before = zx::ticks::now(); |
| ASSERT_OK(the_clock.update(zx::clock::update_args{}.set_rate_adjust(PPM_ADJ))); |
| update_after = zx::ticks::now(); |
| |
| get_details_before = zx::ticks::now(); |
| ASSERT_OK(the_clock.get_details(&details)); |
| get_details_after = zx::ticks::now(); |
| |
| // Sanity check the query time |
| ASSERT_GE(details.query_ticks, get_details_before.get()); |
| ASSERT_LE(details.query_ticks, get_details_after.get()); |
| |
| // The generation counter should have incremented by exactly 2. |
| ASSERT_EQ(last_details.generation_counter + 2, details.generation_counter); |
| |
| // The options should not have changed. |
| ASSERT_EQ(options, details.options); |
| |
| // The error bound should still be "unknown" |
| ASSERT_EQ(ZX_CLOCK_UNKNOWN_ERROR, details.error_bound); |
| |
| // The last value and error bound update times should not have changed. |
| // The last rate adjustment timestamp should be bounded by |
| // update_before/update_after. |
| ASSERT_EQ(last_details.last_value_update_ticks, details.last_value_update_ticks); |
| ASSERT_EQ(last_details.last_error_bounds_update_ticks, |
| details.last_error_bounds_update_ticks); |
| ASSERT_GE(details.last_rate_adjust_update_ticks, update_before.get()); |
| ASSERT_LE(details.last_rate_adjust_update_ticks, update_after.get()); |
| |
| // Validate the various transformation equations. |
| // |
| // Note: this validation behavior assumes a particular behavior of the |
| // kernel. Technically, there are many valid solutions for computing |
| // this equation; the two offsets allow us to write the equation for a |
| // line many different ways. Even so, we expect the kernel to be using |
| // the method we validate here because it is simple, cheap, and precise. |
| // |
| // If the behavior changes, there should be a Very Good Reason, and we |
| // would like this test to break if someone decides to update the |
| // methodology without updating the tests as well. |
| |
| // The expected synthetic clock offset for the transformations should be |
| // the projected value of the last_rate_adjust_ticks time using the previous |
| // transformation. |
| int64_t expected_synth_offset; |
| affine::Transform last_ticks_to_synth = UnpackTransform(last_details.ticks_to_synthetic); |
| expected_synth_offset = last_ticks_to_synth.Apply(details.last_rate_adjust_update_ticks); |
| |
| ASSERT_EQ(expected_synth_offset, details.ticks_to_synthetic.synthetic_offset); |
| ASSERT_EQ(expected_synth_offset, details.mono_to_synthetic.synthetic_offset); |
| |
| // The reference offset for ticks <-> synth should be the update time. |
| // The reference for mono <-> synth should be the ticks reference |
| // converted to mono. |
| expected_mono_reference = ticks_to_mono.Scale(details.ticks_to_synthetic.reference_offset); |
| ASSERT_EQ(expected_mono_reference, details.mono_to_synthetic.reference_offset); |
| ASSERT_EQ(details.last_rate_adjust_update_ticks, details.ticks_to_synthetic.reference_offset); |
| |
| // Check our ratios. We need to be a bit careful here; one cannot |
| // simply compare ratios for equality without reducing them first. |
| // |
| // The mono <-> synth ratio should just be a function of the PPM |
| // adjustment we applied. |
| affine::Ratio expected_mono_ratio{1'000'000 + PPM_ADJ, 1'000'000}; |
| affine::Ratio actual_mono_ratio = UnpackRatio(details.mono_to_synthetic.rate); |
| |
| expected_mono_ratio.Reduce(); |
| actual_mono_ratio.Reduce(); |
| |
| ASSERT_EQ(expected_mono_ratio.numerator(), actual_mono_ratio.numerator()); |
| ASSERT_EQ(expected_mono_ratio.denominator(), actual_mono_ratio.denominator()); |
| |
| // The ticks <-> synth ratio should be the product of ticks to mono and |
| // mono to synth. |
| affine::Ratio expected_ticks_ratio = ticks_to_mono * expected_mono_ratio; |
| affine::Ratio actual_ticks_ratio = UnpackRatio(details.ticks_to_synthetic.rate); |
| |
| expected_ticks_ratio.Reduce(); |
| actual_ticks_ratio.Reduce(); |
| |
| ASSERT_EQ(expected_ticks_ratio.numerator(), actual_ticks_ratio.numerator()); |
| ASSERT_EQ(expected_ticks_ratio.denominator(), actual_ticks_ratio.denominator()); |
| |
| // Update the last_details and move on to the next phase. |
| last_details = details; |
| |
| //////////////////////////////////////////////////////////////////////// |
| // |
| // Phase 4: Update the error bound and verify that it sticks. None |
| // of the other core details should change. |
| // |
| //////////////////////////////////////////////////////////////////////// |
| constexpr uint64_t ERROR_BOUND = 1234567; |
| update_before = zx::ticks::now(); |
| ASSERT_OK(the_clock.update(zx::clock::update_args{}.set_error_bound(ERROR_BOUND))); |
| update_after = zx::ticks::now(); |
| |
| get_details_before = zx::ticks::now(); |
| ASSERT_OK(the_clock.get_details(&details)); |
| get_details_after = zx::ticks::now(); |
| |
| // Sanity check the query time |
| ASSERT_GE(details.query_ticks, get_details_before.get()); |
| ASSERT_LE(details.query_ticks, get_details_after.get()); |
| |
| // The generation counter should have incremented by exactly 2. |
| ASSERT_EQ(last_details.generation_counter + 2, details.generation_counter); |
| |
| // The options should not have changed. |
| ASSERT_EQ(options, details.options); |
| |
| // The error bound should be what we set it to. |
| ASSERT_EQ(ERROR_BOUND, details.error_bound); |
| |
| // The last value and rate adjust update times not have changed. The |
| // last error bound timestamp should be bounded by |
| // update_before/update_after. |
| ASSERT_EQ(last_details.last_value_update_ticks, details.last_value_update_ticks); |
| ASSERT_EQ(last_details.last_rate_adjust_update_ticks, details.last_rate_adjust_update_ticks); |
| ASSERT_GE(details.last_error_bounds_update_ticks, update_before.get()); |
| ASSERT_LE(details.last_error_bounds_update_ticks, update_after.get()); |
| |
| // None of the transformations should have changed. |
| auto CompareTransformation = [](const zx_clock_transformation_t& expected, |
| const zx_clock_transformation_t& actual) { |
| ASSERT_EQ(expected.reference_offset, expected.reference_offset); |
| ASSERT_EQ(expected.synthetic_offset, expected.synthetic_offset); |
| ASSERT_EQ(expected.rate.synthetic_ticks, expected.rate.synthetic_ticks); |
| ASSERT_EQ(expected.rate.reference_ticks, expected.rate.reference_ticks); |
| }; |
| |
| ASSERT_NO_FAILURES( |
| CompareTransformation(last_details.ticks_to_synthetic, details.ticks_to_synthetic)); |
| ASSERT_NO_FAILURES( |
| CompareTransformation(last_details.mono_to_synthetic, details.mono_to_synthetic)); |
| |
| //////////////////////////////////////////////////////////////////////// |
| // |
| // Phase 5: Make sure that attempt to fetch details fail when we mess up |
| // things like the details structure version number, or the V1 structure |
| // size. Note that we need to bypass the zx::clock API for these tests. |
| // As written, the zx::clock API will not allow us to make these mistakes. |
| // |
| //////////////////////////////////////////////////////////////////////// |
| |
| // Test a bad version number |
| ASSERT_STATUS( |
| zx_clock_get_details(the_clock.get_handle(), ZX_CLOCK_ARGS_VERSION(2), &details), |
| ZX_ERR_INVALID_ARGS); |
| |
| // Test a bad pointer. |
| ASSERT_STATUS(zx_clock_get_details(the_clock.get_handle(), ZX_CLOCK_ARGS_VERSION(1), nullptr), |
| ZX_ERR_INVALID_ARGS); |
| |
| // A buffer larger than strictly required should still work. |
| uint8_t big_buffer[sizeof(details) + 8]; |
| ASSERT_OK(zx_clock_get_details(the_clock.get_handle(), ZX_CLOCK_ARGS_VERSION(1), big_buffer)); |
| |
| //////////////////////////////////////////////////////////////////////// |
| // |
| // Phase 6: Finally, reduce the rights on the clock, discarding the READ |
| // right in the process. Make sure that we can no longer get_details. |
| // |
| //////////////////////////////////////////////////////////////////////// |
| ASSERT_OK(the_clock.replace(ZX_DEFAULT_CLOCK_RIGHTS & ~ZX_RIGHT_READ, &the_clock)); |
| ASSERT_EQ(the_clock.get_details(&details), ZX_ERR_ACCESS_DENIED); |
| } |
| } |
| } |
| |
| TEST(KernelClocksTestCase, Update) { |
| zx::clock basic; |
| zx::clock mono; |
| zx::clock mono_cont; |
| |
| // Create three clocks. A basic clock, a monotonic clock, and a monotonic |
| // + continuous clock. |
| ASSERT_OK(zx::clock::create(0, nullptr, &basic)); |
| ASSERT_OK(zx::clock::create(ZX_CLOCK_OPT_MONOTONIC, nullptr, &mono)); |
| ASSERT_OK( |
| zx::clock::create(ZX_CLOCK_OPT_MONOTONIC | ZX_CLOCK_OPT_CONTINUOUS, nullptr, &mono_cont)); |
| |
| // Set each clock to its initial value. All clocks need to allow being |
| // initially set, so this should be just fine. |
| constexpr zx::time INITIAL_VALUE{1'000'000}; |
| zx::clock::update_args args; |
| args.set_value(INITIAL_VALUE); |
| |
| ASSERT_OK(basic.update(args)); |
| ASSERT_OK(mono.update(args)); |
| ASSERT_OK(mono_cont.update(args)); |
| |
| // Attempt to make each clock jump forward. This should succeed for the |
| // basic clock and the monotonic clock, but fail for the continuous clock |
| // with 'invalid args'. Note that this operation is timing sensitive. If |
| // the clocks are permitted to advance to the point that our value is no |
| // longer in the future, then the monotonic set operation will fail as well. |
| // To make sure this does not happen, make the jump be something enormous; |
| // much larger than the maximum conceivable test watchdog timeout. We use a |
| // full day. |
| constexpr zx::duration FWD_JUMP = zx::sec(86400); |
| args.set_value(INITIAL_VALUE + FWD_JUMP); |
| |
| ASSERT_OK(basic.update(args)); |
| ASSERT_OK(mono.update(args)); |
| ASSERT_STATUS(mono_cont.update(args), ZX_ERR_INVALID_ARGS); |
| |
| // Attempt to make each clock jump backwards. This should only succeed the |
| // basic clock. Neither flavor of monotonic should allow this. |
| args.set_value(INITIAL_VALUE - zx::nsec(1)); |
| ASSERT_OK(basic.update(args)); |
| ASSERT_STATUS(mono.update(args), ZX_ERR_INVALID_ARGS); |
| ASSERT_STATUS(mono_cont.update(args), ZX_ERR_INVALID_ARGS); |
| |
| // Test rate adjustments. All clocks should permit rate adjustment, but the |
| // legal rate adjustment is fixed. |
| struct RateTestVector { |
| int32_t adj; |
| zx_status_t expected_result; |
| }; |
| constexpr std::array RATE_TEST_VECTORS = { |
| RateTestVector{0, ZX_OK}, |
| RateTestVector{ZX_CLOCK_UPDATE_MIN_RATE_ADJUST, ZX_OK}, |
| RateTestVector{ZX_CLOCK_UPDATE_MAX_RATE_ADJUST, ZX_OK}, |
| RateTestVector{ZX_CLOCK_UPDATE_MIN_RATE_ADJUST - 1, ZX_ERR_INVALID_ARGS}, |
| RateTestVector{ZX_CLOCK_UPDATE_MAX_RATE_ADJUST + 1, ZX_ERR_INVALID_ARGS}, |
| }; |
| |
| for (const auto& v : RATE_TEST_VECTORS) { |
| args.reset().set_rate_adjust(v.adj); |
| ASSERT_STATUS(basic.update(args), v.expected_result); |
| ASSERT_STATUS(mono.update(args), v.expected_result); |
| ASSERT_STATUS(mono_cont.update(args), v.expected_result); |
| } |
| |
| // Test error bound reporting. Error bounds are just information which is |
| // atomically updated while making adjustments to the clock. The kernel |
| // should permit any value for this. |
| constexpr std::array ERROR_BOUND_VECTORS = { |
| static_cast<uint64_t>(12345), |
| std::numeric_limits<uint64_t>::min(), |
| std::numeric_limits<uint64_t>::max(), |
| ZX_CLOCK_UNKNOWN_ERROR, |
| }; |
| |
| for (const auto& err_bound : ERROR_BOUND_VECTORS) { |
| args.reset().set_error_bound(err_bound); |
| ASSERT_OK(basic.update(args)); |
| ASSERT_OK(mono.update(args)); |
| ASSERT_OK(mono_cont.update(args)); |
| } |
| |
| // Attempt to set an illegal option for update option. This should always |
| // fail, but we have to by pass the zx::clock API in order to test this. |
| // zx::clock will not allow this mistake to be made.. |
| constexpr uint64_t ILLEGAL_OPTION = 0x80000000; |
| static_assert((ZX_CLOCK_UPDATE_OPTIONS_ALL & ILLEGAL_OPTION) == 0, |
| "Illegal opt is actually legal!"); |
| |
| zx_clock_update_args_v1_t update_args; |
| uint64_t options = ZX_CLOCK_ARGS_VERSION(1) | ILLEGAL_OPTION; |
| ASSERT_STATUS(zx_clock_update(basic.get_handle(), options, &update_args), ZX_ERR_INVALID_ARGS); |
| ASSERT_STATUS(zx_clock_update(mono.get_handle(), options, &update_args), ZX_ERR_INVALID_ARGS); |
| ASSERT_STATUS(zx_clock_update(mono_cont.get_handle(), options, &update_args), |
| ZX_ERR_INVALID_ARGS); |
| |
| // Attempt to pass an invalid version number for the update argument struct. |
| options = ZX_CLOCK_ARGS_VERSION(2); |
| ASSERT_STATUS(zx_clock_update(basic.get_handle(), options, &update_args), ZX_ERR_INVALID_ARGS); |
| ASSERT_STATUS(zx_clock_update(mono.get_handle(), options, &update_args), ZX_ERR_INVALID_ARGS); |
| ASSERT_STATUS(zx_clock_update(mono_cont.get_handle(), options, &update_args), |
| ZX_ERR_INVALID_ARGS); |
| |
| // Attempt to pass a bad pointer update argument struct. |
| ASSERT_STATUS(zx_clock_update(basic.get_handle(), options, nullptr), ZX_ERR_INVALID_ARGS); |
| ASSERT_STATUS(zx_clock_update(mono.get_handle(), options, nullptr), ZX_ERR_INVALID_ARGS); |
| ASSERT_STATUS(zx_clock_update(mono_cont.get_handle(), options, nullptr), ZX_ERR_INVALID_ARGS); |
| |
| // Attempt to send an update command with no valid flags at all (eg; a |
| // no-op). This should also fail. |
| args.reset(); |
| ASSERT_STATUS(basic.update(args), ZX_ERR_INVALID_ARGS); |
| ASSERT_STATUS(mono.update(args), ZX_ERR_INVALID_ARGS); |
| ASSERT_STATUS(mono_cont.update(args), ZX_ERR_INVALID_ARGS); |
| |
| // Remove the WRITE rights from the basic clock handle, then verify that we |
| // can no longer update it. |
| args.reset().set_rate_adjust(0); |
| ASSERT_OK(basic.replace(ZX_DEFAULT_CLOCK_RIGHTS & ~ZX_RIGHT_WRITE, &basic)); |
| ASSERT_STATUS(basic.update(args), ZX_ERR_ACCESS_DENIED); |
| } |
| |
| TEST(KernelClocksTestCase, Backstop) { |
| constexpr zx::time INITIAL_VALUE{zx::sec(86400).get()}; |
| constexpr zx::time BACKSTOP{12345}; |
| zx::clock the_clock; |
| zx_time_t read_val; |
| |
| // Create a simple clock with an explicit backstop time |
| ASSERT_OK(CreateClock(0, BACKSTOP, &the_clock)); |
| |
| // Attempt to perform an initial set of the clock which would violate the |
| // backstop. This should fail. |
| zx::clock::update_args args; |
| args.set_value(BACKSTOP - zx::nsec(1)); |
| ASSERT_STATUS(the_clock.update(args), ZX_ERR_INVALID_ARGS); |
| |
| // The clock should still be at its backstop value and not advancing because |
| // the initial set failed. |
| ASSERT_OK(the_clock.read(&read_val)); |
| ASSERT_EQ(BACKSTOP.get(), read_val); |
| |
| zx::nanosleep(zx::deadline_after(zx::msec(10))); |
| ASSERT_OK(the_clock.read(&read_val)); |
| ASSERT_EQ(BACKSTOP.get(), read_val); |
| |
| // Set the clock to a valid initial value. This should succeed. |
| args.set_value(INITIAL_VALUE); |
| ASSERT_OK(the_clock.update(args)); |
| |
| // Attempt to roll the clock back to before the backstop. This should fail, |
| // and the clock should still have a value >= the initial value we set. |
| args.set_value(BACKSTOP - zx::nsec(1)); |
| ASSERT_STATUS(the_clock.update(args), ZX_ERR_INVALID_ARGS); |
| |
| ASSERT_OK(the_clock.read(&read_val)); |
| ASSERT_GE(read_val, INITIAL_VALUE.get()); |
| |
| // Roll the clock all of the way back to the backstop. We did not declare the |
| // clock to be monotonic, so this should be OK. |
| args.set_value(BACKSTOP); |
| ASSERT_OK(the_clock.update(args)); |
| |
| // The clock now must be >= the backstop value we set. Also check to be sure |
| // that it is <= the initial value set. While this is technically a race and |
| // _could_ flake, we chose an initial value of 24hrs and an initial backstop |
| // of 12.345 uSec. If the test framework takes more than 24 hrs - 12 uSec |
| // to get from the update operation to this clock read operation (and has not |
| // timed out in the process), then we have other more serious issues. |
| ASSERT_OK(the_clock.read(&read_val)); |
| ASSERT_GE(read_val, BACKSTOP.get()); |
| ASSERT_LT(read_val, INITIAL_VALUE.get()); |
| } |
| |
| TEST(KernelClocksTestCase, StartedSignal) { |
| // Make a simple clock. |
| zx::clock clock; |
| ASSERT_OK(zx::clock::create(0, nullptr, &clock)); |
| |
| // Wait up to 50msec for the clock to become started. This should time out, |
| // and the pending signals should come back as nothing. |
| zx_signals_t pending = 0; |
| ASSERT_STATUS(clock.wait_one(ZX_CLOCK_STARTED, zx::deadline_after(zx::msec(50)), &pending), |
| ZX_ERR_TIMED_OUT); |
| ASSERT_EQ(pending, 0); |
| |
| // Now go ahead and start the clock running. |
| zx::clock::update_args args; |
| ASSERT_OK(clock.update(args.set_value(zx::time(0)))); |
| |
| // This time, our wait should succeed and the pending signal should indicate |
| // ZX_CLOCK_STARTED |
| ASSERT_OK(clock.wait_one(ZX_CLOCK_STARTED, zx::deadline_after(zx::msec(50)), &pending)); |
| ASSERT_EQ(pending, ZX_CLOCK_STARTED); |
| } |
| |
| TEST(KernelClocksTestCase, DefaultRights) { |
| // Make a simple clock. |
| zx::clock clock; |
| ASSERT_OK(zx::clock::create(0, nullptr, &clock)); |
| |
| // Fetch the basic info from the object. It tell us the object's current rights. |
| zx_info_handle_basic_t basic_info; |
| size_t count; |
| ASSERT_OK(clock.get_info(ZX_INFO_HANDLE_BASIC, &basic_info, sizeof(basic_info), &count, nullptr)); |
| ASSERT_EQ(1, count); |
| |
| // Make sure that the default rights match what we expect. |
| ASSERT_EQ(ZX_DEFAULT_CLOCK_RIGHTS, basic_info.rights); |
| } |
| |
| } // namespace |