rcutils  master
C API providing common utilities and data structures.
Macros | Functions
fault_injection.h File Reference
#include <stdbool.h>
#include <stdio.h>
#include <stdint.h>
#include "rcutils/macros.h"
#include "rcutils/visibility_control.h"
Include dependency graph for fault_injection.h:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Macros

#define RCUTILS_FAULT_INJECTION_NEVER_FAIL   -1
 
#define RCUTILS_FAULT_INJECTION_FAIL_NOW   0
 
#define RCUTILS_FAULT_INJECTION_MAYBE_RETURN_ERROR(return_value_on_error)
 This macro checks and decrements a static global variable atomic counter and returns return_value_on_error if 0. More...
 
#define RCUTILS_FAULT_INJECTION_MAYBE_FAIL(failure_code)
 This macro checks and decrements a static global variable atomic counter and executes failure_code if the counter is 0 inside a scoped block (any variables declared in failure_code) will not be avaliable outside of this scoped block. More...
 
#define RCUTILS_FAULT_INJECTION_TEST(code)
 
#define RCUTILS_NO_FAULT_INJECTION(code)
 

Functions

bool rcutils_fault_injection_is_test_complete (void)
 
void rcutils_fault_injection_set_count (int_least64_t count)
 Atomically set the fault injection counter. More...
 
int_least64_t rcutils_fault_injection_get_count (void)
 Atomically get the fault injection counter value. More...
 
int_least64_t _rcutils_fault_injection_maybe_fail (void)
 Implementation of fault injection decrementer. More...
 

Macro Definition Documentation

◆ RCUTILS_FAULT_INJECTION_NEVER_FAIL

#define RCUTILS_FAULT_INJECTION_NEVER_FAIL   -1

◆ RCUTILS_FAULT_INJECTION_FAIL_NOW

#define RCUTILS_FAULT_INJECTION_FAIL_NOW   0

◆ RCUTILS_FAULT_INJECTION_MAYBE_RETURN_ERROR

#define RCUTILS_FAULT_INJECTION_MAYBE_RETURN_ERROR (   return_value_on_error)
Value:
printf( \
"%s:%d Injecting fault and returning " #return_value_on_error "\n", __FILE__, __LINE__); \
return return_value_on_error; \
}

This macro checks and decrements a static global variable atomic counter and returns return_value_on_error if 0.

This macro is not a function itself, so when this macro returns it will cause the calling function to return with the return value.

Set the counter with RCUTILS_FAULT_INJECTION_SET_COUNT. If the count is less than 0, then RCUTILS_FAULT_INJECTION_MAYBE_RETURN_ERROR will not cause an early return.

This macro is thread-safe, and ensures that at most one invocation results in a failure for each time the fault injection counter is set with RCUTILS_FAULT_INJECTION_SET_COUNT

Parameters
return_value_on_errorthe value to return in the case of fault injected failure.

◆ RCUTILS_FAULT_INJECTION_MAYBE_FAIL

#define RCUTILS_FAULT_INJECTION_MAYBE_FAIL (   failure_code)
Value:
printf( \
"%s:%d Injecting fault and executing " #failure_code "\n", __FILE__, __LINE__); \
failure_code; \
}

This macro checks and decrements a static global variable atomic counter and executes failure_code if the counter is 0 inside a scoped block (any variables declared in failure_code) will not be avaliable outside of this scoped block.

This macro is not a function itself, so it will cause the calling function to execute the code from within an if loop.

Set the counter with RCUTILS_FAULT_INJECTION_SET_COUNT. If the count is less than 0, then RCUTILS_FAULT_INJECTION_MAYBE_FAIL will not execute the failure code.

This macro is thread-safe, and ensures that at most one invocation results in a failure for each time the fault injection counter is set with RCUTILS_FAULT_INJECTION_SET_COUNT

Parameters
failure_codethe code to execute in the case of fault injected failure.

◆ RCUTILS_FAULT_INJECTION_TEST

#define RCUTILS_FAULT_INJECTION_TEST (   code)
Value:
do { \
int fault_injection_count = 0; \
do { \
rcutils_fault_injection_set_count(fault_injection_count++); \
code; \
rcutils_fault_injection_set_count(RCUTILS_FAULT_INJECTION_NEVER_FAIL); \
} while (0)

The fault injection macro for use with unit tests to check that code can tolerate injected failures at all points along the execution path where the indicating macros RCUTILS_CAN_RETURN_WITH_ERROR_OF and RCUTILS_CAN_FAIL_WITH are located.

This macro is intended to be used within a gtest function macro like 'TEST', 'TEST_F', etc.

code is executed within a do-while loop and therefore any variables declared within are in their own scope block.

Here's a simple example: RCUTILS_FAULT_INJECTION_TEST( rcl_ret_t ret = rcl_init(argc, argv, options, context); if (RCL_RET_OK == ret) { ret = rcl_shutdown(context); } });

In this example, you will need have conditional execution based on the return value of rcl_init. If it failed, then it wouldn't make sense to call rcl_shutdown. In your own test, there might be similar logic that requires conditional checks. The goal of writing this test is less about checking the behavior is consistent, but instead that failures do not cause program crashes, memory errors, or unnecessary memory leaks.

◆ RCUTILS_NO_FAULT_INJECTION

#define RCUTILS_NO_FAULT_INJECTION (   code)
Value:
do { \
int64_t no_fault_injection_count = rcutils_fault_injection_get_count(); \
rcutils_fault_injection_set_count(RCUTILS_FAULT_INJECTION_NEVER_FAIL); \
code; \
rcutils_fault_injection_set_count(no_fault_injection_count); \
} while (0)

A convenience macro built around rcutils_fault_injection_set_count() to pause fault injection during code execution. This macro is intended to be used within RCUTILS_FAULT_INJECTION_TEST() blocks.

code is executed within a do-while loop and therefore any variables declared within are in their own scope block.

Here's a simple example: RCUTILS_FAULT_INJECTION_TEST({ rcl_ret_t ret = rcl_init(argc, argv, options, context); if (RCL_RET_OK == ret) { RCUTILS_NO_FAULT_INJECTION({ ret = rcl_shutdown(context); }); } });

In this example, on successful rcl_init(), rcl_shutdown() is called while ensuring that it will not fail due to fault injection.

Function Documentation

◆ rcutils_fault_injection_is_test_complete()

bool rcutils_fault_injection_is_test_complete ( void  )

◆ rcutils_fault_injection_set_count()

void rcutils_fault_injection_set_count ( int_least64_t  count)

Atomically set the fault injection counter.

This is typically not the preferred method of interacting directly with the fault injection logic, instead use RCUTILS_FAULT_INJECTION_TEST instead.

This function may also be used for pausing code inside of a RCUTILS_FAULT_INJECTION_TEST with something like the following:

RCUTILS_FAULT_INJECTION_TEST({ ... // code to run with fault injection int64_t count = rcutils_fault_injection_get_count(); rcutils_fault_injection_set_count(RCUTILS_FAULT_INJECTION_NEVER_FAIL); ... // code to run without fault injection rcutils_fault_injection_set_count(count); ... // code to run with fault injection });

Parameters
countThe count to set the fault injection counter to. If count is negative, then fault injection errors will be disabled. The counter is globally initialized to RCUTILS_FAULT_INJECTION_NEVER_FAIL.

◆ rcutils_fault_injection_get_count()

int_least64_t rcutils_fault_injection_get_count ( void  )

Atomically get the fault injection counter value.

This function is typically not used directly but instead indirectly inside an RCUTILS_FAULT_INJECTION_TEST

◆ _rcutils_fault_injection_maybe_fail()

int_least64_t _rcutils_fault_injection_maybe_fail ( void  )

Implementation of fault injection decrementer.

This is included inside of macros, so it needs to be exported as a public function, but it should not be used directly.

rcutils_fault_injection_is_test_complete
bool rcutils_fault_injection_is_test_complete(void)
RCUTILS_FAULT_INJECTION_NEVER_FAIL
#define RCUTILS_FAULT_INJECTION_NEVER_FAIL
Definition: fault_injection.h:29
_rcutils_fault_injection_maybe_fail
int_least64_t _rcutils_fault_injection_maybe_fail(void)
Implementation of fault injection decrementer.
RCUTILS_FAULT_INJECTION_FAIL_NOW
#define RCUTILS_FAULT_INJECTION_FAIL_NOW
Definition: fault_injection.h:31
rcutils_fault_injection_get_count
int_least64_t rcutils_fault_injection_get_count(void)
Atomically get the fault injection counter value.