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