1 /***************************************************************************/
5 /* Memory debugger (body). */
7 /* Copyright 2001, 2002 by */
8 /* David Turner, Robert Wilhelm, and Werner Lemberg. */
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. */
16 /***************************************************************************/
20 #include FT_CONFIG_CONFIG_H
21 #include FT_INTERNAL_DEBUG_H
22 #include FT_INTERNAL_MEMORY_H
28 #ifdef FT_DEBUG_MEMORY
35 typedef struct FT_MemNodeRec_* FT_MemNode;
36 typedef struct FT_MemTableRec_* FT_MemTable;
38 #define FT_MEM_VAL( addr ) ((FT_ULong)(FT_Pointer)( addr ))
40 typedef struct FT_MemNodeRec_
43 FT_Long size; /* < 0 if the block was freed */
45 const char* alloc_file_name;
46 FT_Long alloc_line_no;
48 const char* free_file_name;
56 typedef struct FT_MemTableRec_
63 FT_ULong alloc_current;
68 FT_ULong alloc_total_max;
71 FT_ULong alloc_count_max;
73 const char* file_name;
77 FT_Pointer memory_user;
80 FT_Realloc_Func realloc;
85 #define FT_MEM_SIZE_MIN 7
86 #define FT_MEM_SIZE_MAX 13845163
88 #define FT_FILENAME( x ) ((x) ? (x) : "unknown file")
91 static const FT_UInt ft_mem_primes[] =
133 ft_mem_debug_panic( const char* fmt, ... )
138 printf( "FreeType.Debug: " );
145 exit( EXIT_FAILURE );
150 ft_mem_closest_prime( FT_ULong num )
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];
160 return FT_MEM_SIZE_MAX;
165 ft_mem_table_alloc( FT_MemTable table,
168 FT_Memory memory = table->memory;
172 memory->user = table->memory_user;
173 block = table->alloc( memory, size );
174 memory->user = table;
181 ft_mem_table_free( FT_MemTable table,
184 FT_Memory memory = table->memory;
187 memory->user = table->memory_user;
188 table->free( memory, block );
189 memory->user = table;
194 ft_mem_table_resize( FT_MemTable table )
199 new_size = ft_mem_closest_prime( table->nodes );
200 if ( new_size != table->size )
202 FT_MemNode* new_buckets ;
206 new_buckets = (FT_MemNode *)
207 ft_mem_table_alloc( table,
208 new_size * sizeof ( FT_MemNode ) );
209 if ( new_buckets == NULL )
212 FT_MEM_ZERO( new_buckets, sizeof ( FT_MemNode ) * new_size );
214 for ( i = 0; i < table->size; i++ )
216 FT_MemNode node, next, *pnode;
220 node = table->buckets[i];
224 hash = FT_MEM_VAL( node->address ) % new_size;
225 pnode = new_buckets + hash;
227 node->link = pnode[0];
234 if ( table->buckets )
235 ft_mem_table_free( table, table->buckets );
237 table->buckets = new_buckets;
238 table->size = new_size;
244 ft_mem_table_new( FT_Memory memory )
249 table = (FT_MemTable)memory->alloc( memory, sizeof ( *table ) );
253 FT_MEM_ZERO( table, sizeof ( *table ) );
255 table->size = FT_MEM_SIZE_MIN;
258 table->memory = memory;
260 table->memory_user = memory->user;
262 table->alloc = memory->alloc;
263 table->realloc = memory->realloc;
264 table->free = memory->free;
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 );
273 memory->free( memory, table );
283 ft_mem_table_destroy( FT_MemTable table )
290 FT_Long leak_count = 0;
294 for ( i = 0; i < table->size; i++ )
296 FT_MemNode *pnode = table->buckets + i, next, node = *pnode;
304 if ( node->size > 0 )
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 );
315 ft_mem_table_free( table, node->address );
318 node->address = NULL;
324 table->buckets[i] = 0;
326 ft_mem_table_free( table, table->buckets );
327 table->buckets = NULL;
333 "FreeType: total memory allocations = %ld\n", table->alloc_total );
335 "FreeType: maximum memory footprint = %ld\n", table->alloc_max );
339 if ( leak_count > 0 )
341 "FreeType: %ld bytes of memory leaked in %ld blocks\n",
343 printf( "FreeType: No memory leaks detected!\n" );
349 ft_mem_table_get_nodep( FT_MemTable table,
353 FT_MemNode *pnode, node;
356 hash = FT_MEM_VAL( address );
357 pnode = table->buckets + ( hash % table->size );
365 if ( node->address == address )
375 ft_mem_table_set( FT_MemTable table,
379 FT_MemNode *pnode, node;
384 pnode = ft_mem_table_get_nodep( table, address );
388 if ( node->size < 0 )
390 /* this block was already freed. This means that our memory is */
391 /* now completely corrupted! */
393 "memory heap corrupted (allocating freed block)" );
397 /* this block was already allocated. This means that our memory */
398 /* is also corrupted! */
400 "memory heap corrupted (re-allocating allocated block)" );
404 /* we need to create a new node in this table */
405 node = (FT_MemNode)ft_mem_table_alloc( table, sizeof ( *node ) );
407 ft_mem_debug_panic( "not enough memory to run memory tests" );
409 node->address = address;
412 node->alloc_file_name = table->file_name;
413 node->alloc_line_no = table->line_no;
415 node->free_file_name = NULL;
416 node->free_line_no = 0;
418 node->link = pnode[0];
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;
428 if ( table->nodes * 3 < table->size ||
429 table->size * 3 < table->nodes )
430 ft_mem_table_resize( table );
436 ft_mem_table_remove( FT_MemTable table,
441 FT_MemNode *pnode, node;
444 pnode = ft_mem_table_get_nodep( table, address );
448 if ( node->size < 0 )
450 "freeing memory block at %p more than once at (%s:%ld)\n"
451 "block allocated at (%s:%ld) and released at (%s:%ld)",
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 );
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 );
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;
468 "trying to free unknown block at %p in (%s:%ld)\n",
470 FT_FILENAME( table->file_name ), table->line_no );
476 ft_mem_debug_alloc( FT_Memory memory,
479 FT_MemTable table = (FT_MemTable)memory->user;
484 ft_mem_debug_panic( "negative block size allocation (%ld)", size );
486 /* return NULL if the maximum number of allocations was reached */
487 if ( table->bound_count &&
488 table->alloc_count >= table->alloc_count_max )
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 )
496 block = (FT_Byte *)ft_mem_table_alloc( table, size );
498 ft_mem_table_set( table, block, (FT_ULong)size );
500 table->alloc_count++;
502 table->file_name = NULL;
505 return (FT_Pointer) block;
510 ft_mem_debug_free( FT_Memory memory,
513 FT_MemTable table = (FT_MemTable)memory->user;
517 ft_mem_debug_panic( "trying to free NULL in (%s:%ld)",
518 FT_FILENAME( table->file_name ),
521 ft_mem_table_remove( table, (FT_Byte*)block );
523 /* we never really free the block */
524 table->file_name = NULL;
530 ft_mem_debug_realloc( FT_Memory memory,
535 FT_MemTable table = (FT_MemTable)memory->user;
536 FT_MemNode node, *pnode;
537 FT_Pointer new_block;
539 const char* file_name = FT_FILENAME( table->file_name );
540 FT_Long line_no = table->line_no;
543 if ( block == NULL || cur_size == 0 )
544 ft_mem_debug_panic( "trying to reallocate NULL in (%s:%ld)",
545 file_name, line_no );
549 "trying to reallocate %p to size 0 (current is %ld) in (%s:%ld)",
550 block, cur_size, file_name, line_no );
552 /* check 'cur_size' value */
553 pnode = ft_mem_table_get_nodep( table, (FT_Byte*)block );
557 "trying to reallocate unknown block at %p in (%s:%ld)",
558 block, file_name, line_no );
560 if ( node->size <= 0 )
562 "trying to reallocate freed block at %p in (%s:%ld)",
563 block, file_name, line_no );
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 );
570 new_block = ft_mem_debug_alloc( memory, new_size );
571 if ( new_block == NULL )
574 ft_memcpy( new_block, block, cur_size < new_size ? cur_size : new_size );
576 table->file_name = file_name;
577 table->line_no = line_no;
579 ft_mem_debug_free( memory, (FT_Byte*)block );
586 ft_mem_debug_init( FT_Memory memory )
592 if ( getenv( "FT2_DEBUG_MEMORY" ) )
594 table = ft_mem_table_new( memory );
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;
604 p = getenv( "FT2_ALLOC_TOTAL_MAX" );
607 FT_Long total_max = atol(p);
611 table->bound_total = 1;
612 table->alloc_total_max = (FT_ULong) total_max;
616 p = getenv( "FT2_ALLOC_COUNT_MAX" );
619 FT_Long total_count = atol(p);
621 if ( total_count > 0 )
623 table->bound_count = 1;
624 table->alloc_count_max = (FT_ULong) total_count;
636 ft_mem_debug_done( FT_Memory memory )
638 FT_MemTable table = (FT_MemTable)memory->user;
643 memory->free = table->free;
644 memory->realloc = table->realloc;
645 memory->alloc = table->alloc;
647 ft_mem_table_destroy( table );
653 FT_BASE_DEF( FT_Error )
654 FT_Alloc_Debug( FT_Memory memory,
657 const char* file_name,
660 FT_MemTable table = (FT_MemTable)memory->user;
665 table->file_name = file_name;
666 table->line_no = line_no;
668 return FT_Alloc( memory, size, P );
672 FT_BASE_DEF( FT_Error )
673 FT_Realloc_Debug( FT_Memory memory,
677 const char* file_name,
680 FT_MemTable table = (FT_MemTable)memory->user;
685 table->file_name = file_name;
686 table->line_no = line_no;
688 return FT_Realloc( memory, current, size, P );
693 FT_Free_Debug( FT_Memory memory,
695 const char* file_name,
698 FT_MemTable table = (FT_MemTable)memory->user;
703 table->file_name = file_name;
704 table->line_no = line_no;
706 FT_Free( memory, (void **)block );
710 #else /* !FT_DEBUG_MEMORY */
712 /* ANSI C doesn't like empty source files */
713 const FT_Byte _debug_mem_dummy = 0;
715 #endif /* !FT_DEBUG_MEMORY */