This adds a new __wine_dbg_vprintf internal export to format and output a debug message at once, using ms_abi varargs calling convention in all cases.
We convert format specifiers for the MSVC extensions, and translate the calling convention by passing format specifiers and their arguments to glibc snprintf one by one.
Signed-off-by: Rémi Bernon rbernon@codeweavers.com ---
As it was mentionned on IRC earlier, I figured I could clean these up a bit and more formally submit them. Also it looks like that +msvcrt is now completely broken, probably because it now traces itself over and over (where it was only tracing the TRACE from PE modules previously, which was already a bit annoying).
These changes also have the nice property of reducing the eventual SSE register spilling induced by the debug helpers being previously sysv_abi in non-PE modules, when called within ms_abi functions. This is solved in all cases by making them ms_abi and doing the calling convention conversion within ntdll.so.
Having the format string parsing in ntdll.so will also let us introduce some other extensions for TRACE messages, to replace common dbgstr_* helpers for instance and reduce the amount of common code and ntdll.so transitions, this is illustrated in the last 3 patches of the series, and the format specifiers could be made different of course.
dlls/ntdll/ntdll.spec | 1 + dlls/ntdll/thread.c | 7 ++ dlls/ntdll/unix/debug.c | 198 +++++++++++++++++++++++++++++++++++++++ dlls/ntdll/unix/loader.c | 1 + dlls/ntdll/unixlib.h | 3 +- dlls/winecrt0/debug.c | 14 +++ include/wine/debug.h | 40 ++++---- 7 files changed, 242 insertions(+), 22 deletions(-)
diff --git a/dlls/ntdll/ntdll.spec b/dlls/ntdll/ntdll.spec index 248ae72a011..3a7adc92752 100644 --- a/dlls/ntdll/ntdll.spec +++ b/dlls/ntdll/ntdll.spec @@ -1616,6 +1616,7 @@ @ cdecl -norelay __wine_dbg_header(long long str) @ cdecl -norelay __wine_dbg_output(str) @ cdecl -norelay __wine_dbg_strdup(str) +@ cdecl -norelay __wine_dbg_vprintf(str ptr)
# Virtual memory @ cdecl -syscall __wine_locked_recvmsg(long ptr long) diff --git a/dlls/ntdll/thread.c b/dlls/ntdll/thread.c index 578c7a5436c..a7231d008c6 100644 --- a/dlls/ntdll/thread.c +++ b/dlls/ntdll/thread.c @@ -74,6 +74,13 @@ int __cdecl __wine_dbg_output( const char *str ) return unix_funcs->dbg_output( str ); }
+/*********************************************************************** + * __wine_dbg_vprintf (NTDLL.@) + */ +int __cdecl __wine_dbg_vprintf( const char *format, __ms_va_list args ) +{ + return unix_funcs->dbg_vprintf( format, args ); +}
/******************************************************************* * KiUserApcDispatcher (NTDLL.@) diff --git a/dlls/ntdll/unix/debug.c b/dlls/ntdll/unix/debug.c index 368baac46f4..fe5c3c64272 100644 --- a/dlls/ntdll/unix/debug.c +++ b/dlls/ntdll/unix/debug.c @@ -30,6 +30,8 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <stdint.h> +#include <limits.h> #ifdef HAVE_UNISTD_H # include <unistd.h> #endif @@ -287,6 +289,202 @@ int __cdecl __wine_dbg_header( enum __wine_debug_class cls, struct __wine_debug_ return append_output( info, buffer, strlen( buffer )); }
+static int __cdecl wine_dbg_vsnprintf( char *buffer, size_t length, const char *format, __ms_va_list args ) +{ + char fmtbuf[1024]; + char *buf = buffer, *end = buffer + length; + char *fmt = fmtbuf, *tmp = fmt; + char old, *spec, *width = NULL, *prec = NULL; + int ret, w, p; + + assert( strlen( format ) < sizeof(fmtbuf) ); + memcpy( fmtbuf, format, strlen( format ) + 1 ); + + while (buf < end && *fmt) + { + if (!(tmp = strchr( tmp + 1, '%' ))) tmp = fmt + strlen( fmt ); + else if (fmt[0] == '%' && tmp == fmt + 1) continue; + old = *tmp; + *tmp = 0; + + if (fmt[0] != '%') spec = tmp; + else spec = fmt + 1 + strcspn( fmt + 1, "AacCdeEfFgGinopsSuxXZ%" ); + + if (fmt[0] != '%') prec = width = NULL; + else if (fmt[1] == '-' || fmt[1] == '+' || fmt[1] == ' ' || fmt[1] == '#' || fmt[1] == '0') width = fmt + 2; + else width = fmt + 1; + + if (!width) w = -1; + else if (*width == '*') w = va_arg( args, int ); + else if (!(w = atoi( width ))) w = -1; + + if (fmt[0] != '%' || !(prec = strchr( fmt, '.' )) || ++prec >= spec) p = INT_MAX; + else if (*prec == '*') p = va_arg( args, int ); + else if (!(p = atoi( prec ))) p = INT_MAX; + +#define append_checked( b, l, x ) \ + do { if ((ret = (x)) >= 0 && ret < (l)) b += ret; \ + else if (ret < 0) return ret; \ + else return b - buffer + ret; } while (0) + + /* dispatch width / precision arguments for all possible %*.*<spec> format specifiers */ +#define snprintf_dispatch( b, l, f, a ) \ + append_checked( b, l, (width && *width == '*' ? (prec && *prec == '*' ? snprintf( b, l, f, w, p, a ) \ + : snprintf( b, l, f, w, a )) \ + : (prec && *prec == '*' ? snprintf( b, l, f, p, a ) \ + : snprintf( b, l, f, a )))) +#define snprintf_checked( b, l, ... ) append_checked( b, l, snprintf( b, l, ## __VA_ARGS__ ) ) + + switch (*spec) + { + case 'c': + case 'C': + if (spec[-1] == 'l' || spec[-1] == 'w' || (spec[0] == 'C' && spec[-1] != 'h')) + { + unsigned int wc = va_arg( args, unsigned int ); + if (wc >= ' ' && wc <= '~') snprintf_checked( buf, end - buf, "%c", wc ); + else snprintf_checked( buf, end - buf, "\U%04x", wc ); + snprintf_checked( buf, end - buf, spec + 1 ); + } + else + { + snprintf_checked( buf, end - buf, "%c", va_arg( args, int ) ); + snprintf_checked( buf, end - buf, spec + 1 ); + } + break; + case 'd': + case 'i': + case 'o': + case 'u': + case 'x': + case 'X': + if (spec[-1] == '4' && spec[-2] == '6' && spec[-3] == 'I') + { + spec[-3] = 'j'; + spec[-2] = spec[0]; + spec[-1] = 0; + snprintf_dispatch( buf, end - buf, fmt, va_arg( args, uintmax_t ) ); + snprintf_checked( buf, end - buf, spec + 1 ); + break; + } + if (spec[-1] == '2' && spec[-2] == '3' && spec[-3] == 'I') + { + spec[-3] = spec[0]; + spec[-2] = 0; + snprintf_dispatch( buf, end - buf, fmt, va_arg( args, unsigned int ) ); + snprintf_checked( buf, end - buf, spec + 1 ); + break; + } + + if (spec[-1] == 'I') spec[-1] = 'z'; + if (spec[-1] == 'j') + snprintf_dispatch( buf, end - buf, fmt, va_arg( args, uintmax_t ) ); + else if (spec[-1] == 'z') + snprintf_dispatch( buf, end - buf, fmt, va_arg( args, size_t ) ); + else if (spec[-1] == 't') + snprintf_dispatch( buf, end - buf, fmt, va_arg( args, ptrdiff_t ) ); + else if (spec[-1] == 'l' && spec[-2] == 'l') + snprintf_dispatch( buf, end - buf, fmt, va_arg( args, long long int ) ); + else if (spec[-1] == 'l') + snprintf_dispatch( buf, end - buf, fmt, va_arg( args, long int ) ); + else + snprintf_dispatch( buf, end - buf, fmt, va_arg( args, int ) ); + break; + case 's': + case 'S': + if (spec[-1] == 'l' || spec[-1] == 'w' || (spec[0] == 'S' && spec[-1] != 'h')) + { + WCHAR *wstr = va_arg( args, WCHAR * ); + while (*wstr && p--) + { + if (*wstr >= ' ' && *wstr <= '~') snprintf_checked( buf, end - buf, "%c", *wstr++ ); + else snprintf_checked( buf, end - buf, "\U%04x", *wstr++ ); + } + snprintf_checked( buf, end - buf, spec + 1 ); + } + else + { + char *str = va_arg( args, char * ); + if (spec[-1] != 'l' && spec[-1] != 'w') + snprintf_dispatch( buf, end - buf, fmt, str ); + else + { + spec[-1] = 's'; + spec[0] = 0; + snprintf_dispatch( buf, end - buf, fmt, str ); + snprintf_checked( buf, end - buf, spec + 1 ); + } + } + break; + case 'Z': + if (spec[-1] == 'l' || spec[-1] == 'w') + { + UNICODE_STRING *ptr = va_arg( args, UNICODE_STRING * ); + WCHAR *wstr = ptr->Buffer; + USHORT len = ptr->Length; + while (len--) + { + if (*wstr >= ' ' && *wstr <= '~') snprintf_checked( buf, end - buf, "%c", *wstr++ ); + else snprintf_checked( buf, end - buf, "\U%04x", *wstr++ ); + } + snprintf_checked( buf, end - buf, spec + 1 ); + } + else + { + ANSI_STRING *ptr = va_arg( args, ANSI_STRING * ); + char *str = ptr->Buffer; + USHORT len = ptr->Length; + snprintf_checked( buf, end - buf, "%.*s", len, str ); + snprintf_checked( buf, end - buf, spec + 1 ); + } + break; + case 'p': + snprintf_dispatch( buf, end - buf, fmt, va_arg( args, void * ) ); + break; + case 'A': + case 'a': + case 'e': + case 'E': + case 'f': + case 'F': + case 'g': + case 'G': + if (spec[-1] == 'l') spec[-1] = 'L'; + if (spec[-1] == 'L') snprintf_dispatch( buf, end - buf, fmt, va_arg( args, long double ) ); + else snprintf_dispatch( buf, end - buf, fmt, va_arg( args, double ) ); + break; + case '%': + case '\0': + snprintf_checked( buf, end - buf, fmt ); + break; + case 'n': + default: + fprintf( stderr, "wine_dbg_vsnprintf: unsupported format string: %s\n", fmt ); + break; + } + +#undef snprintf_checked +#undef snprintf_dispatch +#undef append_checked + + *tmp = old; + fmt = tmp; + } + + return buf - buffer; +} + +/*********************************************************************** + * __wine_dbg_vprintf (NTDLL.@) + */ +int __cdecl __wine_dbg_vprintf( const char *format, __ms_va_list args ) +{ + char buffer[1024]; + wine_dbg_vsnprintf( buffer, sizeof(buffer), format, args ); + return __wine_dbg_output( buffer ); +} + + /*********************************************************************** * dbg_init */ diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c index c2b6ea603e3..310a4dab93c 100644 --- a/dlls/ntdll/unix/loader.c +++ b/dlls/ntdll/unix/loader.c @@ -1546,6 +1546,7 @@ static struct unix_funcs unix_funcs = __wine_dbg_strdup, __wine_dbg_output, __wine_dbg_header, + __wine_dbg_vprintf, };
diff --git a/dlls/ntdll/unixlib.h b/dlls/ntdll/unixlib.h index 7ed3148e4c4..1e98700a3fd 100644 --- a/dlls/ntdll/unixlib.h +++ b/dlls/ntdll/unixlib.h @@ -27,7 +27,7 @@ struct _DISPATCHER_CONTEXT;
/* increment this when you change the function table */ -#define NTDLL_UNIXLIB_VERSION 106 +#define NTDLL_UNIXLIB_VERSION 107
struct unix_funcs { @@ -98,6 +98,7 @@ struct unix_funcs int (CDECL *dbg_output)( const char *str ); int (CDECL *dbg_header)( enum __wine_debug_class cls, struct __wine_debug_channel *channel, const char *function ); + int (CDECL *dbg_vprintf)( const char *format, __ms_va_list args ); };
#endif /* __NTDLL_UNIXLIB_H */ diff --git a/dlls/winecrt0/debug.c b/dlls/winecrt0/debug.c index cb07c7835d4..5dc93acbecb 100644 --- a/dlls/winecrt0/debug.c +++ b/dlls/winecrt0/debug.c @@ -33,6 +33,7 @@ WINE_DECLARE_DEBUG_CHANNEL(timestamp);
static const char * (__cdecl *p__wine_dbg_strdup)( const char *str ); static int (__cdecl *p__wine_dbg_output)( const char *str ); +static int (__cdecl *p__wine_dbg_vprintf)( const char *format, __ms_va_list args ); static unsigned char (__cdecl *p__wine_dbg_get_channel_flags)( struct __wine_debug_channel *channel ); static int (__cdecl *p__wine_dbg_header)( enum __wine_debug_class cls, struct __wine_debug_channel *channel, @@ -178,6 +179,13 @@ static int __cdecl fallback__wine_dbg_output( const char *str ) return fwrite( str, 1, len, stderr ); }
+static int __cdecl fallback__wine_dbg_vprintf( const char *format, __ms_va_list args ) +{ + char str[1024]; + vsnprintf( str, sizeof(str), format, args ); + return __wine_dbg_output( str ); +} + static int __cdecl fallback__wine_dbg_header( enum __wine_debug_class cls, struct __wine_debug_channel *channel, const char *function ) @@ -236,6 +244,12 @@ int __cdecl __wine_dbg_output( const char *str ) return p__wine_dbg_output( str ); }
+int __cdecl __wine_dbg_vprintf( const char *format, __ms_va_list args ) +{ + LOAD_FUNC( __wine_dbg_vprintf ); + return p__wine_dbg_vprintf( format, args ); +} + unsigned char __cdecl __wine_dbg_get_channel_flags( struct __wine_debug_channel *channel ) { LOAD_FUNC( __wine_dbg_get_channel_flags ); diff --git a/include/wine/debug.h b/include/wine/debug.h index e064aaefb8e..7024ef270fc 100644 --- a/include/wine/debug.h +++ b/include/wine/debug.h @@ -148,6 +148,7 @@ extern const char * __cdecl __wine_dbg_strdup( const char *str ); extern int __cdecl __wine_dbg_output( const char *str ); extern int __cdecl __wine_dbg_header( enum __wine_debug_class cls, struct __wine_debug_channel *channel, const char *function ); +extern int __cdecl __wine_dbg_vprintf( const char *format, __ms_va_list args );
/* * Exported definitions and macros @@ -181,27 +182,25 @@ static inline const char * __wine_dbg_cdecl wine_dbg_sprintf( const char *format return __wine_dbg_strdup( buffer ); }
-static int __wine_dbg_cdecl wine_dbg_printf( const char *format, ... ) __WINE_PRINTF_ATTR(1,2); -static inline int __wine_dbg_cdecl wine_dbg_printf( const char *format, ... ) +static int __cdecl wine_dbg_printf( const char *format, ... ) __WINE_PRINTF_ATTR(1,2); +static inline int __cdecl wine_dbg_printf( const char *format, ... ) { - char buffer[1024]; - __wine_dbg_va_list args; - - __wine_dbg_va_start( args, format ); - vsnprintf( buffer, sizeof(buffer), format, args ); - __wine_dbg_va_end( args ); - return __wine_dbg_output( buffer ); + __ms_va_list args; + int ret; + __ms_va_start( args, format ); + ret = __wine_dbg_vprintf( format, args ); + __ms_va_end( args ); + return ret; }
-static int __wine_dbg_cdecl wine_dbg_log( enum __wine_debug_class cls, - struct __wine_debug_channel *channel, const char *func, - const char *format, ... ) __WINE_PRINTF_ATTR(4,5); -static inline int __wine_dbg_cdecl wine_dbg_log( enum __wine_debug_class cls, - struct __wine_debug_channel *channel, - const char *function, const char *format, ... ) +static int __cdecl wine_dbg_log( enum __wine_debug_class cls, + struct __wine_debug_channel *channel, const char *func, + const char *format, ... ) __WINE_PRINTF_ATTR(4,5); +static inline int __cdecl wine_dbg_log( enum __wine_debug_class cls, + struct __wine_debug_channel *channel, + const char *function, const char *format, ... ) { - char buffer[1024]; - __wine_dbg_va_list args; + __ms_va_list args; int ret;
if (*format == '\1') /* special magic to avoid standard prefix */ @@ -211,10 +210,9 @@ static inline int __wine_dbg_cdecl wine_dbg_log( enum __wine_debug_class cls, } if ((ret = __wine_dbg_header( cls, channel, function )) == -1) return ret;
- __wine_dbg_va_start( args, format ); - vsnprintf( buffer, sizeof(buffer), format, args ); - __wine_dbg_va_end( args ); - ret += __wine_dbg_output( buffer ); + __ms_va_start( args, format ); + ret += __wine_dbg_vprintf( format, args ); + __ms_va_end( args ); return ret; }
This adds a new __wine_dbg_vsprintf internal export to format and strdup a debug message at once, using ms_abi varargs calling convention in all cases.
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/ntdll/ntdll.spec | 1 + dlls/ntdll/thread.c | 8 ++++++++ dlls/ntdll/unix/debug.c | 11 +++++++++++ dlls/ntdll/unix/loader.c | 1 + dlls/ntdll/unixlib.h | 3 ++- dlls/winecrt0/debug.c | 14 ++++++++++++++ include/wine/debug.h | 29 +++++++++-------------------- 7 files changed, 46 insertions(+), 21 deletions(-)
diff --git a/dlls/ntdll/ntdll.spec b/dlls/ntdll/ntdll.spec index 3a7adc92752..393db5caf20 100644 --- a/dlls/ntdll/ntdll.spec +++ b/dlls/ntdll/ntdll.spec @@ -1617,6 +1617,7 @@ @ cdecl -norelay __wine_dbg_output(str) @ cdecl -norelay __wine_dbg_strdup(str) @ cdecl -norelay __wine_dbg_vprintf(str ptr) +@ cdecl -norelay __wine_dbg_vsprintf(str ptr)
# Virtual memory @ cdecl -syscall __wine_locked_recvmsg(long ptr long) diff --git a/dlls/ntdll/thread.c b/dlls/ntdll/thread.c index a7231d008c6..ada79bf8843 100644 --- a/dlls/ntdll/thread.c +++ b/dlls/ntdll/thread.c @@ -82,6 +82,14 @@ int __cdecl __wine_dbg_vprintf( const char *format, __ms_va_list args ) return unix_funcs->dbg_vprintf( format, args ); }
+/*********************************************************************** + * __wine_dbg_vsnprintf (NTDLL.@) + */ +const char * __cdecl __wine_dbg_vsprintf( const char *format, __ms_va_list args ) +{ + return unix_funcs->dbg_vsprintf( format, args ); +} + /******************************************************************* * KiUserApcDispatcher (NTDLL.@) */ diff --git a/dlls/ntdll/unix/debug.c b/dlls/ntdll/unix/debug.c index fe5c3c64272..2b2d63b7adf 100644 --- a/dlls/ntdll/unix/debug.c +++ b/dlls/ntdll/unix/debug.c @@ -485,6 +485,17 @@ int __cdecl __wine_dbg_vprintf( const char *format, __ms_va_list args ) }
+/*********************************************************************** + * __wine_dbg_vsprintf (NTDLL.@) + */ +const char * __cdecl __wine_dbg_vsprintf( const char *format, __ms_va_list args ) +{ + char buffer[200]; + wine_dbg_vsnprintf( buffer, sizeof(buffer), format, args ); + return __wine_dbg_strdup( buffer ); +} + + /*********************************************************************** * dbg_init */ diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c index 310a4dab93c..a515e55662d 100644 --- a/dlls/ntdll/unix/loader.c +++ b/dlls/ntdll/unix/loader.c @@ -1547,6 +1547,7 @@ static struct unix_funcs unix_funcs = __wine_dbg_output, __wine_dbg_header, __wine_dbg_vprintf, + __wine_dbg_vsprintf, };
diff --git a/dlls/ntdll/unixlib.h b/dlls/ntdll/unixlib.h index 1e98700a3fd..649ae047e01 100644 --- a/dlls/ntdll/unixlib.h +++ b/dlls/ntdll/unixlib.h @@ -27,7 +27,7 @@ struct _DISPATCHER_CONTEXT;
/* increment this when you change the function table */ -#define NTDLL_UNIXLIB_VERSION 107 +#define NTDLL_UNIXLIB_VERSION 108
struct unix_funcs { @@ -99,6 +99,7 @@ struct unix_funcs int (CDECL *dbg_header)( enum __wine_debug_class cls, struct __wine_debug_channel *channel, const char *function ); int (CDECL *dbg_vprintf)( const char *format, __ms_va_list args ); + const char * (CDECL *dbg_vsprintf)( const char *format, __ms_va_list args ); };
#endif /* __NTDLL_UNIXLIB_H */ diff --git a/dlls/winecrt0/debug.c b/dlls/winecrt0/debug.c index 5dc93acbecb..d8ba2603145 100644 --- a/dlls/winecrt0/debug.c +++ b/dlls/winecrt0/debug.c @@ -34,6 +34,7 @@ WINE_DECLARE_DEBUG_CHANNEL(timestamp); static const char * (__cdecl *p__wine_dbg_strdup)( const char *str ); static int (__cdecl *p__wine_dbg_output)( const char *str ); static int (__cdecl *p__wine_dbg_vprintf)( const char *format, __ms_va_list args ); +static const char * (__cdecl *p__wine_dbg_vsprintf)( const char *format, __ms_va_list args ); static unsigned char (__cdecl *p__wine_dbg_get_channel_flags)( struct __wine_debug_channel *channel ); static int (__cdecl *p__wine_dbg_header)( enum __wine_debug_class cls, struct __wine_debug_channel *channel, @@ -186,6 +187,13 @@ static int __cdecl fallback__wine_dbg_vprintf( const char *format, __ms_va_list return __wine_dbg_output( str ); }
+static const char * __cdecl fallback__wine_dbg_vsprintf( const char *format, __ms_va_list args ) +{ + char str[200]; + vsnprintf( str, sizeof(str), format, args ); + return __wine_dbg_strdup( str ); +} + static int __cdecl fallback__wine_dbg_header( enum __wine_debug_class cls, struct __wine_debug_channel *channel, const char *function ) @@ -250,6 +258,12 @@ int __cdecl __wine_dbg_vprintf( const char *format, __ms_va_list args ) return p__wine_dbg_vprintf( format, args ); }
+const char * __cdecl __wine_dbg_vsprintf( const char *format, __ms_va_list args ) +{ + LOAD_FUNC( __wine_dbg_vsprintf ); + return p__wine_dbg_vsprintf( format, args ); +} + unsigned char __cdecl __wine_dbg_get_channel_flags( struct __wine_debug_channel *channel ) { LOAD_FUNC( __wine_dbg_get_channel_flags ); diff --git a/include/wine/debug.h b/include/wine/debug.h index 7024ef270fc..5321429f5c5 100644 --- a/include/wine/debug.h +++ b/include/wine/debug.h @@ -149,6 +149,7 @@ extern int __cdecl __wine_dbg_output( const char *str ); extern int __cdecl __wine_dbg_header( enum __wine_debug_class cls, struct __wine_debug_channel *channel, const char *function ); extern int __cdecl __wine_dbg_vprintf( const char *format, __ms_va_list args ); +extern const char * __cdecl __wine_dbg_vsprintf( const char *format, __ms_va_list args );
/* * Exported definitions and macros @@ -158,28 +159,16 @@ extern int __cdecl __wine_dbg_vprintf( const char *format, __ms_va_list args ); quotes. The string will be valid for some time, but not indefinitely as strings are re-used. */
-#if (defined(__x86_64__) || defined(__aarch64__)) && defined(__GNUC__) && defined(__WINE_USE_MSVCRT) -# define __wine_dbg_cdecl __cdecl -# define __wine_dbg_va_list __builtin_ms_va_list -# define __wine_dbg_va_start(list,arg) __builtin_ms_va_start(list,arg) -# define __wine_dbg_va_end(list) __builtin_ms_va_end(list) -#else -# define __wine_dbg_cdecl -# define __wine_dbg_va_list va_list -# define __wine_dbg_va_start(list,arg) va_start(list,arg) -# define __wine_dbg_va_end(list) va_end(list) -#endif - -static const char * __wine_dbg_cdecl wine_dbg_sprintf( const char *format, ... ) __WINE_PRINTF_ATTR(1,2); -static inline const char * __wine_dbg_cdecl wine_dbg_sprintf( const char *format, ... ) +static const char * __cdecl wine_dbg_sprintf( const char *format, ... ) __WINE_PRINTF_ATTR(1,2); +static inline const char * __cdecl wine_dbg_sprintf( const char *format, ... ) { - char buffer[200]; - __wine_dbg_va_list args; + const char *ret; + __ms_va_list args;
- __wine_dbg_va_start( args, format ); - vsnprintf( buffer, sizeof(buffer), format, args ); - __wine_dbg_va_end( args ); - return __wine_dbg_strdup( buffer ); + __ms_va_start( args, format ); + ret = __wine_dbg_vsprintf( format, args ); + __ms_va_end( args ); + return ret; }
static int __cdecl wine_dbg_printf( const char *format, ... ) __WINE_PRINTF_ATTR(1,2);
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- include/wine/debug.h | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-)
diff --git a/include/wine/debug.h b/include/wine/debug.h index 5321429f5c5..05d4f0c0d73 100644 --- a/include/wine/debug.h +++ b/include/wine/debug.h @@ -205,7 +205,7 @@ static inline int __cdecl wine_dbg_log( enum __wine_debug_class cls, return ret; }
-static inline const char *wine_dbgstr_an( const char *str, int n ) +static inline const char *__cdecl wine_dbgstr_an( const char *str, int n ) { static const char hex[16] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'}; char buffer[300], *dst = buffer; @@ -249,7 +249,7 @@ static inline const char *wine_dbgstr_an( const char *str, int n ) return __wine_dbg_strdup( buffer ); }
-static inline const char *wine_dbgstr_wn( const WCHAR *str, int n ) +static inline const char *__cdecl wine_dbgstr_wn( const WCHAR *str, int n ) { static const char hex[16] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'}; char buffer[300], *dst = buffer; @@ -295,17 +295,17 @@ static inline const char *wine_dbgstr_wn( const WCHAR *str, int n ) return __wine_dbg_strdup( buffer ); }
-static inline const char *wine_dbgstr_a( const char *s ) +static inline const char *__cdecl wine_dbgstr_a( const char *s ) { return wine_dbgstr_an( s, -1 ); }
-static inline const char *wine_dbgstr_w( const WCHAR *s ) +static inline const char *__cdecl wine_dbgstr_w( const WCHAR *s ) { return wine_dbgstr_wn( s, -1 ); }
-static inline const char *wine_dbgstr_guid( const GUID *id ) +static inline const char *__cdecl wine_dbgstr_guid( const GUID *id ) { if (!id) return "(null)"; if (!((ULONG_PTR)id >> 16)) return wine_dbg_sprintf( "<guid-0x%04hx>", (WORD)(ULONG_PTR)id ); @@ -315,20 +315,20 @@ static inline const char *wine_dbgstr_guid( const GUID *id ) id->Data4[4], id->Data4[5], id->Data4[6], id->Data4[7] ); }
-static inline const char *wine_dbgstr_point( const POINT *pt ) +static inline const char *__cdecl wine_dbgstr_point( const POINT *pt ) { if (!pt) return "(null)"; return wine_dbg_sprintf( "(%d,%d)", pt->x, pt->y ); }
-static inline const char *wine_dbgstr_rect( const RECT *rect ) +static inline const char *__cdecl wine_dbgstr_rect( const RECT *rect ) { if (!rect) return "(null)"; return wine_dbg_sprintf( "(%d,%d)-(%d,%d)", rect->left, rect->top, rect->right, rect->bottom ); }
-static inline const char *wine_dbgstr_longlong( ULONGLONG ll ) +static inline const char *__cdecl wine_dbgstr_longlong( ULONGLONG ll ) { if (sizeof(ll) > sizeof(unsigned long) && ll >> 32) return wine_dbg_sprintf( "%lx%08lx", (unsigned long)(ll >> 32), (unsigned long)ll ); @@ -337,7 +337,7 @@ static inline const char *wine_dbgstr_longlong( ULONGLONG ll )
#if defined(__oaidl_h__) && defined(V_VT)
-static inline const char *wine_dbgstr_vt( VARTYPE vt ) +static inline const char *__cdecl wine_dbgstr_vt( VARTYPE vt ) { static const char *const variant_types[] = { @@ -385,7 +385,7 @@ static inline const char *wine_dbgstr_vt( VARTYPE vt ) return wine_dbg_sprintf( "vt(invalid %x)", vt ); }
-static inline const char *wine_dbgstr_variant( const VARIANT *v ) +static inline const char *__cdecl wine_dbgstr_variant( const VARIANT *v ) { if (!v) return "(null)"; @@ -492,15 +492,15 @@ static inline const char *wine_dbgstr_variant( const VARIANT *v ) #ifdef __WINESRC__ /* Wine uses shorter names that are very likely to conflict with other software */
-static inline const char *debugstr_an( const char * s, int n ) { return wine_dbgstr_an( s, n ); } -static inline const char *debugstr_wn( const WCHAR *s, int n ) { return wine_dbgstr_wn( s, n ); } -static inline const char *debugstr_guid( const struct _GUID *id ) { return wine_dbgstr_guid(id); } -static inline const char *debugstr_a( const char *s ) { return wine_dbgstr_an( s, -1 ); } -static inline const char *debugstr_w( const WCHAR *s ) { return wine_dbgstr_wn( s, -1 ); } +static inline const char *__cdecl debugstr_an( const char * s, int n ) { return wine_dbgstr_an( s, n ); } +static inline const char *__cdecl debugstr_wn( const WCHAR *s, int n ) { return wine_dbgstr_wn( s, n ); } +static inline const char *__cdecl debugstr_guid( const struct _GUID *id ) { return wine_dbgstr_guid(id); } +static inline const char *__cdecl debugstr_a( const char *s ) { return wine_dbgstr_an( s, -1 ); } +static inline const char *__cdecl debugstr_w( const WCHAR *s ) { return wine_dbgstr_wn( s, -1 ); }
#if defined(__oaidl_h__) && defined(V_VT) -static inline const char *debugstr_vt( VARTYPE vt ) { return wine_dbgstr_vt( vt ); } -static inline const char *debugstr_variant( const VARIANT *v ) { return wine_dbgstr_variant( v ); } +static inline const char *__cdecl debugstr_vt( VARTYPE vt ) { return wine_dbgstr_vt( vt ); } +static inline const char *__cdecl debugstr_variant( const VARIANT *v ) { return wine_dbgstr_variant( v ); } #endif
#define TRACE WINE_TRACE
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=82284
Your paranoid android.
=== debiant (32 bit report) ===
ntdll: threadpool.c:1904: Test failed: WaitForSingleObject returned 258
=== debiant (32 bit WoW report) ===
ntdll: change.c:277: Test failed: should be ready
This makes it possible to write TRACE("%p(astr)", "string") and get the equivalent of TRACE("%s", debugstr_a("string")), respectively with "%p(wstr)" and debugstr_w(...).
The width format specifier (including '*') can be used to control the length (GCC format checker doesn't like precision specifier on %p).
Note that the format extension is just here to illustrate, we could think of any other syntax. The main issue is to pass GCC format checker, and as done in the Linux kernel %p suffixes seems to be the best fit.
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/ntdll/unix/debug.c | 96 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 95 insertions(+), 1 deletion(-)
diff --git a/dlls/ntdll/unix/debug.c b/dlls/ntdll/unix/debug.c index 2b2d63b7adf..6c4b86196c8 100644 --- a/dlls/ntdll/unix/debug.c +++ b/dlls/ntdll/unix/debug.c @@ -289,6 +289,90 @@ int __cdecl __wine_dbg_header( enum __wine_debug_class cls, struct __wine_debug_ return append_output( info, buffer, strlen( buffer )); }
+static size_t sprintf_dbgstr_an( char *buffer, size_t length, const char *str, int n ) +{ + static const char hex[16] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'}; + char *dst = buffer; + + if (!str) { if (length >= 7) strcpy(buffer, "(null)"); return 6; } + if (!((ULONG_PTR)str >> 16)) return snprintf( buffer, length, "#%04x", LOWORD(str) ); + if (n == -1) for (n = 0; str[n]; n++) ; + *dst++ = '"'; + while (n-- > 0 && dst <= buffer + length - 9) + { + unsigned char c = *str++; + switch (c) + { + case '\n': *dst++ = '\'; *dst++ = 'n'; break; + case '\r': *dst++ = '\'; *dst++ = 'r'; break; + case '\t': *dst++ = '\'; *dst++ = 't'; break; + case '"': *dst++ = '\'; *dst++ = '"'; break; + case '\': *dst++ = '\'; *dst++ = '\'; break; + default: + if (c < ' ' || c >= 127) + { + *dst++ = '\'; + *dst++ = 'x'; + *dst++ = hex[(c >> 4) & 0x0f]; + *dst++ = hex[c & 0x0f]; + } + else *dst++ = c; + } + } + *dst++ = '"'; + if (n > 0) + { + *dst++ = '.'; + *dst++ = '.'; + *dst++ = '.'; + } + *dst = 0; + return dst - buffer; +} + +static size_t sprintf_dbgstr_wn( char *buffer, size_t length, const WCHAR *str, int n ) +{ + static const char hex[16] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'}; + char *dst = buffer; + + if (!str) { if (length >= 7) strcpy(buffer, "(null)"); return 6; } + if (!((ULONG_PTR)str >> 16)) return snprintf( buffer, length, "#%04x", LOWORD(str) ); + if (n == -1) for (n = 0; str[n]; n++) ; + *dst++ = 'L'; + *dst++ = '"'; + while (n-- > 0 && dst <= buffer + length - 10) + { + WCHAR c = *str++; + switch (c) + { + case '\n': *dst++ = '\'; *dst++ = 'n'; break; + case '\r': *dst++ = '\'; *dst++ = 'r'; break; + case '\t': *dst++ = '\'; *dst++ = 't'; break; + case '"': *dst++ = '\'; *dst++ = '"'; break; + case '\': *dst++ = '\'; *dst++ = '\'; break; + default: + if (c < ' ' || c >= 127) + { + *dst++ = '\'; + *dst++ = hex[(c >> 12) & 0x0f]; + *dst++ = hex[(c >> 8) & 0x0f]; + *dst++ = hex[(c >> 4) & 0x0f]; + *dst++ = hex[c & 0x0f]; + } + else *dst++ = (char)c; + } + } + *dst++ = '"'; + if (n > 0) + { + *dst++ = '.'; + *dst++ = '.'; + *dst++ = '.'; + } + *dst = 0; + return dst - buffer; +} + static int __cdecl wine_dbg_vsnprintf( char *buffer, size_t length, const char *format, __ms_va_list args ) { char fmtbuf[1024]; @@ -439,7 +523,17 @@ static int __cdecl wine_dbg_vsnprintf( char *buffer, size_t length, const char * } break; case 'p': - snprintf_dispatch( buf, end - buf, fmt, va_arg( args, void * ) ); + if (!strncmp( spec, "p(astr)", 7 )) /* debugstr_a / debugstr_an */ + { + append_checked( buf, end - buf, sprintf_dbgstr_an( buf, end - buf, va_arg( args, const char * ), w ) ); + snprintf_checked( buf, end - buf, spec + 7 ); + } + else if (!strncmp( spec, "p(wstr)", 7 )) /* debugstr_w / debugstr_wn */ + { + append_checked( buf, end - buf, sprintf_dbgstr_wn( buf, end - buf, va_arg( args, const WCHAR * ), w ) ); + snprintf_checked( buf, end - buf, spec + 7 ); + } + else snprintf_dispatch( buf, end - buf, fmt, va_arg( args, void * ) ); break; case 'A': case 'a':
Respectively using %p(us), and %p(as) format specifiers.
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/ntdll/unix/debug.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+)
diff --git a/dlls/ntdll/unix/debug.c b/dlls/ntdll/unix/debug.c index 6c4b86196c8..134ae619575 100644 --- a/dlls/ntdll/unix/debug.c +++ b/dlls/ntdll/unix/debug.c @@ -373,6 +373,18 @@ static size_t sprintf_dbgstr_wn( char *buffer, size_t length, const WCHAR *str, return dst - buffer; }
+static size_t sprintf_dbgstr_us( char *buffer, size_t length, const UNICODE_STRING *us ) +{ + if (!us) { if (length >= 7) strcpy(buffer, "<null>"); return 6; } + return sprintf_dbgstr_wn( buffer, length, us->Buffer, us->Length / sizeof(WCHAR) ); +} + +static size_t sprintf_dbgstr_as( char *buffer, size_t length, const ANSI_STRING *as ) +{ + if (!as) { if (length >= 7) strcpy(buffer, "<null>"); return 6; } + return sprintf_dbgstr_an( buffer, length, as->Buffer, as->Length ); +} + static int __cdecl wine_dbg_vsnprintf( char *buffer, size_t length, const char *format, __ms_va_list args ) { char fmtbuf[1024]; @@ -533,6 +545,16 @@ static int __cdecl wine_dbg_vsnprintf( char *buffer, size_t length, const char * append_checked( buf, end - buf, sprintf_dbgstr_wn( buf, end - buf, va_arg( args, const WCHAR * ), w ) ); snprintf_checked( buf, end - buf, spec + 7 ); } + else if (!strncmp( spec, "p(us)", 5 )) /* debugstr_us */ + { + append_checked( buf, end - buf, sprintf_dbgstr_us( buf, end - buf, va_arg( args, const UNICODE_STRING * ) ) ); + snprintf_checked( buf, end - buf, spec + 5 ); + } + else if (!strncmp( spec, "p(as)", 5 )) + { + append_checked( buf, end - buf, sprintf_dbgstr_as( buf, end - buf, va_arg( args, const ANSI_STRING * ) ) ); + snprintf_checked( buf, end - buf, spec + 5 ); + } else snprintf_dispatch( buf, end - buf, fmt, va_arg( args, void * ) ); break; case 'A':
Respectively using %p(guid), %p(point), and %p(rect) format specifiers.
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/ntdll/unix/debug.c | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+)
diff --git a/dlls/ntdll/unix/debug.c b/dlls/ntdll/unix/debug.c index 134ae619575..e82472bafa1 100644 --- a/dlls/ntdll/unix/debug.c +++ b/dlls/ntdll/unix/debug.c @@ -385,6 +385,29 @@ static size_t sprintf_dbgstr_as( char *buffer, size_t length, const ANSI_STRING return sprintf_dbgstr_an( buffer, length, as->Buffer, as->Length ); }
+static size_t sprintf_dbgstr_guid( char *buffer, size_t length, const GUID *id ) +{ + if (!id) { if (length >= 7) strcpy(buffer, "(null)"); return 6; } + if (!((ULONG_PTR)id >> 16)) return snprintf( buffer, length, "<guid-0x%04hx>", (WORD)(ULONG_PTR)id ); + return snprintf( buffer, length, "{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}", + id->Data1, id->Data2, id->Data3, + id->Data4[0], id->Data4[1], id->Data4[2], id->Data4[3], + id->Data4[4], id->Data4[5], id->Data4[6], id->Data4[7] ); +} + +static size_t sprintf_dbgstr_point( char *buffer, size_t length, const POINT *pt ) +{ + if (!pt) { if (length >= 7) strcpy(buffer, "(null)"); return 6; } + return snprintf( buffer, length, "(%d,%d)", pt->x, pt->y ); +} + +static size_t sprintf_dbgstr_rect( char *buffer, size_t length, const RECT *rect ) +{ + if (!rect) { if (length >= 7) strcpy(buffer, "(null)"); return 6; } + return snprintf( buffer, length, "(%d,%d)-(%d,%d)", rect->left, rect->top, + rect->right, rect->bottom ); +} + static int __cdecl wine_dbg_vsnprintf( char *buffer, size_t length, const char *format, __ms_va_list args ) { char fmtbuf[1024]; @@ -555,6 +578,21 @@ static int __cdecl wine_dbg_vsnprintf( char *buffer, size_t length, const char * append_checked( buf, end - buf, sprintf_dbgstr_as( buf, end - buf, va_arg( args, const ANSI_STRING * ) ) ); snprintf_checked( buf, end - buf, spec + 5 ); } + else if (!strncmp( spec, "p(guid)", 7 )) /* debugstr_guid */ + { + append_checked( buf, end - buf, sprintf_dbgstr_guid( buf, end - buf, va_arg( args, const GUID * ) ) ); + snprintf_checked( buf, end - buf, spec + 7 ); + } + else if (!strncmp( spec, "p(point)", 8 )) /* debugstr_point */ + { + append_checked( buf, end - buf, sprintf_dbgstr_point( buf, end - buf, va_arg( args, const POINT * ) ) ); + snprintf_checked( buf, end - buf, spec + 8 ); + } + else if (!strncmp( spec, "p(rect)", 7 )) /* debugstr_rect */ + { + append_checked( buf, end - buf, sprintf_dbgstr_rect( buf, end - buf, va_arg( args, const RECT * ) ) ); + snprintf_checked( buf, end - buf, spec + 7 ); + } else snprintf_dispatch( buf, end - buf, fmt, va_arg( args, void * ) ); break; case 'A':