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