Reactos
at master 555 lines 17 kB view raw
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};