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