diff --git a/score/mw/com/impl/bindings/lola/proxy_event.h b/score/mw/com/impl/bindings/lola/proxy_event.h index 73c3e4bc2..20b5a5bb2 100644 --- a/score/mw/com/impl/bindings/lola/proxy_event.h +++ b/score/mw/com/impl/bindings/lola/proxy_event.h @@ -14,7 +14,11 @@ #define SCORE_MW_COM_IMPL_BINDINGS_LOLA_PROXY_EVENT_H #include "score/mw/com/impl/bindings/lola/event_data_storage.h" +#include "score/mw/com/impl/bindings/lola/event_meta_info.h" #include "score/mw/com/impl/bindings/lola/proxy_event_common.h" + +#include "score/language/safecpp/safe_math/safe_math.h" +#include "score/memory/shared/pointer_arithmetic_util.h" #include "score/mw/com/impl/proxy_event_binding.h" #include "score/mw/com/impl/sample_reference_tracker.h" #include "score/mw/com/impl/subscription_state.h" @@ -26,6 +30,7 @@ #include #include +#include #include #include #include @@ -64,7 +69,9 @@ class ProxyEvent final : public ProxyEventBinding ProxyEvent(Proxy& parent, const ElementFqId element_fq_id, const std::string_view event_name) : ProxyEventBinding{}, proxy_event_common_{parent, element_fq_id, event_name}, - samples_{parent.GetEventDataStorage(element_fq_id)} + meta_info_{parent.GetEventMetaInfo(element_fq_id)}, + aligned_sample_size_{memory::shared::CalculateAlignedSize(sizeof(SampleType), alignof(SampleType))}, + event_slots_raw_array_{InitialiseEventSlotsRawArray()} { } @@ -130,13 +137,36 @@ class ProxyEvent final : public ProxyEventBinding }; private: + const std::uint8_t* InitialiseEventSlotsRawArray(); + Result GetNewSamplesImpl(Callback&& receiver, TrackerGuardFactory& tracker) noexcept; Result GetNumNewSamplesAvailableImpl() const noexcept; ProxyEventCommon proxy_event_common_; - const EventDataStorage& samples_; + const EventMetaInfo& meta_info_; + const std::size_t aligned_sample_size_; + const std::uint8_t* const event_slots_raw_array_; }; +template +inline const std::uint8_t* ProxyEvent::InitialiseEventSlotsRawArray() +{ + auto& event_data_control_local = proxy_event_common_.GetConsumerEventDataControlLocal(); + + const auto event_slots_raw_array_size = safe_math::Multiply( + aligned_sample_size_, event_data_control_local.GetMaxSampleSlots()); + + const void* const event_slots_raw_array = meta_info_.event_slots_raw_array_.get(event_slots_raw_array_size); + + SCORE_LANGUAGE_FUTURECPP_PRECONDITION_PRD_MESSAGE(nullptr != event_slots_raw_array, "Null event slot array"); + SCORE_LANGUAGE_FUTURECPP_PRECONDITION_PRD_MESSAGE(meta_info_.data_type_info_.size == sizeof(SampleType), + "Event sample size mismatch"); + SCORE_LANGUAGE_FUTURECPP_PRECONDITION_PRD_MESSAGE(meta_info_.data_type_info_.alignment == alignof(SampleType), + "Event sample alignment mismatch"); + + return static_cast(event_slots_raw_array); +} + template inline Result ProxyEvent::GetNumNewSamplesAvailable() const noexcept { @@ -191,10 +221,26 @@ inline Result ProxyEvent::GetNewSamplesImpl(Callback&& const auto slot_indices = proxy_event_common_.GetNewSamplesSlotIndices(max_sample_count); auto& event_data_control_local = proxy_event_common_.GetConsumerEventDataControlLocal(); + SCORE_LANGUAGE_FUTURECPP_PRECONDITION_PRD_MESSAGE(nullptr != event_slots_raw_array_, "Null event slot array"); for (auto slot_index_it = slot_indices.begin; slot_index_it != slot_indices.end; ++slot_index_it) { - const SampleType& sample_data{samples_.at(static_cast(*slot_index_it))}; + // TODO: Replace this temporary raw-slot access when the LoLa binding layer is type-erased. + // This is temporary fix: avoids interpreting GenericSkeleton-created storage as EventDataStorage, + // since the DynamicArray element count may not match the typed proxy sample type. + // NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic) The pointer event_slots_raw_array_ points to + // the first byte of the type-erased event sample storage in shared memory. Samples may originate from either a + // typed SkeletonEvent or a GenericSkeletonEvent, therefore slot lookup must use the stable EventMetaInfo raw + // storage address and SampleType stride instead of interpreting the shared-memory DynamicArray object type. + const auto* const object_start_address = &event_slots_raw_array_[aligned_sample_size_ * (*slot_index_it)]; + // NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic) + + // Suppress "AUTOSAR C++14 M5-2-8" rule finding: "An object with integer type or pointer to void type shall + // not be converted to an object with pointer type.". + // The raw storage address is provided through EventMetaInfo. The regular typed proxy validates the expected + // type at construction time and calculates the slot offset with sizeof(SampleType)/alignof(SampleType). + // coverity[autosar_cpp14_m5_2_8_violation] + const SampleType& sample_data{*reinterpret_cast(object_start_address)}; const EventSlotStatus event_slot_status{event_data_control_local[*slot_index_it]}; const EventSlotStatus::EventTimeStamp sample_timestamp{event_slot_status.GetTimeStamp()}; diff --git a/score/mw/com/impl/bindings/lola/proxy_event_common_test.cpp b/score/mw/com/impl/bindings/lola/proxy_event_common_test.cpp index 239cc08d7..4b4df9d8d 100644 --- a/score/mw/com/impl/bindings/lola/proxy_event_common_test.cpp +++ b/score/mw/com/impl/bindings/lola/proxy_event_common_test.cpp @@ -27,7 +27,11 @@ namespace score::mw::com::impl::lola namespace { -using TestSampleType = std::uint16_t; +// TestSampleType must have the same size and alignment as lola::ProxyMockedMemoryFixture::SampleType. +// The fixture derives EventMetaInfo (element size and alignment) from its EventDataStorage, +// and ProxyEvent validates sizeof(T) and alignof(T) against that EventMetaInfo at construction time. +// Using a type with different size or alignment would trigger a precondition failure. +using TestSampleType = std::uint32_t; using namespace ::score::memory::shared; using ::testing::Return; diff --git a/score/mw/com/impl/bindings/lola/proxy_event_test.cpp b/score/mw/com/impl/bindings/lola/proxy_event_test.cpp index a8cdea8d3..d9cb7a5f9 100644 --- a/score/mw/com/impl/bindings/lola/proxy_event_test.cpp +++ b/score/mw/com/impl/bindings/lola/proxy_event_test.cpp @@ -703,6 +703,31 @@ TYPED_TEST(LolaProxyEventDeathTest, FailOnEventNotFound) } using LoLaTypedProxyEventTestFixture = LolaProxyEventFixture; + +TEST_F(LoLaTypedProxyEventTestFixture, GetNewSamplesReturnsTypedSampleFromProviderStorage) +{ + // Given a typed LoLa ProxyEvent that is subscribed to a provider event containing one sample + const std::size_t max_sample_count_subscription{5U}; + this->GivenAProxyEvent(this->element_fq_id_, this->event_name_) + .ThatIsSubscribedWithMaxSamples(max_sample_count_subscription) + .WithSkeletonEventData({{kDummySampleValue, kDummyInputTimestamp}}); + + // When GetNewSamples is called + const std::size_t max_samples{1U}; + TestSampleType received_sample{0U}; + const auto receiver = [&received_sample](impl::SamplePtr sample, + const tracing::ITracingRuntime::TracePointDataId) { + received_sample = *sample; + }; + + const auto num_callbacks_result = this->GetNewSamples(receiver, max_samples); + + // Then one sample is returned to the typed proxy with the data written by the provider + ASSERT_TRUE(num_callbacks_result.has_value()); + EXPECT_EQ(num_callbacks_result.value(), 1U); + EXPECT_EQ(received_sample, kDummySampleValue); +} + TEST_F(LoLaTypedProxyEventTestFixture, SampleConstness) { RecordProperty("Verifies", "SCR-6340729"); @@ -717,5 +742,18 @@ TEST_F(LoLaTypedProxyEventTestFixture, SampleConstness) static_assert(std::is_const::value, "Proxy should hold const slot data."); } +TEST_F(LoLaTypedProxyEventTestFixture, HoldsEventMetaInfoAsConstReference) +{ + // Given a typed LoLa ProxyEvent + this->GivenAProxyEvent(this->element_fq_id_, this->event_name_); + + // When accessing the proxy event through the test attorney + ProxyEventAttorney proxy_event_attorney{*test_proxy_event_}; + + // Then the proxy stores EventMetaInfo as const data + using MetaInfoMemberType = typename std::remove_reference::type; + static_assert(std::is_const::value, "Proxy should hold const event meta info."); +} + } // namespace } // namespace score::mw::com::impl::lola diff --git a/score/mw/com/impl/bindings/lola/skeleton.cpp b/score/mw/com/impl/bindings/lola/skeleton.cpp index ffec40939..19600a539 100644 --- a/score/mw/com/impl/bindings/lola/skeleton.cpp +++ b/score/mw/com/impl/bindings/lola/skeleton.cpp @@ -601,8 +601,9 @@ auto Skeleton::RegisterGeneric(const ElementFqId element_fq_id, } } - auto& event_data_storage = memory_manager_.RetrieveEventDataFromOpenedSharedMemory(element_fq_id); - return {static_cast(&event_data_storage), event_data_control_qm, event_data_control_asil_b}; + auto* const event_data_storage = + memory_manager_.RetrieveGenericEventDataFromOpenedSharedMemory(element_fq_id, element_properties); + return {event_data_storage, event_data_control_qm, event_data_control_asil_b}; } auto* const type_erased_event_data_storage = memory_manager_.CreateGenericEventDataInCreatedSharedMemory( diff --git a/score/mw/com/impl/bindings/lola/skeleton_memory_manager.cpp b/score/mw/com/impl/bindings/lola/skeleton_memory_manager.cpp index b776e8e55..c14fcc963 100644 --- a/score/mw/com/impl/bindings/lola/skeleton_memory_manager.cpp +++ b/score/mw/com/impl/bindings/lola/skeleton_memory_manager.cpp @@ -22,6 +22,7 @@ #include "score/mw/com/impl/runtime.h" #include "score/mw/com/impl/skeleton_event_binding.h" +#include "score/language/safecpp/safe_math/safe_math.h" #include "score/memory/shared/managed_memory_resource.h" #include "score/memory/shared/new_delete_delegate_resource.h" #include "score/memory/shared/shared_memory_factory.h" @@ -232,7 +233,30 @@ void* SkeletonMemoryManager::CreateGenericEventDataInCreatedSharedMemory( SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD_MESSAGE(inserted_meta_info.second, "Couldn't register/emplace event-meta-info in data-section."); - return data_storage; + return event_data_raw_array; +} + +void* SkeletonMemoryManager::RetrieveGenericEventDataFromOpenedSharedMemory( + const ElementFqId element_fq_id, + const SkeletonEventProperties& element_properties) noexcept +{ + SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD_MESSAGE(storage_ != nullptr, "Service data storage is not available."); + + const auto event_meta_info_it = storage_->events_metainfo_.find(element_fq_id); + SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD_MESSAGE(event_meta_info_it != storage_->events_metainfo_.cend(), + "Could not find element fq id in meta info map"); + + const auto sample_size = event_meta_info_it->second.data_type_info_.size; + const auto sample_alignment = event_meta_info_it->second.data_type_info_.alignment; + const auto aligned_sample_size = + memory::shared::CalculateAlignedSize(sample_size, static_cast(sample_alignment)); + const auto total_event_slots_size = safe_math::Multiply( + aligned_sample_size, element_properties.number_of_slots); + + void* const event_slots_raw_array = event_meta_info_it->second.event_slots_raw_array_.get(total_event_slots_size); + SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD_MESSAGE(event_slots_raw_array != nullptr, + "Could not get generic EventDataStorage raw array"); + return event_slots_raw_array; } auto SkeletonMemoryManager::RetrieveEventControlsFromOpenedSharedMemory(const ElementFqId element_fq_id) diff --git a/score/mw/com/impl/bindings/lola/skeleton_memory_manager.h b/score/mw/com/impl/bindings/lola/skeleton_memory_manager.h index 732cfbfb9..7aaa71c0f 100644 --- a/score/mw/com/impl/bindings/lola/skeleton_memory_manager.h +++ b/score/mw/com/impl/bindings/lola/skeleton_memory_manager.h @@ -106,7 +106,7 @@ class SkeletonMemoryManager final /// \param element_properties Properties of the event. /// \param sample_size The size of a single data sample. /// \param sample_alignment The alignment of the data sample. - /// \return A pair containing the data storage pointer (void*) and the control composite. + /// \return A raw pointer to the first byte of the generic event sample storage. auto CreateGenericEventDataInCreatedSharedMemory(const ElementFqId element_fq_id, const SkeletonEventProperties& element_properties, size_t sample_size, @@ -127,6 +127,14 @@ class SkeletonMemoryManager final template auto RetrieveEventDataFromOpenedSharedMemory(const ElementFqId element_fq_id) -> EventDataStorage&; + /// \brief Retrieves the raw event sample storage pointer for a generic event from opened shared memory. + /// + /// Generic events use EventMetaInfo as the stable type-erased contract. No interpretation to a + /// DynamicArray takes place in this case. + auto RetrieveGenericEventDataFromOpenedSharedMemory(const ElementFqId element_fq_id, + const SkeletonEventProperties& element_properties) noexcept + -> void*; + /// \brief Rolls back any existing operations in the TransactionLog corresponding to a SkeletonEvent /// /// The TransactionLog would only exist if a SkeletonEvent in a crashed process had tracing enabled. If tracing was diff --git a/score/mw/com/impl/bindings/lola/test/proxy_event_test_resources.cpp b/score/mw/com/impl/bindings/lola/test/proxy_event_test_resources.cpp index 2933e79a5..52655440c 100644 --- a/score/mw/com/impl/bindings/lola/test/proxy_event_test_resources.cpp +++ b/score/mw/com/impl/bindings/lola/test/proxy_event_test_resources.cpp @@ -156,6 +156,9 @@ void ProxyMockedMemoryFixture::InitialiseDummySkeletonEvent(const ElementFqId el std::tie(event_control_, event_data_storage_) = fake_data_->AddEvent(element_fq_id, skeleton_event_properties); SCORE_LANGUAGE_FUTURECPP_ASSERT(event_control_ != nullptr); + SCORE_LANGUAGE_FUTURECPP_ASSERT(event_data_storage_ != nullptr); + event_slots_raw_array_ = event_data_storage_->data(); + SCORE_LANGUAGE_FUTURECPP_ASSERT(event_slots_raw_array_ != nullptr); consumer_event_data_control_local_.emplace(event_control_->data_control); provider_event_data_control_local_.emplace(event_control_->data_control); } diff --git a/score/mw/com/impl/bindings/lola/test/proxy_event_test_resources.h b/score/mw/com/impl/bindings/lola/test/proxy_event_test_resources.h index 24e03f01c..c94af064b 100644 --- a/score/mw/com/impl/bindings/lola/test/proxy_event_test_resources.h +++ b/score/mw/com/impl/bindings/lola/test/proxy_event_test_resources.h @@ -13,6 +13,7 @@ #ifndef SCORE_MW_COM_IMPL_BINDINGS_LOLA_TEST_PROXY_EVENT_TEST_RESOURCES_H #define SCORE_MW_COM_IMPL_BINDINGS_LOLA_TEST_PROXY_EVENT_TEST_RESOURCES_H +#include "score/mw/com/impl/bindings/lola/event_data_storage.h" #include "score/mw/com/impl/bindings/lola/event_subscription_control.h" #include "score/mw/com/impl/bindings/lola/generic_proxy_event.h" #include "score/mw/com/impl/bindings/lola/i_runtime.h" @@ -94,9 +95,14 @@ class ProxyEventAttorney ProxyEventAttorney(ProxyEvent& proxy_event) noexcept : proxy_event_{proxy_event} {} + auto& GetMetaInfoMember() + { + return proxy_event_.meta_info_; + } + auto& GetSamplesMember() { - return proxy_event_.samples_; + return proxy_event_.event_slots_raw_array_; } private: @@ -215,6 +221,7 @@ class ProxyMockedMemoryFixture : public ::testing::Test std::optional> provider_event_data_control_local_{}; std::optional> consumer_event_data_control_local_{}; EventDataStorage* event_data_storage_{nullptr}; + void* event_slots_raw_array_{nullptr}; RollbackSynchronization rollback_synchronization_{}; std::shared_ptr mock_service_{std::make_shared()}; diff --git a/score/mw/com/impl/plumbing/proxy_event_field_binding_factory_test.cpp b/score/mw/com/impl/plumbing/proxy_event_field_binding_factory_test.cpp index 0c4d7c7f9..c073c6cc0 100644 --- a/score/mw/com/impl/plumbing/proxy_event_field_binding_factory_test.cpp +++ b/score/mw/com/impl/plumbing/proxy_event_field_binding_factory_test.cpp @@ -37,7 +37,11 @@ namespace score::mw::com::impl using namespace ::testing; -using TestSampleType = std::uint8_t; +// TestSampleType must have the same size and alignment as lola::ProxyMockedMemoryFixture::SampleType. +// The fixture derives EventMetaInfo (element size and alignment) from its EventDataStorage, +// and ProxyEvent validates sizeof(T) and alignof(T) against that EventMetaInfo at construction time. +// Using a type with different size or alignment would trigger a precondition failure. +using TestSampleType = std::uint32_t; constexpr auto kDummyEventName{"Event1"}; constexpr auto kDummyFieldName{"Field1"};