| // 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 <inttypes.h> |
| #include <lib/affine/transform.h> |
| #include <lib/zx/clock.h> |
| #include <lib/zx/time.h> |
| #include <zircon/syscalls/clock.h> |
| #include <zircon/syscalls/port.h> |
| |
| #include <array> |
| #include <thread> |
| |
| #include <zxtest/zxtest.h> |
| |
| namespace { |
| |
| constexpr uint32_t kInvalidUpdateVersion = 3u; |
| |
| // Helper class which make it a bit easier for us to test both the V1 and the V2 |
| // versions of the update arguments. |
| template <typename UpdateStructType> |
| class UpdateClockArgs { |
| public: |
| static constexpr bool IsV1 = std::is_same_v<UpdateStructType, zx_clock_update_args_v1_t>; |
| static constexpr bool IsV2 = std::is_same_v<UpdateStructType, zx_clock_update_args_v2_t>; |
| |
| constexpr UpdateClockArgs() { |
| static_assert((IsV1 || IsV2) && (IsV1 != IsV2), "Unsupported clock update args version"); |
| } |
| |
| UpdateClockArgs& reset() { |
| options_ = ZX_CLOCK_ARGS_VERSION(IsV1 ? 1 : 2); |
| return *this; |
| } |
| |
| UpdateClockArgs& override_version(uint32_t version) { |
| options_ &= ~ZX_CLOCK_ARGS_VERSION_MASK; |
| options_ |= ZX_CLOCK_ARGS_VERSION(version); |
| return *this; |
| } |
| |
| UpdateClockArgs& set_value(zx::time synthetic_value) { |
| if constexpr (IsV1) { |
| args_.value = synthetic_value.get(); |
| options_ |= ZX_CLOCK_UPDATE_OPTION_VALUE_VALID; |
| } else { |
| static_assert(IsV2, "Unsupported clock update args version"); |
| args_.synthetic_value = synthetic_value.get(); |
| options_ |= ZX_CLOCK_UPDATE_OPTION_SYNTHETIC_VALUE_VALID; |
| } |
| return *this; |
| } |
| |
| UpdateClockArgs& set_reference_value(zx::time reference_value) { |
| static_assert(IsV2, "Set reference value is only supported for V2 update structures"); |
| args_.reference_value = reference_value.get(); |
| options_ |= ZX_CLOCK_UPDATE_OPTION_REFERENCE_VALUE_VALID; |
| return *this; |
| } |
| |
| UpdateClockArgs& set_both_values(zx::time reference_value, zx::time synthetic_value) { |
| static_assert( |
| IsV2, "Set both synthetic and reference values is only supported for V2 update structures"); |
| args_.reference_value = reference_value.get(); |
| args_.synthetic_value = synthetic_value.get(); |
| options_ |= ZX_CLOCK_UPDATE_OPTION_BOTH_VALUES_VALID; |
| return *this; |
| } |
| |
| UpdateClockArgs& set_rate_adjust(int32_t rate) { |
| args_.rate_adjust = rate; |
| options_ |= ZX_CLOCK_UPDATE_OPTION_RATE_ADJUST_VALID; |
| return *this; |
| } |
| |
| UpdateClockArgs& set_error_bound(uint64_t error_bound) { |
| args_.error_bound = error_bound; |
| options_ |= ZX_CLOCK_UPDATE_OPTION_ERROR_BOUND_VALID; |
| return *this; |
| } |
| |
| const UpdateStructType& args() const { return args_; } |
| uint64_t options() const { return options_; } |
| |
| protected: |
| UpdateStructType args_; |
| uint64_t options_ = ZX_CLOCK_ARGS_VERSION(IsV1 ? 1 : 2); |
| }; |
| |
| using UpdateClockArgsV1 = UpdateClockArgs<zx_clock_update_args_v1_t>; |
| using UpdateClockArgsV2 = UpdateClockArgs<zx_clock_update_args_v2_t>; |
| |
| template <typename UpdateArgsType> |
| zx_status_t ApplyUpdate(const zx::clock& clock, const UpdateArgsType& args) { |
| return zx_clock_update(clock.get(), args.options(), &args.args()); |
| } |
| |
| template <> |
| zx_status_t ApplyUpdate<zx::clock::update_args>(const zx::clock& clock, |
| const zx::clock::update_args& args) { |
| return clock.update(args); |
| } |
| |
| // 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::time MapRefToSynth(const zx::clock& clock, zx::time ref_time) { |
| zx_clock_details_v1_t details; |
| zx_status_t status = clock.get_details(&details); |
| EXPECT_OK(status); |
| return (status == ZX_OK) |
| ? zx::time{UnpackTransform(details.mono_to_synthetic).Apply(ref_time.get())} |
| : zx::time{0}; |
| } |
| |
| 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); |
| } |
| |
| template <typename T> |
| class LoopContext { |
| public: |
| LoopContext(const char* tag, const T& ctx) : tag_(tag), ctx_(ctx) {} |
| ~LoopContext() { |
| if (valid_) { |
| std::array<char, 128> ctx_buf = {0}; |
| ContextToString(ctx_, ctx_buf); |
| printf(" Loop context \"%s\" during failure was : %s\n", tag_, ctx_buf.data()); |
| } |
| } |
| |
| void cancel() { valid_ = false; } |
| |
| private: |
| template <size_t N> |
| static void ContextToString(const T& ctx, std::array<char, N>& buf) { |
| if constexpr (std::is_signed_v<T>) { |
| snprintf(buf.data(), N, "%" PRId64, static_cast<int64_t>(ctx)); |
| } else { |
| snprintf(buf.data(), N, "%" PRIu64, static_cast<uint64_t>(ctx)); |
| } |
| } |
| |
| const char* const tag_; |
| const T ctx_; |
| bool valid_{true}; |
| }; |
| |
| 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 1. |
| ASSERT_EQ(last_details.generation_counter + 1, 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 1. |
| ASSERT_EQ(last_details.generation_counter + 1, 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 1. |
| ASSERT_EQ(last_details.generation_counter + 1, 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); |
| } |
| } |
| } |
| |
| template <typename UpdateArgsType> |
| void DoUpdateTest() { |
| constexpr bool IsV1 = std::is_same_v<UpdateArgsType, UpdateClockArgsV1>; |
| 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}; |
| UpdateArgsType args; |
| args.set_value(INITIAL_VALUE); |
| |
| ASSERT_OK(ApplyUpdate(basic, args)); |
| ASSERT_OK(ApplyUpdate(mono, args)); |
| ASSERT_OK(ApplyUpdate(mono_cont, 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. |
| // |
| // Note: Update of a monotonic clock position using a non-V1 update argument |
| // structure should fail unless the target reference time is also specified. |
| // This change of behavior between the V1 and V2 update structures was part of |
| // RFC-0077, and meant to remove the timing sensitivity mentioned above. |
| constexpr zx::duration FWD_JUMP = zx::sec(86400); |
| args.set_value(INITIAL_VALUE + FWD_JUMP); |
| { |
| constexpr zx_status_t expected_mono_status = IsV1 ? ZX_OK : ZX_ERR_INVALID_ARGS; |
| ASSERT_OK(ApplyUpdate(basic, args)); |
| ASSERT_STATUS(ApplyUpdate(mono, args), expected_mono_status); |
| ASSERT_STATUS(ApplyUpdate(mono_cont, 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(ApplyUpdate(basic, args)); |
| ASSERT_STATUS(ApplyUpdate(mono, args), ZX_ERR_INVALID_ARGS); |
| ASSERT_STATUS(ApplyUpdate(mono_cont, 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(ApplyUpdate(basic, args), v.expected_result); |
| ASSERT_STATUS(ApplyUpdate(mono, args), v.expected_result); |
| ASSERT_STATUS(ApplyUpdate(mono_cont, 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(ApplyUpdate(basic, args)); |
| ASSERT_OK(ApplyUpdate(mono, args)); |
| ASSERT_OK(ApplyUpdate(mono_cont, 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!"); |
| |
| if constexpr (!std::is_same_v<UpdateArgsType, zx::clock::update_args>) { |
| ASSERT_STATUS( |
| zx_clock_update(basic.get_handle(), args.options() | ILLEGAL_OPTION, &args.args()), |
| ZX_ERR_INVALID_ARGS); |
| ASSERT_STATUS(zx_clock_update(mono.get_handle(), args.options() | ILLEGAL_OPTION, &args.args()), |
| ZX_ERR_INVALID_ARGS); |
| ASSERT_STATUS( |
| zx_clock_update(mono_cont.get_handle(), args.options() | ILLEGAL_OPTION, &args.args()), |
| ZX_ERR_INVALID_ARGS); |
| |
| // Attempt to pass a bad pointer update argument struct. |
| ASSERT_STATUS(zx_clock_update(basic.get_handle(), args.options(), nullptr), |
| ZX_ERR_INVALID_ARGS); |
| ASSERT_STATUS(zx_clock_update(mono.get_handle(), args.options(), nullptr), ZX_ERR_INVALID_ARGS); |
| ASSERT_STATUS(zx_clock_update(mono_cont.get_handle(), args.options(), nullptr), |
| ZX_ERR_INVALID_ARGS); |
| |
| // Attempt to pass an invalid version number for the update argument struct. |
| args.override_version(kInvalidUpdateVersion); |
| ASSERT_STATUS(ApplyUpdate(basic, args), ZX_ERR_INVALID_ARGS); |
| ASSERT_STATUS(ApplyUpdate(mono, args), ZX_ERR_INVALID_ARGS); |
| ASSERT_STATUS(ApplyUpdate(mono_cont, args), 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(ApplyUpdate(basic, args), ZX_ERR_INVALID_ARGS); |
| ASSERT_STATUS(ApplyUpdate(mono, args), ZX_ERR_INVALID_ARGS); |
| ASSERT_STATUS(ApplyUpdate(mono_cont, args), ZX_ERR_INVALID_ARGS); |
| |
| // Now test the set of operations which can only be performed using V2 of |
| // the update arguments (note that libzx is using the V2 structures, and |
| // should be capable of these operations as well. |
| if constexpr (!IsV1) { |
| const struct { |
| const zx::clock& clock; |
| uint64_t create_flags; |
| } CLOCKS[] = { |
| {basic, 0u}, |
| {mono, ZX_CLOCK_OPT_MONOTONIC}, |
| {mono_cont, ZX_CLOCK_OPT_MONOTONIC | ZX_CLOCK_OPT_CONTINUOUS}, |
| }; |
| |
| // Perform a series of tests which involve explicitly setting the reference |
| // timeline's value as well as the synthetic value. |
| for (const auto& v : CLOCKS) { |
| LoopContext clock_loop_ctx("clock options", v.create_flags); |
| |
| // Reset each of the clock's rate so it is tracking the reference clock. |
| args.reset().set_rate_adjust(0); |
| ASSERT_OK(ApplyUpdate(v.clock, args)); |
| |
| // Attempting to specify a reference value without also specifying either a |
| // synthetic value or a rate is not allowed. |
| args.reset().set_reference_value(zx::clock::get_monotonic()); |
| ASSERT_STATUS(ApplyUpdate(v.clock, args), ZX_ERR_INVALID_ARGS); |
| |
| // Attempt to make the clock jump both forwards and backwards. Forward |
| // jumps should work for the basic clock, and the monotonic clock, but not |
| // for the continuous clock, while backwards jumps should only ever work |
| // for the basic clock. |
| constexpr std::array JUMP_AMOUNTS{FWD_JUMP, FWD_JUMP}; |
| enum class SetValueType { Individual, Both }; |
| constexpr std::array SET_VALUE_OPS{SetValueType::Individual, SetValueType::Both}; |
| |
| for (const auto jump_amt : JUMP_AMOUNTS) { |
| LoopContext jump_amt_ctx("jump amount", jump_amt.get()); |
| for (const auto op : SET_VALUE_OPS) { |
| LoopContext set_op_ctx("set operation", op); |
| zx_status_t expected_status = |
| ((v.create_flags & ZX_CLOCK_OPT_MONOTONIC) && (jump_amt < zx::duration{0})) || |
| (v.create_flags & ZX_CLOCK_OPT_CONTINUOUS) |
| ? ZX_ERR_INVALID_ARGS |
| : ZX_OK; |
| zx::time now_mono = zx::clock::get_monotonic(); |
| zx::time now_synth = MapRefToSynth(v.clock, now_mono) + jump_amt; |
| |
| if (op == SetValueType::Individual) { |
| args.reset().set_reference_value(now_mono).set_value(zx::time{now_synth}); |
| } else { |
| args.reset().set_both_values(now_mono, now_synth); |
| } |
| ASSERT_STATUS(ApplyUpdate(v.clock, args), expected_status); |
| |
| // If we expected this operation to succeed, make sure that the |
| // transformation passes through the point that we explicitly set. |
| if (expected_status == ZX_OK) { |
| zx::time actual = MapRefToSynth(v.clock, now_mono); |
| ASSERT_EQ(now_synth.get(), actual.get()); |
| } |
| |
| set_op_ctx.cancel(); |
| } |
| jump_amt_ctx.cancel(); |
| } |
| |
| // Attempt to change the rate of the clock, effective at a specified |
| // reference time. This operation is not allowed for monotonic (and |
| // by implication, continuous) clocks. See RFC-0077 for details. |
| constexpr std::array RATE_ADJUSTMENTS{50, -50, 0}; |
| for (const auto rate_adj : RATE_ADJUSTMENTS) { |
| LoopContext rate_adj_ctx("rate adjustment (ppm)", rate_adj); |
| zx_status_t expected_status = |
| (v.create_flags & (ZX_CLOCK_OPT_MONOTONIC | ZX_CLOCK_OPT_CONTINUOUS)) |
| ? ZX_ERR_INVALID_ARGS |
| : ZX_OK; |
| zx::time rate_adj_ref{zx::clock::get_monotonic() - zx::usec(50)}; |
| zx::time prev_synth_time{MapRefToSynth(v.clock, rate_adj_ref)}; |
| |
| // Apply the change. |
| args.reset().set_reference_value(rate_adj_ref).set_rate_adjust(rate_adj); |
| ASSERT_STATUS(ApplyUpdate(v.clock, args), expected_status); |
| |
| // If we expected that to work, check to be sure that: |
| // |
| // 1) Our new transformation passes through the previous transformation |
| // at the point (rate_adj_ref, pref_synth_time) |
| // 2) The slope of the new transformation matches what we specified. |
| if (expected_status == ZX_OK) { |
| ASSERT_EQ(prev_synth_time.get(), MapRefToSynth(v.clock, rate_adj_ref).get()); |
| |
| zx_clock_details_v1_t details; |
| ASSERT_OK(v.clock.get_details(&details)); |
| |
| affine::Ratio expected_ratio = affine::Ratio(1'000'000 + rate_adj, 1'000'000); |
| affine::Ratio actual_ratio = UnpackRatio(details.mono_to_synthetic.rate); |
| |
| expected_ratio.Reduce(); |
| actual_ratio.Reduce(); |
| ASSERT_TRUE(expected_ratio.numerator() == actual_ratio.numerator() && |
| expected_ratio.denominator() == actual_ratio.denominator(), |
| "Expected rate %u/%u does not match actual rate %u/%u", |
| expected_ratio.numerator(), expected_ratio.denominator(), |
| actual_ratio.numerator(), actual_ratio.denominator()); |
| } |
| rate_adj_ctx.cancel(); |
| } |
| |
| // Finally, attempt to change both the value and the rate of the clock at |
| // the same time, and at a specified reference time. This should not work |
| // for continuous or monotonic clocks. |
| for (const auto jump_amt : JUMP_AMOUNTS) { |
| LoopContext jump_amt_ctx("jump amount", jump_amt.get()); |
| for (const auto rate_adj : RATE_ADJUSTMENTS) { |
| LoopContext rate_adj_ctx("rate adjustment (ppm)", rate_adj); |
| zx_status_t expected_status = |
| (v.create_flags & (ZX_CLOCK_OPT_MONOTONIC | ZX_CLOCK_OPT_CONTINUOUS)) |
| ? ZX_ERR_INVALID_ARGS |
| : ZX_OK; |
| zx::time ref_time{zx::clock::get_monotonic()}; |
| zx::time synth_time{MapRefToSynth(v.clock, ref_time) + jump_amt}; |
| |
| // Apply the change. |
| args.reset().set_both_values(ref_time, synth_time).set_rate_adjust(rate_adj); |
| ASSERT_STATUS(ApplyUpdate(v.clock, args), expected_status); |
| |
| // If we expected that to work, check to be sure that: |
| // |
| // 1) Our new transformation passes through the point we explicitly specified. |
| // 2) The slope of the new transformation matches what we specified. |
| if (expected_status == ZX_OK) { |
| ASSERT_EQ(synth_time.get(), MapRefToSynth(v.clock, ref_time).get()); |
| |
| zx_clock_details_v1_t details; |
| ASSERT_OK(v.clock.get_details(&details)); |
| |
| affine::Ratio expected_ratio = affine::Ratio(1'000'000 + rate_adj, 1'000'000); |
| affine::Ratio actual_ratio = UnpackRatio(details.mono_to_synthetic.rate); |
| |
| expected_ratio.Reduce(); |
| actual_ratio.Reduce(); |
| ASSERT_TRUE(expected_ratio.numerator() == actual_ratio.numerator() && |
| expected_ratio.denominator() == actual_ratio.denominator(), |
| "Expected rate %u/%u does not match actual rate %u/%u", |
| expected_ratio.numerator(), expected_ratio.denominator(), |
| actual_ratio.numerator(), actual_ratio.denominator()); |
| } |
| rate_adj_ctx.cancel(); |
| } |
| jump_amt_ctx.cancel(); |
| } |
| clock_loop_ctx.cancel(); |
| } |
| } |
| |
| // 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(ApplyUpdate(basic, args), ZX_ERR_ACCESS_DENIED); |
| } |
| |
| TEST(KernelClocksTestCase, UpdateLibZxStruct) { DoUpdateTest<zx::clock::update_args>(); } |
| TEST(KernelClocksTestCase, UpdateV1Struct) { DoUpdateTest<UpdateClockArgsV1>(); } |
| TEST(KernelClocksTestCase, UpdateV2Struct) { DoUpdateTest<UpdateClockArgsV2>(); } |
| |
| template <typename UpdateArgsType> |
| void DoBackstopTest() { |
| 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. |
| UpdateArgsType args; |
| args.set_value(BACKSTOP - zx::nsec(1)); |
| ASSERT_STATUS(ApplyUpdate(the_clock, 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(ApplyUpdate(the_clock, 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(ApplyUpdate(the_clock, 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(ApplyUpdate(the_clock, 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()); |
| |
| // Now try to cheat our way around the backstop time using some techniques |
| // which cannot be done with V1 update structures. |
| constexpr bool IsV1 = std::is_same_v<UpdateArgsType, UpdateClockArgsV1>; |
| if constexpr (!IsV1) { |
| // Attempt to set the clock to the backstop time, but do so using an |
| // explicit reference time which is in the future (which, should put "now" |
| // on the synthetic timeline in the past). Note that there is an |
| // unavoidable race here. If we allow monotonic time to catch up to the |
| // reference time we specify (meaning, it is no longer in the future), the |
| // update operation will become valid. We use a future offset of one full |
| // day in order to avoid flake. |
| zx::time reference{zx::clock::get_monotonic() + zx::sec(86'400)}; |
| args.reset().set_both_values(reference, BACKSTOP); |
| ASSERT_STATUS(ApplyUpdate(the_clock, args), ZX_ERR_INVALID_ARGS); |
| |
| // Now try to cheat by attempting to slow the clock down at a point in the |
| // past. Start by resetting the clock to it's backstop. Then change the |
| // rate of the clock to run as slowly as possible (-1000ppm) relative to the |
| // reference, and at a point so far enough back on the reference timeline |
| // that "now" on the synthetic timeline ends up in the past. |
| // |
| // At -1000ppm, we lose 1 mSec/Sec, meaning that we need go back about 1000 |
| // days (just about three years) on our reference timeline to have built up |
| // a full day's worth of gap between the backstop and now. Note that |
| // someone does not have to go back nearly this far in order to produce a |
| // violation, we do it in order to mitigate the unavoidable race described |
| // above. |
| ASSERT_OK(ApplyUpdate(the_clock, args.reset().set_value(BACKSTOP))); |
| reference = zx::clock::get_monotonic() - zx::sec(86'400 * 1000); |
| args.reset().set_reference_value(reference).set_rate_adjust(-1000); |
| ASSERT_STATUS(ApplyUpdate(the_clock, args), ZX_ERR_INVALID_ARGS); |
| } |
| } |
| |
| TEST(KernelClocksTestCase, BackstopLibZxStruct) { DoBackstopTest<zx::clock::update_args>(); } |
| TEST(KernelClocksTestCase, BackstopV1Struct) { DoBackstopTest<UpdateClockArgsV1>(); } |
| TEST(KernelClocksTestCase, BackstopV2Struct) { DoBackstopTest<UpdateClockArgsV2>(); } |
| |
| TEST(KernelClocksTestCase, StartedSignal) { |
| std::array OPTIONS{ |
| static_cast<uint64_t>(0), |
| ZX_CLOCK_OPT_MONOTONIC, |
| ZX_CLOCK_OPT_MONOTONIC | ZX_CLOCK_OPT_CONTINUOUS, |
| }; |
| std::array VALUES{ |
| zx::time(0), |
| zx::time(1), |
| }; |
| std::array RATES{ |
| std::optional<int32_t>(), |
| std::optional<int32_t>(0), |
| std::optional<int32_t>(1), |
| }; |
| |
| for (auto option : OPTIONS) { |
| for (auto value : VALUES) { |
| for (auto rate : RATES) { |
| // Make a simple clock. |
| zx::clock clock; |
| ASSERT_OK(zx::clock::create(option, 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); |
| |
| // Try to set the STARTED signal explicitly using zx_object_signal. |
| // This should fail as well. The only way to start a clock which is not |
| // currently started is with a zx_clock_update call which sets the |
| // clock's value. |
| ASSERT_EQ(ZX_ERR_INVALID_ARGS, clock.signal(0, ZX_CLOCK_STARTED)); |
| |
| // Now go ahead and start the clock running. |
| zx::clock::update_args args; |
| if (rate) { |
| args.set_rate_adjust(*rate); |
| } |
| ASSERT_OK(clock.update(args.set_value(value))); |
| |
| // This time, our wait should succeed and the pending signal should indicate |
| // ZX_CLOCK_STARTED. No timeout should be needed. This clock should already |
| // be started. |
| ASSERT_OK(clock.wait_one(ZX_CLOCK_STARTED, zx::time(0), &pending)); |
| ASSERT_EQ(pending, ZX_CLOCK_STARTED); |
| |
| // When the clock is started, it should be ticking. |
| zx_clock_details_v1_t details; |
| ASSERT_OK(clock.get_details(&details)); |
| ASSERT_GT(details.mono_to_synthetic.rate.synthetic_ticks, 0); |
| } |
| } |
| } |
| } |
| |
| TEST(KernelClocksUpdatedSignalTest, ManualSetSignalFails) { |
| std::array options{ |
| static_cast<uint64_t>(0), |
| ZX_CLOCK_OPT_MONOTONIC, |
| ZX_CLOCK_OPT_MONOTONIC | ZX_CLOCK_OPT_CONTINUOUS, |
| }; |
| for (auto option : options) { |
| // Make a simple clock. |
| zx::clock clock; |
| ASSERT_OK(zx::clock::create(option, nullptr, &clock)); |
| |
| // Try to set the ZX_CLOCK_UPDATED signal explicitly using zx_object_signal. |
| // This should fail, as the only way to set the ZX_CLOCK_UPDATED signal is |
| // with a zx_clock_update call. |
| ASSERT_EQ(ZX_ERR_INVALID_ARGS, clock.signal(0, ZX_CLOCK_UPDATED)); |
| } |
| } |
| |
| TEST(KernelClocksUpdatedSignalTest, UpdateBeforeAsyncWait) { |
| // Make a simple clock. |
| zx::clock clock; |
| ASSERT_OK(zx::clock::create(ZX_CLOCK_OPT_MONOTONIC, nullptr, &clock)); |
| |
| // Update clock |
| zx::clock::update_args args; |
| args.set_rate_adjust(1); |
| ASSERT_OK(clock.update(args.set_value(zx::time(1)))); |
| |
| // Start waiting for ZX_CLOCK_UPDATED |
| zx::port port; |
| ASSERT_OK(zx::port::create(0u, &port)); |
| ASSERT_OK(clock.wait_async(port, 0, ZX_CLOCK_UPDATED, 0)); |
| |
| // Wait for async_wait |
| zx_status_t return_status; |
| zx_port_packet_t packet = {}; |
| return_status = port.wait(zx::deadline_after(zx::msec(100)), &packet); |
| |
| // async_wait should time out, since update occurred before async_wait posted |
| ASSERT_STATUS(return_status, ZX_ERR_TIMED_OUT); |
| } |
| |
| TEST(KernelClocksUpdatedSignalTest, UpdateAfterAsyncWait) { |
| // Make a simple clock. |
| zx::clock clock; |
| ASSERT_OK(zx::clock::create(ZX_CLOCK_OPT_MONOTONIC, nullptr, &clock)); |
| |
| // Start waiting for ZX_CLOCK_UPDATED |
| zx::port port; |
| ASSERT_OK(zx::port::create(0u, &port)); |
| ASSERT_OK(clock.wait_async(port, 0, ZX_CLOCK_UPDATED, 0)); |
| |
| // Update clock |
| zx::clock::update_args args; |
| args.set_rate_adjust(1); |
| ASSERT_OK(clock.update(args.set_value(zx::time(1)))); |
| |
| // Wait for async_wait |
| zx_status_t return_status; |
| zx_port_packet_t packet = {}; |
| return_status = port.wait(zx::deadline_after(zx::msec(1000)), &packet); |
| // async_wait should receive pulse, as clock has been updated since async_wait posted |
| ASSERT_OK(return_status); |
| ASSERT_NE(packet.signal.observed & ZX_CLOCK_UPDATED, 0); |
| } |
| |
| // Test that the observer is notified each time the clock is updated (and not only on clock start) |
| TEST(KernelClocksUpdatedSignalTest, UpdateAfterStart) { |
| // Make a simple clock. |
| zx::clock clock; |
| ASSERT_OK(zx::clock::create(0, nullptr, &clock)); |
| |
| // Start waiting for ZX_CLOCK_UPDATED and ZX_CLOCK_STARTED |
| zx::port port; |
| ASSERT_OK(zx::port::create(0u, &port)); |
| ASSERT_OK(clock.wait_async(port, 0, ZX_CLOCK_UPDATED | ZX_CLOCK_STARTED, 0)); |
| |
| // Wait for async_wait |
| zx_status_t return_status; |
| zx_port_packet_t packet = {}; |
| std::thread waiter( |
| [&]() { return_status = port.wait(zx::deadline_after(zx::msec(1000)), &packet); }); |
| |
| // Update the clock. The first update also starts the clock |
| zx::nanosleep(zx::deadline_after(zx::msec(5))); |
| zx::clock::update_args args; |
| args.set_rate_adjust(1); |
| ASSERT_OK(clock.update(args.set_value(zx::time(1)))); |
| |
| waiter.join(); |
| |
| // async_wait should first receive notification that the clock was started and updated |
| ASSERT_OK(return_status); |
| ASSERT_NE(packet.signal.observed & ZX_CLOCK_STARTED, 0); |
| ASSERT_NE(packet.signal.observed & ZX_CLOCK_UPDATED, 0); |
| |
| // Restart the wait, this time watching only for updates |
| ASSERT_OK(clock.wait_async(port, 0, ZX_CLOCK_UPDATED, 0)); |
| waiter = std::thread( |
| [&]() { return_status = port.wait(zx::deadline_after(zx::msec(1000)), &packet); }); |
| |
| // Update the clock again |
| zx::nanosleep(zx::deadline_after(zx::msec(5))); |
| args.set_rate_adjust(2); |
| ASSERT_OK(clock.update(args.set_value(zx::time(2)))); |
| |
| waiter.join(); |
| |
| // async_wait should receive pulse, as clock has been updated since async_wait posted, even though |
| // the status of ZX_CLOCK_STARTED didn't change |
| ASSERT_OK(return_status); |
| ASSERT_NE(packet.signal.observed & ZX_CLOCK_UPDATED, 0); |
| } |
| |
| 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); |
| } |
| |
| template <typename UpdateArgsType> |
| void DoAutoStartedTest() { |
| // Perform this test 3 times, one for each of the valid combinations of the |
| // clock behavior flags. IOW - no guarantees, a guarantee of monotonicity, |
| // but not continuity, and a guarantee of both monotonicity and continuity. |
| constexpr bool IsV1 = std::is_same_v<UpdateArgsType, UpdateClockArgsV1>; |
| constexpr std::array BASE_CREATE_OPTIONS = { |
| static_cast<uint64_t>(0), |
| ZX_CLOCK_OPT_MONOTONIC, |
| ZX_CLOCK_OPT_MONOTONIC | ZX_CLOCK_OPT_CONTINUOUS, |
| }; |
| |
| constexpr zx::time the_dawn_of_time_itself{0}; |
| zx::time one_year_from_now = zx::deadline_after(zx::hour(1) * 24 * 365); |
| zx::clock clock; |
| |
| for (const auto base_create_option : BASE_CREATE_OPTIONS) { |
| // Create a simple clock, but specify that it should start ticking |
| // automatically. |
| ASSERT_OK(zx::clock::create(base_create_option | ZX_CLOCK_OPT_AUTO_START, nullptr, &clock)); |
| |
| // Fetch the details for the clock. Our mono <-> synth transformation |
| // should be identity. |
| zx_clock_details_v1_t details; |
| ASSERT_OK(clock.get_details(&details)); |
| ASSERT_EQ(details.mono_to_synthetic.reference_offset, |
| details.mono_to_synthetic.synthetic_offset); |
| ASSERT_EQ(details.mono_to_synthetic.rate.reference_ticks, |
| details.mono_to_synthetic.rate.synthetic_ticks); |
| ASSERT_NE(0, details.mono_to_synthetic.rate.reference_ticks); |
| |
| // We omitted the create args structure, so the backstop time should be 0. |
| ASSERT_EQ(0, details.backstop_time); |
| |
| // Make sure that the "started" signal has already been set for this clock |
| // (since we auto-started it) |
| zx_signals_t pending = 0; |
| ASSERT_OK(clock.wait_one(ZX_CLOCK_STARTED, zx::time(0), &pending)); |
| ASSERT_EQ(pending, ZX_CLOCK_STARTED); |
| |
| // Spot check the ticks <-> synth transformation by making sure that a |
| // simple read (which uses the ticks <-> synth transform under the hood) is |
| // properly bracketed by before and after observations of clock monotonic. |
| zx::time before = zx::clock::get_monotonic(); |
| zx::time now; |
| ASSERT_OK(clock.read(now.get_address())); |
| zx::time after = zx::clock::get_monotonic(); |
| ASSERT_LE(before, now); |
| ASSERT_GE(after, now); |
| |
| // Check the various set options. We should always be able set the rate of |
| // the clock, as well as the error bound. Setting the value of the clock, |
| // however, is a limited operation. With no restrictions, we should be able |
| // to set the clock to anything, all of the way back to the backstop. For a |
| // monotonic clock, we should not be able to go backwards, but forwards |
| // should be fine. For a continuous clock, neither backwards nor forwards |
| // should be OK. |
| UpdateArgsType args; |
| switch (base_create_option) { |
| case 0: |
| ASSERT_OK(ApplyUpdate(clock, args.reset().set_value(the_dawn_of_time_itself))); |
| ASSERT_OK(ApplyUpdate(clock, args.reset().set_value(one_year_from_now))); |
| break; |
| |
| case ZX_CLOCK_OPT_MONOTONIC: |
| case ZX_CLOCK_OPT_MONOTONIC | ZX_CLOCK_OPT_CONTINUOUS: |
| // Monotonic clocks updated using non-V1 update structures require that |
| // the reference time be explicitly provided. |
| if constexpr (IsV1) { |
| args.reset().set_value(the_dawn_of_time_itself); |
| } else { |
| args.reset().set_both_values(zx::clock::get_monotonic(), the_dawn_of_time_itself); |
| } |
| |
| // Rollback should fail for both continuous and monotonic clocks. |
| ASSERT_STATUS(ZX_ERR_INVALID_ARGS, |
| ApplyUpdate(clock, args.reset().set_value(the_dawn_of_time_itself))); |
| |
| // Jumping forward should succeed for monotonic clocks, but fail for |
| // continuous clocks. |
| zx_status_t expected_status; |
| expected_status = |
| (base_create_option & ZX_CLOCK_OPT_CONTINUOUS) ? ZX_ERR_INVALID_ARGS : ZX_OK; |
| if constexpr (IsV1) { |
| args.reset().set_value(one_year_from_now); |
| } else { |
| args.reset().set_both_values(zx::clock::get_monotonic(), one_year_from_now); |
| } |
| ASSERT_STATUS(expected_status, ApplyUpdate(clock, args)); |
| break; |
| |
| default: |
| // this should never happen unless someone changes the list at the start |
| ZX_ASSERT(false); |
| break; |
| } |
| |
| // Now check to be sure we can adjust the rate and the error bound. |
| // Provided that we have write access to the clock, this should always be |
| // OK. |
| ASSERT_OK(ApplyUpdate(clock, args.reset().set_rate_adjust(35))); |
| ASSERT_OK(ApplyUpdate(clock, args.reset().set_error_bound(100000))); |
| } |
| |
| // Finally, attempt to create an auto-started clock, but specify a backstop |
| // time which is ahead of the current clock monotonic. This should fail since |
| // we are asking for a clock which is supposed to start as a clone of clock |
| // monotonic, but whose current time would violate the backstop time. |
| // |
| // Note: since monotonic is ticking forward while we are attempting to pick a |
| // backstop time ahead of it, this is technically a race in the test. Pick a |
| // backstop time which is ~1 year ahead of now. The assumption here is that |
| // for the race to be lost, this test would need to go out to lunch for about |
| // a year, which should trip the test framework's stuck-test timeout well |
| // before the race is lost. |
| zx_clock_create_args_v1 create_args = {.backstop_time = |
| zx::deadline_after(zx::sec(86400ull * 365)).get()}; |
| ASSERT_STATUS(ZX_ERR_INVALID_ARGS, |
| zx::clock::create(ZX_CLOCK_OPT_AUTO_START, &create_args, &clock)); |
| } |
| |
| TEST(KernelClocksTestCase, AutoStartedLibZxStruct) { DoAutoStartedTest<zx::clock::update_args>(); } |
| TEST(KernelClocksTestCase, AutoStartedV1Struct) { DoAutoStartedTest<UpdateClockArgsV1>(); } |
| TEST(KernelClocksTestCase, AutoStartedV2Struct) { DoAutoStartedTest<UpdateClockArgsV2>(); } |
| |
| TEST(KernelClocksTestCase, TrivialRateUpdates) { |
| // Perform this test a number of times for different combinations of clock |
| // creation arguments. |
| constexpr std::array BASE_CREATE_OPTIONS = { |
| static_cast<uint64_t>(0), |
| ZX_CLOCK_OPT_MONOTONIC, |
| ZX_CLOCK_OPT_MONOTONIC | ZX_CLOCK_OPT_CONTINUOUS, |
| ZX_CLOCK_OPT_AUTO_START, |
| ZX_CLOCK_OPT_AUTO_START | ZX_CLOCK_OPT_MONOTONIC, |
| ZX_CLOCK_OPT_AUTO_START | ZX_CLOCK_OPT_MONOTONIC | ZX_CLOCK_OPT_CONTINUOUS, |
| }; |
| |
| for (const auto base_create_option : BASE_CREATE_OPTIONS) { |
| zx::clock clock; |
| |
| // Create a simple clock, and if it was not configure to auto start, start |
| // it now at an arbitrary time. |
| ASSERT_OK(zx::clock::create(base_create_option, nullptr, &clock)); |
| if (!(base_create_option & ZX_CLOCK_OPT_AUTO_START)) { |
| ASSERT_OK(clock.update(zx::clock::update_args().set_value(zx::time(12345678)))); |
| } |
| |
| // Test trivial rate updates over a range of different adjustments. |
| constexpr std::array TEST_RATES = {0, 1, -1, 20, -20}; |
| for (int32_t rate_adj : TEST_RATES) { |
| zx_clock_details_v1_t last_details, curr_details; |
| zx_ticks_t before_update_ticks, after_update_ticks; |
| |
| zx::clock::update_args args; |
| args.set_rate_adjust(rate_adj); |
| |
| // Set the rate to the rate we are going to test. We can skip this for |
| // the initial test rate adjustment of 0 ppm since newly created clocks |
| // should always start with an initial rate adjustment of 0. |
| if (rate_adj != 0) { |
| ASSERT_OK(clock.update(args)); |
| } |
| |
| // Snapshot the clock's current details before our trivial update. |
| ASSERT_OK(clock.get_details(&last_details)); |
| |
| // Perform the update, paying attention to the tick time before and after |
| // the update request. |
| before_update_ticks = zx_ticks_get(); |
| ASSERT_OK(clock.update(args)); |
| after_update_ticks = zx_ticks_get(); |
| |
| // Get the details after the update and compare them to before. We expect |
| // to see that only two things have changed: |
| // |
| // 1) The last_rate_adjustment ticks |
| // 2) The generation counter |
| ASSERT_OK(clock.get_details(&curr_details)); |
| |
| // These should all be unchanged |
| ASSERT_EQ(last_details.options, curr_details.options); |
| ASSERT_EQ(last_details.backstop_time, curr_details.backstop_time); |
| ASSERT_EQ(last_details.ticks_to_synthetic.reference_offset, |
| curr_details.ticks_to_synthetic.reference_offset); |
| ASSERT_EQ(last_details.ticks_to_synthetic.synthetic_offset, |
| curr_details.ticks_to_synthetic.synthetic_offset); |
| ASSERT_EQ(last_details.ticks_to_synthetic.rate.reference_ticks, |
| curr_details.ticks_to_synthetic.rate.reference_ticks); |
| ASSERT_EQ(last_details.ticks_to_synthetic.rate.synthetic_ticks, |
| curr_details.ticks_to_synthetic.rate.synthetic_ticks); |
| ASSERT_EQ(last_details.mono_to_synthetic.reference_offset, |
| curr_details.mono_to_synthetic.reference_offset); |
| ASSERT_EQ(last_details.mono_to_synthetic.synthetic_offset, |
| curr_details.mono_to_synthetic.synthetic_offset); |
| ASSERT_EQ(last_details.mono_to_synthetic.rate.reference_ticks, |
| curr_details.mono_to_synthetic.rate.reference_ticks); |
| ASSERT_EQ(last_details.mono_to_synthetic.rate.synthetic_ticks, |
| curr_details.mono_to_synthetic.rate.synthetic_ticks); |
| ASSERT_EQ(last_details.error_bound, curr_details.error_bound); |
| ASSERT_EQ(last_details.last_value_update_ticks, curr_details.last_value_update_ticks); |
| ASSERT_EQ(last_details.last_error_bounds_update_ticks, |
| curr_details.last_error_bounds_update_ticks); |
| |
| // The last rate adjustment time should indicate a time between before/after_update_ticks |
| ASSERT_LE(before_update_ticks, curr_details.last_rate_adjust_update_ticks); |
| ASSERT_GE(after_update_ticks, curr_details.last_rate_adjust_update_ticks); |
| |
| // The generation counter should have incremented by exactly 1. |
| ASSERT_EQ(last_details.generation_counter + 1, curr_details.generation_counter); |
| } |
| } |
| } |
| |
| } // namespace |