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