1/****************************************************************************
2 *
3 * ftdebug.c
4 *
5 * Debugging and logging component (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 /* documentation is in ftdebug.h */
97
98 FT_BASE_DEF( void )
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 )
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 )
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 )
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 * )
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 )
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 )
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 )
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 )
374 ft_debug_init( void )
375 {
376 /* nothing */
377 }
378
379
380 FT_BASE_DEF( FT_Int )
381 FT_Trace_Get_Count( void )
382 {
383 return 0;
384 }
385
386
387 FT_BASE_DEF( const char * )
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 )
397 FT_Trace_Disable( void )
398 {
399 /* nothing */
400 }
401
402
403 /* documentation is in ftdebug.h */
404
405 FT_BASE_DEF( 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 )
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 )
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 )
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 )
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 )
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 )
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 )
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 )
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 )
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 )
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 )
615 FT_Trace_Set_Level( const char* level )
616 {
617 FT_UNUSED( level );
618 }
619
620
621 FT_EXPORT_DEF( void )
622 FT_Trace_Set_Default_Level( void )
623 {
624 /* nothing */
625 }
626
627
628 FT_EXPORT_DEF( void )
629 FT_Set_Log_Handler( FT_Custom_Log_Handler handler )
630 {
631 FT_UNUSED( handler );
632 }
633
634
635 FT_EXPORT_DEF( void )
636 FT_Set_Default_Log_Handler( void )
637 {
638 /* nothing */
639 }
640
641#endif /* !FT_DEBUG_LOGGING */
642
643
644/* END */
645