xref: /reactos/sdk/lib/3rdparty/libwine/debug.c (revision c2c66aff)
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(tid);
39 
40 ULONG
41 NTAPI
42 vDbgPrintExWithPrefix(
43   IN LPCSTR Prefix,
44   IN ULONG ComponentId,
45   IN ULONG Level,
46   IN LPCSTR Format,
47   IN va_list ap);
48 
49 
50 static const char * const debug_classes[] = { "fixme", "err", "warn", "trace" };
51 
52 #define MAX_DEBUG_OPTIONS 256
53 
54 static unsigned char default_flags = (1 << __WINE_DBCL_ERR) | (1 << __WINE_DBCL_FIXME);
55 static int nb_debug_options = -1;
56 static struct __wine_debug_channel debug_options[MAX_DEBUG_OPTIONS];
57 
58 static struct __wine_debug_functions funcs;
59 
60 static void debug_init(void);
61 
62 static int __cdecl cmp_name( const void *p1, const void *p2 )
63 {
64     const char *name = p1;
65     const struct __wine_debug_channel *chan = p2;
66     return strcmp( name, chan->name );
67 }
68 
69 /* get the flags to use for a given channel, possibly setting them too in case of lazy init */
70 unsigned char __wine_dbg_get_channel_flags( struct __wine_debug_channel *channel )
71 {
72     if (nb_debug_options == -1) debug_init();
73 
74     if (nb_debug_options)
75     {
76         struct __wine_debug_channel *opt = bsearch( channel->name, debug_options, nb_debug_options,
77                                                     sizeof(debug_options[0]), cmp_name );
78         if (opt) return opt->flags;
79     }
80     /* no option for this channel */
81     if (channel->flags & (1 << __WINE_DBCL_INIT)) channel->flags = default_flags;
82     return default_flags;
83 }
84 
85 /* set the flags to use for a given channel; return 0 if the channel is not available to set */
86 int __wine_dbg_set_channel_flags( struct __wine_debug_channel *channel,
87                                   unsigned char set, unsigned char clear )
88 {
89     if (nb_debug_options == -1) debug_init();
90 
91     if (nb_debug_options)
92     {
93         struct __wine_debug_channel *opt = bsearch( channel->name, debug_options, nb_debug_options,
94                                                     sizeof(debug_options[0]), cmp_name );
95         if (opt)
96         {
97             opt->flags = (opt->flags & ~clear) | set;
98             return 1;
99         }
100     }
101     return 0;
102 }
103 
104 /* add a new debug option at the end of the option list */
105 static void add_option( const char *name, unsigned char set, unsigned char clear )
106 {
107     int min = 0, max = nb_debug_options - 1, pos, res;
108 
109     if (!name[0])  /* "all" option */
110     {
111         default_flags = (default_flags & ~clear) | set;
112         return;
113     }
114     if (strlen(name) >= sizeof(debug_options[0].name)) return;
115 
116     while (min <= max)
117     {
118         pos = (min + max) / 2;
119         res = strcmp( name, debug_options[pos].name );
120         if (!res)
121         {
122             debug_options[pos].flags = (debug_options[pos].flags & ~clear) | set;
123             return;
124         }
125         if (res < 0) max = pos - 1;
126         else min = pos + 1;
127     }
128     if (nb_debug_options >= MAX_DEBUG_OPTIONS) return;
129 
130     pos = min;
131     if (pos < nb_debug_options) memmove( &debug_options[pos + 1], &debug_options[pos],
132                                          (nb_debug_options - pos) * sizeof(debug_options[0]) );
133     strcpy( debug_options[pos].name, name );
134     debug_options[pos].flags = (default_flags & ~clear) | set;
135     nb_debug_options++;
136 }
137 
138 /* parse a set of debugging option specifications and add them to the option list */
139 static void parse_options( const char *str )
140 {
141     char *opt, *next, *options;
142     unsigned int i;
143 
144     if (!(options = _strdup(str))) return;
145     for (opt = options; opt; opt = next)
146     {
147         const char *p;
148         unsigned char set = 0, clear = 0;
149 
150         if ((next = strchr( opt, ',' ))) *next++ = 0;
151 
152         p = opt + strcspn( opt, "+-" );
153         if (!p[0]) p = opt;  /* assume it's a debug channel name */
154 
155         if (p > opt)
156         {
157             for (i = 0; i < sizeof(debug_classes)/sizeof(debug_classes[0]); i++)
158             {
159                 int len = strlen(debug_classes[i]);
160                 if (len != (p - opt)) continue;
161                 if (!memcmp( opt, debug_classes[i], len ))  /* found it */
162                 {
163                     if (*p == '+') set |= 1 << i;
164                     else clear |= 1 << i;
165                     break;
166                 }
167             }
168             if (i == sizeof(debug_classes)/sizeof(debug_classes[0])) /* bad class name, skip it */
169                 continue;
170         }
171         else
172         {
173             if (*p == '-') clear = ~0;
174             else set = ~0;
175         }
176         if (*p == '+' || *p == '-') p++;
177         if (!p[0]) continue;
178 
179         if (!strcmp( p, "all" ))
180             default_flags = (default_flags & ~clear) | set;
181         else
182             add_option( p, set, clear );
183     }
184     free( options );
185 }
186 
187 /* initialize all options at startup */
188 static void debug_init(void)
189 {
190     char *wine_debug;
191     DWORD dwLength;
192     /* GetEnvironmentVariableA will change LastError! */
193     DWORD LastError = GetLastError();
194 
195     if (nb_debug_options != -1) return;  /* already initialized */
196     nb_debug_options = 0;
197 
198     dwLength = GetEnvironmentVariableA("DEBUGCHANNEL", NULL, 0);
199     if (dwLength)
200     {
201         wine_debug = malloc(dwLength);
202         if (wine_debug)
203         {
204             if (GetEnvironmentVariableA("DEBUGCHANNEL", wine_debug, dwLength) < dwLength)
205                 parse_options(wine_debug);
206             free(wine_debug);
207         }
208     }
209     SetLastError(LastError);
210 }
211 
212 /* varargs wrapper for funcs.dbg_vprintf */
213 int wine_dbg_printf( const char *format, ... )
214 {
215     int ret;
216     va_list valist;
217 
218     va_start(valist, format);
219     ret = funcs.dbg_vprintf( format, valist );
220     va_end(valist);
221     return ret;
222 }
223 
224 /* printf with temp buffer allocation */
225 const char *wine_dbg_sprintf( const char *format, ... )
226 {
227     static const int max_size = 200;
228     char *ret;
229     int len;
230     va_list valist;
231 
232     va_start(valist, format);
233     ret = funcs.get_temp_buffer( max_size );
234     len = vsnprintf( ret, max_size, format, valist );
235     if (len == -1 || len >= max_size) ret[max_size-1] = 0;
236     else funcs.release_temp_buffer( ret, len + 1 );
237     va_end(valist);
238     return ret;
239 }
240 
241 
242 /* varargs wrapper for funcs.dbg_vlog */
243 int wine_dbg_log( enum __wine_debug_class cls, struct __wine_debug_channel *channel,
244                   const char *func, const char *format, ... )
245 {
246     int ret;
247     va_list valist;
248 
249     if (!(__wine_dbg_get_channel_flags( channel ) & (1 << cls))) return -1;
250 
251     va_start(valist, format);
252     ret = funcs.dbg_vlog( cls, channel, NULL, func, 0, format, valist );
253     va_end(valist);
254     return ret;
255 }
256 
257 
258 /* ReactOS compliant debug format wrapper for funcs.dbg_vlog */
259 int ros_dbg_log( enum __wine_debug_class cls, struct __wine_debug_channel *channel,
260                   const char *file, const char *func, const int line, const char *format, ... )
261 {
262     int ret;
263     va_list valist;
264 
265     if (!(__wine_dbg_get_channel_flags( channel ) & (1 << cls))) return -1;
266 
267     va_start(valist, format);
268     ret = funcs.dbg_vlog( cls, channel, file, func, line, format, valist );
269     va_end(valist);
270     return ret;
271 }
272 
273 
274 /* allocate some tmp string space */
275 /* FIXME: this is not 100% thread-safe */
276 static char *get_temp_buffer( size_t size )
277 {
278     static char *list[32];
279     static int pos;
280     char *ret;
281     int idx;
282 
283     idx = interlocked_xchg_add( &pos, 1 ) % (sizeof(list)/sizeof(list[0]));
284     if ((ret = realloc( list[idx], size ))) list[idx] = ret;
285     return ret;
286 }
287 
288 
289 /* release unused part of the buffer */
290 static void release_temp_buffer( char *buffer, size_t size )
291 {
292     /* don't bother doing anything */
293 }
294 
295 
296 /* default implementation of wine_dbgstr_an */
297 static const char *default_dbgstr_an( const char *str, int n )
298 {
299     static const char hex[16] = "0123456789abcdef";
300     char *dst, *res;
301     size_t size;
302 
303     if (!((ULONG_PTR)str >> 16))
304     {
305         if (!str) return "(null)";
306         res = funcs.get_temp_buffer( 6 );
307         sprintf( res, "#%04x", LOWORD(str) );
308         return res;
309     }
310     if (n == -1) n = strlen(str);
311     if (n < 0) n = 0;
312     size = 10 + min( 300, n * 4 );
313     dst = res = funcs.get_temp_buffer( size );
314     *dst++ = '"';
315     while (n-- > 0 && dst <= res + size - 9)
316     {
317         unsigned char c = *str++;
318         switch (c)
319         {
320         case '\n': *dst++ = '\\'; *dst++ = 'n'; break;
321         case '\r': *dst++ = '\\'; *dst++ = 'r'; break;
322         case '\t': *dst++ = '\\'; *dst++ = 't'; break;
323         case '"':  *dst++ = '\\'; *dst++ = '"'; break;
324         case '\\': *dst++ = '\\'; *dst++ = '\\'; break;
325         default:
326             if (c >= ' ' && c <= 126)
327                 *dst++ = c;
328             else
329             {
330                 *dst++ = '\\';
331                 *dst++ = 'x';
332                 *dst++ = hex[(c >> 4) & 0x0f];
333                 *dst++ = hex[c & 0x0f];
334             }
335         }
336     }
337     *dst++ = '"';
338     if (n > 0)
339     {
340         *dst++ = '.';
341         *dst++ = '.';
342         *dst++ = '.';
343     }
344     *dst++ = 0;
345     funcs.release_temp_buffer( res, dst - res );
346     return res;
347 }
348 
349 
350 /* default implementation of wine_dbgstr_wn */
351 static const char *default_dbgstr_wn( const WCHAR *str, int n )
352 {
353     char *dst, *res;
354     size_t size;
355 
356     if (!((ULONG_PTR)str >> 16))
357     {
358         if (!str) return "(null)";
359         res = funcs.get_temp_buffer( 6 );
360         sprintf( res, "#%04x", LOWORD(str) );
361         return res;
362     }
363     if (n == -1)
364     {
365         const WCHAR *end = str;
366         while (*end) end++;
367         n = end - str;
368     }
369     if (n < 0) n = 0;
370     size = 12 + min( 300, n * 5 );
371     dst = res = funcs.get_temp_buffer( size );
372     *dst++ = 'L';
373     *dst++ = '"';
374     while (n-- > 0 && dst <= res + size - 10)
375     {
376         WCHAR c = *str++;
377         switch (c)
378         {
379         case '\n': *dst++ = '\\'; *dst++ = 'n'; break;
380         case '\r': *dst++ = '\\'; *dst++ = 'r'; break;
381         case '\t': *dst++ = '\\'; *dst++ = 't'; break;
382         case '"':  *dst++ = '\\'; *dst++ = '"'; break;
383         case '\\': *dst++ = '\\'; *dst++ = '\\'; break;
384         default:
385             if (c >= ' ' && c <= 126)
386                 *dst++ = c;
387             else
388             {
389                 *dst++ = '\\';
390                 sprintf(dst,"%04x",c);
391                 dst+=4;
392             }
393         }
394     }
395     *dst++ = '"';
396     if (n > 0)
397     {
398         *dst++ = '.';
399         *dst++ = '.';
400         *dst++ = '.';
401     }
402     *dst++ = 0;
403     funcs.release_temp_buffer( res, dst - res );
404     return res;
405 }
406 
407 
408 /* default implementation of wine_dbg_vprintf */
409 static int default_dbg_vprintf( const char *format, va_list args )
410 {
411     return vDbgPrintExWithPrefix("", -1, 0, format, args);
412 }
413 
414 
415 /* default implementation of wine_dbg_vlog */
416 static int default_dbg_vlog( enum __wine_debug_class cls, struct __wine_debug_channel *channel,
417                              const char *file, const char *func, const int line, const char *format, va_list args )
418 {
419     int ret = 0;
420 
421     if (TRACE_ON(tid))
422         ret += wine_dbg_printf("%04x:", HandleToULong(NtCurrentTeb()->ClientId.UniqueThread));
423 
424     if (cls < sizeof(debug_classes)/sizeof(debug_classes[0]))
425         ret += wine_dbg_printf( "%s:", debug_classes[cls] );
426 
427     if (file && line)
428         ret += wine_dbg_printf ( "(%s:%d) ", file, line );
429     else
430         ret += wine_dbg_printf( "%s:%s: ", channel->name, func );
431 
432     if (format)
433         ret += funcs.dbg_vprintf( format, args );
434     return ret;
435 }
436 
437 /* wrappers to use the function pointers */
438 
439 const char *wine_dbgstr_an( const char * s, int n )
440 {
441     return funcs.dbgstr_an(s, n);
442 }
443 
444 const char *wine_dbgstr_wn( const WCHAR *s, int n )
445 {
446     return funcs.dbgstr_wn(s, n);
447 }
448 
449 void __wine_dbg_set_functions( const struct __wine_debug_functions *new_funcs,
450                                struct __wine_debug_functions *old_funcs, size_t size )
451 {
452     if (old_funcs) memcpy( old_funcs, &funcs, min(sizeof(funcs),size) );
453     if (new_funcs) memcpy( &funcs, new_funcs, min(sizeof(funcs),size) );
454 }
455 
456 static struct __wine_debug_functions funcs =
457 {
458     get_temp_buffer,
459     release_temp_buffer,
460     default_dbgstr_an,
461     default_dbgstr_wn,
462     default_dbg_vprintf,
463     default_dbg_vlog
464 };
465