add test for thread local (#21976)

### Details:
 - *add test for thread_local*
 - *thread_local may cause memory leak during AppVerifier check*

### Tickets:
 - *CVS-129729*

---------

Co-authored-by: Chen Peter <peter.chen@intel.com>
This commit is contained in:
Fang Xu 2024-04-25 14:51:08 +08:00 committed by GitHub
parent 9e899af4d8
commit cf96284f90
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 354 additions and 0 deletions

View File

@ -0,0 +1,28 @@
BasedOnStyle: Google
IndentWidth: 4
UseTab: Never
ColumnLimit: 120
Language: Cpp
Standard: Cpp11
AccessModifierOffset: -4
AlignConsecutiveMacros: true
AllowAllArgumentsOnNextLine: false
AllowAllConstructorInitializersOnNextLine: false
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortFunctionsOnASingleLine: Empty
AllowShortIfStatementsOnASingleLine: Never
AllowShortLambdasOnASingleLine: Empty
AllowShortLoopsOnASingleLine: false
AlwaysBreakBeforeMultilineStrings: false
BinPackArguments: false
BinPackParameters: false
CommentPragmas: '^#'
DerivePointerAlignment: false
FixNamespaceComments: true
IndentCaseLabels: false
IndentPPDirectives: AfterHash
ForEachMacros:
- foreach
- FOREACH_CHILD

View File

@ -0,0 +1,31 @@
# Copyright (C) 2018-2024 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
#
cmake_minimum_required(VERSION 3.13)
project(appverifier_tests)
if(NOT WIN32)
message(FATAL_ERROR "This test only supports windows OS")
endif()
set(CMAKE_BUILD_TYPE "Release" CACHE STRING "CMake build type")
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Release" "Debug" "RelWithDebInfo" "MinSizeRel")
set(OpenVINO_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../")
set(ENABLE_CLANG_FORMAT ON)
find_package(OpenVINODeveloperScripts REQUIRED
PATHS "${OpenVINO_SOURCE_DIR}/cmake/developer_package"
NO_CMAKE_FIND_ROOT_PATH
NO_DEFAULT_PATH)
# Define directory where artifacts will be placed
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/test")
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/test")
set(CMAKE_COMPILE_PDB_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/test")
set(CMAKE_PDB_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/test")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/test")
add_subdirectory(thread_local)
add_subdirectory(appverifier_tests)

View File

@ -0,0 +1,31 @@
# AppVerifier Tests Suite
This test suite is used to detect whether AppVerifier will report a memory leak.
## Getting Started
AppVerifier tests are based on the googletest framework. You can filter tests with
`--gtest_filter` and explore tests available with `--gtest_list_tests` options.
### Pre-requisites
- Windows OS to build the tests.
### Building Tests
To build the tests, you need to have OpenVINO™ installed or build from source.
Before build the tests, open a terminal, set OpenVINO™ environment, and after that
run the commands below:
``` bash
<OpenVINO_install_dir>/setupvars.bat
mkdir build && cd build
cmake .. && cmake --build . --config Release -j8
```
### Running Tests
``` bash
.\test\Release\ov_appverifier_tests.exe
```
This test can be run directly using the above command, but if you want to detect whether there is a memory leak, the test executable file need to be added in AppVerifier (refer to https://learn.microsoft.com/en-us/windows-hardware/drivers/devtest/application-verifier-testing-applications) before running the above command.

View File

@ -0,0 +1,21 @@
# Copyright (C) 2018-2024 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
#
set(TARGET_NAME ov_appverifier_tests)
file (GLOB_RECURSE SRC *.cpp)
file (GLOB_RECURSE HDR *.h)
# Create library file from sources.
add_executable(${TARGET_NAME} ${HDR} ${SRC})
target_link_libraries(${TARGET_NAME}
PUBLIC
gtest
pugixml
gflags
PRIVATE
gtest_main)
ov_add_clang_format_target(${TARGET_NAME}_clang FOR_TARGETS ${TARGET_NAME})

View File

@ -0,0 +1,109 @@
// Copyright (C) 2018-2024 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#include <gtest/gtest.h>
#include <windows.h>
#include <future>
#include <thread>
typedef void (*TestFunc)(const std::string&);
class ThreadLocalTest : public ::testing::Test, public ::testing::WithParamInterface<std::string> {
public:
void SetUp() {
target_device = GetParam();
}
static std::string getTestCaseName(testing::TestParamInfo<std::string> obj) {
return obj.param;
}
public:
std::string target_device = "";
};
TEST(LoadLibraryTest, load_free_library) {
std::promise<void> free_promise;
std::future<void> free_future = free_promise.get_future();
std::promise<void> thread_exit_promise;
std::future<void> thread_exit_future = thread_exit_promise.get_future();
auto shared_object = LoadLibraryA("openvino.dll");
if (!shared_object) {
std::cout << "LoadLibrary openvino.dll fail" << std::endl;
return;
}
std::thread sub_thread = std::thread([&] {
free_promise.set_value();
thread_exit_future.get();
});
free_future.get();
FreeLibrary(shared_object);
thread_exit_promise.set_value();
if (sub_thread.joinable()) {
sub_thread.join();
}
}
TEST_P(ThreadLocalTest, get_property_test) {
auto shared_object = LoadLibraryA("ov_thread_local.dll");
if (!shared_object) {
std::cout << "LoadLibrary ov_thread_local.dll fail" << std::endl;
return;
}
auto procAddr = reinterpret_cast<TestFunc>(GetProcAddress(shared_object, "core_get_property_test"));
procAddr(target_device);
FreeLibrary(shared_object);
}
TEST_P(ThreadLocalTest, infer_test) {
auto shared_object = LoadLibraryA("ov_thread_local.dll");
if (!shared_object) {
std::cout << "LoadLibrary ov_thread_local.dll fail" << std::endl;
return;
}
auto procAddr = reinterpret_cast<TestFunc>(GetProcAddress(shared_object, "core_infer_test"));
procAddr(target_device);
FreeLibrary(shared_object);
}
void process_sub_thread(const std::string& func_name, const std::string& target_device) {
auto shared_object = LoadLibraryA("ov_thread_local.dll");
if (!shared_object) {
std::cout << "LoadLibrary ov_thread_local.dll fail" << std::endl;
return;
}
auto procAddr = reinterpret_cast<TestFunc>(GetProcAddress(shared_object, func_name.c_str()));
std::promise<void> free_promise;
std::future<void> free_future = free_promise.get_future();
std::promise<void> thread_exit_promise;
std::future<void> thread_exit_future = thread_exit_promise.get_future();
std::thread sub_thread = std::thread([&] {
procAddr(target_device);
free_promise.set_value();
thread_exit_future.get();
});
free_future.get();
FreeLibrary(shared_object);
thread_exit_promise.set_value();
if (sub_thread.joinable()) {
sub_thread.join();
}
}
TEST_P(ThreadLocalTest, get_property_test_subthread) {
process_sub_thread("core_get_property_test", target_device);
}
TEST_P(ThreadLocalTest, infer_test_subthread) {
process_sub_thread("core_infer_test", target_device);
}
INSTANTIATE_TEST_SUITE_P(OV_ThreadLocalTests,
ThreadLocalTest,
::testing::Values("CPU", "GPU"),
ThreadLocalTest::getTestCaseName);

View File

@ -0,0 +1,39 @@
# Copyright (C) 2018-2024 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
#
set(TARGET_NAME ov_thread_local)
# Search OpenVINO Runtime installed
find_package(PkgConfig QUIET)
# TODO: fix cross-compilation later
if(PkgConfig_FOUND AND NOT CMAKE_CROSSCOMPILING AND CMAKE_BUILD_TYPE STREQUAL "Release")
pkg_search_module(openvino REQUIRED
IMPORTED_TARGET
openvino)
set(ov_link_libraries PkgConfig::openvino)
else()
find_package(OpenVINO REQUIRED COMPONENTS Runtime)
set(ov_link_libraries openvino::runtime)
endif()
file (GLOB_RECURSE SRC *.cpp)
file (GLOB_RECURSE HDR *.h)
add_library(${TARGET_NAME} SHARED ${SRC} ${HDR})
target_include_directories(${TARGET_NAME} PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include")
add_subdirectory(${OpenVINO_SOURCE_DIR}/thirdparty/gflags
${CMAKE_CURRENT_BINARY_DIR}/gflags_build
EXCLUDE_FROM_ALL)
add_subdirectory(${OpenVINO_SOURCE_DIR}/thirdparty/gtest
${CMAKE_CURRENT_BINARY_DIR}/gtest_build
EXCLUDE_FROM_ALL)
add_subdirectory(${OpenVINO_SOURCE_DIR}/thirdparty/pugixml
${CMAKE_CURRENT_BINARY_DIR}/pugixml_build
EXCLUDE_FROM_ALL)
add_subdirectory("${OpenVINO_SOURCE_DIR}/tests/lib" tests_shared_lib)
target_link_libraries(${TARGET_NAME} PUBLIC tests_shared_lib)
ov_add_clang_format_target(${TARGET_NAME}_clang FOR_TARGETS ${TARGET_NAME})

View File

@ -0,0 +1,8 @@
// Copyright (C) 2018-2024 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#include <iostream>
#include <string>
extern "C" __declspec(dllexport) void core_get_property_test(const std::string& target_device);
extern "C" __declspec(dllexport) void core_infer_test(const std::string& target_device);

View File

@ -0,0 +1,87 @@
// Copyright (C) 2018-2024 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#include "thread_local.hpp"
#include <future>
#include <iostream>
#include <string>
#include "openvino/core/model.hpp"
#include "openvino/core/node_vector.hpp"
#include "openvino/core/type/element_type.hpp"
#include "openvino/op/concat.hpp"
#include "openvino/op/constant.hpp"
#include "openvino/op/reshape.hpp"
#include "openvino/op/split.hpp"
#include "openvino/openvino.hpp"
#include "openvino/runtime/properties.hpp"
std::shared_ptr<ov::Model> make_split_concat(std::vector<size_t> inputShape = {1, 4, 24, 24},
ov::element::Type_t type = ov::element::Type_t::f32);
void core_get_property_test(const std::string& target_device) {
std::promise<void> call_finish_promise;
auto call_finish_future = call_finish_promise.get_future();
std::promise<void> thread_exit_promise;
auto thread_exit_future = thread_exit_promise.get_future();
std::thread sub_thread;
{
ov::Core ie;
sub_thread = std::thread([&] {
ie.get_property(target_device, ov::supported_properties);
call_finish_promise.set_value();
thread_exit_future.get();
});
call_finish_future.get();
}
thread_exit_promise.set_value();
if (sub_thread.joinable()) {
sub_thread.join();
}
}
std::shared_ptr<ov::Model> make_split_concat(std::vector<size_t> inputShape, ov::element::Type_t type) {
auto param1 = std::make_shared<ov::op::v0::Parameter>(type, ov::Shape{inputShape});
param1->set_friendly_name("Param1");
param1->output(0).get_tensor().set_names({"data1"});
auto axis_node = ov::op::v0::Constant::create(ov::element::i64, ov::Shape{}, {1});
auto split = std::make_shared<ov::op::v1::Split>(param1, axis_node, 2);
split->set_friendly_name("Split");
split->output(0).get_tensor().set_names({"tensor_split_1"});
split->output(1).get_tensor().set_names({"tensor_split_2"});
auto concat = std::make_shared<ov::op::v0::Concat>(ov::OutputVector{split->output(0), split->output(1)}, 1);
concat->set_friendly_name("Concat_op");
concat->output(0).get_tensor().set_names({"Concat"});
auto result = std::make_shared<ov::op::v0::Result>(concat);
result->set_friendly_name("Result");
auto model_ptr = std::make_shared<ov::Model>(ov::ResultVector{result}, ov::ParameterVector{param1});
model_ptr->set_friendly_name("SplitConcat");
return model_ptr;
}
void core_infer_test(const std::string& target_device) {
std::promise<void> call_finish_promise;
std::future<void> call_finish_future = call_finish_promise.get_future();
std::promise<void> thread_exit_promise;
std::future<void> thread_exit_future = thread_exit_promise.get_future();
std::thread sub_thread;
auto actualNetwork = make_split_concat();
{
ov::Core ie;
sub_thread = std::thread([&] {
auto net = ie.compile_model(actualNetwork, target_device);
auto infer_req = net.create_infer_request();
ie.get_property(target_device, ov::supported_properties);
infer_req.infer();
call_finish_promise.set_value();
thread_exit_future.get();
});
call_finish_future.get();
}
thread_exit_promise.set_value();
if (sub_thread.joinable()) {
sub_thread.join();
}
}