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 <emma@anholt.net>
Reviewed-by: Jesse Natalie <jenatali@microsoft.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/21454>
This commit is contained in:
Chia-I Wu 2023-02-21 10:02:35 -08:00 committed by Marge Bot
parent 63864d4dfc
commit 7a18a1712a

View file

@ -88,24 +88,98 @@ level_to_str(enum mesa_log_level l)
unreachable("bad mesa_log_level"); 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 static void
logger_file(enum mesa_log_level level, logger_file(enum mesa_log_level level,
const char *tag, const char *tag,
const char *format, const char *format,
va_list va) va_list va)
{ {
#if !DETECT_OS_WINDOWS FILE *fp = stderr;
flockfile(stderr); char local_msg[1024];
#endif 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)); fprintf(fp, "%s", msg);
vfprintf(stderr, format, va);
if (format[strlen(format) - 1] != '\n')
fprintf(stderr, "\n");
#if !DETECT_OS_WINDOWS if (msg != local_msg)
funlockfile(stderr); free(msg);
#endif
} }
#if DETECT_OS_ANDROID #if DETECT_OS_ANDROID