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