Branch data Line data Source code
1 : : /* A memory statistics tracking infrastructure.
2 : : Copyright (C) 2015-2024 Free Software Foundation, Inc.
3 : : Contributed by Martin Liska <mliska@suse.cz>
4 : :
5 : : This file is part of GCC.
6 : :
7 : : GCC is free software; you can redistribute it and/or modify it under
8 : : the terms of the GNU General Public License as published by the Free
9 : : Software Foundation; either version 3, or (at your option) any later
10 : : version.
11 : :
12 : : GCC is distributed in the hope that it will be useful, but WITHOUT ANY
13 : : WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 : : FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 : : for more details.
16 : :
17 : : You should have received a copy of the GNU General Public License
18 : : along with GCC; see the file COPYING3. If not see
19 : : <http://www.gnu.org/licenses/>. */
20 : :
21 : : #ifndef GCC_MEM_STATS_H
22 : : #define GCC_MEM_STATS_H
23 : :
24 : : /* Forward declaration. */
25 : : template<typename Key, typename Value,
26 : : typename Traits = simple_hashmap_traits<default_hash_traits<Key>,
27 : : Value> >
28 : : class hash_map;
29 : :
30 : : #define LOCATION_LINE_EXTRA_SPACE 30
31 : : #define LOCATION_LINE_WIDTH 48
32 : :
33 : : /* Memory allocation location. */
34 : : class mem_location
35 : : {
36 : : public:
37 : : /* Default constructor. */
38 : : inline
39 : 1511874 : mem_location () {}
40 : :
41 : : /* Constructor. */
42 : : inline
43 : 76972055 : mem_location (mem_alloc_origin origin, bool ggc,
44 : : const char *filename = NULL, int line = 0,
45 : 76972055 : const char *function = NULL):
46 : 76972055 : m_filename (filename), m_function (function), m_line (line), m_origin
47 : 61414244 : (origin), m_ggc (ggc) {}
48 : :
49 : : /* Copy constructor. */
50 : : inline
51 : : mem_location (mem_location &other): m_filename (other.m_filename),
52 : : m_function (other.m_function), m_line (other.m_line),
53 : : m_origin (other.m_origin), m_ggc (other.m_ggc) {}
54 : :
55 : : /* Compute hash value based on file name, function name and line in
56 : : source code. As there is just a single pointer registered for every
57 : : constant that points to e.g. the same file name, we can use hash
58 : : of the pointer. */
59 : : hashval_t
60 : : hash ()
61 : : {
62 : : inchash::hash hash;
63 : :
64 : : hash.add_ptr (m_filename);
65 : : hash.add_ptr (m_function);
66 : : hash.add_int (m_line);
67 : :
68 : : return hash.end ();
69 : : }
70 : :
71 : : /* Return true if the memory location is equal to OTHER. */
72 : : int
73 : : equal (const mem_location &other)
74 : : {
75 : : return m_filename == other.m_filename && m_function == other.m_function
76 : : && m_line == other.m_line;
77 : : }
78 : :
79 : : /* Return trimmed filename for the location. */
80 : : inline const char *
81 : 0 : get_trimmed_filename ()
82 : : {
83 : 0 : const char *s1 = m_filename;
84 : 0 : const char *s2;
85 : :
86 : 0 : while ((s2 = strstr (s1, "gcc/")))
87 : 0 : s1 = s2 + 4;
88 : :
89 : 0 : return s1;
90 : : }
91 : :
92 : : inline char *
93 : : to_string ()
94 : : {
95 : : unsigned l = strlen (get_trimmed_filename ()) + strlen (m_function)
96 : : + LOCATION_LINE_EXTRA_SPACE;
97 : :
98 : : char *s = XNEWVEC (char, l);
99 : : sprintf (s, "%s:%i (%s)", get_trimmed_filename (),
100 : : m_line, m_function);
101 : :
102 : : s[MIN (LOCATION_LINE_WIDTH, l - 1)] = '\0';
103 : :
104 : : return s;
105 : : }
106 : :
107 : : /* Return display name associated to ORIGIN type. */
108 : : static const char *
109 : 0 : get_origin_name (mem_alloc_origin origin)
110 : : {
111 : 0 : return mem_alloc_origin_names[(unsigned) origin];
112 : : }
113 : :
114 : : /* File name of source code. */
115 : : const char *m_filename;
116 : : /* Funcation name. */
117 : : const char *m_function;
118 : : /* Line number in source code. */
119 : : int m_line;
120 : : /* Origin type. */
121 : : mem_alloc_origin m_origin;
122 : : /* Flag if used by GGC allocation. */
123 : : bool m_ggc;
124 : : };
125 : :
126 : : /* Memory usage register to a memory location. */
127 : : class mem_usage
128 : : {
129 : : public:
130 : : /* Default constructor. */
131 : 0 : mem_usage (): m_allocated (0), m_times (0), m_peak (0), m_instances (1) {}
132 : :
133 : : /* Constructor. */
134 : 0 : mem_usage (size_t allocated, size_t times, size_t peak, size_t instances = 0):
135 : 0 : m_allocated (allocated), m_times (times), m_peak (peak),
136 : 0 : m_instances (instances) {}
137 : :
138 : : /* Register overhead of SIZE bytes. */
139 : : inline void
140 : 0 : register_overhead (size_t size)
141 : : {
142 : 0 : m_allocated += size;
143 : 0 : m_times++;
144 : :
145 : 0 : if (m_peak < m_allocated)
146 : 0 : m_peak = m_allocated;
147 : : }
148 : :
149 : : /* Release overhead of SIZE bytes. */
150 : : inline void
151 : 0 : release_overhead (size_t size)
152 : : {
153 : 0 : gcc_assert (size <= m_allocated);
154 : :
155 : 0 : m_allocated -= size;
156 : 0 : }
157 : :
158 : : /* Sum the usage with SECOND usage. */
159 : : mem_usage
160 : : operator+ (const mem_usage &second)
161 : : {
162 : : return mem_usage (m_allocated + second.m_allocated,
163 : : m_times + second.m_times,
164 : : m_peak + second.m_peak,
165 : : m_instances + second.m_instances);
166 : : }
167 : :
168 : : /* Equality operator. */
169 : : inline bool
170 : 0 : operator== (const mem_usage &second) const
171 : : {
172 : 0 : return (m_allocated == second.m_allocated
173 : 0 : && m_peak == second.m_peak
174 : 0 : && m_times == second.m_times);
175 : : }
176 : :
177 : : /* Comparison operator. */
178 : : inline bool
179 : 0 : operator< (const mem_usage &second) const
180 : : {
181 : 0 : if (*this == second)
182 : : return false;
183 : :
184 : 0 : return (m_allocated == second.m_allocated ?
185 : 0 : (m_peak == second.m_peak ? m_times < second.m_times
186 : 0 : : m_peak < second.m_peak) : m_allocated < second.m_allocated);
187 : : }
188 : :
189 : : /* Compare wrapper used by qsort method. */
190 : : static int
191 : 0 : compare (const void *first, const void *second)
192 : : {
193 : 0 : typedef std::pair<mem_location *, mem_usage *> mem_pair_t;
194 : :
195 : 0 : const mem_pair_t f = *(const mem_pair_t *)first;
196 : 0 : const mem_pair_t s = *(const mem_pair_t *)second;
197 : :
198 : 0 : if (*f.second == *s.second)
199 : : return 0;
200 : :
201 : 0 : return *f.second < *s.second ? 1 : -1;
202 : : }
203 : :
204 : : /* Dump usage coupled to LOC location, where TOTAL is sum of all rows. */
205 : : inline void
206 : : dump (mem_location *loc, const mem_usage &total) const
207 : : {
208 : : char *location_string = loc->to_string ();
209 : :
210 : : fprintf (stderr, "%-48s " PRsa (9) ":%5.1f%%"
211 : : PRsa (9) PRsa (9) ":%5.1f%%%10s\n",
212 : : location_string, SIZE_AMOUNT (m_allocated),
213 : : get_percent (m_allocated, total.m_allocated),
214 : : SIZE_AMOUNT (m_peak), SIZE_AMOUNT (m_times),
215 : : get_percent (m_times, total.m_times), loc->m_ggc ? "ggc" : "heap");
216 : :
217 : : free (location_string);
218 : : }
219 : :
220 : : /* Dump footer. */
221 : : inline void
222 : : dump_footer () const
223 : : {
224 : : fprintf (stderr, "%s" PRsa (53) PRsa (26) "\n", "Total",
225 : : SIZE_AMOUNT (m_allocated), SIZE_AMOUNT (m_times));
226 : : }
227 : :
228 : : /* Return fraction of NOMINATOR and DENOMINATOR in percent. */
229 : : static inline float
230 : : get_percent (size_t nominator, size_t denominator)
231 : : {
232 : : return denominator == 0 ? 0.0f : nominator * 100.0 / denominator;
233 : : }
234 : :
235 : : /* Print line made of dashes. */
236 : : static inline void
237 : 0 : print_dash_line (size_t count = 140)
238 : : {
239 : 0 : while (count--)
240 : 0 : fputc ('-', stderr);
241 : 0 : fputc ('\n', stderr);
242 : 0 : }
243 : :
244 : : /* Dump header with NAME. */
245 : : static inline void
246 : : dump_header (const char *name)
247 : : {
248 : : fprintf (stderr, "%-48s %11s%16s%10s%17s\n", name, "Leak", "Peak",
249 : : "Times", "Type");
250 : : }
251 : :
252 : : /* Current number of allocated bytes. */
253 : : size_t m_allocated;
254 : : /* Number of allocations. */
255 : : size_t m_times;
256 : : /* Peak allocation in bytes. */
257 : : size_t m_peak;
258 : : /* Number of container instances. */
259 : : size_t m_instances;
260 : : };
261 : :
262 : : /* Memory usage pair that connectes memory usage and number
263 : : of allocated bytes. */
264 : : template <class T>
265 : : class mem_usage_pair
266 : : {
267 : : public:
268 : 0 : mem_usage_pair (T *usage_, size_t allocated_): usage (usage_),
269 : 0 : allocated (allocated_) {}
270 : :
271 : : T *usage;
272 : : size_t allocated;
273 : : };
274 : :
275 : : /* Memory allocation description. */
276 : : template <class T>
277 : : class mem_alloc_description
278 : : {
279 : : public:
280 : : struct mem_location_hash : nofree_ptr_hash <mem_location>
281 : : {
282 : : static hashval_t
283 : 0 : hash (value_type l)
284 : : {
285 : 0 : inchash::hash hstate;
286 : :
287 : 0 : hstate.add_ptr ((const void *)l->m_filename);
288 : 0 : hstate.add_ptr (l->m_function);
289 : 0 : hstate.add_int (l->m_line);
290 : :
291 : 0 : return hstate.end ();
292 : : }
293 : :
294 : : static bool
295 : 0 : equal (value_type l1, value_type l2)
296 : : {
297 : 0 : return (l1->m_filename == l2->m_filename
298 : 0 : && l1->m_function == l2->m_function
299 : 0 : && l1->m_line == l2->m_line);
300 : : }
301 : : };
302 : :
303 : : /* Internal class type definitions. */
304 : : typedef hash_map <mem_location_hash, T *> mem_map_t;
305 : : typedef hash_map <const void *, mem_usage_pair<T> > reverse_mem_map_t;
306 : : typedef hash_map <const void *, std::pair<T *, size_t> > reverse_object_map_t;
307 : : typedef std::pair <mem_location *, T *> mem_list_t;
308 : :
309 : : /* Default contructor. */
310 : : mem_alloc_description ();
311 : :
312 : : /* Default destructor. */
313 : : ~mem_alloc_description ();
314 : :
315 : : /* Returns true if instance PTR is registered by the memory description. */
316 : : bool contains_descriptor_for_instance (const void *ptr);
317 : :
318 : : /* Return descriptor for instance PTR. */
319 : : T *get_descriptor_for_instance (const void *ptr);
320 : :
321 : : /* Register memory allocation descriptor for container PTR which is
322 : : described by a memory LOCATION. */
323 : : T *register_descriptor (const void *ptr, mem_location *location);
324 : :
325 : : /* Register memory allocation descriptor for container PTR. ORIGIN identifies
326 : : type of container and GGC identifes if the allocation is handled in GGC
327 : : memory. Each location is identified by file NAME, LINE in source code and
328 : : FUNCTION name. */
329 : : T *register_descriptor (const void *ptr, mem_alloc_origin origin,
330 : : bool ggc, const char *name, int line,
331 : : const char *function);
332 : :
333 : : /* Register instance overhead identified by PTR pointer. Allocation takes
334 : : SIZE bytes. */
335 : : T *register_instance_overhead (size_t size, const void *ptr);
336 : :
337 : : /* For containers (and GGC) where we want to track every instance object,
338 : : we register allocation of SIZE bytes, identified by PTR pointer, belonging
339 : : to USAGE descriptor. */
340 : : void register_object_overhead (T *usage, size_t size, const void *ptr);
341 : :
342 : : /* Release PTR pointer of SIZE bytes. If REMOVE_FROM_MAP is set to true,
343 : : remove the instance from reverse map. Return memory usage that belongs
344 : : to this memory description. */
345 : : T *release_instance_overhead (void *ptr, size_t size,
346 : : bool remove_from_map = false);
347 : :
348 : : /* Release instance object identified by PTR pointer. */
349 : : void release_object_overhead (void *ptr);
350 : :
351 : : /* Unregister a memory allocation descriptor registered with
352 : : register_descriptor (remove from reverse map), unless it is
353 : : unregistered through release_instance_overhead with
354 : : REMOVE_FROM_MAP = true. */
355 : : void unregister_descriptor (void *ptr);
356 : :
357 : : /* Get sum value for ORIGIN type of allocation for the descriptor. */
358 : : T get_sum (mem_alloc_origin origin);
359 : :
360 : : /* Get all tracked instances registered by the description. Items
361 : : are filtered by ORIGIN type, LENGTH is return value where we register
362 : : the number of elements in the list. If we want to process custom order,
363 : : CMP comparator can be provided. */
364 : : mem_list_t *get_list (mem_alloc_origin origin, unsigned *length);
365 : :
366 : : /* Dump all tracked instances of type ORIGIN. If we want to process custom
367 : : order, CMP comparator can be provided. */
368 : : void dump (mem_alloc_origin origin);
369 : :
370 : : /* Reverse object map used for every object allocation mapping. */
371 : : reverse_object_map_t *m_reverse_object_map;
372 : :
373 : : private:
374 : : /* Register overhead of SIZE bytes of ORIGIN type. PTR pointer is allocated
375 : : in NAME source file, at LINE in source code, in FUNCTION. */
376 : : T *register_overhead (size_t size, mem_alloc_origin origin, const char *name,
377 : : int line, const char *function, const void *ptr);
378 : :
379 : : /* Allocation location coupled to the description. */
380 : : mem_location m_location;
381 : :
382 : : /* Location to usage mapping. */
383 : : mem_map_t *m_map;
384 : :
385 : : /* Reverse pointer to usage mapping. */
386 : : reverse_mem_map_t *m_reverse_map;
387 : : };
388 : :
389 : : /* Returns true if instance PTR is registered by the memory description. */
390 : :
391 : : template <class T>
392 : : inline bool
393 : 0 : mem_alloc_description<T>::contains_descriptor_for_instance (const void *ptr)
394 : : {
395 : 0 : return m_reverse_map->get (ptr);
396 : : }
397 : :
398 : : /* Return descriptor for instance PTR. */
399 : :
400 : : template <class T>
401 : : inline T*
402 : : mem_alloc_description<T>::get_descriptor_for_instance (const void *ptr)
403 : : {
404 : : return m_reverse_map->get (ptr) ? (*m_reverse_map->get (ptr)).usage : NULL;
405 : : }
406 : :
407 : : /* Register memory allocation descriptor for container PTR which is
408 : : described by a memory LOCATION. */
409 : :
410 : : template <class T>
411 : : inline T*
412 : 0 : mem_alloc_description<T>::register_descriptor (const void *ptr,
413 : : mem_location *location)
414 : : {
415 : 0 : T *usage = NULL;
416 : :
417 : 0 : T **slot = m_map->get (location);
418 : 0 : if (slot)
419 : : {
420 : 0 : delete location;
421 : 0 : usage = *slot;
422 : 0 : usage->m_instances++;
423 : : }
424 : : else
425 : : {
426 : 0 : usage = new T ();
427 : 0 : m_map->put (location, usage);
428 : : }
429 : :
430 : 0 : if (!m_reverse_map->get (ptr))
431 : 0 : m_reverse_map->put (ptr, mem_usage_pair<T> (usage, 0));
432 : :
433 : 0 : return usage;
434 : : }
435 : :
436 : : /* Register memory allocation descriptor for container PTR. ORIGIN identifies
437 : : type of container and GGC identifes if the allocation is handled in GGC
438 : : memory. Each location is identified by file NAME, LINE in source code and
439 : : FUNCTION name. */
440 : :
441 : : template <class T>
442 : : inline T*
443 : 0 : mem_alloc_description<T>::register_descriptor (const void *ptr,
444 : : mem_alloc_origin origin,
445 : : bool ggc,
446 : : const char *filename,
447 : : int line,
448 : : const char *function)
449 : : {
450 : 0 : mem_location *l = new mem_location (origin, ggc, filename, line, function);
451 : 0 : return register_descriptor (ptr, l);
452 : : }
453 : :
454 : : /* Register instance overhead identified by PTR pointer. Allocation takes
455 : : SIZE bytes. */
456 : :
457 : : template <class T>
458 : : inline T*
459 : 0 : mem_alloc_description<T>::register_instance_overhead (size_t size,
460 : : const void *ptr)
461 : : {
462 : 0 : mem_usage_pair <T> *slot = m_reverse_map->get (ptr);
463 : 0 : if (!slot)
464 : : {
465 : : /* Due to PCH, it can really happen. */
466 : : return NULL;
467 : : }
468 : :
469 : 0 : T *usage = (*slot).usage;
470 : 0 : usage->register_overhead (size);
471 : :
472 : : return usage;
473 : : }
474 : :
475 : : /* For containers (and GGC) where we want to track every instance object,
476 : : we register allocation of SIZE bytes, identified by PTR pointer, belonging
477 : : to USAGE descriptor. */
478 : :
479 : : template <class T>
480 : : void
481 : 0 : mem_alloc_description<T>::register_object_overhead (T *usage, size_t size,
482 : : const void *ptr)
483 : : {
484 : : /* In case of GGC, it is possible to have already occupied the memory
485 : : location. */
486 : 0 : m_reverse_object_map->put (ptr, std::pair<T *, size_t> (usage, size));
487 : : }
488 : :
489 : : /* Register overhead of SIZE bytes of ORIGIN type. PTR pointer is allocated
490 : : in NAME source file, at LINE in source code, in FUNCTION. */
491 : :
492 : : template <class T>
493 : : inline T*
494 : : mem_alloc_description<T>::register_overhead (size_t size,
495 : : mem_alloc_origin origin,
496 : : const char *filename,
497 : : int line,
498 : : const char *function,
499 : : const void *ptr)
500 : : {
501 : : T *usage = register_descriptor (ptr, origin, filename, line, function);
502 : : usage->register_overhead (size);
503 : :
504 : : return usage;
505 : : }
506 : :
507 : : /* Release PTR pointer of SIZE bytes. */
508 : :
509 : : template <class T>
510 : : inline T *
511 : 0 : mem_alloc_description<T>::release_instance_overhead (void *ptr, size_t size,
512 : : bool remove_from_map)
513 : : {
514 : 0 : mem_usage_pair<T> *slot = m_reverse_map->get (ptr);
515 : :
516 : 0 : if (!slot)
517 : : {
518 : : /* Due to PCH, it can really happen. */
519 : : return NULL;
520 : : }
521 : :
522 : 0 : T *usage = (*slot).usage;
523 : 0 : usage->release_overhead (size);
524 : :
525 : 0 : if (remove_from_map)
526 : 0 : m_reverse_map->remove (ptr);
527 : :
528 : : return usage;
529 : : }
530 : :
531 : : /* Release instance object identified by PTR pointer. */
532 : :
533 : : template <class T>
534 : : inline void
535 : 0 : mem_alloc_description<T>::release_object_overhead (void *ptr)
536 : : {
537 : 0 : std::pair <T *, size_t> *entry = m_reverse_object_map->get (ptr);
538 : 0 : entry->first->release_overhead (entry->second);
539 : 0 : m_reverse_object_map->remove (ptr);
540 : 0 : }
541 : :
542 : : /* Unregister a memory allocation descriptor registered with
543 : : register_descriptor (remove from reverse map), unless it is
544 : : unregistered through release_instance_overhead with
545 : : REMOVE_FROM_MAP = true. */
546 : : template <class T>
547 : : inline void
548 : : mem_alloc_description<T>::unregister_descriptor (void *ptr)
549 : : {
550 : : m_reverse_map->remove (ptr);
551 : : }
552 : :
553 : : /* Default contructor. */
554 : :
555 : : template <class T>
556 : : inline
557 : 1511874 : mem_alloc_description<T>::mem_alloc_description ()
558 : : {
559 : 1511874 : m_map = new mem_map_t (13, false, false, false);
560 : 1511874 : m_reverse_map = new reverse_mem_map_t (13, false, false, false);
561 : 1511874 : m_reverse_object_map = new reverse_object_map_t (13, false, false, false);
562 : 1511874 : }
563 : :
564 : : /* Default destructor. */
565 : :
566 : : template <class T>
567 : : inline
568 : 1511874 : mem_alloc_description<T>::~mem_alloc_description ()
569 : : {
570 : 1511874 : for (typename mem_map_t::iterator it = m_map->begin (); it != m_map->end ();
571 : 0 : ++it)
572 : : {
573 : 0 : delete (*it).first;
574 : 0 : delete (*it).second;
575 : : }
576 : :
577 : 3023748 : delete m_map;
578 : 3023748 : delete m_reverse_map;
579 : 3023748 : delete m_reverse_object_map;
580 : 1511874 : }
581 : :
582 : : /* Get all tracked instances registered by the description. Items are filtered
583 : : by ORIGIN type, LENGTH is return value where we register the number of
584 : : elements in the list. If we want to process custom order, CMP comparator
585 : : can be provided. */
586 : :
587 : : template <class T>
588 : : inline
589 : : typename mem_alloc_description<T>::mem_list_t *
590 : 0 : mem_alloc_description<T>::get_list (mem_alloc_origin origin, unsigned *length)
591 : : {
592 : : /* vec data structure is not used because all vectors generate memory
593 : : allocation info a it would create a cycle. */
594 : 0 : size_t element_size = sizeof (mem_list_t);
595 : 0 : mem_list_t *list = XCNEWVEC (mem_list_t, m_map->elements ());
596 : 0 : unsigned i = 0;
597 : :
598 : 0 : for (typename mem_map_t::iterator it = m_map->begin (); it != m_map->end ();
599 : 0 : ++it)
600 : 0 : if ((*it).first->m_origin == origin)
601 : 0 : list[i++] = std::pair<mem_location*, T*> (*it);
602 : :
603 : 0 : qsort (list, i, element_size, T::compare);
604 : 0 : *length = i;
605 : :
606 : 0 : return list;
607 : : }
608 : :
609 : : /* Get sum value for ORIGIN type of allocation for the descriptor. */
610 : :
611 : : template <class T>
612 : : inline T
613 : 0 : mem_alloc_description<T>::get_sum (mem_alloc_origin origin)
614 : : {
615 : : unsigned length;
616 : 0 : mem_list_t *list = get_list (origin, &length);
617 : 0 : T sum;
618 : :
619 : 0 : for (unsigned i = 0; i < length; i++)
620 : 0 : sum = sum + *list[i].second;
621 : :
622 : 0 : XDELETEVEC (list);
623 : :
624 : 0 : return sum;
625 : : }
626 : :
627 : : /* Dump all tracked instances of type ORIGIN. If we want to process custom
628 : : order, CMP comparator can be provided. */
629 : :
630 : : template <class T>
631 : : inline void
632 : 0 : mem_alloc_description<T>::dump (mem_alloc_origin origin)
633 : : {
634 : : unsigned length;
635 : :
636 : 0 : fprintf (stderr, "\n");
637 : :
638 : 0 : mem_list_t *list = get_list (origin, &length);
639 : 0 : T total = get_sum (origin);
640 : :
641 : 0 : T::print_dash_line ();
642 : 0 : T::dump_header (mem_location::get_origin_name (origin));
643 : 0 : T::print_dash_line ();
644 : 0 : for (int i = length - 1; i >= 0; i--)
645 : 0 : list[i].second->dump (list[i].first, total);
646 : 0 : T::print_dash_line ();
647 : :
648 : 0 : T::dump_header (mem_location::get_origin_name (origin));
649 : 0 : T::print_dash_line ();
650 : 0 : total.dump_footer ();
651 : 0 : T::print_dash_line ();
652 : :
653 : 0 : XDELETEVEC (list);
654 : :
655 : 0 : fprintf (stderr, "\n");
656 : 0 : }
657 : :
658 : : #endif // GCC_MEM_STATS_H
|