diff options
-rw-r--r-- | libmisc/include/libmisc/_intercept.h | 6 | ||||
-rw-r--r-- | libmisc/include/libmisc/log.h | 11 | ||||
-rw-r--r-- | libmisc/intercept.c | 7 | ||||
-rw-r--r-- | libmisc/log.c | 20 | ||||
-rw-r--r-- | libmisc/tests/test_log.c | 95 |
5 files changed, 113 insertions, 26 deletions
diff --git a/libmisc/include/libmisc/_intercept.h b/libmisc/include/libmisc/_intercept.h index 87993e4..f9b7c8c 100644 --- a/libmisc/include/libmisc/_intercept.h +++ b/libmisc/include/libmisc/_intercept.h @@ -28,4 +28,10 @@ size_t __lm_printf(const char *format, ...); [[gnu::format(printf, 1, 2)]] size_t __lm_light_printf(const char *format, ...); +/* While newlib defines putchar() to be [[gnu::weak]], pico_stdio does + * not. Plus the above about optimizations. + */ + +void __lm_putchar(unsigned char c); + #endif /* _LIBMISC__INTERCEPT_H_ */ diff --git a/libmisc/include/libmisc/log.h b/libmisc/include/libmisc/log.h index 4529946..78ee1e7 100644 --- a/libmisc/include/libmisc/log.h +++ b/libmisc/include/libmisc/log.h @@ -10,6 +10,7 @@ #include <stdint.h> /* for uint8_t */ #include <libmisc/macro.h> +#include <libmisc/fmt.h> #include <libmisc/_intercept.h> #ifdef NDEBUG @@ -20,15 +21,25 @@ const char *const_byte_str(uint8_t b); +extern lo_interface fmt_dest _log_dest; + #define log_n_errorf(nam, fmt, ...) do { __lm_printf("error: " LM_STR_(nam) ": " fmt "\n" __VA_OPT__(,) __VA_ARGS__); } while (0) #define log_n_infof(nam, fmt, ...) do { __lm_printf("info : " LM_STR_(nam) ": " fmt "\n" __VA_OPT__(,) __VA_ARGS__); } while (0) #define log_n_debugf(nam, fmt, ...) do { if (LM_CAT3_(CONFIG_, nam, _DEBUG) && !_LOG_NDEBUG) \ __lm_printf("debug: " LM_STR_(nam) ": " fmt "\n" __VA_OPT__(,) __VA_ARGS__); } while (0) +#define log_n_errorln(nam, ...) fmt_print(_log_dest, "error: " LM_STR_(nam) ": ", __VA_ARGS__, "\n") +#define log_n_infoln(nam, ...) fmt_print(_log_dest, "info : " LM_STR_(nam) ": ", __VA_ARGS__, "\n") +#define log_n_debugln(nam, ...) do { if (LM_CAT3_(CONFIG_, nam, _DEBUG) && !_LOG_NDEBUG) \ + fmt_print(_log_dest, "debug: " LM_STR_(nam) ": ", __VA_ARGS__, "\n"); } while (0) + #endif /* _LIBMISC_LOG_H_ */ #if defined(LOG_NAME) && !defined(log_errorf) #define log_errorf(fmt, ...) log_n_errorf(LOG_NAME, fmt, __VA_ARGS__) #define log_infof(fmt, ...) log_n_infof(LOG_NAME, fmt, __VA_ARGS__) #define log_debugf(fmt, ...) log_n_debugf(LOG_NAME, fmt, __VA_ARGS__) +#define log_errorln(...) log_n_errorln(LOG_NAME, __VA_ARGS__) +#define log_infoln(...) log_n_infoln(LOG_NAME, __VA_ARGS__) +#define log_debugln(...) log_n_debugln(LOG_NAME, __VA_ARGS__) #endif diff --git a/libmisc/intercept.c b/libmisc/intercept.c index 332bfa5..af10ea3 100644 --- a/libmisc/intercept.c +++ b/libmisc/intercept.c @@ -5,7 +5,7 @@ */ #include <stdarg.h> /* for va_list, va_start(), va_end() */ -#include <stdio.h> /* for vprintf() */ +#include <stdio.h> /* for vprintf(), putchar() */ #include <stdlib.h> /* for abort() */ #include <libmisc/_intercept.h> @@ -29,6 +29,11 @@ size_t __lm_light_printf(const char *format, ...) { } [[gnu::weak]] +void __lm_putchar(unsigned char c) { + (void) putchar(c); +} + +[[gnu::weak]] void __lm_abort(void) { abort(); } diff --git a/libmisc/log.c b/libmisc/log.c index be87de6..da4c92e 100644 --- a/libmisc/log.c +++ b/libmisc/log.c @@ -8,6 +8,26 @@ #include <libmisc/assert.h> /* for static_assert() */ +#include <libmisc/log.h> +#include <libmisc/_intercept.h> + +struct log_stdout {}; +LO_IMPLEMENTATION_H(fmt_dest, struct log_stdout, log_stdout); +LO_IMPLEMENTATION_C(fmt_dest, struct log_stdout, log_stdout, static); + +static size_t log_bytes = 0; + +static void log_stdout_putb(struct log_stdout *, uint8_t b) { + __lm_putchar(b); + log_bytes++; +} + +static size_t log_stdout_tell(struct log_stdout *) { + return log_bytes; +} + +lo_interface fmt_dest _log_dest = { .vtable = &_lo_log_stdout_fmt_dest_vtable }; + static const char *byte_strs[] = { "0x00", "0x01", diff --git a/libmisc/tests/test_log.c b/libmisc/tests/test_log.c index 1468b5a..e5abb61 100644 --- a/libmisc/tests/test_log.c +++ b/libmisc/tests/test_log.c @@ -4,11 +4,10 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -#define _GNU_SOURCE /* for vasprintf() */ #include <stdarg.h> /* for va_list */ -#include <stdio.h> /* for vasprintf() */ -#include <stdlib.h> /* for free() */ -#include <string.h> /* for strcmp() */ +#include <stdio.h> /* for vsnprintf() */ +#include <stdlib.h> /* for realloc(), free() */ +#include <string.h> /* for strlen(), strcmp() */ #define LOG_NAME FROBNICATE #include <libmisc/log.h> @@ -19,40 +18,63 @@ /* Intercept output ***********************************************************/ -static char *log_output = NULL; +static struct fmt_buf log_output = {}; size_t __lm_printf(const char *format, ...) { + restart: va_list va; va_start(va, format); - size_t ret = (size_t) vasprintf(&log_output, format, va); + size_t ret = (size_t) vsnprintf(log_output.dat, log_output.cap, format, va); va_end(va); + + if (ret > log_output.cap) { + log_output.cap = ret+8; + log_output.dat = realloc(log_output.dat, log_output.cap); + memset(log_output.dat, 0, log_output.cap); + goto restart; + } + log_output.len = ret; return ret; } +void __lm_putchar(unsigned char c) { + if (log_output.len+1 >= log_output.cap) { + log_output.cap += 16; + log_output.dat = realloc(log_output.dat, log_output.cap); + memset(log_output.dat + log_output.len, 0, log_output.cap - log_output.len); + } + ((uint8_t *)log_output.dat)[log_output.len++] = (uint8_t)c; +} + +static void log_output_clear(void) { + if (log_output.dat) + memset(log_output.dat, 0, log_output.cap); + log_output.len = 0; +} + /* Actual tests ***************************************************************/ -#define should_print(_exp, cmd) do { \ - char *exp = _exp; \ - test_assert(!log_output); \ - cmd; \ - if (!exp) \ - test_assert(!log_output); \ - else { \ - test_assert(log_output); \ - if (strcmp(log_output, exp)) { \ - printf("exp = \"%s\"\n" \ - "act = \"%s\"\n", \ - exp, log_output); \ - test_assert(0); \ - } \ - } \ - if (log_output) { \ - free(log_output); \ - log_output = NULL; \ - } \ +#define should_print(_exp, cmd) do { \ + char *exp = _exp; \ + test_assert(log_output.len == 0); \ + cmd; \ + if (!exp) \ + test_assert(log_output.len == 0); \ + else { \ + test_assert(log_output.dat); \ + test_assert(strlen(log_output.dat) == log_output.len); \ + if (strcmp(log_output.dat, exp)) { \ + printf("exp = \"%s\"\n" \ + "act = \"%s\"\n", \ + exp, (char *)log_output.dat); \ + test_assert(0); \ + } \ + } \ + log_output_clear(); \ } while (0) int main() { + /* printf */ should_print("error: FROBNICATE: val=42\n", log_errorf("val=%d", 42)); should_print("info : FROBNICATE: val=0\n", @@ -65,6 +87,29 @@ int main() { #define CONFIG_FROBNICATE_DEBUG 0 should_print(NULL, log_debugf("val=%d", -2)); +#undef CONFIG_FROBNICATE_DEBUG +#endif + + /* libmisc/fmt.h */ + should_print("error: FROBNICATE: val=42\n", + log_errorln("val=", 42)); + should_print("info : FROBNICATE: val=0\n", + log_infoln("val=", 0)); +#ifndef NDEBUG +#define CONFIG_FROBNICATE_DEBUG 1 + should_print("debug: FROBNICATE: val=-2\n", + log_debugln("val=", -2)); +#undef CONFIG_FROBNICATE_DEBUG +#define CONFIG_FROBNICATE_DEBUG 0 + should_print(NULL, + log_debugln("val=", -2)); +#undef CONFIG_FROBNICATE_DEBUG #endif + + if (log_output.dat) { + free(log_output.dat); + log_output.dat = NULL; + log_output.cap = 0; + } return 0; } |