From 9b9051af3c2b03411f358940a859692e47cf4e8d Mon Sep 17 00:00:00 2001 From: Richard Huang Date: Fri, 27 Sep 2024 10:36:48 +0100 Subject: [PATCH] Fix SVACE error in USD loader Change-Id: Ie67d09a62106fe952aedd646979308300423d46e --- .../CMakeLists.txt | 98 ++++++++++++ ...-loader-dynamic-lib-func-override-core.cpp | 9 ++ ...c-Dali-UsdLoaderDynamicLibFuncOverride.cpp | 148 ++++++++++++++++++ .../public-api/loader/model-loader.cpp | 102 ++++++++---- 4 files changed, 328 insertions(+), 29 deletions(-) create mode 100644 automated-tests/src/dali-usd-loader-dynamic-lib-func-override/CMakeLists.txt create mode 100644 automated-tests/src/dali-usd-loader-dynamic-lib-func-override/tct-dali-usd-loader-dynamic-lib-func-override-core.cpp create mode 100644 automated-tests/src/dali-usd-loader-dynamic-lib-func-override/utc-Dali-UsdLoaderDynamicLibFuncOverride.cpp diff --git a/automated-tests/src/dali-usd-loader-dynamic-lib-func-override/CMakeLists.txt b/automated-tests/src/dali-usd-loader-dynamic-lib-func-override/CMakeLists.txt new file mode 100644 index 0000000000..f60432a7b8 --- /dev/null +++ b/automated-tests/src/dali-usd-loader-dynamic-lib-func-override/CMakeLists.txt @@ -0,0 +1,98 @@ +SET(PKG_NAME "dali-usd-loader-dynamic-lib-func-override") + +SET(EXEC_NAME "tct-${PKG_NAME}-core") +SET(RPM_NAME "core-${PKG_NAME}-tests") + +SET(CAPI_LIB "dali-usd-loader-dynamic-lib-func-override") + +# List of test case sources (Only these get parsed for test cases) +SET(TC_SOURCES + utc-Dali-UsdLoaderDynamicLibFuncOverride.cpp + ) + +# List of test harness files (Won't get parsed for test cases) +SET(TEST_HARNESS_DIR "../dali-toolkit/dali-toolkit-test-utils") + +SET(TEST_HARNESS_SOURCES + ${TEST_HARNESS_DIR}/toolkit-adaptor.cpp + ${TEST_HARNESS_DIR}/toolkit-application.cpp + ${TEST_HARNESS_DIR}/toolkit-async-task-manager.cpp + ${TEST_HARNESS_DIR}/toolkit-event-thread-callback.cpp + ${TEST_HARNESS_DIR}/toolkit-environment-variable.cpp + ${TEST_HARNESS_DIR}/toolkit-input-method-context.cpp + ${TEST_HARNESS_DIR}/toolkit-input-method-options.cpp + ${TEST_HARNESS_DIR}/toolkit-lifecycle-controller.cpp + ${TEST_HARNESS_DIR}/toolkit-orientation.cpp + ${TEST_HARNESS_DIR}/toolkit-style-monitor.cpp + ${TEST_HARNESS_DIR}/toolkit-test-application.cpp + ${TEST_HARNESS_DIR}/toolkit-timer.cpp + ${TEST_HARNESS_DIR}/toolkit-trigger-event-factory.cpp + ${TEST_HARNESS_DIR}/toolkit-window.cpp + ${TEST_HARNESS_DIR}/toolkit-scene-holder.cpp + ${TEST_HARNESS_DIR}/dali-test-suite-utils.cpp + ${TEST_HARNESS_DIR}/dali-toolkit-test-suite-utils.cpp + ${TEST_HARNESS_DIR}/dummy-control.cpp + ${TEST_HARNESS_DIR}/mesh-builder.cpp + ${TEST_HARNESS_DIR}/test-actor-utils.cpp + ${TEST_HARNESS_DIR}/test-animation-data.cpp + ${TEST_HARNESS_DIR}/test-application.cpp + ${TEST_HARNESS_DIR}/test-button.cpp + ${TEST_HARNESS_DIR}/test-harness.cpp + ${TEST_HARNESS_DIR}/test-gesture-generator.cpp + ${TEST_HARNESS_DIR}/test-gl-abstraction.cpp + ${TEST_HARNESS_DIR}/test-graphics-sync-impl.cpp + ${TEST_HARNESS_DIR}/test-graphics-sync-object.cpp + ${TEST_HARNESS_DIR}/test-graphics-buffer.cpp + ${TEST_HARNESS_DIR}/test-graphics-command-buffer.cpp + ${TEST_HARNESS_DIR}/test-graphics-framebuffer.cpp + ${TEST_HARNESS_DIR}/test-graphics-controller.cpp + ${TEST_HARNESS_DIR}/test-graphics-texture.cpp + ${TEST_HARNESS_DIR}/test-graphics-sampler.cpp + ${TEST_HARNESS_DIR}/test-graphics-program.cpp + ${TEST_HARNESS_DIR}/test-graphics-pipeline.cpp + ${TEST_HARNESS_DIR}/test-graphics-shader.cpp + ${TEST_HARNESS_DIR}/test-graphics-reflection.cpp + ${TEST_HARNESS_DIR}/test-platform-abstraction.cpp + ${TEST_HARNESS_DIR}/test-render-controller.cpp + ${TEST_HARNESS_DIR}/test-render-surface.cpp + ${TEST_HARNESS_DIR}/test-trace-call-stack.cpp +) + +PKG_CHECK_MODULES(${CAPI_LIB} REQUIRED + dali2-core + dali2-adaptor + dali2-toolkit + dali2-scene3d +) + +ADD_COMPILE_OPTIONS( -O0 -ggdb --coverage -Wall -Werror -DDEBUG_ENABLED) +ADD_COMPILE_OPTIONS( ${${CAPI_LIB}_CFLAGS_OTHER} ) + +ADD_DEFINITIONS(-DTEST_RESOURCE_DIR=\"${CMAKE_CURRENT_SOURCE_DIR}/../../resources\" ) + +FOREACH(directory ${${CAPI_LIB}_LIBRARY_DIRS}) + SET(CMAKE_CXX_LINK_FLAGS "${CMAKE_CXX_LINK_FLAGS} -L${directory}") +ENDFOREACH(directory ${CAPI_LIB_LIBRARY_DIRS}) + +INCLUDE_DIRECTORIES( + ../../../ + ${${CAPI_LIB}_INCLUDE_DIRS} + ../dali-toolkit/dali-toolkit-test-utils +) + +ADD_CUSTOM_COMMAND( + COMMAND ${SCRIPT_DIR}/tcheadgen.sh ${EXEC_NAME}.h ${TC_SOURCES} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + OUTPUT ${EXEC_NAME}.h + COMMENT "Generating test tables" + ) + +ADD_EXECUTABLE(${EXEC_NAME} ${EXEC_NAME}.h ${EXEC_NAME}.cpp ${TC_SOURCES} ${TEST_HARNESS_SOURCES}) +TARGET_LINK_LIBRARIES(${EXEC_NAME} + ${${CAPI_LIB}_LIBRARIES} + -lpthread -ldl --coverage +) + +INSTALL(PROGRAMS ${EXEC_NAME} + DESTINATION ${BIN_DIR}/${EXEC_NAME} +) diff --git a/automated-tests/src/dali-usd-loader-dynamic-lib-func-override/tct-dali-usd-loader-dynamic-lib-func-override-core.cpp b/automated-tests/src/dali-usd-loader-dynamic-lib-func-override/tct-dali-usd-loader-dynamic-lib-func-override-core.cpp new file mode 100644 index 0000000000..e479c8e387 --- /dev/null +++ b/automated-tests/src/dali-usd-loader-dynamic-lib-func-override/tct-dali-usd-loader-dynamic-lib-func-override-core.cpp @@ -0,0 +1,9 @@ +#include + +// Must come second +#include "tct-dali-usd-loader-dynamic-lib-func-override-core.h" + +int main(int argc, char* const argv[]) +{ + return TestHarness::RunTests(argc, argv, tc_array); +} diff --git a/automated-tests/src/dali-usd-loader-dynamic-lib-func-override/utc-Dali-UsdLoaderDynamicLibFuncOverride.cpp b/automated-tests/src/dali-usd-loader-dynamic-lib-func-override/utc-Dali-UsdLoaderDynamicLibFuncOverride.cpp new file mode 100644 index 0000000000..556cc9a1d8 --- /dev/null +++ b/automated-tests/src/dali-usd-loader-dynamic-lib-func-override/utc-Dali-UsdLoaderDynamicLibFuncOverride.cpp @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2024 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// Enable debug log for test coverage +#define DEBUG_ENABLED 1 + +#include +#include +#include +#include +#include +#include +#include + +using namespace Dali; +using namespace Dali::Scene3D::Loader; + +namespace +{ +struct Context +{ + ResourceBundle::PathProvider pathProvider = [](ResourceType::Value type) { + return TEST_RESOURCE_DIR "/"; + }; + + ResourceBundle resources; + SceneDefinition scene; + SceneMetadata metaData; + + std::vector animations; + std::vector animationGroups; + std::vector cameras; + std::vector lights; + + LoadResult loadResult{ + resources, + scene, + metaData, + animations, + animationGroups, + cameras, + lights}; + + Dali::Scene3D::Loader::ModelLoader* loader; +}; + +bool gDlopenOverrideEnabled = false; +bool gDlsymOverrideEnabled = false; + +extern "C" void* DlopenProxy(const char* filename, int flag) +{ + if(gDlopenOverrideEnabled) + { + return nullptr; + } + else + { + return dlopen("libdali2-scene3d.so", RTLD_LAZY); + } +} + +extern "C" void* DlsymProxy(void* handle, const char* symbol) +{ + if(gDlsymOverrideEnabled) + { + return nullptr; + } + else + { + return dlsym(handle, symbol); + } +} + +} // namespace + +int UtcDaliUsdLoaderDlopenFail(void) +{ + // Only make dlopen fail + gDlopenOverrideEnabled = true; + gDlsymOverrideEnabled = false; + + Context ctx; + + ctx.loader = new Dali::Scene3D::Loader::ModelLoader(TEST_RESOURCE_DIR "/usd/CesiumMan.usdz", ctx.pathProvider(ResourceType::Mesh) + "/", ctx.loadResult); + DALI_TEST_EQUAL(ctx.loader->LoadModel(ctx.pathProvider, true), false); + + DALI_TEST_EQUAL(0, ctx.scene.GetRoots().size()); + DALI_TEST_EQUAL(0, ctx.scene.GetNodeCount()); + + DALI_TEST_EQUAL(0, ctx.resources.mEnvironmentMaps.size()); + DALI_TEST_EQUAL(0, ctx.resources.mMaterials.size()); + DALI_TEST_EQUAL(0, ctx.resources.mMeshes.size()); + DALI_TEST_EQUAL(0, ctx.resources.mShaders.size()); + DALI_TEST_EQUAL(0, ctx.resources.mSkeletons.size()); + + DALI_TEST_EQUAL(0, ctx.cameras.size()); + DALI_TEST_EQUAL(0, ctx.lights.size()); + DALI_TEST_EQUAL(0, ctx.animations.size()); + DALI_TEST_EQUAL(0, ctx.animationGroups.size()); + + delete ctx.loader; + + END_TEST; +} + +int UtcDaliUsdLoaderDlsymFail(void) +{ + // Only make dlsym fail + gDlopenOverrideEnabled = false; + gDlsymOverrideEnabled = true; + + Context ctx; + + ctx.loader = new Dali::Scene3D::Loader::ModelLoader(TEST_RESOURCE_DIR "/usd/CesiumMan.usdz", ctx.pathProvider(ResourceType::Mesh) + "/", ctx.loadResult); + DALI_TEST_EQUAL(ctx.loader->LoadModel(ctx.pathProvider, true), false); + + DALI_TEST_EQUAL(0, ctx.scene.GetRoots().size()); + DALI_TEST_EQUAL(0, ctx.scene.GetNodeCount()); + + DALI_TEST_EQUAL(0, ctx.resources.mEnvironmentMaps.size()); + DALI_TEST_EQUAL(0, ctx.resources.mMaterials.size()); + DALI_TEST_EQUAL(0, ctx.resources.mMeshes.size()); + DALI_TEST_EQUAL(0, ctx.resources.mShaders.size()); + DALI_TEST_EQUAL(0, ctx.resources.mSkeletons.size()); + + DALI_TEST_EQUAL(0, ctx.cameras.size()); + DALI_TEST_EQUAL(0, ctx.lights.size()); + DALI_TEST_EQUAL(0, ctx.animations.size()); + DALI_TEST_EQUAL(0, ctx.animationGroups.size()); + + delete ctx.loader; + + END_TEST; +} diff --git a/dali-scene3d/public-api/loader/model-loader.cpp b/dali-scene3d/public-api/loader/model-loader.cpp index 595e1871c4..939a438f01 100644 --- a/dali-scene3d/public-api/loader/model-loader.cpp +++ b/dali-scene3d/public-api/loader/model-loader.cpp @@ -34,19 +34,48 @@ namespace Dali::Scene3D::Loader { namespace { -static constexpr std::string_view OBJ_EXTENSION = ".obj"; -static constexpr std::string_view GLTF_EXTENSION = ".gltf"; -static constexpr std::string_view GLB_EXTENSION = ".glb"; -static constexpr std::string_view DLI_EXTENSION = ".dli"; -static constexpr std::string_view USD_EXTENSION = ".usd"; -static constexpr std::string_view USDZ_EXTENSION = ".usdz"; -static constexpr std::string_view USDA_EXTENSION = ".usda"; -static constexpr std::string_view USDC_EXTENSION = ".usdc"; -static constexpr std::string_view METADATA_EXTENSION = "metadata"; +constexpr std::string_view OBJ_EXTENSION = ".obj"; +constexpr std::string_view GLTF_EXTENSION = ".gltf"; +constexpr std::string_view GLB_EXTENSION = ".glb"; +constexpr std::string_view DLI_EXTENSION = ".dli"; +constexpr std::string_view USD_EXTENSION = ".usd"; +constexpr std::string_view USDZ_EXTENSION = ".usdz"; +constexpr std::string_view USDA_EXTENSION = ".usda"; +constexpr std::string_view USDC_EXTENSION = ".usdc"; +constexpr std::string_view METADATA_EXTENSION = "metadata"; const char* USD_LOADER_SO("libdali2-usd-loader.so"); const char* CREATE_USD_LOADER_SYMBOL("CreateUsdLoader"); +// Custom deleter for dlopen handles +void DlcloseDeleter(void* handle) +{ + if(handle) + { + dlclose(handle); + } +} + +// Static shared pointer to store a pointer to the dlopen handle +std::shared_ptr gUsdLoaderHandle(nullptr, DlcloseDeleter); + +using CreateUsdLoaderFunc = ModelLoaderImpl* (*)(); +CreateUsdLoaderFunc gCreateUsdLoaderFunc(nullptr); + +// Poxy function for `dlopen` to allow easy overriding in test environments. +extern "C" void* DlopenProxy(const char* filename, int flag) +{ + // Calls the real dlopen + return dlopen(filename, flag); +} + +// Poxy function for `dlsym` to allow easy overriding in test environments. +extern "C" void* DlsymProxy(void* handle, const char* name) +{ + // Calls the real dlsym + return dlsym(handle, name); +} + } // namespace ModelLoader::ModelLoader(const std::string& modelUrl, const std::string& resourceDirectoryUrl, Dali::Scene3D::Loader::LoadResult& loadResult) @@ -138,29 +167,45 @@ void ModelLoader::CreateModelLoader() // Attempt to load the USD loader library dynamically // Once loaded we will keep it open so that any subsequent loading of USD models // doesn't require loading the same library repeatedly. - void* handle(dlopen(USD_LOADER_SO, RTLD_LAZY)); - if(!handle) + if(!gUsdLoaderHandle || !(*gUsdLoaderHandle)) { - // The shared library failed to load - DALI_LOG_ERROR("ModelLoader::CreateModelLoader, dlopen error: %s\n", dlerror()); - return; - } - - // Dynamically link to the CreateUsdLoader function in the shared library at runtime. - using CreateUsdLoaderFunc = Dali::Scene3D::Loader::ModelLoaderImpl* (*)(); - CreateUsdLoaderFunc createUsdLoader = reinterpret_cast(dlsym(handle, CREATE_USD_LOADER_SYMBOL)); - - if(!createUsdLoader) - { - // Close the shared library if the symbol couldn't be found - dlclose(handle); - DALI_LOG_ERROR("Cannot find CreateUsdLoader function: %s\n", dlerror()); - - return; + void* handle = DlopenProxy(USD_LOADER_SO, RTLD_LAZY); + + if(!handle) + { + // The shared library failed to load + DALI_LOG_ERROR("ModelLoader::CreateModelLoader, dlopen error: %s\n", dlerror()); + return; + } + + // Store the handle in the shared_ptr (passing the handle and the custom deleter) + gUsdLoaderHandle = std::shared_ptr(new void*(handle), [](void* ptr) { + if(ptr) + { + DlcloseDeleter(*(static_cast(ptr))); // Call custom deleter + delete static_cast(ptr); // Clean up dynamically allocated memory + } + }); + + // Dynamically link to the CreateUsdLoader function in the shared library at runtime. + // Cache the function pointer only if it hasn't been loaded yet + if(!gCreateUsdLoaderFunc) + { + gCreateUsdLoaderFunc = reinterpret_cast(DlsymProxy(*gUsdLoaderHandle.get(), CREATE_USD_LOADER_SYMBOL)); + if(!gCreateUsdLoaderFunc) + { + // If the symbol couldn't be found, reset the shared_ptr to invoke the custom deleter + gUsdLoaderHandle.reset(); // This will automatically call dlclose via the custom deleter + + DALI_LOG_ERROR("Cannot find CreateUsdLoader function: %s\n", dlerror()); + + return; + } + } } // Create an instance of USD loader - mImpl = ModelLoaderImplUniquePtr(createUsdLoader()); + mImpl = ModelLoaderImplUniquePtr(gCreateUsdLoaderFunc()); } else { @@ -193,5 +238,4 @@ void ModelLoader::LoadResource(Dali::Scene3D::Loader::ResourceBundle::PathProvid GetResources().LoadResources(pathProvider); } } - } // namespace Dali::Scene3D::Loader -- 2.34.1