xref: /reactos/sdk/lib/3rdparty/libwine/debug.c (revision 8a978a17)
1 /*
2  * Management of the debugging channels
3  *
4  * Copyright 2000 Alexandre Julliard
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20 
21 #include "wine/config.h"
22 #include "wine/port.h"
23 
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <stdarg.h>
27 #include <string.h>
28 #include <ctype.h>
29 #include <excpt.h>
30 
31 #define WIN32_NO_STATUS
32 #include "wine/debug.h"
33 #include "wine/library.h"
34 
35 #include <rtlfuncs.h>
36 #include <cmfuncs.h>
37 
38 WINE_DECLARE_DEBUG_CHANNEL(pid);
39 WINE_DECLARE_DEBUG_CHANNEL(tid);
40 
41 ULONG
42 NTAPI
43 vDbgPrintExWithPrefix(
44   IN LPCSTR Prefix,
45   IN ULONG ComponentId,
46   IN ULONG Level,
47   IN LPCSTR Format,
48   IN va_list ap);
49 
50 
51 static const char * const debug_classes[] = { "fixme", "err", "warn", "trace" };
52 
53 #define MAX_DEBUG_OPTIONS 256
54 
55 static unsigned char default_flags = (1 << __WINE_DBCL_ERR) | (1 << __WINE_DBCL_FIXME);
56 static int nb_debug_options = -1;
57 static struct __wine_debug_channel debug_options[MAX_DEBUG_OPTIONS];
58 
59 static struct __wine_debug_functions funcs;
60 
61 static void debug_init(void);
62 
63 
64 /* Wine format */
65 static int winefmt_default_dbg_vlog( enum __wine_debug_class cls, struct __wine_debug_channel *channel,
66                                      const char *file, const char *func, const int line, const char *format, va_list args );
67 /* ReactOS format (default) */
68 static int rosfmt_default_dbg_vlog( enum __wine_debug_class cls, struct __wine_debug_channel *channel,
69                                     const char *file, const char *func, const int line, const char *format, va_list args );
70 /* Extended format */
71 static int extfmt_default_dbg_vlog( enum __wine_debug_class cls, struct __wine_debug_channel *channel,
72                                     const char *file, const char *func, const int line, const char *format, va_list args );
73 
74 
75 static int __cdecl cmp_name( const void *p1, const void *p2 )
76 {
77     const char *name = p1;
78     const struct __wine_debug_channel *chan = p2;
79     return strcmp( name, chan->name );
80 }
81 
82 /* get the flags to use for a given channel, possibly setting them too in case of lazy init */
83 unsigned char __wine_dbg_get_channel_flags( struct __wine_debug_channel *channel )
84 {
85     if (nb_debug_options == -1) debug_init();
86 
87     if (nb_debug_options)
88     {
89         struct __wine_debug_channel *opt = bsearch( channel->name, debug_options, nb_debug_options,
90                                                     sizeof(debug_options[0]), cmp_name );
91         if (opt) return opt->flags;
92     }
93     /* no option for this channel */
94     if (channel->flags & (1 << __WINE_DBCL_INIT)) channel->flags = default_flags;
95     return default_flags;
96 }
97 
98 /* set the flags to use for a given channel; return 0 if the channel is not available to set */
99 int __wine_dbg_set_channel_flags( struct __wine_debug_channel *channel,
100                                   unsigned char set, unsigned char clear )
101 {
102     if (nb_debug_options == -1) debug_init();
103 
104     if (nb_debug_options)
105     {
106         struct __wine_debug_channel *opt = bsearch( channel->name, debug_options, nb_debug_options,
107                                                     sizeof(debug_options[0]), cmp_name );
108         if (opt)
109         {
110             opt->flags = (opt->flags & ~clear) | set;
111             return 1;
112         }
113     }
114     return 0;
115 }
116 
117 /* add a new debug option at the end of the option list */
118 static void add_option( const char *name, unsigned char set, unsigned char clear )
119 {
120     int min = 0, max = nb_debug_options - 1, pos, res;
121 
122     if (!name[0])  /* "all" option */
123     {
124         default_flags = (default_flags & ~clear) | set;
125         return;
126     }
127     if (strlen(name) >= sizeof(debug_options[0].name)) return;
128 
129     while (min <= max)
130     {
131         pos = (min + max) / 2;
132         res = strcmp( name, debug_options[pos].name );
133         if (!res)
134         {
135             debug_options[pos].flags = (debug_options[pos].flags & ~clear) | set;
136             return;
137         }
138         if (res < 0) max = pos - 1;
139         else min = pos + 1;
140     }
141     if (nb_debug_options >= MAX_DEBUG_OPTIONS) return;
142 
143     pos = min;
144     if (pos < nb_debug_options) memmove( &debug_options[pos + 1], &debug_options[pos],
145                                          (nb_debug_options - pos) * sizeof(debug_options[0]) );
146     strcpy( debug_options[pos].name, name );
147     debug_options[pos].flags = (default_flags & ~clear) | set;
148     nb_debug_options++;
149 }
150 
151 /* parse a set of debugging option specifications and add them to the option list */
152 static void parse_options( const char *str )
153 {
154     char *opt, *next, *options;
155     unsigned int i;
156 
157     if (!(options = _strdup(str))) return;
158     for (opt = options; opt; opt = next)
159     {
160         const char *p;
161         unsigned char set = 0, clear = 0;
162 
163         if ((next = strchr( opt, ',' ))) *next++ = 0;
164 
165         p = opt + strcspn( opt, "+-" );
166         if (!p[0]) p = opt;  /* assume it's a debug channel name */
167 
168         if (p > opt)
169         {
170             for (i = 0; i < sizeof(debug_classes)/sizeof(debug_classes[0]); i++)
171             {
172                 int len = strlen(debug_classes[i]);
173                 if (len != (p - opt)) continue;
174                 if (!memcmp( opt, debug_classes[i], len ))  /* found it */
175                 {
176                     if (*p == '+') set |= 1 << i;
177                     else clear |= 1 << i;
178                     break;
179                 }
180             }
181             if (i == sizeof(debug_classes)/sizeof(debug_classes[0])) /* bad class name, skip it */
182                 continue;
183         }
184         else
185         {
186             if (*p == '-') clear = ~0;
187             else set = ~0;
188         }
189         if (*p == '+' || *p == '-') p++;
190         if (!p[0]) continue;
191 
192         if (!strcmp( p, "all" ))
193             default_flags = (default_flags & ~clear) | set;
194         else
195             add_option( p, set, clear );
196     }
197     free( options );
198 }
199 
200 /*
201  * The syntax of the DEBUGCHANNEL environment variable is:
202  *   DEBUGCHANNEL=[class]+xxx,[class]-yyy,...
203  *
204  * For example: DEBUGCHANNEL=+all,warn-heap
205  *     turns on all messages except warning heap messages.
206  *
207  * The available message classes are: err, warn, fixme, trace.
208  *
209  * In order to select a different debug trace format, the
210  * DEBUGFORMAT environment variable should be used:
211  *
212  *   DEBUGFORMAT=fmt
213  *
214  * where fmt is the format name: 'wine', or 'extended' (abbreviation: 'ext').
215  * If no format or an invalid one is specified, the fall-back default format
216  * is used instead.
217  */
218 
219 /* initialize all options at startup */
220 static void debug_init(void)
221 {
222     char *wine_debug;
223     DWORD dwLength;
224     /* GetEnvironmentVariableA will change LastError! */
225     DWORD LastError = GetLastError();
226 
227     if (nb_debug_options != -1) return;  /* already initialized */
228     nb_debug_options = 0;
229 
230     dwLength = GetEnvironmentVariableA("DEBUGCHANNEL", NULL, 0);
231     if (dwLength)
232     {
233         wine_debug = malloc(dwLength);
234         if (wine_debug)
235         {
236             if (GetEnvironmentVariableA("DEBUGCHANNEL", wine_debug, dwLength) < dwLength)
237                 parse_options(wine_debug);
238             free(wine_debug);
239         }
240     }
241 
242     dwLength = GetEnvironmentVariableA("DEBUGFORMAT", NULL, 0);
243     if (dwLength)
244     {
245         wine_debug = malloc(dwLength);
246         if (wine_debug)
247         {
248             if (GetEnvironmentVariableA("DEBUGFORMAT", wine_debug, dwLength) < dwLength)
249             {
250                 if (strcmp(wine_debug, "wine") == 0)
251                 {
252                     funcs.dbg_vlog = winefmt_default_dbg_vlog;
253                 }
254                 else
255                 if (strcmp(wine_debug, "extended") == 0 ||
256                     strcmp(wine_debug, "ext") == 0)
257                 {
258                     funcs.dbg_vlog = extfmt_default_dbg_vlog;
259                 }
260                 else
261                 {
262                     funcs.dbg_vlog = rosfmt_default_dbg_vlog;
263                 }
264             }
265             free(wine_debug);
266         }
267     }
268 
269     SetLastError(LastError);
270 }
271 
272 /* varargs wrapper for funcs.dbg_vprintf */
273 int wine_dbg_printf( const char *format, ... )
274 {
275     int ret;
276     va_list valist;
277 
278     va_start(valist, format);
279     ret = funcs.dbg_vprintf( format, valist );
280     va_end(valist);
281     return ret;
282 }
283 
284 /* printf with temp buffer allocation */
285 const char *wine_dbg_sprintf( const char *format, ... )
286 {
287     static const int max_size = 200;
288     char *ret;
289     int len;
290     va_list valist;
291 
292     va_start(valist, format);
293     ret = funcs.get_temp_buffer( max_size );
294     len = vsnprintf( ret, max_size, format, valist );
295     if (len == -1 || len >= max_size) ret[max_size-1] = 0;
296     else funcs.release_temp_buffer( ret, len + 1 );
297     va_end(valist);
298     return ret;
299 }
300 
301 
302 /* varargs wrapper for funcs.dbg_vlog */
303 int wine_dbg_log( enum __wine_debug_class cls, struct __wine_debug_channel *channel,
304                   const char *func, const char *format, ... )
305 {
306     int ret;
307     va_list valist;
308 
309     if (!(__wine_dbg_get_channel_flags( channel ) & (1 << cls))) return -1;
310 
311     va_start(valist, format);
312     ret = funcs.dbg_vlog( cls, channel, NULL, func, 0, format, valist );
313     va_end(valist);
314     return ret;
315 }
316 
317 
318 /* ReactOS compliant debug format wrapper for funcs.dbg_vlog */
319 int ros_dbg_log( enum __wine_debug_class cls, struct __wine_debug_channel *channel,
320                   const char *file, const char *func, const int line, const char *format, ... )
321 {
322     int ret;
323     va_list valist;
324 
325     if (!(__wine_dbg_get_channel_flags( channel ) & (1 << cls))) return -1;
326 
327     va_start(valist, format);
328     ret = funcs.dbg_vlog( cls, channel, file, func, line, format, valist );
329     va_end(valist);
330     return ret;
331 }
332 
333 
334 /* allocate some tmp string space */
335 /* FIXME: this is not 100% thread-safe */
336 static char *get_temp_buffer( size_t size )
337 {
338     static char *list[32];
339     static int pos;
340     char *ret;
341     int idx;
342 
343     idx = interlocked_xchg_add( &pos, 1 ) % (sizeof(list)/sizeof(list[0]));
344     if ((ret = realloc( list[idx], size ))) list[idx] = ret;
345     return ret;
346 }
347 
348 
349 /* release unused part of the buffer */
350 static void release_temp_buffer( char *buffer, size_t size )
351 {
352     /* don't bother doing anything */
353 }
354 
355 
356 /* default implementation of wine_dbgstr_an */
357 static const char *default_dbgstr_an( const char *str, int n )
358 {
359     static const char hex[16] = "0123456789abcdef";
360     char *dst, *res;
361     size_t size;
362 
363     if (!((ULONG_PTR)str >> 16))
364     {
365         if (!str) return "(null)";
366         res = funcs.get_temp_buffer( 6 );
367         sprintf( res, "#%04x", LOWORD(str) );
368         return res;
369     }
370     if (n == -1) n = strlen(str);
371     if (n < 0) n = 0;
372     size = 10 + min( 300, n * 4 );
373     dst = res = funcs.get_temp_buffer( size );
374     *dst++ = '"';
375     while (n-- > 0 && dst <= res + size - 9)
376     {
377         unsigned char c = *str++;
378         switch (c)
379         {
380         case '\n': *dst++ = '\\'; *dst++ = 'n'; break;
381         case '\r': *dst++ = '\\'; *dst++ = 'r'; break;
382         case '\t': *dst++ = '\\'; *dst++ = 't'; break;
383         case '"':  *dst++ = '\\'; *dst++ = '"'; break;
384         case '\\': *dst++ = '\\'; *dst++ = '\\'; break;
385         default:
386             if (c >= ' ' && c <= 126)
387                 *dst++ = c;
388             else
389             {
390                 *dst++ = '\\';
391                 *dst++ = 'x';
392                 *dst++ = hex[(c >> 4) & 0x0f];
393                 *dst++ = hex[c & 0x0f];
394             }
395         }
396     }
397     *dst++ = '"';
398     if (n > 0)
399     {
400         *dst++ = '.';
401         *dst++ = '.';
402         *dst++ = '.';
403     }
404     *dst++ = 0;
405     funcs.release_temp_buffer( res, dst - res );
406     return res;
407 }
408 
409 
410 /* default implementation of wine_dbgstr_wn */
411 static const char *default_dbgstr_wn( const WCHAR *str, int n )
412 {
413     char *dst, *res;
414     size_t size;
415 
416     if (!((ULONG_PTR)str >> 16))
417     {
418         if (!str) return "(null)";
419         res = funcs.get_temp_buffer( 6 );
420         sprintf( res, "#%04x", LOWORD(str) );
421         return res;
422     }
423     if (n == -1)
424     {
425         const WCHAR *end = str;
426         while (*end) end++;
427         n = end - str;
428     }
429     if (n < 0) n = 0;
430     size = 12 + min( 300, n * 5 );
431     dst = res = funcs.get_temp_buffer( size );
432     *dst++ = 'L';
433     *dst++ = '"';
434     while (n-- > 0 && dst <= res + size - 10)
435     {
436         WCHAR c = *str++;
437         switch (c)
438         {
439         case '\n': *dst++ = '\\'; *dst++ = 'n'; break;
440         case '\r': *dst++ = '\\'; *dst++ = 'r'; break;
441         case '\t': *dst++ = '\\'; *dst++ = 't'; break;
442         case '"':  *dst++ = '\\'; *dst++ = '"'; break;
443         case '\\': *dst++ = '\\'; *dst++ = '\\'; break;
444         default:
445             if (c >= ' ' && c <= 126)
446                 *dst++ = c;
447             else
448             {
449                 *dst++ = '\\';
450                 sprintf(dst,"%04x",c);
451                 dst+=4;
452             }
453         }
454     }
455     *dst++ = '"';
456     if (n > 0)
457     {
458         *dst++ = '.';
459         *dst++ = '.';
460         *dst++ = '.';
461     }
462     *dst++ = 0;
463     funcs.release_temp_buffer( res, dst - res );
464     return res;
465 }
466 
467 
468 /* default implementation of wine_dbg_vprintf */
469 static int default_dbg_vprintf( const char *format, va_list args )
470 {
471     return vDbgPrintExWithPrefix("", -1, 0, format, args);
472 }
473 
474 
475 /* default implementation of wine_dbg_vlog */
476 /* Wine format */
477 static int winefmt_default_dbg_vlog( enum __wine_debug_class cls, struct __wine_debug_channel *channel,
478                                      const char *file, const char *func, const int line, const char *format, va_list args )
479 {
480     int ret = 0;
481 
482     if (TRACE_ON(pid))
483         ret += wine_dbg_printf( "%04x:", HandleToULong(NtCurrentTeb()->ClientId.UniqueProcess) );
484     ret += wine_dbg_printf( "%04x:", HandleToULong(NtCurrentTeb()->ClientId.UniqueThread) );
485 
486     if (cls < sizeof(debug_classes)/sizeof(debug_classes[0]))
487         ret += wine_dbg_printf( "%s:%s:%s ", debug_classes[cls], channel->name, func );
488     if (format)
489         ret += funcs.dbg_vprintf( format, args );
490     return ret;
491 }
492 /* ReactOS format (default) */
493 static int rosfmt_default_dbg_vlog( enum __wine_debug_class cls, struct __wine_debug_channel *channel,
494                                     const char *file, const char *func, const int line, const char *format, va_list args )
495 {
496     int ret = 0;
497 
498     if (TRACE_ON(tid))
499         ret += wine_dbg_printf( "%04x:", HandleToULong(NtCurrentTeb()->ClientId.UniqueThread) );
500 
501     if (cls < sizeof(debug_classes)/sizeof(debug_classes[0]))
502         ret += wine_dbg_printf( "%s:", debug_classes[cls] );
503 
504     if (file && line)
505         ret += wine_dbg_printf( "(%s:%d) ", file, line );
506     else
507         ret += wine_dbg_printf( "%s:%s: ", channel->name, func );
508 
509     if (format)
510         ret += funcs.dbg_vprintf( format, args );
511     return ret;
512 }
513 /* Extended format */
514 static int extfmt_default_dbg_vlog( enum __wine_debug_class cls, struct __wine_debug_channel *channel,
515                                     const char *file, const char *func, const int line, const char *format, va_list args )
516 {
517     int ret = 0;
518 
519     if (TRACE_ON(pid) || TRACE_ON(tid))
520     {
521         ret += wine_dbg_printf( "[%04x:%04x]:",
522                                 (TRACE_ON(pid) ? HandleToULong(NtCurrentTeb()->ClientId.UniqueProcess) : 0),
523                                 (TRACE_ON(tid) ? HandleToULong(NtCurrentTeb()->ClientId.UniqueThread)  : 0) );
524     }
525 
526     if (cls < sizeof(debug_classes)/sizeof(debug_classes[0]))
527         ret += wine_dbg_printf( "%s:", debug_classes[cls] );
528 
529     if (file && line)
530         ret += wine_dbg_printf( "(%s:%d):", file, line );
531 
532     ret += wine_dbg_printf( "%s:%s ", channel->name, func );
533 
534     if (format)
535         ret += funcs.dbg_vprintf( format, args );
536     return ret;
537 }
538 
539 /* wrappers to use the function pointers */
540 
541 const char *wine_dbgstr_an( const char * s, int n )
542 {
543     return funcs.dbgstr_an(s, n);
544 }
545 
546 const char *wine_dbgstr_wn( const WCHAR *s, int n )
547 {
548     return funcs.dbgstr_wn(s, n);
549 }
550 
551 void __wine_dbg_set_functions( const struct __wine_debug_functions *new_funcs,
552                                struct __wine_debug_functions *old_funcs, size_t size )
553 {
554     if (old_funcs) memcpy( old_funcs, &funcs, min(sizeof(funcs),size) );
555     if (new_funcs) memcpy( &funcs, new_funcs, min(sizeof(funcs),size) );
556 }
557 
558 static struct __wine_debug_functions funcs =
559 {
560     get_temp_buffer,
561     release_temp_buffer,
562     default_dbgstr_an,
563     default_dbgstr_wn,
564     default_dbg_vprintf,
565     rosfmt_default_dbg_vlog
566 };
567