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

Reply via email to