summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke T. Shumaker <lukeshu@lukeshu.com>2025-05-13 18:46:08 -0600
committerLuke T. Shumaker <lukeshu@lukeshu.com>2025-05-15 14:50:03 -0600
commitf01b89b599d40df5d6c127310b0030cd41c8dc85 (patch)
treef0f7c2d3746e684ba869784f9f3769ba4e7a1730
parent22336fb34f464dc751f86ad552fa1e40f06921ed (diff)
libmisc: log.h: Add support for libmisc/fmt.h printing
-rw-r--r--libmisc/include/libmisc/_intercept.h6
-rw-r--r--libmisc/include/libmisc/log.h11
-rw-r--r--libmisc/intercept.c7
-rw-r--r--libmisc/log.c20
-rw-r--r--libmisc/tests/test_log.c95
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;
}