From b4e4fcf786e5d41e0ae81344a31596341a29e363 Mon Sep 17 00:00:00 2001 From: Mateusz Hoppe Date: Tue, 16 Dec 2025 08:02:20 +0000 Subject: [PATCH] feature: add experimental extension to verify memory in aub mode Related-To: NEO-14153, NEO-17038 Signed-off-by: Mateusz Hoppe --- .../public/zex_cmdlist.cpp | 36 +++++++++ level_zero/core/source/cmdlist/cmdlist.cpp | 7 ++ level_zero/core/source/cmdlist/cmdlist.h | 4 + .../driver/extension_function_address.cpp | 1 + .../unit_tests/experimental/CMakeLists.txt | 1 + .../experimental/test_cmdlist_exp.cpp | 79 +++++++++++++++++++ .../unit_tests/sources/driver/test_driver.cpp | 6 +- .../driver_experimental/zex_cmdlist.h | 7 ++ .../driver_experimental/zex_common.h | 6 ++ .../tbx_command_stream_receiver_hw.inl | 1 + shared/test/common/mocks/mock_aub_csr.h | 10 +++ 11 files changed, 157 insertions(+), 1 deletion(-) create mode 100644 level_zero/core/test/unit_tests/experimental/test_cmdlist_exp.cpp diff --git a/level_zero/api/driver_experimental/public/zex_cmdlist.cpp b/level_zero/api/driver_experimental/public/zex_cmdlist.cpp index cf2f449194..02f50bb261 100644 --- a/level_zero/api/driver_experimental/public/zex_cmdlist.cpp +++ b/level_zero/api/driver_experimental/public/zex_cmdlist.cpp @@ -176,6 +176,32 @@ zexCommandListSetCleanupCallback(ze_command_list_handle_t hCommandList, zex_comm return ZE_RESULT_SUCCESS; } +ze_result_t ZE_APICALL +zexCommandListVerifyMemory(ze_command_list_handle_t hCommandList, + const void *allocationPtr, + const void *expectedData, + size_t sizeOfComparison, + zex_verify_memory_compare_type_t comparisonMode) { + auto cmdList = L0::CommandList::fromHandle(hCommandList); + + if (!cmdList) { + return ZE_RESULT_ERROR_INVALID_NULL_HANDLE; + } + + if (!cmdList->isImmediateType()) { + return ZE_RESULT_ERROR_UNSUPPORTED_FEATURE; + } + + if (!allocationPtr || !expectedData) { + return ZE_RESULT_ERROR_INVALID_NULL_POINTER; + } + if (sizeOfComparison == 0) { + return ZE_RESULT_ERROR_INVALID_SIZE; + } + + return cmdList->verifyMemory(allocationPtr, expectedData, sizeOfComparison, comparisonMode) ? ZE_RESULT_SUCCESS : ZE_RESULT_ERROR_UNKNOWN; +} + } // namespace L0 extern "C" { @@ -253,4 +279,14 @@ zexCommandListSetCleanupCallback(ze_command_list_handle_t hCommandList, zex_comm return L0::zexCommandListSetCleanupCallback(hCommandList, pfnCallback, pUserData, pNext); } +ZE_APIEXPORT ze_result_t ZE_APICALL +zexCommandListVerifyMemory(ze_command_list_handle_t hCommandList, + const void *allocationPtr, + const void *expectedData, + size_t sizeOfComparison, + zex_verify_memory_compare_type_t comparisonMode) { + + return L0::zexCommandListVerifyMemory(hCommandList, allocationPtr, expectedData, sizeOfComparison, comparisonMode); +} + } // extern "C" diff --git a/level_zero/core/source/cmdlist/cmdlist.cpp b/level_zero/core/source/cmdlist/cmdlist.cpp index 2d512e947f..1e566cedf9 100644 --- a/level_zero/core/source/cmdlist/cmdlist.cpp +++ b/level_zero/core/source/cmdlist/cmdlist.cpp @@ -299,4 +299,11 @@ void CommandList::executeCleanupCallbacks() { } } +bool CommandList::verifyMemory(const void *allocationPtr, + const void *expectedData, + size_t sizeOfComparison, + uint32_t comparisonMode) const { + return getCsr(false)->expectMemory(allocationPtr, expectedData, sizeOfComparison, comparisonMode); +} + } // namespace L0 diff --git a/level_zero/core/source/cmdlist/cmdlist.h b/level_zero/core/source/cmdlist/cmdlist.h index 742466dd2b..d63050b15d 100644 --- a/level_zero/core/source/cmdlist/cmdlist.h +++ b/level_zero/core/source/cmdlist/cmdlist.h @@ -557,6 +557,10 @@ struct CommandList : _ze_command_list_handle_t { } void executeCleanupCallbacks(); + bool verifyMemory(const void *allocationPtr, + const void *expectedData, + size_t sizeOfComparison, + uint32_t comparisonMode) const; protected: using CleanupCallbackT = std::pair; diff --git a/level_zero/core/source/driver/extension_function_address.cpp b/level_zero/core/source/driver/extension_function_address.cpp index 7864f8ace7..ef1bc34790 100644 --- a/level_zero/core/source/driver/extension_function_address.cpp +++ b/level_zero/core/source/driver/extension_function_address.cpp @@ -72,6 +72,7 @@ void *ExtensionFunctionAddressHelper::getExtensionFunctionAddress(const std::str RETURN_FUNC_PTR_IF_EXIST(zeCommandListAppendHostFunction); RETURN_FUNC_PTR_IF_EXIST(zexCommandListAppendMemoryCopyWithParameters); RETURN_FUNC_PTR_IF_EXIST(zexCommandListAppendMemoryFillWithParameters); + RETURN_FUNC_PTR_IF_EXIST(zexCommandListVerifyMemory); // mutable command list extension RETURN_FUNC_PTR_IF_EXIST(zexCommandListGetVariable); diff --git a/level_zero/core/test/unit_tests/experimental/CMakeLists.txt b/level_zero/core/test/unit_tests/experimental/CMakeLists.txt index 90b647ab01..2c36c95dd0 100644 --- a/level_zero/core/test/unit_tests/experimental/CMakeLists.txt +++ b/level_zero/core/test/unit_tests/experimental/CMakeLists.txt @@ -6,6 +6,7 @@ target_sources(${TARGET_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/CMakeLists.txt + ${CMAKE_CURRENT_SOURCE_DIR}/test_cmdlist_exp.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_graph.h ${CMAKE_CURRENT_SOURCE_DIR}/test_graph.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_graph_export.h diff --git a/level_zero/core/test/unit_tests/experimental/test_cmdlist_exp.cpp b/level_zero/core/test/unit_tests/experimental/test_cmdlist_exp.cpp new file mode 100644 index 0000000000..bcdd015b18 --- /dev/null +++ b/level_zero/core/test/unit_tests/experimental/test_cmdlist_exp.cpp @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2025 Intel Corporation + * + * SPDX-License-Identifier: MIT + * + */ + +#include "shared/test/common/mocks/mock_aub_csr.h" +#include "shared/test/common/test_macros/hw_test.h" + +#include "level_zero/core/test/unit_tests/fixtures/device_fixture.h" +#include "level_zero/core/test/unit_tests/mocks/mock_cmdlist.h" +#include "level_zero/core/test/unit_tests/mocks/mock_cmdqueue.h" + +using namespace NEO; + +namespace L0 { + +namespace ult { + +using CommandListExpTest = Test; + +HWTEST_F(CommandListExpTest, givenCmdListWithSimulatedCsrWhenVerifyMemoryCalledThenExpectMemoryIsCalled) { + ze_command_queue_desc_t desc = {}; + desc.mode = ZE_COMMAND_QUEUE_MODE_SYNCHRONOUS; + + ze_result_t returnValue; + std::unique_ptr commandList(CommandList::createImmediate(productFamily, device, &desc, false, NEO::EngineGroupType::renderCompute, returnValue)); + auto &commandListImmediate = static_cast &>(*commandList); + + MockAubCsr mockCommandStreamReceiver("", true, *neoDevice->executionEnvironment, neoDevice->getRootDeviceIndex(), neoDevice->getDeviceBitfield()); + mockCommandStreamReceiver.callBaseExpectMemory = false; + Mock mockCommandQueue(device, &mockCommandStreamReceiver, &desc); + + auto oldCommandQueue = commandListImmediate.cmdQImmediate; + commandListImmediate.cmdQImmediate = &mockCommandQueue; + void *ptr = reinterpret_cast(0x1000); + char data[10] = {}; + EXPECT_EQ(ZE_RESULT_SUCCESS, zexCommandListVerifyMemory(commandList->toHandle(), ptr, data, sizeof(data), zex_verify_memory_compare_type_t::ZEX_VERIFY_MEMORY_COMPARE_EQUAL)); + EXPECT_TRUE(mockCommandStreamReceiver.expectMemoryCalled); + + commandListImmediate.cmdQImmediate = oldCommandQueue; +} + +HWTEST_F(CommandListExpTest, givenCmdListWithSimulatedCsrWhenVerifyMemoryCalledWithInvalidParamsThenErrorIsReturned) { + ze_command_queue_desc_t desc = {}; + desc.mode = ZE_COMMAND_QUEUE_MODE_SYNCHRONOUS; + + ze_result_t returnValue; + std::unique_ptr commandList(CommandList::createImmediate(productFamily, device, &desc, false, NEO::EngineGroupType::renderCompute, returnValue)); + auto &commandListImmediate = static_cast &>(*commandList); + + MockAubCsr mockCommandStreamReceiver("", true, *neoDevice->executionEnvironment, neoDevice->getRootDeviceIndex(), neoDevice->getDeviceBitfield()); + mockCommandStreamReceiver.callBaseExpectMemory = false; + Mock mockCommandQueue(device, &mockCommandStreamReceiver, &desc); + + auto oldCommandQueue = commandListImmediate.cmdQImmediate; + commandListImmediate.cmdQImmediate = &mockCommandQueue; + void *ptr = reinterpret_cast(0x1000); + char data[10] = {}; + EXPECT_EQ(ZE_RESULT_ERROR_INVALID_NULL_HANDLE, zexCommandListVerifyMemory(nullptr, ptr, data, sizeof(data), zex_verify_memory_compare_type_t::ZEX_VERIFY_MEMORY_COMPARE_EQUAL)); + EXPECT_EQ(ZE_RESULT_ERROR_INVALID_NULL_POINTER, zexCommandListVerifyMemory(commandList->toHandle(), nullptr, data, sizeof(data), zex_verify_memory_compare_type_t::ZEX_VERIFY_MEMORY_COMPARE_EQUAL)); + EXPECT_EQ(ZE_RESULT_ERROR_INVALID_NULL_POINTER, zexCommandListVerifyMemory(commandList->toHandle(), ptr, nullptr, sizeof(data), zex_verify_memory_compare_type_t::ZEX_VERIFY_MEMORY_COMPARE_EQUAL)); + EXPECT_EQ(ZE_RESULT_ERROR_INVALID_SIZE, zexCommandListVerifyMemory(commandList->toHandle(), ptr, data, 0, zex_verify_memory_compare_type_t::ZEX_VERIFY_MEMORY_COMPARE_EQUAL)); + EXPECT_FALSE(mockCommandStreamReceiver.expectMemoryCalled); + + commandListImmediate.cmdQImmediate = oldCommandQueue; +} + +HWTEST_F(CommandListExpTest, givenRegularCmdListWithSimulatedCsrWhenVerifyMemoryCalledThenExpectMemoryIsCalled) { + ze_result_t returnValue; + std::unique_ptr commandList(CommandList::create(productFamily, device, NEO::EngineGroupType::renderCompute, 0, returnValue, false)); + void *ptr = reinterpret_cast(0x1000); + char data[10] = {}; + EXPECT_EQ(ZE_RESULT_ERROR_UNSUPPORTED_FEATURE, zexCommandListVerifyMemory(commandList->toHandle(), ptr, data, sizeof(data), zex_verify_memory_compare_type_t::ZEX_VERIFY_MEMORY_COMPARE_EQUAL)); +} + +} // namespace ult +} // namespace L0 diff --git a/level_zero/core/test/unit_tests/sources/driver/test_driver.cpp b/level_zero/core/test/unit_tests/sources/driver/test_driver.cpp index 278bdc1720..247c4e7b93 100644 --- a/level_zero/core/test/unit_tests/sources/driver/test_driver.cpp +++ b/level_zero/core/test/unit_tests/sources/driver/test_driver.cpp @@ -1359,6 +1359,7 @@ TEST_F(DriverExperimentalApiTest, whenRetrievingApiFunctionThenExpectProperPoint using pfnCommandListAppendMILoadRegImm = decltype(&zexCommandListAppendMILoadRegImm); using pfnCommandListAppendMIStoreRegMem = decltype(&zexCommandListAppendMIStoreRegMem); using pfnCommandListAppendMIMath = decltype(&zexCommandListAppendMIMath); + using pfnCommandListVerifyMemory = decltype(&zexCommandListVerifyMemory); decltype(&zexDriverImportExternalPointer) expectedImport = zexDriverImportExternalPointer; decltype(&zexDriverReleaseImportedPointer) expectedRelease = zexDriverReleaseImportedPointer; @@ -1405,7 +1406,7 @@ TEST_F(DriverExperimentalApiTest, whenRetrievingApiFunctionThenExpectProperPoint pfnCommandListAppendMILoadRegImm expectedCommandListAppendMILoadRegImm = zexCommandListAppendMILoadRegImm; pfnCommandListAppendMIStoreRegMem expectedCommandListAppendMIStoreRegMem = zexCommandListAppendMIStoreRegMem; pfnCommandListAppendMIMath expectedCommandListAppendMIMath = zexCommandListAppendMIMath; - + pfnCommandListVerifyMemory expectedCommandListVerifyMemory = zexCommandListVerifyMemory; // Add EXPECT_EQ tests to verify function pointers void *funPtr = nullptr; @@ -1528,6 +1529,9 @@ TEST_F(DriverExperimentalApiTest, whenRetrievingApiFunctionThenExpectProperPoint EXPECT_EQ(ZE_RESULT_SUCCESS, zeDriverGetExtensionFunctionAddress(driverHandle, "zexCommandListAppendMIMath", &funPtr)); EXPECT_EQ(expectedCommandListAppendMIMath, reinterpret_cast(funPtr)); + + EXPECT_EQ(ZE_RESULT_SUCCESS, zeDriverGetExtensionFunctionAddress(driverHandle, "zexCommandListVerifyMemory", &funPtr)); + EXPECT_EQ(expectedCommandListVerifyMemory, reinterpret_cast(funPtr)); } TEST_F(DriverExperimentalApiTest, givenHostPointerApiExistWhenImportingPtrThenExpectProperBehavior) { diff --git a/level_zero/include/level_zero/driver_experimental/zex_cmdlist.h b/level_zero/include/level_zero/driver_experimental/zex_cmdlist.h index 96cf1c1817..bc2ac77b59 100644 --- a/level_zero/include/level_zero/driver_experimental/zex_cmdlist.h +++ b/level_zero/include/level_zero/driver_experimental/zex_cmdlist.h @@ -84,6 +84,13 @@ zexCommandListSetCleanupCallback( void *pUserData, ///< [in] user specific data that would be passed to function const void *pNext); ///< [in][optional] must be null or a pointer to an extension-specific structure +ZE_APIEXPORT ze_result_t ZE_APICALL +zexCommandListVerifyMemory(ze_command_list_handle_t hCommandList, + const void *allocationPtr, + const void *expectedData, + size_t sizeOfComparison, + zex_verify_memory_compare_type_t comparisonMode); + #if defined(__cplusplus) } // extern "C" #endif diff --git a/level_zero/include/level_zero/driver_experimental/zex_common.h b/level_zero/include/level_zero/driver_experimental/zex_common.h index b4e82ce806..2b5278fa29 100644 --- a/level_zero/include/level_zero/driver_experimental/zex_common.h +++ b/level_zero/include/level_zero/driver_experimental/zex_common.h @@ -240,6 +240,12 @@ typedef struct _zex_counter_based_event_external_storage_properties_t { uint64_t completionValue; ///< [in] final completion value, when value under deviceAddress is equal or greater then this value then event is considered as completed } zex_counter_based_event_external_storage_properties_t; +typedef enum _zex_verify_memory_compare_type_t { + ZEX_VERIFY_MEMORY_COMPARE_EQUAL = 0, // compare memory for equality + ZEX_VERIFY_MEMORY_COMPARE_NOT_EQUAL = 1, // compare memory for inequality + ZEX_VERIFY_MEMORY_COMPARE_FORCE_UINT32 = 0x7fffffff ///< Value marking end of ZEX_VERIFY_MEMORY_COMPARE* ENUMs +} zex_verify_memory_compare_type_t; + #if defined(__cplusplus) } // extern "C" #endif diff --git a/shared/source/command_stream/tbx_command_stream_receiver_hw.inl b/shared/source/command_stream/tbx_command_stream_receiver_hw.inl index cb0908d809..d9d1af11d6 100644 --- a/shared/source/command_stream/tbx_command_stream_receiver_hw.inl +++ b/shared/source/command_stream/tbx_command_stream_receiver_hw.inl @@ -313,6 +313,7 @@ bool TbxCommandStreamReceiverHw::expectMemory(const void *gfxAddress, hardwareContextController->readMemory((uint64_t)gfxAddress, readMemory.get(), length, this->getMemoryBankForGtt(), MemoryConstants::pageSize64k); auto isMemoryEqual = (memcmp(readMemory.get(), srcAddress, length) == 0); auto isEqualMemoryExpected = (compareOperation == aub_stream::CompareOperationValues::CompareEqual); + hardwareContextController->expectMemory(reinterpret_cast(gfxAddress), srcAddress, length, compareOperation); return (isMemoryEqual == isEqualMemoryExpected); } diff --git a/shared/test/common/mocks/mock_aub_csr.h b/shared/test/common/mocks/mock_aub_csr.h index a3a67fa043..036d6eda7f 100644 --- a/shared/test/common/mocks/mock_aub_csr.h +++ b/shared/test/common/mocks/mock_aub_csr.h @@ -99,6 +99,14 @@ struct MockAubCsr : public AUBCommandStreamReceiverHw { skipTaskCountCheckForCompletionPoll = skipTaskCountCheck; } + bool expectMemory(const void *gfxAddress, const void *srcAddress, size_t length, uint32_t compareOperation) override { + expectMemoryCalled = true; + if (callBaseExpectMemory) { + return AUBCommandStreamReceiverHw::expectMemory(gfxAddress, srcAddress, length, compareOperation); + } + return true; + } + bool expectMemoryEqual(void *gfxAddress, const void *srcAddress, size_t length) override { expectMemoryEqualCalled = true; return AUBCommandStreamReceiverHw::expectMemoryEqual(gfxAddress, srcAddress, length); @@ -139,6 +147,7 @@ struct MockAubCsr : public AUBCommandStreamReceiverHw { bool writeMMIOCalled = false; bool submitBatchBufferCalled = false; bool pollForCompletionCalled = false; + bool expectMemoryCalled = false; bool expectMemoryEqualCalled = false; bool expectMemoryNotEqualCalled = false; bool expectMemoryCompressedCalled = false; @@ -146,6 +155,7 @@ struct MockAubCsr : public AUBCommandStreamReceiverHw { bool dumpAllocationCalled = false; bool skipTaskCountCheckForCompletionPoll = false; bool lockStreamCalled = false; + bool callBaseExpectMemory = true; std::unique_lock lockStream() override { lockStreamCalled = true;