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