verbosity: fix the logger audit's correctness tier

The whole-tree logger audit's actionable findings, the ones that are
defects rather than policy:

- Xbox1 wrote the message body with wvsprintf, which takes no size:
  any expansion past the 256-byte stack buffer wrote off its end.
  Both Xbox1 and WinRT now go through bounded vsnprintf (the msvc
  compat header maps it for that toolchain) into a 1024-byte line,
  matching the generic branch's budget; truncation stays the policy
  on this heapless path.

- The ORBIS HAVE_LOGGER va_list macros handed debugNetPrintf
  (tag, fmt, vp): the tag became the format string and a raw va_list
  rode as a value argument, and RARCH_WARN_V mapped to a different
  debugnet level than RARCH_WARN. The non-ORBIS logger_send spelling
  was two calls per line - prefix plus tag, then body - which
  interleave under concurrent writers, with one branch carrying a
  stray "[WARN] :: " prefix. All eight *_V sites now route through
  one bounded format-then-send helper per backend: one call per
  line, body formatted, levels matching the non-V spellings
  (debugnet has no WARN level, so warnings are INFO on both
  spellings).

- LibNX ran mutexInit on every retro_main_log_file_init: a
  file->console->file cycle re-initialized a mutex another thread
  may hold for a write. Once per process now, and deinit still never
  destroys it, for the same reason.

- The Android file arm logged the bare body with no tag - the one
  line-format inconsistency across platforms - through its own
  vfprintf pair. The generic branch's format-once single-write is
  now a shared helper, and Android's file arm goes through it: tag
  included, line whole under stdio's per-call lock (logcat keeps
  receiving the tag as a priority).

- RARCH_LOG_BUFFER computed data + len before any NULL check - UB on
  a NULL buffer - and carried a dead 'end' from an older loop. NULL
  with a length returns; the leftover is gone.

samples/frontend/logger_macros compiles verbosity.h under all four
HAVE_LOGGER configurations (ORBIS x IS_SALAMANDER) against capturing
sinks and drives the *_V macros with real va_lists: one sink call
per line, body formatted, tag and prefix present, WARN_V at
RARCH_WARN's level. Against the pre-fix header the logger
configuration fails on exactly the audited shapes (two calls per
line, tag lost, prefix lost); the harness also documents that the
old macros only ever compiled with literal tags, since they pasted
the tag into a string constant.

The audit's policy items stay open deliberately: per-line fflush vs
crash durability, macro early-outs and a compile-time RARCH_DBG,
printf format attributes (a tree-wide -Wformat sweep of its own),
the verbosity_get_ptr writer walk, and asl_close on the Apple
client. Each changes behavior or blast radius and gets its own
decision.

Verified: the logger-macro harness green on all four configurations
and red as stated on the pre-fix header, log_atomicity green, host
syntax passes under -DHAVE_LIBNX (stubbed mutex) and -DANDROID
(stubbed logcat) with implicit-function-declaration promoted to an
error, five forced atomic backends, C89 gate at baseline, full build
scanned clean for the warning classes, thread-read audit 0 findings
/ 0 allowlisted pairs, hot-function budgets and the 74-lane compile
matrix.
This commit is contained in:
LibretroAdmin authored and libretroadmin committed 2026-09-16 09:33:57 +02:00
1 parent 56b0b748d3
commit 0205b6c016
6 files changed
+372 -111

No files matched your search

+10
View File
@@ -0,0 +1,10 @@
TARGET := logger_macros_test
all:
@sh ./build.sh
clean:
rm -f $(TARGET)
rm -rf dbgnet_stub
.PHONY: all clean
+28
View File
@@ -0,0 +1,28 @@
#!/bin/sh
# Logger-macro capture harness: compiles verbosity.h under the four
# HAVE_LOGGER configurations (ORBIS x IS_SALAMANDER) against
# capturing sinks and runs each. The ORBIS builds use the stubbed
# debugnet header beside this script.
set -eu
cd "$(dirname "$0")"
CC="${CC:-gcc}"
INC="-I../../../libretro-common/include -I../../.."
mkdir -p dbgnet_stub
cat > dbgnet_stub/debugnet.h <<'HDR'
#ifndef DEBUGNET_H
#define DEBUGNET_H
#define DEBUGNET_NONE 0
#define DEBUGNET_INFO 1
#define DEBUGNET_ERROR 2
#define DEBUGNET_DEBUG 3
void debugNetPrintf(int level, const char *fmt, ...);
#endif
HDR
for CFG in "" "-DIS_SALAMANDER" "-DORBIS -Idbgnet_stub" \
"-DIS_SALAMANDER -DORBIS -Idbgnet_stub"; do
$CC -std=gnu99 -Wall -Werror -g $INC $CFG \
-o logger_macros_test logger_macros_test.c
./logger_macros_test >/dev/null
echo "ok: ${CFG:-plain HAVE_LOGGER}"
done
echo "built samples/frontend/logger_macros (all four configurations green)"
@@ -0,0 +1,8 @@
#ifndef DEBUGNET_H
#define DEBUGNET_H
#define DEBUGNET_NONE 0
#define DEBUGNET_INFO 1
#define DEBUGNET_ERROR 2
#define DEBUGNET_DEBUG 3
void debugNetPrintf(int level, const char *fmt, ...);
#endif
@@ -0,0 +1,187 @@
/* The HAVE_LOGGER va_list macros ship one formatted line.
*
* Compiles verbosity.h under the logger configurations that a
* desktop build never sees and drives the *_V macros against
* capturing sinks:
*
* ORBIS (either salamander flavour): debugNetPrintf is
* printf-style with no va_list variant. The old macros handed it
* (tag, fmt, vp) - the tag became the format string and a raw
* va_list rode as a value argument - and mapped RARCH_WARN_V to a
* different level than RARCH_WARN. The sink must now receive one
* call per line, at the level matching the non-V spelling, with
* the body formatted.
*
* logger_send (non-ORBIS): the old macros were two calls - prefix
* plus tag, then body - which interleave under concurrent
* writers. One call per line now, prefix, tag and formatted body
* together, and the "[WARN] :: " oddity gone.
*
* Build selects the region with -DIS_SALAMANDER / -DORBIS; the
* build script runs all four combinations.
*/
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#define HAVE_LOGGER 1
#include "../../../verbosity.h"
static unsigned failures = 0;
#define CHECK(cond, ...) \
do { \
if (!(cond)) \
{ \
fprintf(stderr, "FAIL %s:%d: ", __FILE__, __LINE__); \
fprintf(stderr, __VA_ARGS__); \
fprintf(stderr, "\n"); \
failures++; \
} \
} while (0)
/* Capturing sinks: every call lands here as one formatted string
* plus, for debugnet, its level. */
static unsigned sink_calls = 0;
static int sink_level = -1;
static char sink_text[600];
#ifdef ORBIS
void debugNetPrintf(int level, const char *fmt, ...)
{
va_list ap;
sink_calls++;
sink_level = level;
va_start(ap, fmt);
vsnprintf(sink_text, sizeof(sink_text), fmt, ap);
va_end(ap);
}
#else
void logger_init(void) { }
void logger_shutdown(void) { }
void logger_send(const char *fmt, ...)
{
va_list ap;
sink_calls++;
va_start(ap, fmt);
vsnprintf(sink_text, sizeof(sink_text), fmt, ap);
va_end(ap);
}
void logger_send_v(const char *fmt, va_list ap)
{
/* The interleave-prone second call of the old shape: with the
* fix, nothing reaches it from the *_V macros. */
sink_calls++;
vsnprintf(sink_text, sizeof(sink_text), fmt, ap);
}
#endif
static void reset(void)
{
sink_calls = 0;
sink_level = -1;
sink_text[0] = '\0';
}
/* The macros take a va_list; these build one the way the function
* wrappers do. The tag is baked in as a literal: the logger macros
* paste it into a string constant, so a runtime tag never compiled
* against them. */
static void drive_log_v(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
RARCH_LOG_V("[Video]", fmt, ap);
va_end(ap);
}
static void drive_warn_v(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
#ifdef ORBIS
RARCH_WARN_V("[Audio]", fmt, ap);
#else
RARCH_WARN_V("[Input]", fmt, ap);
#endif
va_end(ap);
}
static void drive_err_v(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
#ifdef ORBIS
RARCH_ERR_V("[Core]", fmt, ap);
#else
RARCH_ERR_V("[Config]", fmt, ap);
#endif
va_end(ap);
}
int main(void)
{
/* One call, formatted body present, tag present. */
reset();
drive_log_v("mode %ux%u set\n", 640u, 480u);
CHECK(sink_calls == 1,
"LOG_V made %u sink calls, want exactly 1", sink_calls);
CHECK(strstr(sink_text, "640x480") != NULL /* %ux%u */,
"LOG_V body not formatted: \"%.80s\"", sink_text);
CHECK(strstr(sink_text, "[Video]") != NULL,
"LOG_V tag missing: \"%.80s\"", sink_text);
#ifdef ORBIS
CHECK(sink_level == DEBUGNET_INFO,
"LOG_V level %d, want DEBUGNET_INFO", sink_level);
/* WARN_V at the same level as RARCH_WARN. */
reset();
RARCH_WARN("plain warn\n");
{
int warn_level = sink_level;
reset();
drive_warn_v("underrun of %d frames\n", 32);
CHECK(sink_calls == 1,
"WARN_V made %u sink calls, want 1", sink_calls);
CHECK(sink_level == warn_level,
"WARN_V level %d differs from RARCH_WARN's %d",
sink_level, warn_level);
}
reset();
drive_err_v("load failed: %s\n", "missing file");
CHECK(sink_level == DEBUGNET_ERROR,
"ERR_V level %d, want DEBUGNET_ERROR", sink_level);
CHECK(strstr(sink_text, "missing file") != NULL,
"ERR_V body not formatted: \"%.80s\"", sink_text);
#else
/* No " :: " residue, and the error prefix rides the one call. */
reset();
drive_warn_v("pad %d gone\n", 2);
CHECK(sink_calls == 1,
"WARN_V made %u sink calls, want 1", sink_calls);
CHECK(strstr(sink_text, " :: ") == NULL,
"WARN_V still carries the :: oddity: \"%.80s\"", sink_text);
CHECK(strstr(sink_text, "pad 2 gone") != NULL,
"WARN_V body not formatted: \"%.80s\"", sink_text);
reset();
drive_err_v("parse error at %d\n", 7);
CHECK(sink_calls == 1,
"ERR_V made %u sink calls, want 1", sink_calls);
CHECK(strstr(sink_text, "[ERROR]") != NULL,
"ERR_V prefix missing: \"%.80s\"", sink_text);
CHECK(strstr(sink_text, "parse error at 7") != NULL,
"ERR_V body not formatted: \"%.80s\"", sink_text);
#endif
if (failures)
{
fprintf(stderr, "FAILURES (%u)\n", failures);
return 1;
}
printf("logger_macros: all lanes passed\n");
return 0;
}
+83 -55
View File
@@ -181,12 +181,23 @@ bool *verbosity_get_ptr(void)
void retro_main_log_file_init(const char *path, bool append)
{
FILE *tmp;
#ifdef HAVE_LIBNX
static bool mtx_inited = false;
#endif
if (main_verbosity_st.initialized)
return;
#ifdef HAVE_LIBNX
mutexInit(&main_verbosity_st.mtx);
/* Once for the process: a file->console->file cycle re-enters
* here, and re-initializing a mutex another thread may be
* holding for a write is exactly the race the mutex exists to
* prevent. Deinit never destroys it for the same reason. */
if (!mtx_inited)
{
mutexInit(&main_verbosity_st.mtx);
mtx_inited = true;
}
#endif
/* Default to stderr; only override when a valid path is given */
@@ -235,10 +246,71 @@ void retro_main_log_file_deinit(void)
}
#if !defined(HAVE_LOGGER)
/* Compiled for its two consumers: the Android file arm and the
* generic branch. Xbox/WinRT formats into its own bounded buffer,
* and the Apple arms go through their platform sinks. */
#if defined(ANDROID) \
|| (!defined(_XBOX1) && !defined(__WINRT__) && !defined(__APPLE__))
/* Format tag and message into one buffer and write it with a single
* stdio call: the per-call lock then keeps concurrent lines whole.
* Lines wider than the stack buffer take an exact-sized heap detour
* rather than truncating; if that allocation fails, the truncated
* stack line ships - a shortened message over a dropped one. */
static void rarch_log_line_write(FILE *fp, const char *tag_v,
const char *fmt, va_list ap)
{
char line[1024];
char *out = line;
char *heap = NULL;
int t_len = snprintf(line, sizeof(line), "%s ", tag_v);
int b_len;
va_list ap_cp;
if (t_len < 0 || t_len >= (int)sizeof(line))
t_len = 0;
va_copy(ap_cp, ap);
b_len = vsnprintf(line + t_len, sizeof(line) - (size_t)t_len,
fmt, ap_cp);
va_end(ap_cp);
if (b_len >= (int)(sizeof(line) - (size_t)t_len))
{
size_t need = (size_t)t_len + (size_t)b_len + 1;
if ((heap = (char*)malloc(need)))
{
memcpy(heap, line, (size_t)t_len);
vsnprintf(heap + t_len, need - (size_t)t_len, fmt, ap);
out = heap;
}
}
else if (b_len < 0)
line[t_len] = '\0';
#if defined(HAVE_LIBNX)
/* Around exactly one write and its flush; libnx newlib's stdio
* locking history is why this exists at all. */
mutexLock(&main_verbosity_st.mtx);
#endif
fputs(out, fp);
fflush(fp);
#if defined(HAVE_LIBNX)
mutexUnlock(&main_verbosity_st.mtx);
#endif
free(heap);
}
#endif
void RARCH_LOG_V(const char *tag, const char *fmt, va_list ap)
{
#if defined(_XBOX1) || defined(__WINRT__)
char buffer[256];
/* wvsprintf takes no size and writes past any small buffer on a
* long expansion; vsnprintf is bounded on both targets (the msvc
* compat header maps it for the Xbox toolchain), and 1024 matches
* the line budget the generic branch uses. Truncation stays the
* policy here - no heap on this target. */
char buffer[1024];
int _len;
const char *tag_v = tag ? tag : FILE_PATH_LOG_INFO;
buffer[0] = '\0';
@@ -246,13 +318,7 @@ void RARCH_LOG_V(const char *tag, const char *fmt, va_list ap)
"%s: %s ", FILE_PATH_PROGRAM_NAME, tag_v);
if (_len > 0 && _len < (int)sizeof(buffer))
{
#if defined(__WINRT__)
vsnprintf(buffer + _len, sizeof(buffer) - (size_t)_len, fmt, ap);
#else
wvsprintf(buffer + _len, fmt, ap);
#endif
}
#ifdef _DEBUG
OutputDebugStringA(buffer);
#endif
@@ -267,9 +333,11 @@ void RARCH_LOG_V(const char *tag, const char *fmt, va_list ap)
FILE *fp = main_verbosity_st.fp;
if (main_verbosity_st.initialized && fp)
{
/* Already logging to file: single write path, no Android log overhead */
vfprintf(fp, fmt, ap);
fflush(fp);
/* Logging to file: one formatted write, tag included -
* the file carries the same line format as every other
* platform (logcat gets the tag as a priority instead). */
const char *tag_v = tag ? tag : FILE_PATH_LOG_INFO;
rarch_log_line_write(fp, tag_v, fmt, ap);
}
else
{
@@ -402,48 +470,7 @@ apple_log_done:;
* body-call pair could interleave between threads; lines
* wider than the stack buffer take an exact-sized heap
* detour rather than truncating. */
char line[1024];
char *out = line;
char *heap = NULL;
int t_len = snprintf(line, sizeof(line), "%s ", tag_v);
int b_len;
va_list ap_cp;
if (t_len < 0 || t_len >= (int)sizeof(line))
t_len = 0;
va_copy(ap_cp, ap);
b_len = vsnprintf(line + t_len, sizeof(line) - (size_t)t_len,
fmt, ap_cp);
va_end(ap_cp);
if (b_len >= (int)(sizeof(line) - (size_t)t_len))
{
size_t need = (size_t)t_len + (size_t)b_len + 1;
if ((heap = (char*)malloc(need)))
{
memcpy(heap, line, (size_t)t_len);
vsnprintf(heap + t_len, need - (size_t)t_len, fmt, ap);
out = heap;
}
/* On allocation failure the truncated stack line ships:
* a shortened message over a dropped one. */
}
else if (b_len < 0)
line[t_len] = '\0';
# if defined(HAVE_LIBNX)
/* Around exactly one write and its flush; libnx newlib's
* stdio locking history is why this exists at all. */
mutexLock(&main_verbosity_st.mtx);
# endif
fputs(out, fp);
fflush(fp);
# if defined(HAVE_LIBNX)
mutexUnlock(&main_verbosity_st.mtx);
# endif
free(heap);
rarch_log_line_write(fp, tag_v, fmt, ap);
}
#endif
}
@@ -454,9 +481,11 @@ void RARCH_LOG_BUFFER(uint8_t *data, size_t len)
{
size_t i;
size_t offset = 0;
const uint8_t *end = data + len;
uint8_t buf[16];
if (!data && len)
return;
RARCH_LOG("== %d-byte buffer ==================\n", (int)len);
/* Zero-init once; tail padding written below */
@@ -486,7 +515,6 @@ void RARCH_LOG_BUFFER(uint8_t *data, size_t len)
buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14], buf[15]);
}
RARCH_LOG("==================================\n");
(void)end; /* suppress unused-variable warning */
}
void RARCH_DBG(const char *fmt, ...)
+56 -56
View File
@@ -17,6 +17,7 @@
#define __RARCH_VERBOSITY_H
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <boolean.h>
@@ -60,6 +61,25 @@ void logger_shutdown (void);
void logger_send (const char *__format,...);
void logger_send_v(const char *__format, va_list args);
/* The va_list spellings format the body first: debugNetPrintf is
* printf-style with no va_list variant (handing it (tag, fmt, vp)
* makes the tag the format string and passes a raw va_list as a
* value), and two logger_send calls interleave under concurrent
* writers. Bounded at 512; logger consoles have no heap detour. */
#ifdef ORBIS
#define RARCH_LOGGER_V_IMPL(level, tag, fmt, vp) do { \
char _rarch_log_line[512]; \
vsnprintf(_rarch_log_line, sizeof(_rarch_log_line), fmt, vp); \
debugNetPrintf(level, "%s %s", tag, _rarch_log_line); \
} while (0)
#else
#define RARCH_LOGGER_V_IMPL(prefix, tag, fmt, vp) do { \
char _rarch_log_line[512]; \
vsnprintf(_rarch_log_line, sizeof(_rarch_log_line), fmt, vp); \
logger_send("%s%s %s", prefix, tag, _rarch_log_line); \
} while (0)
#endif
#ifdef IS_SALAMANDER
#ifdef ORBIS
@@ -71,33 +91,31 @@ void logger_send_v(const char *__format, va_list args);
debugNetPrintf(DEBUGNET_INFO,"" __VA_ARGS__); \
} while (0)
#define RARCH_LOG_V(tag, fmt, vp) do { \
debugNetPrintf(DEBUGNET_DEBUG,tag,fmt,vp); \
} while (0)
#define RARCH_LOG_V(tag, fmt, vp) \
RARCH_LOGGER_V_IMPL(DEBUGNET_INFO, tag, fmt, vp)
#define RARCH_ERR(...) do { \
debugNetPrintf(DEBUGNET_ERROR,"" __VA_ARGS__); \
} while (0)
#define RARCH_ERR_V(tag, fmt, vp) do { \
debugNetPrintf(DEBUGNET_ERROR,tag,fmt,vp); \
} while (0)
#define RARCH_ERR_V(tag, fmt, vp) \
RARCH_LOGGER_V_IMPL(DEBUGNET_ERROR, tag, fmt, vp)
#define RARCH_WARN(...) do { \
debugNetPrintf(DEBUGNET_INFO,"" __VA_ARGS__); \
} while (0)
#define RARCH_WARN_V(tag, fmt, vp) do { \
debugNetPrintf(DEBUGNET_DEBUG,tag,fmt,vp); \
} while (0)
/* debugnet has no WARN level: INFO on both spellings, matching
* RARCH_WARN, instead of one INFO and one DEBUG. */
#define RARCH_WARN_V(tag, fmt, vp) \
RARCH_LOGGER_V_IMPL(DEBUGNET_INFO, tag, fmt, vp)
#define RARCH_LOG_OUTPUT(...) do { \
debugNetPrintf(DEBUGNET_INFO,"" __VA_ARGS__); \
} while (0)
#define RARCH_LOG_OUTPUT_V(tag, fmt, vp) do { \
debugNetPrintf(DEBUGNET_INFO,tag,fmt,vp); \
} while (0)
#define RARCH_LOG_OUTPUT_V(tag, fmt, vp) \
RARCH_LOGGER_V_IMPL(DEBUGNET_INFO, tag, fmt, vp)
#else
#define RARCH_DBG(...) do { \
@@ -108,37 +126,29 @@ void logger_send_v(const char *__format, va_list args);
logger_send("RetroArch Salamander: " __VA_ARGS__); \
} while (0)
#define RARCH_LOG_V(tag, fmt, vp) do { \
logger_send("RetroArch Salamander: " tag); \
logger_send_v(fmt, vp); \
} while (0)
#define RARCH_LOG_V(tag, fmt, vp) \
RARCH_LOGGER_V_IMPL("RetroArch Salamander: ", tag, fmt, vp)
#define RARCH_LOG_OUTPUT(...) do { \
logger_send("[OUTPUT] " __VA_ARGS__); \
} while (0)
#define RARCH_LOG_OUTPUT_V(tag, fmt, vp) do { \
logger_send("[OUTPUT] " tag); \
logger_send_v(fmt, vp); \
} while (0)
#define RARCH_LOG_OUTPUT_V(tag, fmt, vp) \
RARCH_LOGGER_V_IMPL("[OUTPUT] ", tag, fmt, vp)
#define RARCH_ERR(...) do { \
logger_send("[ERROR] " __VA_ARGS__); \
} while (0)
#define RARCH_ERR_V(tag, fmt, vp) do { \
logger_send("[ERROR] " tag); \
logger_send_v(fmt, vp); \
} while (0)
#define RARCH_ERR_V(tag, fmt, vp) \
RARCH_LOGGER_V_IMPL("[ERROR] ", tag, fmt, vp)
#define RARCH_WARN(...) do { \
logger_send("[WARN] " __VA_ARGS__); \
} while (0)
#define RARCH_WARN_V(tag, fmt, vp) do { \
logger_send("[WARN] " tag); \
logger_send_v(fmt, vp); \
} while (0)
#define RARCH_WARN_V(tag, fmt, vp) \
RARCH_LOGGER_V_IMPL("[WARN] ", tag, fmt, vp)
#endif
#else /* IS_SALAMANDER */
@@ -151,33 +161,31 @@ void logger_send_v(const char *__format, va_list args);
debugNetPrintf(DEBUGNET_INFO,"" __VA_ARGS__); \
} while (0)
#define RARCH_LOG_V(tag, fmt, vp) do { \
debugNetPrintf(DEBUGNET_DEBUG,tag,fmt,vp); \
} while (0)
#define RARCH_LOG_V(tag, fmt, vp) \
RARCH_LOGGER_V_IMPL(DEBUGNET_INFO, tag, fmt, vp)
#define RARCH_ERR(...) do { \
debugNetPrintf(DEBUGNET_ERROR,"" __VA_ARGS__); \
} while (0)
#define RARCH_ERR_V(tag, fmt, vp) do { \
debugNetPrintf(DEBUGNET_ERROR,tag,fmt,vp); \
} while (0)
#define RARCH_ERR_V(tag, fmt, vp) \
RARCH_LOGGER_V_IMPL(DEBUGNET_ERROR, tag, fmt, vp)
#define RARCH_WARN(...) do { \
debugNetPrintf(DEBUGNET_INFO,"" __VA_ARGS__); \
} while (0)
#define RARCH_WARN_V(tag, fmt, vp) do { \
debugNetPrintf(DEBUGNET_DEBUG,tag,fmt,vp); \
} while (0)
/* debugnet has no WARN level: INFO on both spellings, matching
* RARCH_WARN, instead of one INFO and one DEBUG. */
#define RARCH_WARN_V(tag, fmt, vp) \
RARCH_LOGGER_V_IMPL(DEBUGNET_INFO, tag, fmt, vp)
#define RARCH_LOG_OUTPUT(...) do { \
debugNetPrintf(DEBUGNET_INFO,"" __VA_ARGS__); \
} while (0)
#define RARCH_LOG_OUTPUT_V(tag, fmt, vp) do { \
debugNetPrintf(DEBUGNET_INFO,tag,fmt,vp); \
} while (0)
#define RARCH_LOG_OUTPUT_V(tag, fmt, vp) \
RARCH_LOGGER_V_IMPL(DEBUGNET_INFO, tag, fmt, vp)
#else
#define RARCH_DBG(...) do { \
@@ -188,37 +196,29 @@ void logger_send_v(const char *__format, va_list args);
logger_send("" __VA_ARGS__); \
} while (0)
#define RARCH_LOG_V(tag, fmt, vp) do { \
logger_send("" tag); \
logger_send_v(fmt, vp); \
} while (0)
#define RARCH_LOG_V(tag, fmt, vp) \
RARCH_LOGGER_V_IMPL("", tag, fmt, vp)
#define RARCH_ERR(...) do { \
logger_send("[ERROR] " __VA_ARGS__); \
} while (0)
#define RARCH_ERR_V(tag, fmt, vp) do { \
logger_send("[ERROR] " tag); \
logger_send_v(fmt, vp); \
} while (0)
#define RARCH_ERR_V(tag, fmt, vp) \
RARCH_LOGGER_V_IMPL("[ERROR] ", tag, fmt, vp)
#define RARCH_WARN(...) do { \
logger_send("[WARN] " __VA_ARGS__); \
} while (0)
#define RARCH_WARN_V(tag, fmt, vp) do { \
logger_send("[WARN] :: " tag); \
logger_send_v(fmt, vp); \
} while (0)
#define RARCH_WARN_V(tag, fmt, vp) \
RARCH_LOGGER_V_IMPL("[WARN] ", tag, fmt, vp)
#define RARCH_LOG_OUTPUT(...) do { \
logger_send("[OUTPUT] " __VA_ARGS__); \
} while (0)
#define RARCH_LOG_OUTPUT_V(tag, fmt, vp) do { \
logger_send("[OUTPUT] " tag); \
logger_send_v(fmt, vp); \
} while (0)
#define RARCH_LOG_OUTPUT_V(tag, fmt, vp) \
RARCH_LOGGER_V_IMPL("[OUTPUT] ", tag, fmt, vp)
#endif
#endif
#define RARCH_LOG_BUFFER(...) do { } while (0)