This is an automated email from the ASF dual-hosted git repository. pengzheng pushed a commit to branch feature/error_injector in repository https://gitbox.apache.org/repos/asf/celix.git
commit 60a0ddf222b599c0d52f5dfb675e8658611e2a3d Author: PengZheng <[email protected]> AuthorDate: Mon Jan 16 20:21:25 2023 +0800 Add basic error injector mechanism. An injector for malloc family is implemented. --- CMakeLists.txt | 5 ++ .../pubsub_protocol_lib/gtest/CMakeLists.txt | 5 +- .../gtest/src/PS_WP_common_tests.cc | 71 ++---------------- .../gtest => misc/error_injector}/CMakeLists.txt | 15 ++-- misc/error_injector/error_injector.h | 86 ++++++++++++++++++++++ .../error_injector/malloc}/CMakeLists.txt | 13 ++-- misc/error_injector/malloc/malloc_ei.cpp | 36 +++++++++ misc/error_injector/malloc/malloc_ei.h | 34 +++++++++ 8 files changed, 182 insertions(+), 83 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 034a679e..66b7f5b1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -203,6 +203,11 @@ add_subdirectory(bundles) #Experimental Bundles/Libraries add_subdirectory(misc/experimental) +# Error Injectors +if (ENABLE_TESTING) + add_subdirectory(misc/error_injector) +endif () + #Example as last, because some example will check if underlining options are enabled add_subdirectory(examples/celix-examples examples) diff --git a/bundles/pubsub/pubsub_protocol/pubsub_protocol_lib/gtest/CMakeLists.txt b/bundles/pubsub/pubsub_protocol/pubsub_protocol_lib/gtest/CMakeLists.txt index d21d3c90..bba51785 100644 --- a/bundles/pubsub/pubsub_protocol/pubsub_protocol_lib/gtest/CMakeLists.txt +++ b/bundles/pubsub/pubsub_protocol/pubsub_protocol_lib/gtest/CMakeLists.txt @@ -17,10 +17,7 @@ add_executable(celix_pswp_common_tests src/PS_WP_common_tests.cc) target_include_directories(celix_pswp_common_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../src) -target_link_libraries(celix_pswp_common_tests PRIVATE celix_pubsub_protocol_lib GTest::gtest Celix::pubsub_spi GTest::gtest_main ${CMAKE_DL_LIBS}) -if (NOT ENABLE_ADDRESS_SANITIZER) - target_compile_definitions(celix_pswp_common_tests PRIVATE ENABLE_MALLOC_RETURN_NULL_TESTS) -endif () +target_link_libraries(celix_pswp_common_tests PRIVATE celix_pubsub_protocol_lib GTest::gtest Celix::pubsub_spi GTest::gtest_main malloc_ei) add_test(NAME celix_pswp_common_tests COMMAND celix_pswp_common_tests) setup_target_for_coverage(celix_pswp_common_tests SCAN_DIR ..) \ No newline at end of file diff --git a/bundles/pubsub/pubsub_protocol/pubsub_protocol_lib/gtest/src/PS_WP_common_tests.cc b/bundles/pubsub/pubsub_protocol/pubsub_protocol_lib/gtest/src/PS_WP_common_tests.cc index 68fc3bfa..25a90210 100644 --- a/bundles/pubsub/pubsub_protocol/pubsub_protocol_lib/gtest/src/PS_WP_common_tests.cc +++ b/bundles/pubsub/pubsub_protocol/pubsub_protocol_lib/gtest/src/PS_WP_common_tests.cc @@ -22,65 +22,18 @@ #include <gtest/gtest.h> #include <iostream> #include <cstring> -#include <dlfcn.h> +#include <malloc_ei.h> #include "pubsub_wire_protocol_common.h" class WireProtocolCommonTest : public ::testing::Test { public: WireProtocolCommonTest() = default; - ~WireProtocolCommonTest() override = default; + ~WireProtocolCommonTest() override { + celix_ei_expect_realloc(nullptr, 0, NULL); + }; }; -#ifdef ENABLE_MALLOC_RETURN_NULL_TESTS -/** - * If set to true the mocked malloc will always return NULL. - * Should be read/written using __atomic builtins. - */ -static int mallocFailAfterCalls = 0; -static int mallocCurrentCallCount = 0; - -/** - * mocked malloc to ensure testing can be done for the "malloc returns NULL" scenario - */ -extern "C" void* malloc(size_t size) { - int count = __atomic_add_fetch(&mallocCurrentCallCount, 1, __ATOMIC_ACQ_REL); - int target = __atomic_load_n(&mallocFailAfterCalls, __ATOMIC_ACQUIRE); - if (target > 0 && count == target) { - return nullptr; - } - static auto* orgMallocFp = (void*(*)(size_t))dlsym(RTLD_NEXT, "malloc"); - if (orgMallocFp == nullptr) { - perror("Cannot find malloc symbol"); - return nullptr; - } - return orgMallocFp(size); -} - -extern "C" void* realloc(void* buf, size_t newSize) { - int count = __atomic_add_fetch(&mallocCurrentCallCount, 1, __ATOMIC_ACQ_REL); - int target = __atomic_load_n(&mallocFailAfterCalls, __ATOMIC_ACQUIRE); - if (target > 0 && count == target) { - return nullptr; - } - static auto* orgReallocFp = (void*(*)(void*, size_t))dlsym(RTLD_NEXT, "realloc"); - if (orgReallocFp == nullptr) { - perror("Cannot find realloc symbol"); - return nullptr; - } - return orgReallocFp(buf, newSize); -} - -void setupMallocFailAfterNrCalls(int nrOfCalls) { - __atomic_store_n(&mallocFailAfterCalls, nrOfCalls, __ATOMIC_RELEASE); - __atomic_store_n(&mallocCurrentCallCount, 0, __ATOMIC_RELEASE); -} - -void disableMallocFail() { - __atomic_store_n(&mallocFailAfterCalls, 0, __ATOMIC_RELEASE); -} -#endif - TEST_F(WireProtocolCommonTest, WireProtocolCommonTest_EncodeMetadataWithSingleEntries) { pubsub_protocol_message_t message; message.header.convertEndianess = 0; @@ -217,7 +170,6 @@ TEST_F(WireProtocolCommonTest, WireProtocolCommonTest_EncodeWithExistinBufferWhi celix_properties_destroy(message.metadata.metadata); } -#ifdef ENABLE_MALLOC_RETURN_NULL_TESTS TEST_F(WireProtocolCommonTest, WireProtocolCommonTest_EncodeMetadataWithNoMemoryLeft) { pubsub_protocol_message_t message; message.header.convertEndianess = 0; @@ -225,8 +177,8 @@ TEST_F(WireProtocolCommonTest, WireProtocolCommonTest_EncodeMetadataWithNoMemory celix_properties_set(message.metadata.metadata, "key1", "value1"); //Scenario: No mem with no pre-allocated data - //Given (mocked) malloc is forced to return NULL - setupMallocFailAfterNrCalls(1); + //Given (mocked) realloc is forced to return NULL + celix_ei_expect_realloc((void *)pubsubProtocol_encodeMetadata, 0, nullptr); //When I try to encode a metadata char *data = nullptr; @@ -236,16 +188,13 @@ TEST_F(WireProtocolCommonTest, WireProtocolCommonTest_EncodeMetadataWithNoMemory //Then I expect a failure EXPECT_NE(status, CELIX_SUCCESS); - //reset malloc - disableMallocFail(); - //Scenario: No mem with some pre-allocated data //Given a data set with some space data = (char*)malloc(16); length = 16; - //And (mocked) malloc is forced to return NULL - setupMallocFailAfterNrCalls(1); + //And (mocked) realloc is forced to return NULL + celix_ei_expect_realloc((void *)pubsubProtocol_encodeMetadata, 0, nullptr); //When I try to encode a metadata status = pubsubProtocol_encodeMetadata(&message, &data, &length, &contentLength); @@ -253,13 +202,9 @@ TEST_F(WireProtocolCommonTest, WireProtocolCommonTest_EncodeMetadataWithNoMemory //Then I expect a failure EXPECT_NE(status, CELIX_SUCCESS); - //reset malloc - disableMallocFail(); - free(data); celix_properties_destroy(message.metadata.metadata); } -#endif TEST_F(WireProtocolCommonTest, WireProtocolCommonTest_DecodeMetadataWithSingleEntries) { pubsub_protocol_message_t message; diff --git a/bundles/pubsub/pubsub_protocol/pubsub_protocol_lib/gtest/CMakeLists.txt b/misc/error_injector/CMakeLists.txt similarity index 57% copy from bundles/pubsub/pubsub_protocol/pubsub_protocol_lib/gtest/CMakeLists.txt copy to misc/error_injector/CMakeLists.txt index d21d3c90..a522ac19 100644 --- a/bundles/pubsub/pubsub_protocol/pubsub_protocol_lib/gtest/CMakeLists.txt +++ b/misc/error_injector/CMakeLists.txt @@ -15,12 +15,11 @@ # specific language governing permissions and limitations # under the License. -add_executable(celix_pswp_common_tests src/PS_WP_common_tests.cc) -target_include_directories(celix_pswp_common_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../src) -target_link_libraries(celix_pswp_common_tests PRIVATE celix_pubsub_protocol_lib GTest::gtest Celix::pubsub_spi GTest::gtest_main ${CMAKE_DL_LIBS}) -if (NOT ENABLE_ADDRESS_SANITIZER) - target_compile_definitions(celix_pswp_common_tests PRIVATE ENABLE_MALLOC_RETURN_NULL_TESTS) -endif () +add_library(error_injector INTERFACE error_injector.h) +target_include_directories(error_injector INTERFACE ${CMAKE_CURRENT_LIST_DIR}) +# get caller address of the target function, into which errors are injected +target_link_libraries(error_injector INTERFACE dl) +target_link_options(error_injector INTERFACE -rdynamic) +target_compile_options(error_injector INTERFACE -Wno-frame-address) -add_test(NAME celix_pswp_common_tests COMMAND celix_pswp_common_tests) -setup_target_for_coverage(celix_pswp_common_tests SCAN_DIR ..) \ No newline at end of file +add_subdirectory(malloc) \ No newline at end of file diff --git a/misc/error_injector/error_injector.h b/misc/error_injector/error_injector.h new file mode 100644 index 00000000..26e5825e --- /dev/null +++ b/misc/error_injector/error_injector.h @@ -0,0 +1,86 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you 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. + */ + +#ifndef CELIX_ERROR_INJECTOR_H +#define CELIX_ERROR_INJECTOR_H +#ifdef __cplusplus +extern "C" { +#endif + +#include <assert.h> +#include <dlfcn.h> +#include <stddef.h> + +#define CELIX_EI_GET_CALLER(addr, level) \ +do { \ + Dl_info dlinfo; \ + void *dlret; \ + dlret = __builtin_return_address((level)); \ + assert(dladdr(dlret, &dlinfo)); \ + (addr) = dlinfo.dli_saddr; \ +} while(0) + +#define CELIX_EI_UNKNOWN_CALLER ((void *)-1) + +#define CELIX_EI_DECLARE(name, ret_type) \ +void celix_ei_expect_##name(void *caller, unsigned int level, ret_type ret, size_t ordinal=1) + +#define CELIX_EI_DEFINE(name, ret_type) \ +static void *name ## _caller; \ +static unsigned int name ## _caller_level; \ +static ret_type name ## _ret; \ +static size_t name ## _ordinal; \ + \ +void celix_ei_expect_##name(void *caller, unsigned int level, ret_type ret, size_t ordinal) \ +{ \ + name ## _caller = (caller); \ + name ## _caller_level = (level); \ + name ## _ret = (ret); \ + name ## _ordinal = (ordinal); \ +} + + +#define CELIX_EI_IMPL0(name) \ +do { \ + void *addr = CELIX_EI_UNKNOWN_CALLER; \ + if(name##_caller) { \ + if(name ## _caller != CELIX_EI_UNKNOWN_CALLER) { \ + /* we can not use CELIX_EI_GET_CALLER(addr, name ## _caller_level) */ \ + switch(name ## _caller_level) { \ + case 0: \ + CELIX_EI_GET_CALLER(addr, 0); \ + break; \ + case 1: \ + CELIX_EI_GET_CALLER(addr, 1); \ + break; \ + default: \ + assert(0); \ + } \ + } \ + if(name ## _caller == addr) { \ + if(__atomic_fetch_sub(&(name ## _ordinal), 1, __ATOMIC_RELAXED) == 1 && name ## _ret == 0) \ + return name ## _ret; \ + } \ + } \ +} while(0) + +#ifdef __cplusplus +} +#endif +#endif //CELIX_ERROR_INJECTOR_H diff --git a/bundles/pubsub/pubsub_protocol/pubsub_protocol_lib/gtest/CMakeLists.txt b/misc/error_injector/malloc/CMakeLists.txt similarity index 57% copy from bundles/pubsub/pubsub_protocol/pubsub_protocol_lib/gtest/CMakeLists.txt copy to misc/error_injector/malloc/CMakeLists.txt index d21d3c90..130a1239 100644 --- a/bundles/pubsub/pubsub_protocol/pubsub_protocol_lib/gtest/CMakeLists.txt +++ b/misc/error_injector/malloc/CMakeLists.txt @@ -15,12 +15,9 @@ # specific language governing permissions and limitations # under the License. -add_executable(celix_pswp_common_tests src/PS_WP_common_tests.cc) -target_include_directories(celix_pswp_common_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../src) -target_link_libraries(celix_pswp_common_tests PRIVATE celix_pubsub_protocol_lib GTest::gtest Celix::pubsub_spi GTest::gtest_main ${CMAKE_DL_LIBS}) -if (NOT ENABLE_ADDRESS_SANITIZER) - target_compile_definitions(celix_pswp_common_tests PRIVATE ENABLE_MALLOC_RETURN_NULL_TESTS) -endif () +add_library(malloc_ei STATIC malloc_ei.cpp) -add_test(NAME celix_pswp_common_tests COMMAND celix_pswp_common_tests) -setup_target_for_coverage(celix_pswp_common_tests SCAN_DIR ..) \ No newline at end of file +target_include_directories(malloc_ei PUBLIC ${CMAKE_CURRENT_LIST_DIR}) +target_link_libraries(malloc_ei PUBLIC error_injector) +# It plays nicely with address sanitizer this way. +target_link_options(malloc_ei INTERFACE -Wl,-wrap,malloc -Wl,-wrap,realloc) \ No newline at end of file diff --git a/misc/error_injector/malloc/malloc_ei.cpp b/misc/error_injector/malloc/malloc_ei.cpp new file mode 100644 index 00000000..aadb4f28 --- /dev/null +++ b/misc/error_injector/malloc/malloc_ei.cpp @@ -0,0 +1,36 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you 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. + */ + +#include <malloc_ei.h> + +extern "C" { +void *__real_malloc(size_t); +CELIX_EI_DEFINE(malloc, void *) +void *__wrap_malloc(size_t size) { + CELIX_EI_IMPL0(malloc); + return __real_malloc(size); +} + +void *__real_realloc(void *__ptr, size_t __size); +CELIX_EI_DEFINE(realloc, void *) +void *__wrap_realloc(void *__ptr, size_t __size) { + CELIX_EI_IMPL0(realloc); + return __real_realloc(__ptr, __size); +} +} \ No newline at end of file diff --git a/misc/error_injector/malloc/malloc_ei.h b/misc/error_injector/malloc/malloc_ei.h new file mode 100644 index 00000000..7d00c327 --- /dev/null +++ b/misc/error_injector/malloc/malloc_ei.h @@ -0,0 +1,34 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you 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. + */ + +#ifndef CELIX_MALLOC_EI_H +#define CELIX_MALLOC_EI_H +#ifdef __cplusplus +extern "C" { +#endif + +#include <error_injector.h> + +CELIX_EI_DECLARE(malloc, void *); +CELIX_EI_DECLARE(realloc, void *); + +#ifdef __cplusplus +} +#endif +#endif //CELIX_MALLOC_EI_H
