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