1 /**************************************************************************** 2 * 3 * ftdebug.c 4 * 5 * Debugging and logging component for Win32 (body). 6 * 7 * Copyright (C) 1996-2023 by 8 * David Turner, Robert Wilhelm, and Werner Lemberg. 9 * 10 * This file is part of the FreeType project, and may only be used, 11 * modified, and distributed under the terms of the FreeType project 12 * license, LICENSE.TXT. By continuing to use, modify, or distribute 13 * this file you indicate that you have read the license and 14 * understand and accept it fully. 15 * 16 */ 17 18 19 /************************************************************************** 20 * 21 * This component contains various macros and functions used to ease the 22 * debugging of the FreeType engine. Its main purpose is in assertion 23 * checking, tracing, and error detection. 24 * 25 * There are now three debugging modes: 26 * 27 * - trace mode 28 * 29 * Error and trace messages are sent to the log file (which can be the 30 * standard error output). 31 * 32 * - error mode 33 * 34 * Only error messages are generated. 35 * 36 * - release mode: 37 * 38 * No error message is sent or generated. The code is free from any 39 * debugging parts. 40 * 41 */ 42 43 44 #include <freetype/freetype.h> 45 #include <freetype/ftlogging.h> 46 #include <freetype/internal/ftdebug.h> 47 #include <freetype/internal/ftobjs.h> 48 49 50 #ifdef FT_DEBUG_LOGGING 51 52 /************************************************************************** 53 * 54 * Variables used to control logging. 55 * 56 * 1. `ft_default_trace_level` stores the value of trace levels, which are 57 * provided to FreeType using the `FT2_DEBUG` environment variable. 58 * 59 * 2. `ft_fileptr` stores the `FILE*` handle. 60 * 61 * 3. `ft_component` is a string that holds the name of `FT_COMPONENT`. 62 * 63 * 4. The flag `ft_component_flag` prints the name of `FT_COMPONENT` along 64 * with the actual log message if set to true. 65 * 66 * 5. The flag `ft_timestamp_flag` prints time along with the actual log 67 * message if set to ture. 68 * 69 * 6. `ft_have_newline_char` is used to differentiate between a log 70 * message with and without a trailing newline character. 71 * 72 * 7. `ft_custom_trace_level` stores the custom trace level value, which 73 * is provided by the user at run-time. 74 * 75 * We use `static` to avoid 'unused variable' warnings. 76 * 77 */ 78 static const char* ft_default_trace_level = NULL; 79 static FILE* ft_fileptr = NULL; 80 static const char* ft_component = NULL; 81 static FT_Bool ft_component_flag = FALSE; 82 static FT_Bool ft_timestamp_flag = FALSE; 83 static FT_Bool ft_have_newline_char = TRUE; 84 static const char* ft_custom_trace_level = NULL; 85 86 /* declared in ftdebug.h */ 87 88 dlg_handler ft_default_log_handler = NULL; 89 FT_Custom_Log_Handler custom_output_handler = NULL; 90 91 #endif /* FT_DEBUG_LOGGING */ 92 93 94 #ifdef FT_DEBUG_LEVEL_ERROR 95 96 #define WIN32_LEAN_AND_MEAN 97 #include <windows.h> 98 99 100 #ifdef _WIN32_WCE 101 102 FT_LOACAL_DEF( void ) OutputDebugStringA(LPCSTR lpOutputString)103 OutputDebugStringA( LPCSTR lpOutputString ) 104 { 105 int len; 106 LPWSTR lpOutputStringW; 107 108 109 /* allocate memory space for converted string */ 110 len = MultiByteToWideChar( CP_ACP, MB_ERR_INVALID_CHARS, 111 lpOutputString, -1, NULL, 0 ); 112 113 lpOutputStringW = (LPWSTR)_alloca( len * sizeof ( WCHAR ) ); 114 115 if ( !len || !lpOutputStringW ) 116 return; 117 118 /* now it is safe to do the translation */ 119 MultiByteToWideChar( CP_ACP, MB_ERR_INVALID_CHARS, 120 lpOutputString, -1, lpOutputStringW, len ); 121 122 OutputDebugStringW( lpOutputStringW ); 123 } 124 125 #endif /* _WIN32_WCE */ 126 127 128 /* documentation is in ftdebug.h */ 129 130 FT_BASE_DEF( void ) FT_Message(const char * fmt,...)131 FT_Message( const char* fmt, 132 ... ) 133 { 134 va_list ap; 135 136 137 va_start( ap, fmt ); 138 vfprintf( stderr, fmt, ap ); 139 #if ( defined( _WIN32_WINNT ) && _WIN32_WINNT >= 0x0400 ) || \ 140 ( defined( _WIN32_WCE ) && _WIN32_WCE >= 0x0600 ) 141 if ( IsDebuggerPresent() ) 142 { 143 static char buf[1024]; 144 145 146 vsnprintf( buf, sizeof buf, fmt, ap ); 147 OutputDebugStringA( buf ); 148 } 149 #endif 150 va_end( ap ); 151 } 152 153 154 /* documentation is in ftdebug.h */ 155 156 FT_BASE_DEF( void ) FT_Panic(const char * fmt,...)157 FT_Panic( const char* fmt, 158 ... ) 159 { 160 va_list ap; 161 162 163 va_start( ap, fmt ); 164 vfprintf( stderr, fmt, ap ); 165 #if ( defined( _WIN32_WINNT ) && _WIN32_WINNT >= 0x0400 ) || \ 166 ( defined( _WIN32_WCE ) && _WIN32_WCE >= 0x0600 ) 167 if ( IsDebuggerPresent() ) 168 { 169 static char buf[1024]; 170 171 172 vsnprintf( buf, sizeof buf, fmt, ap ); 173 OutputDebugStringA( buf ); 174 } 175 #endif 176 va_end( ap ); 177 178 exit( EXIT_FAILURE ); 179 } 180 181 182 /* documentation is in ftdebug.h */ 183 184 FT_BASE_DEF( int ) FT_Throw(FT_Error error,int line,const char * file)185 FT_Throw( FT_Error error, 186 int line, 187 const char* file ) 188 { 189 #if 0 190 /* activating the code in this block makes FreeType very chatty */ 191 fprintf( stderr, 192 "%s:%d: error 0x%02x: %s\n", 193 file, 194 line, 195 error, 196 FT_Error_String( error ) ); 197 #else 198 FT_UNUSED( error ); 199 FT_UNUSED( line ); 200 FT_UNUSED( file ); 201 #endif 202 203 return 0; 204 } 205 206 #endif /* FT_DEBUG_LEVEL_ERROR */ 207 208 209 #ifdef FT_DEBUG_LEVEL_TRACE 210 211 /* array of trace levels, initialized to 0; */ 212 /* this gets adjusted at run-time */ 213 static int ft_trace_levels_enabled[trace_count]; 214 215 /* array of trace levels, always initialized to 0 */ 216 static int ft_trace_levels_disabled[trace_count]; 217 218 /* a pointer to either `ft_trace_levels_enabled' */ 219 /* or `ft_trace_levels_disabled' */ 220 int* ft_trace_levels; 221 222 /* define array of trace toggle names */ 223 #define FT_TRACE_DEF( x ) #x , 224 225 static const char* ft_trace_toggles[trace_count + 1] = 226 { 227 #include <freetype/internal/fttrace.h> 228 NULL 229 }; 230 231 #undef FT_TRACE_DEF 232 233 234 /* documentation is in ftdebug.h */ 235 236 FT_BASE_DEF( FT_Int ) FT_Trace_Get_Count(void)237 FT_Trace_Get_Count( void ) 238 { 239 return trace_count; 240 } 241 242 243 /* documentation is in ftdebug.h */ 244 245 FT_BASE_DEF( const char * ) FT_Trace_Get_Name(FT_Int idx)246 FT_Trace_Get_Name( FT_Int idx ) 247 { 248 int max = FT_Trace_Get_Count(); 249 250 251 if ( idx < max ) 252 return ft_trace_toggles[idx]; 253 else 254 return NULL; 255 } 256 257 258 /* documentation is in ftdebug.h */ 259 260 FT_BASE_DEF( void ) FT_Trace_Disable(void)261 FT_Trace_Disable( void ) 262 { 263 ft_trace_levels = ft_trace_levels_disabled; 264 } 265 266 267 /* documentation is in ftdebug.h */ 268 269 FT_BASE_DEF( void ) FT_Trace_Enable(void)270 FT_Trace_Enable( void ) 271 { 272 ft_trace_levels = ft_trace_levels_enabled; 273 } 274 275 276 /************************************************************************** 277 * 278 * Initialize the tracing sub-system. This is done by retrieving the 279 * value of the `FT2_DEBUG' environment variable. It must be a list of 280 * toggles, separated by spaces, `;', or `,'. Example: 281 * 282 * export FT2_DEBUG="any:3 memory:7 stream:5" 283 * 284 * This requests that all levels be set to 3, except the trace level for 285 * the memory and stream components which are set to 7 and 5, 286 * respectively. 287 * 288 * See the file `include/freetype/internal/fttrace.h' for details of 289 * the available toggle names. 290 * 291 * The level must be between 0 and 7; 0 means quiet (except for serious 292 * runtime errors), and 7 means _very_ verbose. 293 */ 294 FT_BASE_DEF( void ) ft_debug_init(void)295 ft_debug_init( void ) 296 { 297 const char* ft2_debug = NULL; 298 299 300 #ifdef FT_DEBUG_LOGGING 301 if ( ft_custom_trace_level != NULL ) 302 ft2_debug = ft_custom_trace_level; 303 else 304 ft2_debug = ft_default_trace_level; 305 #else 306 ft2_debug = ft_getenv( "FT2_DEBUG" ); 307 #endif 308 309 if ( ft2_debug ) 310 { 311 const char* p = ft2_debug; 312 const char* q; 313 314 315 for ( ; *p; p++ ) 316 { 317 /* skip leading whitespace and separators */ 318 if ( *p == ' ' || *p == '\t' || *p == ',' || *p == ';' || *p == '=' ) 319 continue; 320 321 #ifdef FT_DEBUG_LOGGING 322 323 /* check extra arguments for logging */ 324 if ( *p == '-' ) 325 { 326 const char* r = ++p; 327 328 329 if ( *r == 'v' ) 330 { 331 const char* s = ++r; 332 333 334 ft_component_flag = TRUE; 335 336 if ( *s == 't' ) 337 { 338 ft_timestamp_flag = TRUE; 339 p++; 340 } 341 342 p++; 343 } 344 345 else if ( *r == 't' ) 346 { 347 const char* s = ++r; 348 349 350 ft_timestamp_flag = TRUE; 351 352 if ( *s == 'v' ) 353 { 354 ft_component_flag = TRUE; 355 p++; 356 } 357 358 p++; 359 } 360 } 361 362 #endif /* FT_DEBUG_LOGGING */ 363 364 /* read toggle name, followed by ':' */ 365 q = p; 366 while ( *p && *p != ':' ) 367 p++; 368 369 if ( !*p ) 370 break; 371 372 if ( *p == ':' && p > q ) 373 { 374 FT_Int n, i, len = (FT_Int)( p - q ); 375 FT_Int level = -1, found = -1; 376 377 378 for ( n = 0; n < trace_count; n++ ) 379 { 380 const char* toggle = ft_trace_toggles[n]; 381 382 383 for ( i = 0; i < len; i++ ) 384 { 385 if ( toggle[i] != q[i] ) 386 break; 387 } 388 389 if ( i == len && toggle[i] == 0 ) 390 { 391 found = n; 392 break; 393 } 394 } 395 396 /* read level */ 397 p++; 398 if ( *p ) 399 { 400 level = *p - '0'; 401 if ( level < 0 || level > 7 ) 402 level = -1; 403 } 404 405 if ( found >= 0 && level >= 0 ) 406 { 407 if ( found == trace_any ) 408 { 409 /* special case for `any' */ 410 for ( n = 0; n < trace_count; n++ ) 411 ft_trace_levels_enabled[n] = level; 412 } 413 else 414 ft_trace_levels_enabled[found] = level; 415 } 416 } 417 } 418 } 419 420 ft_trace_levels = ft_trace_levels_enabled; 421 } 422 423 424 #else /* !FT_DEBUG_LEVEL_TRACE */ 425 426 427 FT_BASE_DEF( void ) ft_debug_init(void)428 ft_debug_init( void ) 429 { 430 /* nothing */ 431 } 432 433 434 FT_BASE_DEF( FT_Int ) FT_Trace_Get_Count(void)435 FT_Trace_Get_Count( void ) 436 { 437 return 0; 438 } 439 440 441 FT_BASE_DEF( const char * ) FT_Trace_Get_Name(FT_Int idx)442 FT_Trace_Get_Name( FT_Int idx ) 443 { 444 FT_UNUSED( idx ); 445 446 return NULL; 447 } 448 449 450 FT_BASE_DEF( void ) FT_Trace_Disable(void)451 FT_Trace_Disable( void ) 452 { 453 /* nothing */ 454 } 455 456 457 /* documentation is in ftdebug.h */ 458 459 FT_BASE_DEF( void ) FT_Trace_Enable(void)460 FT_Trace_Enable( void ) 461 { 462 /* nothing */ 463 } 464 465 #endif /* !FT_DEBUG_LEVEL_TRACE */ 466 467 468 #ifdef FT_DEBUG_LOGGING 469 470 /************************************************************************** 471 * 472 * Initialize and de-initialize 'dlg' library. 473 * 474 */ 475 476 FT_BASE_DEF( void ) ft_logging_init(void)477 ft_logging_init( void ) 478 { 479 ft_default_log_handler = ft_log_handler; 480 ft_default_trace_level = ft_getenv( "FT2_DEBUG" ); 481 482 if ( ft_getenv( "FT_LOGGING_FILE" ) ) 483 ft_fileptr = ft_fopen( ft_getenv( "FT_LOGGING_FILE" ), "w" ); 484 else 485 ft_fileptr = stderr; 486 487 ft_debug_init(); 488 489 /* Set the default output handler for 'dlg'. */ 490 dlg_set_handler( ft_default_log_handler, NULL ); 491 } 492 493 494 FT_BASE_DEF( void ) ft_logging_deinit(void)495 ft_logging_deinit( void ) 496 { 497 if ( ft_fileptr != stderr ) 498 ft_fclose( ft_fileptr ); 499 } 500 501 502 /************************************************************************** 503 * 504 * An output log handler for FreeType. 505 * 506 */ 507 FT_BASE_DEF( void ) ft_log_handler(const struct dlg_origin * origin,const char * string,void * data)508 ft_log_handler( const struct dlg_origin* origin, 509 const char* string, 510 void* data ) 511 { 512 char features_buf[128]; 513 char* bufp = features_buf; 514 515 FT_UNUSED( data ); 516 517 518 if ( ft_have_newline_char ) 519 { 520 const char* features = NULL; 521 size_t features_length = 0; 522 523 524 #define FEATURES_TIMESTAMP "[%h:%m] " 525 #define FEATURES_COMPONENT "[%t] " 526 #define FEATURES_TIMESTAMP_COMPONENT "[%h:%m %t] " 527 528 if ( ft_timestamp_flag && ft_component_flag ) 529 { 530 features = FEATURES_TIMESTAMP_COMPONENT; 531 features_length = sizeof ( FEATURES_TIMESTAMP_COMPONENT ); 532 } 533 else if ( ft_timestamp_flag ) 534 { 535 features = FEATURES_TIMESTAMP; 536 features_length = sizeof ( FEATURES_TIMESTAMP ); 537 } 538 else if ( ft_component_flag ) 539 { 540 features = FEATURES_COMPONENT; 541 features_length = sizeof ( FEATURES_COMPONENT ); 542 } 543 544 if ( ft_component_flag || ft_timestamp_flag ) 545 { 546 ft_strncpy( features_buf, features, features_length ); 547 bufp += features_length - 1; 548 } 549 550 if ( ft_component_flag ) 551 { 552 size_t tag_length = ft_strlen( *origin->tags ); 553 size_t i; 554 555 556 /* To vertically align tracing messages we compensate the */ 557 /* different FT_COMPONENT string lengths by inserting an */ 558 /* appropriate amount of space characters. */ 559 for ( i = 0; 560 i < FT_MAX_TRACE_LEVEL_LENGTH - tag_length; 561 i++ ) 562 *bufp++ = ' '; 563 } 564 } 565 566 /* Finally add the format string for the tracing message. */ 567 *bufp++ = '%'; 568 *bufp++ = 'c'; 569 *bufp = '\0'; 570 571 dlg_generic_outputf_stream( ft_fileptr, 572 (const char*)features_buf, 573 origin, 574 string, 575 dlg_default_output_styles, 576 true ); 577 578 if ( ft_strrchr( string, '\n' ) ) 579 ft_have_newline_char = TRUE; 580 else 581 ft_have_newline_char = FALSE; 582 } 583 584 585 /* documentation is in ftdebug.h */ 586 FT_BASE_DEF( void ) ft_add_tag(const char * tag)587 ft_add_tag( const char* tag ) 588 { 589 ft_component = tag; 590 591 dlg_add_tag( tag, NULL ); 592 } 593 594 595 /* documentation is in ftdebug.h */ 596 FT_BASE_DEF( void ) ft_remove_tag(const char * tag)597 ft_remove_tag( const char* tag ) 598 { 599 dlg_remove_tag( tag, NULL ); 600 } 601 602 603 /* documentation is in ftlogging.h */ 604 605 FT_EXPORT_DEF( void ) FT_Trace_Set_Level(const char * level)606 FT_Trace_Set_Level( const char* level ) 607 { 608 ft_component_flag = FALSE; 609 ft_timestamp_flag = FALSE; 610 ft_custom_trace_level = level; 611 612 ft_debug_init(); 613 } 614 615 616 /* documentation is in ftlogging.h */ 617 618 FT_EXPORT_DEF( void ) FT_Trace_Set_Default_Level(void)619 FT_Trace_Set_Default_Level( void ) 620 { 621 ft_component_flag = FALSE; 622 ft_timestamp_flag = FALSE; 623 ft_custom_trace_level = NULL; 624 625 ft_debug_init(); 626 } 627 628 629 /************************************************************************** 630 * 631 * Functions to handle a custom log handler. 632 * 633 */ 634 635 /* documentation is in ftlogging.h */ 636 637 FT_EXPORT_DEF( void ) FT_Set_Log_Handler(FT_Custom_Log_Handler handler)638 FT_Set_Log_Handler( FT_Custom_Log_Handler handler ) 639 { 640 custom_output_handler = handler; 641 } 642 643 644 /* documentation is in ftlogging.h */ 645 646 FT_EXPORT_DEF( void ) FT_Set_Default_Log_Handler(void)647 FT_Set_Default_Log_Handler( void ) 648 { 649 custom_output_handler = NULL; 650 } 651 652 653 /* documentation is in ftdebug.h */ 654 FT_BASE_DEF( void ) FT_Logging_Callback(const char * fmt,...)655 FT_Logging_Callback( const char* fmt, 656 ... ) 657 { 658 va_list ap; 659 660 661 va_start( ap, fmt ); 662 custom_output_handler( ft_component, fmt, ap ); 663 va_end( ap ); 664 } 665 666 #else /* !FT_DEBUG_LOGGING */ 667 668 FT_EXPORT_DEF( void ) FT_Trace_Set_Level(const char * level)669 FT_Trace_Set_Level( const char* level ) 670 { 671 FT_UNUSED( level ); 672 } 673 674 675 FT_EXPORT_DEF( void ) FT_Trace_Set_Default_Level(void)676 FT_Trace_Set_Default_Level( void ) 677 { 678 /* nothing */ 679 } 680 681 682 FT_EXPORT_DEF( void ) FT_Set_Log_Handler(FT_Custom_Log_Handler handler)683 FT_Set_Log_Handler( FT_Custom_Log_Handler handler ) 684 { 685 FT_UNUSED( handler ); 686 } 687 688 689 FT_EXPORT_DEF( void ) FT_Set_Default_Log_Handler(void)690 FT_Set_Default_Log_Handler( void ) 691 { 692 /* nothing */ 693 } 694 695 #endif /* !FT_DEBUG_LOGGING */ 696 697 698 /* END */ 699