From 6217745d283de7f1f97b895fb547f7a7f977b153 Mon Sep 17 00:00:00 2001 From: Rudresh Shirwal Date: Thu, 28 May 2026 14:04:31 +0530 Subject: [PATCH] Fix typed proxy access to GenericSkeleton event storage Typed ProxyEvent now accesses event samples through EventMetaInfo raw slot storage instead of interpreting provider storage as EventDataStorage. This keeps typed proxies compatible with GenericSkeleton-created storage while preserving typed skeleton behavior. Proxy event tests and fixtures were updated to emulate production typed storage with a type-erased EventMetaInfo view. Existing proxy event construction tests were aligned with the fixture sample type so EventMetaInfo validation matches the dummy provider storage. --- score/mw/com/impl/bindings/lola/proxy_event.h | 52 +++++++++++++++++-- .../bindings/lola/proxy_event_common_test.cpp | 6 ++- .../impl/bindings/lola/proxy_event_test.cpp | 38 ++++++++++++++ score/mw/com/impl/bindings/lola/skeleton.cpp | 5 +- .../bindings/lola/skeleton_memory_manager.cpp | 26 +++++++++- .../bindings/lola/skeleton_memory_manager.h | 10 +++- .../lola/test/proxy_event_test_resources.cpp | 3 ++ .../lola/test/proxy_event_test_resources.h | 9 +++- ...proxy_event_field_binding_factory_test.cpp | 6 ++- 9 files changed, 145 insertions(+), 10 deletions(-) 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"};