util/log: improve logger_file newline handling
authorChia-I Wu <olvaffe@gmail.com>
Tue, 21 Feb 2023 18:02:35 +0000 (10:02 -0800)
committerMarge Bot <emma+marge@anholt.net>
Wed, 22 Feb 2023 17:55:40 +0000 (17:55 +0000)
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>

src/util/log.c

index e951bdb..5a5527d 100644 (file)
@@ -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