OLD | NEW |
| (Empty) |
1 /***************************************************************************/ | |
2 /* */ | |
3 /* ftdbgmem.c */ | |
4 /* */ | |
5 /* Memory debugger (body). */ | |
6 /* */ | |
7 /* Copyright 2001-2006, 2009, 2013 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 "../../include/ft2build.h" | |
20 #include "../../include/freetype/config/ftconfig.h" | |
21 #include "../../include/freetype/internal/ftdebug.h" | |
22 #include "../../include/freetype/internal/ftmemory.h" | |
23 #include "../../include/freetype/ftsystem.h" | |
24 #include "../../include/freetype/fterrors.h" | |
25 #include "../../include/freetype/fttypes.h" | |
26 | |
27 | |
28 #ifdef FT_DEBUG_MEMORY | |
29 | |
30 #define KEEPALIVE /* `Keep alive' means that freed blocks aren't released | |
31 * to the heap. This is useful to detect double-frees | |
32 * or weird heap corruption, but it uses large amounts of | |
33 * memory, however. | |
34 */ | |
35 | |
36 #include "../../include/freetype/config/ftstdlib.h" | |
37 | |
38 FT_BASE_DEF( const char* ) _ft_debug_file = 0; | |
39 FT_BASE_DEF( long ) _ft_debug_lineno = 0; | |
40 | |
41 extern void | |
42 FT_DumpMemory( FT_Memory memory ); | |
43 | |
44 | |
45 typedef struct FT_MemSourceRec_* FT_MemSource; | |
46 typedef struct FT_MemNodeRec_* FT_MemNode; | |
47 typedef struct FT_MemTableRec_* FT_MemTable; | |
48 | |
49 | |
50 #define FT_MEM_VAL( addr ) ((FT_PtrDist)(FT_Pointer)( addr )) | |
51 | |
52 /* | |
53 * This structure holds statistics for a single allocation/release | |
54 * site. This is useful to know where memory operations happen the | |
55 * most. | |
56 */ | |
57 typedef struct FT_MemSourceRec_ | |
58 { | |
59 const char* file_name; | |
60 long line_no; | |
61 | |
62 FT_Long cur_blocks; /* current number of allocated blocks */ | |
63 FT_Long max_blocks; /* max. number of allocated blocks */ | |
64 FT_Long all_blocks; /* total number of blocks allocated */ | |
65 | |
66 FT_Long cur_size; /* current cumulative allocated size */ | |
67 FT_Long max_size; /* maximum cumulative allocated size */ | |
68 FT_Long all_size; /* total cumulative allocated size */ | |
69 | |
70 FT_Long cur_max; /* current maximum allocated size */ | |
71 | |
72 FT_UInt32 hash; | |
73 FT_MemSource link; | |
74 | |
75 } FT_MemSourceRec; | |
76 | |
77 | |
78 /* | |
79 * We don't need a resizable array for the memory sources, because | |
80 * their number is pretty limited within FreeType. | |
81 */ | |
82 #define FT_MEM_SOURCE_BUCKETS 128 | |
83 | |
84 /* | |
85 * This structure holds information related to a single allocated | |
86 * memory block. If KEEPALIVE is defined, blocks that are freed by | |
87 * FreeType are never released to the system. Instead, their `size' | |
88 * field is set to -size. This is mainly useful to detect double frees, | |
89 * at the price of large memory footprint during execution. | |
90 */ | |
91 typedef struct FT_MemNodeRec_ | |
92 { | |
93 FT_Byte* address; | |
94 FT_Long size; /* < 0 if the block was freed */ | |
95 | |
96 FT_MemSource source; | |
97 | |
98 #ifdef KEEPALIVE | |
99 const char* free_file_name; | |
100 FT_Long free_line_no; | |
101 #endif | |
102 | |
103 FT_MemNode link; | |
104 | |
105 } FT_MemNodeRec; | |
106 | |
107 | |
108 /* | |
109 * The global structure, containing compound statistics and all hash | |
110 * tables. | |
111 */ | |
112 typedef struct FT_MemTableRec_ | |
113 { | |
114 FT_ULong size; | |
115 FT_ULong nodes; | |
116 FT_MemNode* buckets; | |
117 | |
118 FT_ULong alloc_total; | |
119 FT_ULong alloc_current; | |
120 FT_ULong alloc_max; | |
121 FT_ULong alloc_count; | |
122 | |
123 FT_Bool bound_total; | |
124 FT_ULong alloc_total_max; | |
125 | |
126 FT_Bool bound_count; | |
127 FT_ULong alloc_count_max; | |
128 | |
129 FT_MemSource sources[FT_MEM_SOURCE_BUCKETS]; | |
130 | |
131 FT_Bool keep_alive; | |
132 | |
133 FT_Memory memory; | |
134 FT_Pointer memory_user; | |
135 FT_Alloc_Func alloc; | |
136 FT_Free_Func free; | |
137 FT_Realloc_Func realloc; | |
138 | |
139 } FT_MemTableRec; | |
140 | |
141 | |
142 #define FT_MEM_SIZE_MIN 7 | |
143 #define FT_MEM_SIZE_MAX 13845163 | |
144 | |
145 #define FT_FILENAME( x ) ((x) ? (x) : "unknown file") | |
146 | |
147 | |
148 /* | |
149 * Prime numbers are ugly to handle. It would be better to implement | |
150 * L-Hashing, which is 10% faster and doesn't require divisions. | |
151 */ | |
152 static const FT_UInt ft_mem_primes[] = | |
153 { | |
154 7, | |
155 11, | |
156 19, | |
157 37, | |
158 73, | |
159 109, | |
160 163, | |
161 251, | |
162 367, | |
163 557, | |
164 823, | |
165 1237, | |
166 1861, | |
167 2777, | |
168 4177, | |
169 6247, | |
170 9371, | |
171 14057, | |
172 21089, | |
173 31627, | |
174 47431, | |
175 71143, | |
176 106721, | |
177 160073, | |
178 240101, | |
179 360163, | |
180 540217, | |
181 810343, | |
182 1215497, | |
183 1823231, | |
184 2734867, | |
185 4102283, | |
186 6153409, | |
187 9230113, | |
188 13845163, | |
189 }; | |
190 | |
191 | |
192 static FT_ULong | |
193 ft_mem_closest_prime( FT_ULong num ) | |
194 { | |
195 FT_UInt i; | |
196 | |
197 | |
198 for ( i = 0; | |
199 i < sizeof ( ft_mem_primes ) / sizeof ( ft_mem_primes[0] ); i++ ) | |
200 if ( ft_mem_primes[i] > num ) | |
201 return ft_mem_primes[i]; | |
202 | |
203 return FT_MEM_SIZE_MAX; | |
204 } | |
205 | |
206 | |
207 extern void | |
208 ft_mem_debug_panic( const char* fmt, | |
209 ... ) | |
210 { | |
211 va_list ap; | |
212 | |
213 | |
214 printf( "FreeType.Debug: " ); | |
215 | |
216 va_start( ap, fmt ); | |
217 vprintf( fmt, ap ); | |
218 va_end( ap ); | |
219 | |
220 printf( "\n" ); | |
221 exit( EXIT_FAILURE ); | |
222 } | |
223 | |
224 | |
225 static FT_Pointer | |
226 ft_mem_table_alloc( FT_MemTable table, | |
227 FT_Long size ) | |
228 { | |
229 FT_Memory memory = table->memory; | |
230 FT_Pointer block; | |
231 | |
232 | |
233 memory->user = table->memory_user; | |
234 block = table->alloc( memory, size ); | |
235 memory->user = table; | |
236 | |
237 return block; | |
238 } | |
239 | |
240 | |
241 static void | |
242 ft_mem_table_free( FT_MemTable table, | |
243 FT_Pointer block ) | |
244 { | |
245 FT_Memory memory = table->memory; | |
246 | |
247 | |
248 memory->user = table->memory_user; | |
249 table->free( memory, block ); | |
250 memory->user = table; | |
251 } | |
252 | |
253 | |
254 static void | |
255 ft_mem_table_resize( FT_MemTable table ) | |
256 { | |
257 FT_ULong new_size; | |
258 | |
259 | |
260 new_size = ft_mem_closest_prime( table->nodes ); | |
261 if ( new_size != table->size ) | |
262 { | |
263 FT_MemNode* new_buckets; | |
264 FT_ULong i; | |
265 | |
266 | |
267 new_buckets = (FT_MemNode *) | |
268 ft_mem_table_alloc( table, | |
269 new_size * sizeof ( FT_MemNode ) ); | |
270 if ( new_buckets == NULL ) | |
271 return; | |
272 | |
273 FT_ARRAY_ZERO( new_buckets, new_size ); | |
274 | |
275 for ( i = 0; i < table->size; i++ ) | |
276 { | |
277 FT_MemNode node, next, *pnode; | |
278 FT_PtrDist hash; | |
279 | |
280 | |
281 node = table->buckets[i]; | |
282 while ( node ) | |
283 { | |
284 next = node->link; | |
285 hash = FT_MEM_VAL( node->address ) % new_size; | |
286 pnode = new_buckets + hash; | |
287 | |
288 node->link = pnode[0]; | |
289 pnode[0] = node; | |
290 | |
291 node = next; | |
292 } | |
293 } | |
294 | |
295 if ( table->buckets ) | |
296 ft_mem_table_free( table, table->buckets ); | |
297 | |
298 table->buckets = new_buckets; | |
299 table->size = new_size; | |
300 } | |
301 } | |
302 | |
303 | |
304 static FT_MemTable | |
305 ft_mem_table_new( FT_Memory memory ) | |
306 { | |
307 FT_MemTable table; | |
308 | |
309 | |
310 table = (FT_MemTable)memory->alloc( memory, sizeof ( *table ) ); | |
311 if ( table == NULL ) | |
312 goto Exit; | |
313 | |
314 FT_ZERO( table ); | |
315 | |
316 table->size = FT_MEM_SIZE_MIN; | |
317 table->nodes = 0; | |
318 | |
319 table->memory = memory; | |
320 | |
321 table->memory_user = memory->user; | |
322 | |
323 table->alloc = memory->alloc; | |
324 table->realloc = memory->realloc; | |
325 table->free = memory->free; | |
326 | |
327 table->buckets = (FT_MemNode *) | |
328 memory->alloc( memory, | |
329 table->size * sizeof ( FT_MemNode ) ); | |
330 if ( table->buckets ) | |
331 FT_ARRAY_ZERO( table->buckets, table->size ); | |
332 else | |
333 { | |
334 memory->free( memory, table ); | |
335 table = NULL; | |
336 } | |
337 | |
338 Exit: | |
339 return table; | |
340 } | |
341 | |
342 | |
343 static void | |
344 ft_mem_table_destroy( FT_MemTable table ) | |
345 { | |
346 FT_ULong i; | |
347 FT_Long leak_count = 0; | |
348 FT_ULong leaks = 0; | |
349 | |
350 | |
351 FT_DumpMemory( table->memory ); | |
352 | |
353 /* remove all blocks from the table, revealing leaked ones */ | |
354 for ( i = 0; i < table->size; i++ ) | |
355 { | |
356 FT_MemNode *pnode = table->buckets + i, next, node = *pnode; | |
357 | |
358 | |
359 while ( node ) | |
360 { | |
361 next = node->link; | |
362 node->link = 0; | |
363 | |
364 if ( node->size > 0 ) | |
365 { | |
366 printf( | |
367 "leaked memory block at address %p, size %8ld in (%s:%ld)\n", | |
368 node->address, node->size, | |
369 FT_FILENAME( node->source->file_name ), | |
370 node->source->line_no ); | |
371 | |
372 leak_count++; | |
373 leaks += node->size; | |
374 | |
375 ft_mem_table_free( table, node->address ); | |
376 } | |
377 | |
378 node->address = NULL; | |
379 node->size = 0; | |
380 | |
381 ft_mem_table_free( table, node ); | |
382 node = next; | |
383 } | |
384 table->buckets[i] = 0; | |
385 } | |
386 | |
387 ft_mem_table_free( table, table->buckets ); | |
388 table->buckets = NULL; | |
389 | |
390 table->size = 0; | |
391 table->nodes = 0; | |
392 | |
393 /* remove all sources */ | |
394 for ( i = 0; i < FT_MEM_SOURCE_BUCKETS; i++ ) | |
395 { | |
396 FT_MemSource source, next; | |
397 | |
398 | |
399 for ( source = table->sources[i]; source != NULL; source = next ) | |
400 { | |
401 next = source->link; | |
402 ft_mem_table_free( table, source ); | |
403 } | |
404 | |
405 table->sources[i] = NULL; | |
406 } | |
407 | |
408 printf( "FreeType: total memory allocations = %ld\n", | |
409 table->alloc_total ); | |
410 printf( "FreeType: maximum memory footprint = %ld\n", | |
411 table->alloc_max ); | |
412 | |
413 ft_mem_table_free( table, table ); | |
414 | |
415 if ( leak_count > 0 ) | |
416 ft_mem_debug_panic( | |
417 "FreeType: %ld bytes of memory leaked in %ld blocks\n", | |
418 leaks, leak_count ); | |
419 | |
420 printf( "FreeType: no memory leaks detected\n" ); | |
421 } | |
422 | |
423 | |
424 static FT_MemNode* | |
425 ft_mem_table_get_nodep( FT_MemTable table, | |
426 FT_Byte* address ) | |
427 { | |
428 FT_PtrDist hash; | |
429 FT_MemNode *pnode, node; | |
430 | |
431 | |
432 hash = FT_MEM_VAL( address ); | |
433 pnode = table->buckets + ( hash % table->size ); | |
434 | |
435 for (;;) | |
436 { | |
437 node = pnode[0]; | |
438 if ( !node ) | |
439 break; | |
440 | |
441 if ( node->address == address ) | |
442 break; | |
443 | |
444 pnode = &node->link; | |
445 } | |
446 return pnode; | |
447 } | |
448 | |
449 | |
450 static FT_MemSource | |
451 ft_mem_table_get_source( FT_MemTable table ) | |
452 { | |
453 FT_UInt32 hash; | |
454 FT_MemSource node, *pnode; | |
455 | |
456 | |
457 /* cast to FT_PtrDist first since void* can be larger */ | |
458 /* than FT_UInt32 and GCC 4.1.1 emits a warning */ | |
459 hash = (FT_UInt32)(FT_PtrDist)(void*)_ft_debug_file + | |
460 (FT_UInt32)( 5 * _ft_debug_lineno ); | |
461 pnode = &table->sources[hash % FT_MEM_SOURCE_BUCKETS]; | |
462 | |
463 for ( ;; ) | |
464 { | |
465 node = *pnode; | |
466 if ( node == NULL ) | |
467 break; | |
468 | |
469 if ( node->file_name == _ft_debug_file && | |
470 node->line_no == _ft_debug_lineno ) | |
471 goto Exit; | |
472 | |
473 pnode = &node->link; | |
474 } | |
475 | |
476 node = (FT_MemSource)ft_mem_table_alloc( table, sizeof ( *node ) ); | |
477 if ( node == NULL ) | |
478 ft_mem_debug_panic( | |
479 "not enough memory to perform memory debugging\n" ); | |
480 | |
481 node->file_name = _ft_debug_file; | |
482 node->line_no = _ft_debug_lineno; | |
483 | |
484 node->cur_blocks = 0; | |
485 node->max_blocks = 0; | |
486 node->all_blocks = 0; | |
487 | |
488 node->cur_size = 0; | |
489 node->max_size = 0; | |
490 node->all_size = 0; | |
491 | |
492 node->cur_max = 0; | |
493 | |
494 node->link = NULL; | |
495 node->hash = hash; | |
496 *pnode = node; | |
497 | |
498 Exit: | |
499 return node; | |
500 } | |
501 | |
502 | |
503 static void | |
504 ft_mem_table_set( FT_MemTable table, | |
505 FT_Byte* address, | |
506 FT_ULong size, | |
507 FT_Long delta ) | |
508 { | |
509 FT_MemNode *pnode, node; | |
510 | |
511 | |
512 if ( table ) | |
513 { | |
514 FT_MemSource source; | |
515 | |
516 | |
517 pnode = ft_mem_table_get_nodep( table, address ); | |
518 node = *pnode; | |
519 if ( node ) | |
520 { | |
521 if ( node->size < 0 ) | |
522 { | |
523 /* This block was already freed. Our memory is now completely */ | |
524 /* corrupted! */ | |
525 /* This can only happen in keep-alive mode. */ | |
526 ft_mem_debug_panic( | |
527 "memory heap corrupted (allocating freed block)" ); | |
528 } | |
529 else | |
530 { | |
531 /* This block was already allocated. This means that our memory */ | |
532 /* is also corrupted! */ | |
533 ft_mem_debug_panic( | |
534 "memory heap corrupted (re-allocating allocated block at" | |
535 " %p, of size %ld)\n" | |
536 "org=%s:%d new=%s:%d\n", | |
537 node->address, node->size, | |
538 FT_FILENAME( node->source->file_name ), node->source->line_no, | |
539 FT_FILENAME( _ft_debug_file ), _ft_debug_lineno ); | |
540 } | |
541 } | |
542 | |
543 /* we need to create a new node in this table */ | |
544 node = (FT_MemNode)ft_mem_table_alloc( table, sizeof ( *node ) ); | |
545 if ( node == NULL ) | |
546 ft_mem_debug_panic( "not enough memory to run memory tests" ); | |
547 | |
548 node->address = address; | |
549 node->size = size; | |
550 node->source = source = ft_mem_table_get_source( table ); | |
551 | |
552 if ( delta == 0 ) | |
553 { | |
554 /* this is an allocation */ | |
555 source->all_blocks++; | |
556 source->cur_blocks++; | |
557 if ( source->cur_blocks > source->max_blocks ) | |
558 source->max_blocks = source->cur_blocks; | |
559 } | |
560 | |
561 if ( size > (FT_ULong)source->cur_max ) | |
562 source->cur_max = size; | |
563 | |
564 if ( delta != 0 ) | |
565 { | |
566 /* we are growing or shrinking a reallocated block */ | |
567 source->cur_size += delta; | |
568 table->alloc_current += delta; | |
569 } | |
570 else | |
571 { | |
572 /* we are allocating a new block */ | |
573 source->cur_size += size; | |
574 table->alloc_current += size; | |
575 } | |
576 | |
577 source->all_size += size; | |
578 | |
579 if ( source->cur_size > source->max_size ) | |
580 source->max_size = source->cur_size; | |
581 | |
582 node->free_file_name = NULL; | |
583 node->free_line_no = 0; | |
584 | |
585 node->link = pnode[0]; | |
586 | |
587 pnode[0] = node; | |
588 table->nodes++; | |
589 | |
590 table->alloc_total += size; | |
591 | |
592 if ( table->alloc_current > table->alloc_max ) | |
593 table->alloc_max = table->alloc_current; | |
594 | |
595 if ( table->nodes * 3 < table->size || | |
596 table->size * 3 < table->nodes ) | |
597 ft_mem_table_resize( table ); | |
598 } | |
599 } | |
600 | |
601 | |
602 static void | |
603 ft_mem_table_remove( FT_MemTable table, | |
604 FT_Byte* address, | |
605 FT_Long delta ) | |
606 { | |
607 if ( table ) | |
608 { | |
609 FT_MemNode *pnode, node; | |
610 | |
611 | |
612 pnode = ft_mem_table_get_nodep( table, address ); | |
613 node = *pnode; | |
614 if ( node ) | |
615 { | |
616 FT_MemSource source; | |
617 | |
618 | |
619 if ( node->size < 0 ) | |
620 ft_mem_debug_panic( | |
621 "freeing memory block at %p more than once at (%s:%ld)\n" | |
622 "block allocated at (%s:%ld) and released at (%s:%ld)", | |
623 address, | |
624 FT_FILENAME( _ft_debug_file ), _ft_debug_lineno, | |
625 FT_FILENAME( node->source->file_name ), node->source->line_no, | |
626 FT_FILENAME( node->free_file_name ), node->free_line_no ); | |
627 | |
628 /* scramble the node's content for additional safety */ | |
629 FT_MEM_SET( address, 0xF3, node->size ); | |
630 | |
631 if ( delta == 0 ) | |
632 { | |
633 source = node->source; | |
634 | |
635 source->cur_blocks--; | |
636 source->cur_size -= node->size; | |
637 | |
638 table->alloc_current -= node->size; | |
639 } | |
640 | |
641 if ( table->keep_alive ) | |
642 { | |
643 /* we simply invert the node's size to indicate that the node */ | |
644 /* was freed. */ | |
645 node->size = -node->size; | |
646 node->free_file_name = _ft_debug_file; | |
647 node->free_line_no = _ft_debug_lineno; | |
648 } | |
649 else | |
650 { | |
651 table->nodes--; | |
652 | |
653 *pnode = node->link; | |
654 | |
655 node->size = 0; | |
656 node->source = NULL; | |
657 | |
658 ft_mem_table_free( table, node ); | |
659 | |
660 if ( table->nodes * 3 < table->size || | |
661 table->size * 3 < table->nodes ) | |
662 ft_mem_table_resize( table ); | |
663 } | |
664 } | |
665 else | |
666 ft_mem_debug_panic( | |
667 "trying to free unknown block at %p in (%s:%ld)\n", | |
668 address, | |
669 FT_FILENAME( _ft_debug_file ), _ft_debug_lineno ); | |
670 } | |
671 } | |
672 | |
673 | |
674 extern FT_Pointer | |
675 ft_mem_debug_alloc( FT_Memory memory, | |
676 FT_Long size ) | |
677 { | |
678 FT_MemTable table = (FT_MemTable)memory->user; | |
679 FT_Byte* block; | |
680 | |
681 | |
682 if ( size <= 0 ) | |
683 ft_mem_debug_panic( "negative block size allocation (%ld)", size ); | |
684 | |
685 /* return NULL if the maximum number of allocations was reached */ | |
686 if ( table->bound_count && | |
687 table->alloc_count >= table->alloc_count_max ) | |
688 return NULL; | |
689 | |
690 /* return NULL if this allocation would overflow the maximum heap size */ | |
691 if ( table->bound_total && | |
692 table->alloc_total_max - table->alloc_current > (FT_ULong)size ) | |
693 return NULL; | |
694 | |
695 block = (FT_Byte *)ft_mem_table_alloc( table, size ); | |
696 if ( block ) | |
697 { | |
698 ft_mem_table_set( table, block, (FT_ULong)size, 0 ); | |
699 | |
700 table->alloc_count++; | |
701 } | |
702 | |
703 _ft_debug_file = "<unknown>"; | |
704 _ft_debug_lineno = 0; | |
705 | |
706 return (FT_Pointer)block; | |
707 } | |
708 | |
709 | |
710 extern void | |
711 ft_mem_debug_free( FT_Memory memory, | |
712 FT_Pointer block ) | |
713 { | |
714 FT_MemTable table = (FT_MemTable)memory->user; | |
715 | |
716 | |
717 if ( block == NULL ) | |
718 ft_mem_debug_panic( "trying to free NULL in (%s:%ld)", | |
719 FT_FILENAME( _ft_debug_file ), | |
720 _ft_debug_lineno ); | |
721 | |
722 ft_mem_table_remove( table, (FT_Byte*)block, 0 ); | |
723 | |
724 if ( !table->keep_alive ) | |
725 ft_mem_table_free( table, block ); | |
726 | |
727 table->alloc_count--; | |
728 | |
729 _ft_debug_file = "<unknown>"; | |
730 _ft_debug_lineno = 0; | |
731 } | |
732 | |
733 | |
734 extern FT_Pointer | |
735 ft_mem_debug_realloc( FT_Memory memory, | |
736 FT_Long cur_size, | |
737 FT_Long new_size, | |
738 FT_Pointer block ) | |
739 { | |
740 FT_MemTable table = (FT_MemTable)memory->user; | |
741 FT_MemNode node, *pnode; | |
742 FT_Pointer new_block; | |
743 FT_Long delta; | |
744 | |
745 const char* file_name = FT_FILENAME( _ft_debug_file ); | |
746 FT_Long line_no = _ft_debug_lineno; | |
747 | |
748 | |
749 /* unlikely, but possible */ | |
750 if ( new_size == cur_size ) | |
751 return block; | |
752 | |
753 /* the following is valid according to ANSI C */ | |
754 #if 0 | |
755 if ( block == NULL || cur_size == 0 ) | |
756 ft_mem_debug_panic( "trying to reallocate NULL in (%s:%ld)", | |
757 file_name, line_no ); | |
758 #endif | |
759 | |
760 /* while the following is allowed in ANSI C also, we abort since */ | |
761 /* such case should be handled by FreeType. */ | |
762 if ( new_size <= 0 ) | |
763 ft_mem_debug_panic( | |
764 "trying to reallocate %p to size 0 (current is %ld) in (%s:%ld)", | |
765 block, cur_size, file_name, line_no ); | |
766 | |
767 /* check `cur_size' value */ | |
768 pnode = ft_mem_table_get_nodep( table, (FT_Byte*)block ); | |
769 node = *pnode; | |
770 if ( !node ) | |
771 ft_mem_debug_panic( | |
772 "trying to reallocate unknown block at %p in (%s:%ld)", | |
773 block, file_name, line_no ); | |
774 | |
775 if ( node->size <= 0 ) | |
776 ft_mem_debug_panic( | |
777 "trying to reallocate freed block at %p in (%s:%ld)", | |
778 block, file_name, line_no ); | |
779 | |
780 if ( node->size != cur_size ) | |
781 ft_mem_debug_panic( "invalid ft_realloc request for %p. cur_size is " | |
782 "%ld instead of %ld in (%s:%ld)", | |
783 block, cur_size, node->size, file_name, line_no ); | |
784 | |
785 /* return NULL if the maximum number of allocations was reached */ | |
786 if ( table->bound_count && | |
787 table->alloc_count >= table->alloc_count_max ) | |
788 return NULL; | |
789 | |
790 delta = (FT_Long)( new_size - cur_size ); | |
791 | |
792 /* return NULL if this allocation would overflow the maximum heap size */ | |
793 if ( delta > 0 && | |
794 table->bound_total && | |
795 table->alloc_current + (FT_ULong)delta > table->alloc_total_max ) | |
796 return NULL; | |
797 | |
798 new_block = (FT_Byte *)ft_mem_table_alloc( table, new_size ); | |
799 if ( new_block == NULL ) | |
800 return NULL; | |
801 | |
802 ft_mem_table_set( table, (FT_Byte*)new_block, new_size, delta ); | |
803 | |
804 ft_memcpy( new_block, block, cur_size < new_size ? cur_size : new_size ); | |
805 | |
806 ft_mem_table_remove( table, (FT_Byte*)block, delta ); | |
807 | |
808 _ft_debug_file = "<unknown>"; | |
809 _ft_debug_lineno = 0; | |
810 | |
811 if ( !table->keep_alive ) | |
812 ft_mem_table_free( table, block ); | |
813 | |
814 return new_block; | |
815 } | |
816 | |
817 | |
818 extern FT_Int | |
819 ft_mem_debug_init( FT_Memory memory ) | |
820 { | |
821 FT_MemTable table; | |
822 FT_Int result = 0; | |
823 | |
824 | |
825 if (1)/* XYQ: always enable ( getenv( "FT2_DEBUG_MEMORY" ) ) */ | |
826 { | |
827 table = ft_mem_table_new( memory ); | |
828 if ( table ) | |
829 { | |
830 const char* p; | |
831 | |
832 | |
833 memory->user = table; | |
834 memory->alloc = ft_mem_debug_alloc; | |
835 memory->realloc = ft_mem_debug_realloc; | |
836 memory->free = ft_mem_debug_free; | |
837 | |
838 p = getenv( "FT2_ALLOC_TOTAL_MAX" ); | |
839 if ( p != NULL ) | |
840 { | |
841 FT_Long total_max = ft_atol( p ); | |
842 | |
843 | |
844 if ( total_max > 0 ) | |
845 { | |
846 table->bound_total = 1; | |
847 table->alloc_total_max = (FT_ULong)total_max; | |
848 } | |
849 } | |
850 | |
851 p = getenv( "FT2_ALLOC_COUNT_MAX" ); | |
852 if ( p != NULL ) | |
853 { | |
854 FT_Long total_count = ft_atol( p ); | |
855 | |
856 | |
857 if ( total_count > 0 ) | |
858 { | |
859 table->bound_count = 1; | |
860 table->alloc_count_max = (FT_ULong)total_count; | |
861 } | |
862 } | |
863 | |
864 p = getenv( "FT2_KEEP_ALIVE" ); | |
865 if ( p != NULL ) | |
866 { | |
867 FT_Long keep_alive = ft_atol( p ); | |
868 | |
869 | |
870 if ( keep_alive > 0 ) | |
871 table->keep_alive = 1; | |
872 } | |
873 | |
874 result = 1; | |
875 } | |
876 } | |
877 return result; | |
878 } | |
879 | |
880 | |
881 extern void | |
882 ft_mem_debug_done( FT_Memory memory ) | |
883 { | |
884 FT_MemTable table = (FT_MemTable)memory->user; | |
885 | |
886 | |
887 if ( table ) | |
888 { | |
889 memory->free = table->free; | |
890 memory->realloc = table->realloc; | |
891 memory->alloc = table->alloc; | |
892 | |
893 ft_mem_table_destroy( table ); | |
894 memory->user = NULL; | |
895 } | |
896 } | |
897 | |
898 | |
899 | |
900 static int | |
901 ft_mem_source_compare( const void* p1, | |
902 const void* p2 ) | |
903 { | |
904 FT_MemSource s1 = *(FT_MemSource*)p1; | |
905 FT_MemSource s2 = *(FT_MemSource*)p2; | |
906 | |
907 | |
908 if ( s2->max_size > s1->max_size ) | |
909 return 1; | |
910 else if ( s2->max_size < s1->max_size ) | |
911 return -1; | |
912 else | |
913 return 0; | |
914 } | |
915 | |
916 | |
917 extern void | |
918 FT_DumpMemory( FT_Memory memory ) | |
919 { | |
920 FT_MemTable table = (FT_MemTable)memory->user; | |
921 | |
922 | |
923 if ( table ) | |
924 { | |
925 FT_MemSource* bucket = table->sources; | |
926 FT_MemSource* limit = bucket + FT_MEM_SOURCE_BUCKETS; | |
927 FT_MemSource* sources; | |
928 FT_UInt nn, count; | |
929 const char* fmt; | |
930 | |
931 | |
932 count = 0; | |
933 for ( ; bucket < limit; bucket++ ) | |
934 { | |
935 FT_MemSource source = *bucket; | |
936 | |
937 | |
938 for ( ; source; source = source->link ) | |
939 count++; | |
940 } | |
941 | |
942 sources = (FT_MemSource*)ft_mem_table_alloc( | |
943 table, sizeof ( *sources ) * count ); | |
944 | |
945 count = 0; | |
946 for ( bucket = table->sources; bucket < limit; bucket++ ) | |
947 { | |
948 FT_MemSource source = *bucket; | |
949 | |
950 | |
951 for ( ; source; source = source->link ) | |
952 sources[count++] = source; | |
953 } | |
954 | |
955 ft_qsort( sources, count, sizeof ( *sources ), ft_mem_source_compare ); | |
956 | |
957 printf( "FreeType Memory Dump: " | |
958 "current=%ld max=%ld total=%ld count=%ld\n", | |
959 table->alloc_current, table->alloc_max, | |
960 table->alloc_total, table->alloc_count ); | |
961 printf( " block block sizes sizes sizes source\n" ); | |
962 printf( " count high sum highsum max location\n" ); | |
963 printf( "-------------------------------------------------\n" ); | |
964 | |
965 fmt = "%6ld %6ld %8ld %8ld %8ld %s:%d\n"; | |
966 | |
967 for ( nn = 0; nn < count; nn++ ) | |
968 { | |
969 FT_MemSource source = sources[nn]; | |
970 | |
971 | |
972 printf( fmt, | |
973 source->cur_blocks, source->max_blocks, | |
974 source->cur_size, source->max_size, source->cur_max, | |
975 FT_FILENAME( source->file_name ), | |
976 source->line_no ); | |
977 } | |
978 printf( "------------------------------------------------\n" ); | |
979 | |
980 ft_mem_table_free( table, sources ); | |
981 } | |
982 } | |
983 | |
984 #else /* !FT_DEBUG_MEMORY */ | |
985 | |
986 /* ANSI C doesn't like empty source files */ | |
987 typedef int _debug_mem_dummy; | |
988 | |
989 #endif /* !FT_DEBUG_MEMORY */ | |
990 | |
991 | |
992 /* END */ | |
OLD | NEW |