From 7a18a1712a0a19b4251beaf582baba03805bb9f3 Mon Sep 17 00:00:00 2001 From: Chia-I Wu Date: Tue, 21 Feb 2023 10:02:35 -0800 Subject: [PATCH] util/log: improve logger_file newline handling Add logger_vasnprintf that will be used by other loggers. For logger_file, it improves newline handling for mesa_logd("%s", "hello\n"); Reviewed-by: Emma Anholt Reviewed-by: Jesse Natalie Part-of: --- src/util/log.c | 94 ++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 84 insertions(+), 10 deletions(-) diff --git a/src/util/log.c b/src/util/log.c index e951bdb7f1d..5a5527defac 100644 --- a/src/util/log.c +++ b/src/util/log.c @@ -88,24 +88,98 @@ level_to_str(enum mesa_log_level l) unreachable("bad mesa_log_level"); } +enum logger_vasnprintf_affix { + LOGGER_VASNPRINTF_AFFIX_TAG = 1 << 0, + LOGGER_VASNPRINTF_AFFIX_LEVEL = 1 << 1, + LOGGER_VASNPRINTF_AFFIX_NEWLINE = 1 << 2, +}; + +/* Try vsnprintf first and fall back to vasprintf if buf is too small. This + * function handles all errors and never fails. + */ +static char * +logger_vasnprintf(char *buf, + int size, + int affixes, + enum mesa_log_level level, + const char *tag, + const char *format, + va_list va) +{ + struct { + char *cur; + int rem; + int total; + bool invalid; + } state = { + .cur = buf, + .rem = size, + }; + +#define APPEND(state, func, ...) \ + do { \ + int ret = func(state.cur, state.rem, __VA_ARGS__); \ + if (ret < 0) { \ + state.invalid = true; \ + } else { \ + state.total += ret; \ + if (ret >= state.rem) \ + ret = state.rem; \ + state.cur += ret; \ + state.rem -= ret; \ + } \ + } while (false) + + if (affixes & LOGGER_VASNPRINTF_AFFIX_TAG) + APPEND(state, snprintf, "%s: ", tag); + if (affixes & LOGGER_VASNPRINTF_AFFIX_LEVEL) + APPEND(state, snprintf, "%s: ", level_to_str(level)); + + APPEND(state, vsnprintf, format, va); + + if (affixes & LOGGER_VASNPRINTF_AFFIX_NEWLINE) { + if (state.cur == buf || state.cur[-1] != '\n') + APPEND(state, snprintf, "\n"); + } +#undef APPEND + + assert(size >= 64); + if (state.invalid) { + strncpy(buf, "invalid message format", size); + } else if (state.total >= size) { + /* print again into alloc to avoid truncation */ + void *alloc = malloc(state.total + 1); + if (alloc) { + buf = logger_vasnprintf(alloc, state.total + 1, affixes, level, tag, + format, va); + assert(buf == alloc); + } else { + /* pretty-truncate the message */ + strncpy(buf + size - 4, "...", 4); + } + } + + return buf; +} + static void logger_file(enum mesa_log_level level, const char *tag, const char *format, va_list va) { -#if !DETECT_OS_WINDOWS - flockfile(stderr); -#endif + FILE *fp = stderr; + char local_msg[1024]; + char *msg = logger_vasnprintf(local_msg, sizeof(local_msg), + LOGGER_VASNPRINTF_AFFIX_TAG | + LOGGER_VASNPRINTF_AFFIX_LEVEL | + LOGGER_VASNPRINTF_AFFIX_NEWLINE, + level, tag, format, va); - fprintf(stderr, "%s: %s: ", tag, level_to_str(level)); - vfprintf(stderr, format, va); - if (format[strlen(format) - 1] != '\n') - fprintf(stderr, "\n"); + fprintf(fp, "%s", msg); -#if !DETECT_OS_WINDOWS - funlockfile(stderr); -#endif + if (msg != local_msg) + free(msg); } #if DETECT_OS_ANDROID