diff --git a/include/asterisk/logger.h b/include/asterisk/logger.h index 4392e5adf9b29bbf9370bd0ff5c5f939abca903c..67e4df74f0dc1319976752be673c6e4d53006d2c 100644 --- a/include/asterisk/logger.h +++ b/include/asterisk/logger.h @@ -715,10 +715,107 @@ void __attribute__((format (printf, 5, 6))) __ast_trace(const char *file, int li } \ int __scopevar ## __LINE__ ## __RETURN __attribute__((unused)) = __scopevar ## __LINE__ ## __ENTER() +/*! + * \brief Non RAII_VAR Scope Trace macros + * The advantage of these macros is that the EXITs will have the actual + * line number where the scope exited. Much less code is required as well. + */ + +/*! + * \brief Scope Enter + * + * \param level The trace level + * \param (optional) A printf style format string + * \param (optional) Arguments + */ +#define SCOPE_ENTER(level, ...) \ + int __scope_level = level; \ + if (TRACE_ATLEAST(level)) { \ + __ast_trace(__FILE__, __LINE__, __PRETTY_FUNCTION__, AST_TRACE_INDENT_INC_AFTER, " " __VA_ARGS__); \ + } \ + +/*! + * \brief Scope Exit + * + * \param (optional) A printf style format string + * \param (optional) Arguments + * + * \details + * This macro can be used at the exit points of a statement block since it just prints the message. + */ +#define SCOPE_EXIT(...) \ + if (TRACE_ATLEAST(__scope_level)) { \ + __ast_trace(__FILE__, __LINE__, __PRETTY_FUNCTION__, AST_TRACE_INDENT_DEC_BEFORE, " " __VA_ARGS__); \ + } \ + +/*! + * \brief Scope Exit with expression + * + * \param __expr An expression to execute after printing the message + * \param (optional) A printf style format string + * \param (optional) Arguments + * + * \details + * Handy for getting out of or continuing loops. + * + * \example + * while(something) { + * SCOPE_ENTER(2, "In a while\n"); + * if (something) { + * SCOPE_EXIT_EXPR(break, "Somethiung broke me\n"); + * } else { + * SCOPE_EXIT_EXPR(contniue, "Somethiung continued me\n"); + * } + * } + */ +#define SCOPE_EXIT_EXPR(__expr, ...) \ + if (TRACE_ATLEAST(__scope_level)) { \ + __ast_trace(__FILE__, __LINE__, __PRETTY_FUNCTION__, AST_TRACE_INDENT_DEC_BEFORE, " " __VA_ARGS__); \ + } \ + __expr + +/*! + * \brief Scope Exit with return + * + * \param (optional) A printf style format string + * \param (optional) Arguments + * + * \details + * This macro can be used at the exit points of a function when no value + * needs to be returned. + */ +#define SCOPE_EXIT_RTN(...) \ + if (TRACE_ATLEAST(__scope_level)) { \ + __ast_trace(__FILE__, __LINE__, __PRETTY_FUNCTION__, AST_TRACE_INDENT_DEC_BEFORE, " " __VA_ARGS__); \ + } \ + return + +/*! + * \brief Scope Exit with return value + * + * \param __return_value The return value + * \param (optional) A printf style format string + * \param (optional) Arguments + * + * \details + * This macro can be used at the exit points of a function when a value + * needs to be returned. + */ +#define SCOPE_EXIT_RTN_VALUE(__return_value, ...) \ + if (TRACE_ATLEAST(__scope_level)) { \ + __ast_trace(__FILE__, __LINE__, __PRETTY_FUNCTION__, AST_TRACE_INDENT_DEC_BEFORE, " " __VA_ARGS__); \ + } \ + return(__return_value) + #else -#define ast_trace_raw(__level, __indent_type, __fmt, ...) -#define ast_trace(__level) -#define SCOPE_TRACE(__level) +#define ast_trace_raw(__level, __indent_type, ...) +#define ast_trace(__level, ...) +#define SCOPE_TRACE(__level, ...) +#define SCOPE_ENTER(level, ...) +#define SCOPE_EXIT(...) +#define SCOPE_EXIT_EXPR(__expr, ...) +#define SCOPE_EXIT_RTN(...) +#define SCOPE_EXIT_RTN_VALUE(__return_value, ...) #endif #if defined(__cplusplus) || defined(c_plusplus) diff --git a/include/asterisk/strings.h b/include/asterisk/strings.h index c56a13940d97cbe7f226b7bd12b04f3af95a911e..e9d786c2f3ce6724b39af00380892bf181b7b3db 100644 --- a/include/asterisk/strings.h +++ b/include/asterisk/strings.h @@ -1090,6 +1090,59 @@ int __attribute__((format(printf, 3, 4))) ast_str_append( } ) +/*! + * \brief Provides a temporary ast_str and returns a copy of its buffer + * \since 16.12 + * \since 17.6 + * \since 18.0 + * + * \param init_len The initial length of the temporary ast_str needed. + * \param __expr An expression that needs the temporary ast_str and returns a char *. + * + * \returns A copy of __expr's return buffer allocated on the stack. + * + * \details + * There are a few query functions scattered around that need an ast_str in which + * to assemble the results but it's not always convenient to create an ast_str + * and ensure it's freed just to print a log message. For example... + * + * struct ast_str *temp = ast_str_create(128); + * ast_log(LOG_INFO, "Format caps: %s\n", ast_format_cap_get_names(caps, &temp)); + * ast_free(temp); + * + * That's not bad if you only have to do it once but some of our code that deals + * with streams and codecs is pretty complex and good instrumentation is essential. + * The aim of this function is to make that easier. + * + * With this macro, the above code can be simplified as follows... + * \example + * ast_log(LOG_INFO, "Format caps: %s\n", + * ast_str_tmp(128, ast_format_cap_get_names(caps, &STR_TMP)); + * + * STR_TMP will always be a reference to the temporary ast_str created + * by the macro. Its scope is limited by the macro so you can use it multiple + * times without conflict. + * + * \example + * ast_log(LOG_INFO, "Format caps in: %s Format caps out: %s\n", + * ast_str_tmp(128, ast_format_cap_get_names(caps_in, &STR_TMP), + * ast_str_tmp(128, ast_format_cap_get_names(caps_out, &STR_TMP) + * ); + * + * \warning + * The returned string is stack allocated so don't go overboard. + * + */ +#define ast_str_tmp(init_len, __expr) \ +({ \ + struct ast_str *STR_TMP = ast_str_create(init_len); \ + char *ret = ast_strdupa(__expr); \ + ast_free(STR_TMP); \ + ret; \ +}) + + + /*! * \brief Check if a string is only digits * diff --git a/tests/test_scope_trace.c b/tests/test_scope_trace.c index 6ab480aa4b0956f28a29fa8824aafa48beb81d6e..56f0875d6f2e2ba575f5c329be7acc2776e772f1 100644 --- a/tests/test_scope_trace.c +++ b/tests/test_scope_trace.c @@ -38,58 +38,77 @@ #include "asterisk/test.h" #include "asterisk/logger.h" -static void test_scope2(void) + +static const char *str_appender(struct ast_str**buf, char *a) { - SCOPE_TRACE(1); + ast_str_append(buf, 0, "<append %s>", a); + return ast_str_buffer(*buf); } -static void test_scope(void) +static void test_scope_trace(void) { - SCOPE_TRACE(1, "nested function: %d * %d = %d\n", 6, 7, (6 * 7)); - - test_scope2(); + SCOPE_ENTER(1, "subfunction\n"); + SCOPE_EXIT_RTN("got out\n"); +} - ast_trace(1, "test no variables\n"); +static int test_scope_enter_function(void) +{ + SCOPE_ENTER(1, "%s %s %s %s %s %s %s\n", + ast_str_tmp(12, str_appender(&STR_TMP, "str1")), + ast_str_tmp(12, str_appender(&STR_TMP, "str2")), + ast_str_tmp(32, str_appender(&STR_TMP, "AAAAAAAAAAAAAAAAAAAAAAAA")), + ast_str_tmp(12, str_appender(&STR_TMP, "B")), + "ccccccccccccc", + ast_str_tmp(12, str_appender(&STR_TMP, "DDDDD")), + ast_str_tmp(12, str_appender(&STR_TMP, "ww")) + ); + + test_scope_trace(); + + SCOPE_EXIT_RTN_VALUE(AST_TEST_PASS, "test no variables\n"); } AST_TEST_DEFINE(scope_test) { - SCOPE_TRACE(1, "top %s function\n", "scope_test"); + SCOPE_ENTER(1, "top %s function\n", "scope_test"); ast_trace(1, "%s\n", "test outer"); switch (cmd) { case TEST_INIT: + { + SCOPE_ENTER(1, "TEST_INIT\n"); info->name = "scope_test"; info->category = "/main/logging/"; info->summary = "Scope Trace Tests"; info->description = "Scope Trace Tests"; - return AST_TEST_NOT_RUN; + /* need to exit the case scope */ + SCOPE_EXIT("TEST_INIT\n"); + /* need to exit the function */ + SCOPE_EXIT_RTN_VALUE(AST_TEST_NOT_RUN, "BYE\n"); + } case TEST_EXECUTE: - { - SCOPE_TRACE(1, "CASE statement\n"); - ast_trace(1, "%s\n", "test case"); - } - break; + { + SCOPE_ENTER(1, "TEST_EXECUTE\n"); + ast_trace(1, "%s\n", "test execute"); + SCOPE_EXIT_EXPR(break, "TEST_EXECUTE\n"); + } + default: + ast_test_status_update(test, "Shouldn't have gotten here\n"); + return AST_TEST_FAIL; } if (1) { SCOPE_TRACE(1, "IF block\n"); - - test_scope(); + test_scope_enter_function(); } ast_trace(1); - ast_trace(1, "test no variables\n"); - - - - ast_trace(1, "%s\n", "test variable"); - return AST_TEST_PASS; + SCOPE_EXIT_RTN_VALUE(AST_TEST_PASS, "Something: %d\n", AST_TEST_PASS); } static int unload_module(void) diff --git a/tests/test_strings.c b/tests/test_strings.c index 90b0d3606c751ba0e2781132993a29d1dd537fb1..3d697e59fa4e2a430c6adc7e98589f2f0f956083 100644 --- a/tests/test_strings.c +++ b/tests/test_strings.c @@ -583,6 +583,43 @@ AST_TEST_DEFINE(strings_match) return AST_TEST_PASS; } +/*! + * \brief Function that needs a temporary ast_str + */ +static const char *str_appender(struct ast_str**buf, char *a) +{ + ast_str_append(buf, 0, "<%s>", a); + return ast_str_buffer(*buf); +} + +AST_TEST_DEFINE(temp_strings) +{ + char *return_buffer = ast_malloc(128); + switch (cmd) { + case TEST_INIT: + info->name = "temp_strings"; + info->category = "/main/strings/"; + info->summary = "Test ast_str_temp_buffer"; + info->description = "Test ast_str_temp_buffer"; + return AST_TEST_NOT_RUN; + case TEST_EXECUTE: + break; + } + + snprintf(return_buffer, 128, "%s %s %s %s %s", + ast_str_tmp(12, str_appender(&STR_TMP, "str1")), + ast_str_tmp(12, str_appender(&STR_TMP, "str2")), + ast_str_tmp(12, str_appender(&STR_TMP, "B")), + "ccccccccccccc", + ast_str_tmp(12, str_appender(&STR_TMP, "ww")) + ); + + ast_test_validate(test, ast_strings_match(return_buffer, "=", "<str1> <str2> <B> ccccccccccccc <ww>")); + + ast_free(return_buffer); + return AST_TEST_PASS; +} + static int unload_module(void) { AST_TEST_UNREGISTER(str_test); @@ -592,6 +629,7 @@ static int unload_module(void) AST_TEST_UNREGISTER(escape_semicolons_test); AST_TEST_UNREGISTER(escape_test); AST_TEST_UNREGISTER(strings_match); + AST_TEST_UNREGISTER(temp_strings); return 0; } @@ -604,6 +642,7 @@ static int load_module(void) AST_TEST_REGISTER(escape_semicolons_test); AST_TEST_REGISTER(escape_test); AST_TEST_REGISTER(strings_match); + AST_TEST_REGISTER(temp_strings); return AST_MODULE_LOAD_SUCCESS; }