diff --git a/src/include/libpmem.h b/src/include/libpmem.h index a1201889644..94ec291c727 100644 --- a/src/include/libpmem.h +++ b/src/include/libpmem.h @@ -1,5 +1,5 @@ /* SPDX-License-Identifier: BSD-3-Clause */ -/* Copyright 2014-2023, Intel Corporation */ +/* Copyright 2014-2024, Intel Corporation */ /* * libpmem.h -- definitions of libpmem entry points @@ -91,6 +91,50 @@ const char *pmem_check_version(unsigned major_required, const char *pmem_errormsg(void); +/* + * Available log levels. Log levels are used in the logging API calls + * to indicate logging message severity. Log levels are also used + * to define thresholds for the logging. + */ +enum pmem_log_level { + /* only basic library info */ + PMEM_LOG_LEVEL_HARK, + /* an error that causes the library to stop working immediately */ + PMEM_LOG_LEVEL_FATAL, + /* an error that causes the library to stop working properly */ + PMEM_LOG_LEVEL_ERROR, + /* an errors that could be handled in the upper level */ + PMEM_LOG_LEVEL_WARNING, + /* non-massive info mainly related to public API function completions */ + PMEM_LOG_LEVEL_NOTICE, + /* massive info e.g. every write operation indication */ + PMEM_LOG_LEVEL_INFO, + /* debug info e.g. write operation dump */ + PMEM_LOG_LEVEL_DEBUG, +}; + +/* + * the type used for defining logging functions + */ +typedef void pmem_log_function( + /* the log level of the message */ + enum pmem_log_level level, + /* name of the source file where the message coming from */ + const char *file_name, + /* the source file line where the message coming from */ + const int line_no, + /* the function name where the message coming from */ + const char *function_name, + /* message */ + const char *message); + +#define PMEM_LOG_USE_DEFAULT_FUNCTION (NULL) + +/* + * pmem_log_set_function - set the logging function + */ +int pmem_log_set_function(pmem_log_function *log_function); + #ifdef __cplusplus } #endif diff --git a/src/libpmem/libpmem.c b/src/libpmem/libpmem.c index d2705f67cfb..a1cef0f29b7 100644 --- a/src/libpmem/libpmem.c +++ b/src/libpmem/libpmem.c @@ -96,3 +96,14 @@ pmem_errormsg(void) { return pmem_errormsgU(); } + +/* + * pmem_log_set_function -- set the log function pointer either to + * a user-provided function pointer or to the default logging function. + */ +int +pmem_log_set_function(pmem_log_function *log_function) +{ + int ret = core_log_set_function((core_log_function *)log_function); + return core_log_error_translate(ret); +} diff --git a/src/test/Makefile b/src/test/Makefile index 961172eac35..07129133832 100644 --- a/src/test/Makefile +++ b/src/test/Makefile @@ -161,6 +161,7 @@ PMEM_TESTS = \ pmem_include\ pmem_is_pmem\ pmem_is_pmem_posix\ + pmem_log_set_function\ pmem_map_file\ pmem_has_auto_flush\ pmem_deep_persist\ diff --git a/src/test/pmem_log_set_function/.gitignore b/src/test/pmem_log_set_function/.gitignore new file mode 100644 index 00000000000..ee9f2a39097 --- /dev/null +++ b/src/test/pmem_log_set_function/.gitignore @@ -0,0 +1 @@ +pmem_log_set_function diff --git a/src/test/pmem_log_set_function/Makefile b/src/test/pmem_log_set_function/Makefile new file mode 100644 index 00000000000..ac703e1e285 --- /dev/null +++ b/src/test/pmem_log_set_function/Makefile @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright 2024, Intel Corporation + +TARGET = pmem_log_set_function +OBJS = pmem_log_set_function.o + +BUILD_STATIC_DEBUG=n +BUILD_STATIC_NONDEBUG=n + +# required for proper mock integration +LIBPMEMCOMMON=internal-debug +LIBPMEM=internal-debug + +include ../Makefile.inc +LDFLAGS += $(call extract_funcs, pmem_log_set_function.c) diff --git a/src/test/pmem_log_set_function/TESTS.py b/src/test/pmem_log_set_function/TESTS.py new file mode 100755 index 00000000000..0058a101a1a --- /dev/null +++ b/src/test/pmem_log_set_function/TESTS.py @@ -0,0 +1,27 @@ +#!../env.py +# SPDX-License-Identifier: BSD-3-Clause +# Copyright 2024, Intel Corporation +# + + +import testframework as t +from testframework import granularity as g + + +@g.require_granularity(g.ANY) +# The 'debug' build is chosen arbitrarily to ensure these tests are run only +# once. No dynamic libraries are used nor .static_* builds are available. +@t.require_build('debug') +class PMEM_LOG(t.BaseTest): + test_type = t.Short + + def run(self, ctx): + ctx.exec('pmem_log_set_function', self.test_case) + + +class TEST0(PMEM_LOG): + test_case = 'test_log_set_function' + + +class TEST1(PMEM_LOG): + test_case = 'test_log_set_function_EAGAIN' diff --git a/src/test/pmem_log_set_function/pmem_log_set_function.c b/src/test/pmem_log_set_function/pmem_log_set_function.c new file mode 100644 index 00000000000..9f92d9cbd15 --- /dev/null +++ b/src/test/pmem_log_set_function/pmem_log_set_function.c @@ -0,0 +1,91 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* Copyright 2024, Intel Corporation */ + +/* + * pmem_log_set_function.c -- unit test for pmem_log_set_function + */ + +#include "unittest.h" +#include "log_internal.h" +#include "libpmem.h" + +#define NO_ARGS_CONSUMED 0 + +#define VALIDATED_CALL 127 +#define CALLED (VALIDATED_CALL + 1) + +#define PMEM_LOG_CUSTOM_FUNCTION_MOCK ((pmem_log_function *) 0xA1C5D68F) + +/* Mock */ +static struct { + int ret; +} Core_log_set_function; + +FUNC_MOCK(core_log_set_function, int, core_log_function *log_function) + FUNC_MOCK_RUN(VALIDATED_CALL) { + UT_ASSERTeq((void *)log_function, + (void *)PMEM_LOG_CUSTOM_FUNCTION_MOCK); + return Core_log_set_function.ret; + } +FUNC_MOCK_RUN_DEFAULT { + return _FUNC_REAL(core_log_set_function)(log_function); +} +FUNC_MOCK_END + +/* Helper */ +static int +test_log_set_function_helper(int error) +{ + errno = 0; + Core_log_set_function.ret = error == NO_ERRNO ? 0 : error; + FUNC_MOCK_RCOUNTER_SET(core_log_set_function, VALIDATED_CALL); + int ret = pmem_log_set_function(PMEM_LOG_CUSTOM_FUNCTION_MOCK); + if (error == NO_ERRNO) { + UT_ASSERTeq(ret, 0); + } else { + UT_ASSERTeq(ret, 1); + UT_ASSERTeq(errno, error); + } + UT_ASSERTeq(RCOUNTER(core_log_set_function), CALLED); + + return NO_ARGS_CONSUMED; +} + +/* Tests */ +/* + * Check: + * - if core_log_set_function is called with proper argument + * - if pmem_log_set_function return 0 (no error) + * - no errno is set + */ +static int +test_log_set_function(const struct test_case *tc, int argc, char *argv[]) +{ + return test_log_set_function_helper(NO_ERRNO); +} + +/* + * core_log_set_function() with EAGAIN error + * Check: + * - if core_log_set_function is called with proper argument + * - if pmem_log_set_function return 1 (error via errno) + * - errno is set to EAGAIN + */ +static int +test_log_set_function_EAGAIN(const struct test_case *tc, int argc, char *argv[]) +{ + return test_log_set_function_helper(EAGAIN); +} + +static struct test_case test_cases[] = { + TEST_CASE(test_log_set_function), + TEST_CASE(test_log_set_function_EAGAIN), +}; + +int +main(int argc, char *argv[]) +{ + START(argc, argv, "pmem_log_set_function"); + TEST_CASE_PROCESS(argc, argv, test_cases, ARRAY_SIZE(test_cases)); + DONE(NULL); +}