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