15d186a200dd1a431807f73ae388db6073b05f5c
[reactos.git] / lib / freetype / src / base / ftdbgmem.c
1 /***************************************************************************/
2 /*                                                                         */
3 /*  ftdbgmem.c                                                             */
4 /*                                                                         */
5 /*    Memory debugger (body).                                              */
6 /*                                                                         */
7 /*  Copyright 2001, 2002 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 #include <ft2build.h>
20 #include FT_CONFIG_CONFIG_H
21 #include FT_INTERNAL_DEBUG_H
22 #include FT_INTERNAL_MEMORY_H
23 #include FT_SYSTEM_H
24 #include FT_ERRORS_H
25 #include FT_TYPES_H
26
27
28 #ifdef FT_DEBUG_MEMORY
29
30
31 #include <stdio.h>
32 #include <stdlib.h>
33
34
35   typedef struct FT_MemNodeRec_*   FT_MemNode;
36   typedef struct FT_MemTableRec_*  FT_MemTable;
37
38 #define FT_MEM_VAL( addr )  ((FT_ULong)(FT_Pointer)( addr ))
39
40   typedef struct  FT_MemNodeRec_
41   {
42     FT_Byte*     address;
43     FT_Long      size;     /* < 0 if the block was freed */
44
45     const char*  alloc_file_name;
46     FT_Long      alloc_line_no;
47
48     const char*  free_file_name;
49     FT_Long      free_line_no;
50
51     FT_MemNode   link;
52
53   } FT_MemNodeRec;
54
55
56   typedef struct  FT_MemTableRec_
57   {
58     FT_ULong         size;
59     FT_ULong         nodes;
60     FT_MemNode*      buckets;
61
62     FT_ULong         alloc_total;
63     FT_ULong         alloc_current;
64     FT_ULong         alloc_max;
65     FT_ULong         alloc_count;
66
67     FT_Bool          bound_total;    
68     FT_ULong         alloc_total_max;
69     
70     FT_Bool          bound_count;
71     FT_ULong         alloc_count_max;
72
73     const char*      file_name;
74     FT_Long          line_no;
75
76     FT_Memory        memory;
77     FT_Pointer       memory_user;
78     FT_Alloc_Func    alloc;
79     FT_Free_Func     free;
80     FT_Realloc_Func  realloc;
81
82   } FT_MemTableRec;
83
84
85 #define FT_MEM_SIZE_MIN  7
86 #define FT_MEM_SIZE_MAX  13845163
87
88 #define FT_FILENAME( x )  ((x) ? (x) : "unknown file")
89
90
91   static const FT_UInt  ft_mem_primes[] =
92   {
93     7,
94     11,
95     19,
96     37,
97     73,
98     109,
99     163,
100     251,
101     367,
102     557,
103     823,
104     1237,
105     1861,
106     2777,
107     4177,
108     6247,
109     9371,
110     14057,
111     21089,
112     31627,
113     47431,
114     71143,
115     106721,
116     160073,
117     240101,
118     360163,
119     540217,
120     810343,
121     1215497,
122     1823231,
123     2734867,
124     4102283,
125     6153409,
126     9230113,
127     13845163,
128   };
129
130
131
132   extern void
133   ft_mem_debug_panic( const char*  fmt, ... )
134   {
135     va_list  ap;
136
137
138     printf( "FreeType.Debug: " );
139
140     va_start( ap, fmt );
141     vprintf( fmt, ap );
142     va_end( ap );
143
144     printf( "\n" );
145     exit( EXIT_FAILURE );
146   }
147
148
149   static FT_ULong
150   ft_mem_closest_prime( FT_ULong  num )
151   {
152     FT_UInt  i;
153
154
155     for ( i = 0;
156           i < sizeof ( ft_mem_primes ) / sizeof ( ft_mem_primes[0] ); i++ )
157       if ( ft_mem_primes[i] > num )
158         return ft_mem_primes[i];
159
160     return FT_MEM_SIZE_MAX;
161   }
162
163
164   static FT_Pointer
165   ft_mem_table_alloc( FT_MemTable  table,
166                       FT_Long      size )
167   {
168     FT_Memory   memory = table->memory;
169     FT_Pointer  block;
170
171
172     memory->user = table->memory_user;
173     block = table->alloc( memory, size );
174     memory->user = table;
175
176    return block;
177   }
178
179
180   static void
181   ft_mem_table_free( FT_MemTable  table,
182                      FT_Pointer   block )
183   {
184     FT_Memory  memory = table->memory;
185
186
187     memory->user = table->memory_user;
188     table->free( memory, block );
189     memory->user = table;
190   }
191
192
193   static void
194   ft_mem_table_resize( FT_MemTable  table )
195   {
196     FT_ULong  new_size;
197
198
199     new_size = ft_mem_closest_prime( table->nodes );
200     if ( new_size != table->size )
201     {
202       FT_MemNode*  new_buckets ;
203       FT_ULong     i;
204
205
206       new_buckets = (FT_MemNode *)
207                     ft_mem_table_alloc( table,
208                                         new_size * sizeof ( FT_MemNode ) );
209       if ( new_buckets == NULL )
210         return;
211
212       FT_MEM_ZERO( new_buckets, sizeof ( FT_MemNode ) * new_size );
213
214       for ( i = 0; i < table->size; i++ )
215       {
216         FT_MemNode  node, next, *pnode;
217         FT_ULong    hash;
218
219
220         node = table->buckets[i];
221         while ( node )
222         {
223           next  = node->link;
224           hash  = FT_MEM_VAL( node->address ) % new_size;
225           pnode = new_buckets + hash;
226
227           node->link = pnode[0];
228           pnode[0]   = node;
229
230           node = next;
231         }
232       }
233
234       if ( table->buckets )
235         ft_mem_table_free( table, table->buckets );
236
237       table->buckets = new_buckets;
238       table->size    = new_size;
239     }
240   }
241
242
243   static FT_MemTable
244   ft_mem_table_new( FT_Memory  memory )
245   {
246     FT_MemTable  table;
247
248
249     table = (FT_MemTable)memory->alloc( memory, sizeof ( *table ) );
250     if ( table == NULL )
251       goto Exit;
252
253     FT_MEM_ZERO( table, sizeof ( *table ) );
254
255     table->size  = FT_MEM_SIZE_MIN;
256     table->nodes = 0;
257
258     table->memory = memory;
259
260     table->memory_user = memory->user;
261
262     table->alloc   = memory->alloc;
263     table->realloc = memory->realloc;
264     table->free    = memory->free;
265
266     table->buckets = (FT_MemNode *)
267                      memory->alloc( memory,
268                                     table->size * sizeof ( FT_MemNode ) );
269     if ( table->buckets )
270       FT_MEM_ZERO( table->buckets, sizeof ( FT_MemNode ) * table->size );
271     else
272     {
273       memory->free( memory, table );
274       table = NULL;
275     }
276
277   Exit:
278     return table;
279   }
280
281
282   static void
283   ft_mem_table_destroy( FT_MemTable  table )
284   {
285     FT_ULong  i;
286
287
288     if ( table )
289     {
290       FT_Long    leak_count = 0;
291       FT_ULong   leaks = 0;
292
293
294       for ( i = 0; i < table->size; i++ )
295       {
296         FT_MemNode  *pnode = table->buckets + i, next, node = *pnode;
297
298
299         while ( node )
300         {
301           next       = node->link;
302           node->link = 0;
303
304           if ( node->size > 0 )
305           {
306             printf(
307               "leaked memory block at address %p, size %8ld in (%s:%ld)\n",
308               node->address, node->size,
309               FT_FILENAME( node->alloc_file_name ),
310               node->alloc_line_no );
311
312             leak_count++;
313             leaks += node->size;
314
315             ft_mem_table_free( table, node->address );
316           }
317
318           node->address = NULL;
319           node->size    = 0;
320
321           free( node );
322           node = next;
323         }
324         table->buckets[i] = 0;
325       }
326       ft_mem_table_free( table, table->buckets );
327       table->buckets = NULL;
328
329       table->size   = 0;
330       table->nodes  = 0;
331
332       printf(
333         "FreeType: total memory allocations = %ld\n", table->alloc_total );
334       printf(
335         "FreeType: maximum memory footprint = %ld\n", table->alloc_max );
336
337       free( table );
338
339       if ( leak_count > 0 )
340         ft_mem_debug_panic(
341           "FreeType: %ld bytes of memory leaked in %ld blocks\n",
342           leaks, leak_count );
343       printf( "FreeType: No memory leaks detected!\n" );
344     }
345   }
346
347
348   static FT_MemNode*
349   ft_mem_table_get_nodep( FT_MemTable  table,
350                           FT_Byte*     address )
351   {
352     FT_ULong     hash;
353     FT_MemNode  *pnode, node;
354
355
356     hash  = FT_MEM_VAL( address );
357     pnode = table->buckets + ( hash % table->size );
358
359     for (;;)
360     {
361       node = pnode[0];
362       if ( !node )
363         break;
364
365       if ( node->address == address )
366         break;
367
368       pnode = &node->link;
369     }
370     return pnode;
371   }
372
373
374   static void
375   ft_mem_table_set( FT_MemTable  table,
376                     FT_Byte*     address,
377                     FT_ULong     size )
378   {
379     FT_MemNode  *pnode, node;
380
381
382     if ( table )
383     {
384       pnode = ft_mem_table_get_nodep( table, address );
385       node  = *pnode;
386       if ( node )
387       {
388         if ( node->size < 0 )
389         {
390           /* this block was already freed.  This means that our memory is */
391           /* now completely corrupted!                                    */
392           ft_mem_debug_panic(
393             "memory heap corrupted (allocating freed block)" );
394         }
395         else
396         {
397           /* this block was already allocated.  This means that our memory */
398           /* is also corrupted!                                            */
399           ft_mem_debug_panic(
400             "memory heap corrupted (re-allocating allocated block)" );
401         }
402       }
403
404       /* we need to create a new node in this table */
405       node = (FT_MemNode)ft_mem_table_alloc( table, sizeof ( *node ) );
406       if ( node == NULL )
407         ft_mem_debug_panic( "not enough memory to run memory tests" );
408
409       node->address = address;
410       node->size    = size;
411
412       node->alloc_file_name = table->file_name;
413       node->alloc_line_no   = table->line_no;
414
415       node->free_file_name = NULL;
416       node->free_line_no   = 0;
417
418       node->link = pnode[0];
419
420       pnode[0] = node;
421       table->nodes++;
422
423       table->alloc_total   += size;
424       table->alloc_current += size;
425       if ( table->alloc_current > table->alloc_max )
426         table->alloc_max = table->alloc_current;
427
428       if ( table->nodes * 3 < table->size  ||
429            table->size  * 3 < table->nodes )
430         ft_mem_table_resize( table );
431     }
432   }
433
434
435   static void
436   ft_mem_table_remove( FT_MemTable  table,
437                        FT_Byte*     address )
438   {
439     if ( table )
440     {
441       FT_MemNode  *pnode, node;
442
443
444       pnode = ft_mem_table_get_nodep( table, address );
445       node  = *pnode;
446       if ( node )
447       {
448         if ( node->size < 0 )
449           ft_mem_debug_panic(
450             "freeing memory block at %p more than once at (%s:%ld)\n"
451             "block allocated at (%s:%ld) and released at (%s:%ld)",
452             address,
453             FT_FILENAME( table->file_name ), table->line_no,
454             FT_FILENAME( node->alloc_file_name ), node->alloc_line_no,
455             FT_FILENAME( node->free_file_name ), node->free_line_no );
456
457         /* we simply invert the node's size to indicate that the node */
458         /* was freed.  We also change its contents.                   */
459         FT_MEM_SET( address, 0xF3, node->size );
460
461         table->alloc_current -= node->size;
462         node->size            = -node->size;
463         node->free_file_name  = table->file_name;
464         node->free_line_no    = table->line_no;
465       }
466       else
467         ft_mem_debug_panic(
468           "trying to free unknown block at %p in (%s:%ld)\n",
469           address,
470           FT_FILENAME( table->file_name ), table->line_no );
471     }
472   }
473
474
475   extern FT_Pointer
476   ft_mem_debug_alloc( FT_Memory  memory,
477                       FT_Long    size )
478   {
479     FT_MemTable  table = (FT_MemTable)memory->user;
480     FT_Byte*     block;
481
482
483     if ( size <= 0 )
484       ft_mem_debug_panic( "negative block size allocation (%ld)", size );
485
486     /* return NULL if the maximum number of allocations was reached */
487     if ( table->bound_count &&
488          table->alloc_count >= table->alloc_count_max )
489       return NULL;
490
491     /* return NULL if this allocation would overflow the maximum heap size */
492     if ( table->bound_total && 
493          table->alloc_current + (FT_ULong)size > table->alloc_total_max )
494       return NULL;         
495
496     block = (FT_Byte *)ft_mem_table_alloc( table, size );
497     if ( block )
498       ft_mem_table_set( table, block, (FT_ULong)size );
499
500     table->alloc_count++;
501
502     table->file_name = NULL;
503     table->line_no   = 0;
504
505     return (FT_Pointer) block;
506   }
507
508
509   extern void
510   ft_mem_debug_free( FT_Memory   memory,
511                      FT_Pointer  block )
512   {
513     FT_MemTable  table = (FT_MemTable)memory->user;
514
515
516     if ( block == NULL )
517       ft_mem_debug_panic( "trying to free NULL in (%s:%ld)",
518                           FT_FILENAME( table->file_name ),
519                           table->line_no );
520
521     ft_mem_table_remove( table, (FT_Byte*)block );
522
523     /* we never really free the block */
524     table->file_name = NULL;
525     table->line_no   = 0;
526   }
527
528
529   extern FT_Pointer
530   ft_mem_debug_realloc( FT_Memory   memory,
531                         FT_Long     cur_size,
532                         FT_Long     new_size,
533                         FT_Pointer  block )
534   {
535     FT_MemTable  table = (FT_MemTable)memory->user;
536     FT_MemNode   node, *pnode;
537     FT_Pointer   new_block;
538
539     const char*  file_name = FT_FILENAME( table->file_name );
540     FT_Long      line_no   = table->line_no;
541
542
543     if ( block == NULL || cur_size == 0 )
544       ft_mem_debug_panic( "trying to reallocate NULL in (%s:%ld)",
545                            file_name, line_no );
546
547     if ( new_size <= 0 )
548       ft_mem_debug_panic(
549         "trying to reallocate %p to size 0 (current is %ld) in (%s:%ld)",
550         block, cur_size, file_name, line_no );
551
552     /* check 'cur_size' value */
553     pnode = ft_mem_table_get_nodep( table, (FT_Byte*)block );
554     node  = *pnode;
555     if ( !node )
556       ft_mem_debug_panic(
557         "trying to reallocate unknown block at %p in (%s:%ld)",
558         block, file_name, line_no );
559
560     if ( node->size <= 0 )
561       ft_mem_debug_panic(
562         "trying to reallocate freed block at %p in (%s:%ld)",
563         block, file_name, line_no );
564
565     if ( node->size != cur_size )
566       ft_mem_debug_panic( "invalid ft_realloc request for %p. cur_size is "
567                           "%ld instead of %ld in (%s:%ld)",
568                           block, cur_size, node->size, file_name, line_no );
569
570     new_block = ft_mem_debug_alloc( memory, new_size );
571     if ( new_block == NULL )
572       return NULL;
573
574     ft_memcpy( new_block, block, cur_size < new_size ? cur_size : new_size );
575
576     table->file_name = file_name;
577     table->line_no   = line_no;
578
579     ft_mem_debug_free( memory, (FT_Byte*)block );
580
581     return new_block;
582   }
583
584
585   extern FT_Int
586   ft_mem_debug_init( FT_Memory  memory )
587   {
588     FT_MemTable  table;
589     FT_Int       result = 0;
590
591
592     if ( getenv( "FT2_DEBUG_MEMORY" ) )
593     {
594       table = ft_mem_table_new( memory );
595       if ( table )
596       {
597         const char*  p;
598         
599         memory->user    = table;
600         memory->alloc   = ft_mem_debug_alloc;
601         memory->realloc = ft_mem_debug_realloc;
602         memory->free    = ft_mem_debug_free;
603         
604         p = getenv( "FT2_ALLOC_TOTAL_MAX" );
605         if ( p != NULL )
606         {
607           FT_Long   total_max = atol(p);
608           
609           if ( total_max > 0 )
610           {
611             table->bound_total     = 1;
612             table->alloc_total_max = (FT_ULong) total_max;
613           }
614         }
615         
616         p = getenv( "FT2_ALLOC_COUNT_MAX" );
617         if ( p != NULL )
618         {
619           FT_Long  total_count = atol(p);
620           
621           if ( total_count > 0 )
622           {
623             table->bound_count     = 1;
624             table->alloc_count_max = (FT_ULong) total_count;
625           }
626         }
627
628         result = 1;
629       }
630     }
631     return result;
632   }
633
634
635   extern void
636   ft_mem_debug_done( FT_Memory  memory )
637   {
638     FT_MemTable  table = (FT_MemTable)memory->user;
639
640
641     if ( table )
642     {
643       memory->free    = table->free;
644       memory->realloc = table->realloc;
645       memory->alloc   = table->alloc;
646
647       ft_mem_table_destroy( table );
648       memory->user = NULL;
649     }
650   }
651
652
653   FT_BASE_DEF( FT_Error )
654   FT_Alloc_Debug( FT_Memory    memory,
655                   FT_Long      size,
656                   void*       *P,
657                   const char*  file_name,
658                   FT_Long      line_no )
659   {
660     FT_MemTable  table = (FT_MemTable)memory->user;
661
662
663     if ( table )
664     {
665       table->file_name = file_name;
666       table->line_no   = line_no;
667     }
668     return FT_Alloc( memory, size, P );
669   }
670
671
672   FT_BASE_DEF( FT_Error )
673   FT_Realloc_Debug( FT_Memory    memory,
674                     FT_Long      current,
675                     FT_Long      size,
676                     void*       *P,
677                     const char*  file_name,
678                     FT_Long      line_no )
679   {
680     FT_MemTable  table = (FT_MemTable)memory->user;
681
682
683     if ( table )
684     {
685       table->file_name = file_name;
686       table->line_no   = line_no;
687     }
688     return FT_Realloc( memory, current, size, P );
689   }
690
691
692   FT_BASE_DEF( void )
693   FT_Free_Debug( FT_Memory    memory,
694                  FT_Pointer   block,
695                  const char*  file_name,
696                  FT_Long      line_no )
697   {
698     FT_MemTable  table = (FT_MemTable)memory->user;
699
700
701     if ( table )
702     {
703       table->file_name = file_name;
704       table->line_no   = line_no;
705     }
706     FT_Free( memory, (void **)block );
707   }
708
709
710 #else  /* !FT_DEBUG_MEMORY */
711
712   /* ANSI C doesn't like empty source files */
713   const FT_Byte  _debug_mem_dummy = 0;
714
715 #endif /* !FT_DEBUG_MEMORY */
716
717
718 /* END */