1 /**************************************************************************** 2 * 3 * ftdebug.c 4 * 5 * Debugging and logging component for Win32 (body). 6 * 7 * Copyright (C) 1996-2021 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 ( IsDebuggerPresent() ) 140 { 141 static char buf[1024]; 142 143 144 vsnprintf( buf, sizeof buf, fmt, ap ); 145 OutputDebugStringA( buf ); 146 } 147 va_end( ap ); 148 } 149 150 151 /* documentation is in ftdebug.h */ 152 153 FT_BASE_DEF( void ) FT_Panic(const char * fmt,...)154 FT_Panic( const char* fmt, 155 ... ) 156 { 157 va_list ap; 158 159 160 va_start( ap, fmt ); 161 vfprintf( stderr, fmt, ap ); 162 if ( IsDebuggerPresent() ) 163 { 164 static char buf[1024]; 165 166 167 vsnprintf( buf, sizeof buf, fmt, ap ); 168 OutputDebugStringA( buf ); 169 } 170 va_end( ap ); 171 172 exit( EXIT_FAILURE ); 173 } 174 175 176 /* documentation is in ftdebug.h */ 177 178 FT_BASE_DEF( int ) FT_Throw(FT_Error error,int line,const char * file)179 FT_Throw( FT_Error error, 180 int line, 181 const char* file ) 182 { 183 #if 0 184 /* activating the code in this block makes FreeType very chatty */ 185 fprintf( stderr, 186 "%s:%d: error 0x%02x: %s\n", 187 file, 188 line, 189 error, 190 FT_Error_String( error ) ); 191 #else 192 FT_UNUSED( error ); 193 FT_UNUSED( line ); 194 FT_UNUSED( file ); 195 #endif 196 197 return 0; 198 } 199 200 #endif /* FT_DEBUG_LEVEL_ERROR */ 201 202 203 #ifdef FT_DEBUG_LEVEL_TRACE 204 205 /* array of trace levels, initialized to 0; */ 206 /* this gets adjusted at run-time */ 207 static int ft_trace_levels_enabled[trace_count]; 208 209 /* array of trace levels, always initialized to 0 */ 210 static int ft_trace_levels_disabled[trace_count]; 211 212 /* a pointer to either `ft_trace_levels_enabled' */ 213 /* or `ft_trace_levels_disabled' */ 214 int* ft_trace_levels; 215 216 /* define array of trace toggle names */ 217 #define FT_TRACE_DEF( x ) #x , 218 219 static const char* ft_trace_toggles[trace_count + 1] = 220 { 221 #include <freetype/internal/fttrace.h> 222 NULL 223 }; 224 225 #undef FT_TRACE_DEF 226 227 228 /* documentation is in ftdebug.h */ 229 230 FT_BASE_DEF( FT_Int ) FT_Trace_Get_Count(void)231 FT_Trace_Get_Count( void ) 232 { 233 return trace_count; 234 } 235 236 237 /* documentation is in ftdebug.h */ 238 239 FT_BASE_DEF( const char * ) FT_Trace_Get_Name(FT_Int idx)240 FT_Trace_Get_Name( FT_Int idx ) 241 { 242 int max = FT_Trace_Get_Count(); 243 244 245 if ( idx < max ) 246 return ft_trace_toggles[idx]; 247 else 248 return NULL; 249 } 250 251 252 /* documentation is in ftdebug.h */ 253 254 FT_BASE_DEF( void ) FT_Trace_Disable(void)255 FT_Trace_Disable( void ) 256 { 257 ft_trace_levels = ft_trace_levels_disabled; 258 } 259 260 261 /* documentation is in ftdebug.h */ 262 263 FT_BASE_DEF( void ) FT_Trace_Enable(void)264 FT_Trace_Enable( void ) 265 { 266 ft_trace_levels = ft_trace_levels_enabled; 267 } 268 269 270 /************************************************************************** 271 * 272 * Initialize the tracing sub-system. This is done by retrieving the 273 * value of the `FT2_DEBUG' environment variable. It must be a list of 274 * toggles, separated by spaces, `;', or `,'. Example: 275 * 276 * export FT2_DEBUG="any:3 memory:7 stream:5" 277 * 278 * This requests that all levels be set to 3, except the trace level for 279 * the memory and stream components which are set to 7 and 5, 280 * respectively. 281 * 282 * See the file `include/freetype/internal/fttrace.h' for details of 283 * the available toggle names. 284 * 285 * The level must be between 0 and 7; 0 means quiet (except for serious 286 * runtime errors), and 7 means _very_ verbose. 287 */ 288 FT_BASE_DEF( void ) ft_debug_init(void)289 ft_debug_init( void ) 290 { 291 const char* ft2_debug = NULL; 292 293 294 #ifdef FT_DEBUG_LOGGING 295 if ( ft_custom_trace_level != NULL ) 296 ft2_debug = ft_custom_trace_level; 297 else 298 ft2_debug = ft_default_trace_level; 299 #else 300 ft2_debug = ft_getenv( "FT2_DEBUG" ); 301 #endif 302 303 if ( ft2_debug ) 304 { 305 const char* p = ft2_debug; 306 const char* q; 307 308 309 for ( ; *p; p++ ) 310 { 311 /* skip leading whitespace and separators */ 312 if ( *p == ' ' || *p == '\t' || *p == ',' || *p == ';' || *p == '=' ) 313 continue; 314 315 #ifdef FT_DEBUG_LOGGING 316 317 /* check extra arguments for logging */ 318 if ( *p == '-' ) 319 { 320 const char* r = ++p; 321 322 323 if ( *r == 'v' ) 324 { 325 const char* s = ++r; 326 327 328 ft_component_flag = TRUE; 329 330 if ( *s == 't' ) 331 { 332 ft_timestamp_flag = TRUE; 333 p++; 334 } 335 336 p++; 337 } 338 339 else if ( *r == 't' ) 340 { 341 const char* s = ++r; 342 343 344 ft_timestamp_flag = TRUE; 345 346 if ( *s == 'v' ) 347 { 348 ft_component_flag = TRUE; 349 p++; 350 } 351 352 p++; 353 } 354 } 355 356 #endif /* FT_DEBUG_LOGGING */ 357 358 /* read toggle name, followed by ':' */ 359 q = p; 360 while ( *p && *p != ':' ) 361 p++; 362 363 if ( !*p ) 364 break; 365 366 if ( *p == ':' && p > q ) 367 { 368 FT_Int n, i, len = (FT_Int)( p - q ); 369 FT_Int level = -1, found = -1; 370 371 372 for ( n = 0; n < trace_count; n++ ) 373 { 374 const char* toggle = ft_trace_toggles[n]; 375 376 377 for ( i = 0; i < len; i++ ) 378 { 379 if ( toggle[i] != q[i] ) 380 break; 381 } 382 383 if ( i == len && toggle[i] == 0 ) 384 { 385 found = n; 386 break; 387 } 388 } 389 390 /* read level */ 391 p++; 392 if ( *p ) 393 { 394 level = *p - '0'; 395 if ( level < 0 || level > 7 ) 396 level = -1; 397 } 398 399 if ( found >= 0 && level >= 0 ) 400 { 401 if ( found == trace_any ) 402 { 403 /* special case for `any' */ 404 for ( n = 0; n < trace_count; n++ ) 405 ft_trace_levels_enabled[n] = level; 406 } 407 else 408 ft_trace_levels_enabled[found] = level; 409 } 410 } 411 } 412 } 413 414 ft_trace_levels = ft_trace_levels_enabled; 415 } 416 417 418 #else /* !FT_DEBUG_LEVEL_TRACE */ 419 420 421 FT_BASE_DEF( void ) ft_debug_init(void)422 ft_debug_init( void ) 423 { 424 /* nothing */ 425 } 426 427 428 FT_BASE_DEF( FT_Int ) FT_Trace_Get_Count(void)429 FT_Trace_Get_Count( void ) 430 { 431 return 0; 432 } 433 434 435 FT_BASE_DEF( const char * ) FT_Trace_Get_Name(FT_Int idx)436 FT_Trace_Get_Name( FT_Int idx ) 437 { 438 FT_UNUSED( idx ); 439 440 return NULL; 441 } 442 443 444 FT_BASE_DEF( void ) FT_Trace_Disable(void)445 FT_Trace_Disable( void ) 446 { 447 /* nothing */ 448 } 449 450 451 /* documentation is in ftdebug.h */ 452 453 FT_BASE_DEF( void ) FT_Trace_Enable(void)454 FT_Trace_Enable( void ) 455 { 456 /* nothing */ 457 } 458 459 #endif /* !FT_DEBUG_LEVEL_TRACE */ 460 461 462 #ifdef FT_DEBUG_LOGGING 463 464 /************************************************************************** 465 * 466 * Initialize and de-initialize 'dlg' library. 467 * 468 */ 469 470 FT_BASE_DEF( void ) ft_logging_init(void)471 ft_logging_init( void ) 472 { 473 ft_default_log_handler = ft_log_handler; 474 ft_default_trace_level = ft_getenv( "FT2_DEBUG" ); 475 476 if ( ft_getenv( "FT_LOGGING_FILE" ) ) 477 ft_fileptr = ft_fopen( ft_getenv( "FT_LOGGING_FILE" ), "w" ); 478 else 479 ft_fileptr = stderr; 480 481 ft_debug_init(); 482 483 /* Set the default output handler for 'dlg'. */ 484 dlg_set_handler( ft_default_log_handler, NULL ); 485 } 486 487 488 FT_BASE_DEF( void ) ft_logging_deinit(void)489 ft_logging_deinit( void ) 490 { 491 if ( ft_fileptr != stderr ) 492 ft_fclose( ft_fileptr ); 493 } 494 495 496 /************************************************************************** 497 * 498 * An output log handler for FreeType. 499 * 500 */ 501 FT_BASE_DEF( void ) ft_log_handler(const struct dlg_origin * origin,const char * string,void * data)502 ft_log_handler( const struct dlg_origin* origin, 503 const char* string, 504 void* data ) 505 { 506 char features_buf[128]; 507 char* bufp = features_buf; 508 509 FT_UNUSED( data ); 510 511 512 if ( ft_have_newline_char ) 513 { 514 const char* features = NULL; 515 size_t features_length = 0; 516 517 518 #define FEATURES_TIMESTAMP "[%h:%m] " 519 #define FEATURES_COMPONENT "[%t] " 520 #define FEATURES_TIMESTAMP_COMPONENT "[%h:%m %t] " 521 522 if ( ft_timestamp_flag && ft_component_flag ) 523 { 524 features = FEATURES_TIMESTAMP_COMPONENT; 525 features_length = sizeof ( FEATURES_TIMESTAMP_COMPONENT ); 526 } 527 else if ( ft_timestamp_flag ) 528 { 529 features = FEATURES_TIMESTAMP; 530 features_length = sizeof ( FEATURES_TIMESTAMP ); 531 } 532 else if ( ft_component_flag ) 533 { 534 features = FEATURES_COMPONENT; 535 features_length = sizeof ( FEATURES_COMPONENT ); 536 } 537 538 if ( ft_component_flag || ft_timestamp_flag ) 539 { 540 ft_strncpy( features_buf, features, features_length ); 541 bufp += features_length - 1; 542 } 543 544 if ( ft_component_flag ) 545 { 546 size_t tag_length = ft_strlen( *origin->tags ); 547 size_t i; 548 549 550 /* To vertically align tracing messages we compensate the */ 551 /* different FT_COMPONENT string lengths by inserting an */ 552 /* appropriate amount of space characters. */ 553 for ( i = 0; 554 i < FT_MAX_TRACE_LEVEL_LENGTH - tag_length; 555 i++ ) 556 *bufp++ = ' '; 557 } 558 } 559 560 /* Finally add the format string for the tracing message. */ 561 *bufp++ = '%'; 562 *bufp++ = 'c'; 563 *bufp = '\0'; 564 565 dlg_generic_outputf_stream( ft_fileptr, 566 (const char*)features_buf, 567 origin, 568 string, 569 dlg_default_output_styles, 570 true ); 571 572 if ( ft_strrchr( string, '\n' ) ) 573 ft_have_newline_char = TRUE; 574 else 575 ft_have_newline_char = FALSE; 576 } 577 578 579 /* documentation is in ftdebug.h */ 580 FT_BASE_DEF( void ) ft_add_tag(const char * tag)581 ft_add_tag( const char* tag ) 582 { 583 ft_component = tag; 584 585 dlg_add_tag( tag, NULL ); 586 } 587 588 589 /* documentation is in ftdebug.h */ 590 FT_BASE_DEF( void ) ft_remove_tag(const char * tag)591 ft_remove_tag( const char* tag ) 592 { 593 dlg_remove_tag( tag, NULL ); 594 } 595 596 597 /* documentation is in ftlogging.h */ 598 599 FT_EXPORT_DEF( void ) FT_Trace_Set_Level(const char * level)600 FT_Trace_Set_Level( const char* level ) 601 { 602 ft_component_flag = FALSE; 603 ft_timestamp_flag = FALSE; 604 ft_custom_trace_level = level; 605 606 ft_debug_init(); 607 } 608 609 610 /* documentation is in ftlogging.h */ 611 612 FT_EXPORT_DEF( void ) FT_Trace_Set_Default_Level(void)613 FT_Trace_Set_Default_Level( void ) 614 { 615 ft_component_flag = FALSE; 616 ft_timestamp_flag = FALSE; 617 ft_custom_trace_level = NULL; 618 619 ft_debug_init(); 620 } 621 622 623 /************************************************************************** 624 * 625 * Functions to handle a custom log handler. 626 * 627 */ 628 629 /* documentation is in ftlogging.h */ 630 631 FT_EXPORT_DEF( void ) FT_Set_Log_Handler(FT_Custom_Log_Handler handler)632 FT_Set_Log_Handler( FT_Custom_Log_Handler handler ) 633 { 634 custom_output_handler = handler; 635 } 636 637 638 /* documentation is in ftlogging.h */ 639 640 FT_EXPORT_DEF( void ) FT_Set_Default_Log_Handler(void)641 FT_Set_Default_Log_Handler( void ) 642 { 643 custom_output_handler = NULL; 644 } 645 646 647 /* documentation is in ftdebug.h */ 648 FT_BASE_DEF( void ) FT_Logging_Callback(const char * fmt,...)649 FT_Logging_Callback( const char* fmt, 650 ... ) 651 { 652 va_list ap; 653 654 655 va_start( ap, fmt ); 656 custom_output_handler( ft_component, fmt, ap ); 657 va_end( ap ); 658 } 659 660 #else /* !FT_DEBUG_LOGGING */ 661 662 FT_EXPORT_DEF( void ) FT_Trace_Set_Level(const char * level)663 FT_Trace_Set_Level( const char* level ) 664 { 665 FT_UNUSED( level ); 666 } 667 668 669 FT_EXPORT_DEF( void ) FT_Trace_Set_Default_Level(void)670 FT_Trace_Set_Default_Level( void ) 671 { 672 /* nothing */ 673 } 674 675 676 FT_EXPORT_DEF( void ) FT_Set_Log_Handler(FT_Custom_Log_Handler handler)677 FT_Set_Log_Handler( FT_Custom_Log_Handler handler ) 678 { 679 FT_UNUSED( handler ); 680 } 681 682 683 FT_EXPORT_DEF( void ) FT_Set_Default_Log_Handler(void)684 FT_Set_Default_Log_Handler( void ) 685 { 686 /* nothing */ 687 } 688 689 #endif /* !FT_DEBUG_LOGGING */ 690 691 692 /* END */ 693