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