1 /* A memory statistics tracking infrastructure. 2 Copyright (C) 2015-2020 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 mem_location () {} 40 41 /* Constructor. */ 42 inline 43 mem_location (mem_alloc_origin origin, bool ggc, 44 const char *filename = NULL, int line = 0, 45 const char *function = NULL): 46 m_filename (filename), m_function (function), m_line (line), m_origin 47 (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 (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 get_trimmed_filename () 82 { 83 const char *s1 = m_filename; 84 const char *s2; 85 86 while ((s2 = strstr (s1, "gcc/"))) 87 s1 = s2 + 4; 88 89 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 get_origin_name (mem_alloc_origin origin) 110 { 111 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 mem_usage (): m_allocated (0), m_times (0), m_peak (0), m_instances (1) {} 132 133 /* Constructor. */ 134 mem_usage (size_t allocated, size_t times, size_t peak, size_t instances = 0): 135 m_allocated (allocated), m_times (times), m_peak (peak), 136 m_instances (instances) {} 137 138 /* Register overhead of SIZE bytes. */ 139 inline void 140 register_overhead (size_t size) 141 { 142 m_allocated += size; 143 m_times++; 144 145 if (m_peak < m_allocated) 146 m_peak = m_allocated; 147 } 148 149 /* Release overhead of SIZE bytes. */ 150 inline void 151 release_overhead (size_t size) 152 { 153 gcc_assert (size <= m_allocated); 154 155 m_allocated -= size; 156 } 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 operator== (const mem_usage &second) const 171 { 172 return (m_allocated == second.m_allocated 173 && m_peak == second.m_peak 174 && m_times == second.m_times); 175 } 176 177 /* Comparison operator. */ 178 inline bool 179 operator< (const mem_usage &second) const 180 { 181 if (*this == second) 182 return false; 183 184 return (m_allocated == second.m_allocated ? 185 (m_peak == second.m_peak ? m_times < second.m_times 186 : m_peak < second.m_peak) : m_allocated < second.m_allocated); 187 } 188 189 /* Compare wrapper used by qsort method. */ 190 static int 191 compare (const void *first, const void *second) 192 { 193 typedef std::pair<mem_location *, mem_usage *> mem_pair_t; 194 195 const mem_pair_t f = *(const mem_pair_t *)first; 196 const mem_pair_t s = *(const mem_pair_t *)second; 197 198 if (*f.second == *s.second) 199 return 0; 200 201 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, 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 print_dash_line (size_t count = 140) 238 { 239 while (count--) 240 fputc ('-', stderr); 241 fputc ('\n', stderr); 242 } 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 mem_usage_pair (T *usage_, size_t allocated_): usage (usage_), 269 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 hash (value_type l) 284 { 285 inchash::hash hstate; 286 287 hstate.add_ptr ((const void *)l->m_filename); 288 hstate.add_ptr (l->m_function); 289 hstate.add_int (l->m_line); 290 291 return hstate.end (); 292 } 293 294 static bool 295 equal (value_type l1, value_type l2) 296 { 297 return (l1->m_filename == l2->m_filename 298 && l1->m_function == l2->m_function 299 && 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 mem_alloc_description<T>::contains_descriptor_for_instance (const void *ptr) 394 { 395 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 mem_alloc_description<T>::register_descriptor (const void *ptr, 413 mem_location *location) 414 { 415 T *usage = NULL; 416 417 T **slot = m_map->get (location); 418 if (slot) 419 { 420 delete location; 421 usage = *slot; 422 usage->m_instances++; 423 } 424 else 425 { 426 usage = new T (); 427 m_map->put (location, usage); 428 } 429 430 if (!m_reverse_map->get (ptr)) 431 m_reverse_map->put (ptr, mem_usage_pair<T> (usage, 0)); 432 433 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 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 mem_location *l = new mem_location (origin, ggc, filename, line, function); 451 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 mem_alloc_description<T>::register_instance_overhead (size_t size, 460 const void *ptr) 461 { 462 mem_usage_pair <T> *slot = m_reverse_map->get (ptr); 463 if (!slot) 464 { 465 /* Due to PCH, it can really happen. */ 466 return NULL; 467 } 468 469 T *usage = (*slot).usage; 470 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 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 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 mem_alloc_description<T>::release_instance_overhead (void *ptr, size_t size, 512 bool remove_from_map) 513 { 514 mem_usage_pair<T> *slot = m_reverse_map->get (ptr); 515 516 if (!slot) 517 { 518 /* Due to PCH, it can really happen. */ 519 return NULL; 520 } 521 522 T *usage = (*slot).usage; 523 usage->release_overhead (size); 524 525 if (remove_from_map) 526 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 mem_alloc_description<T>::release_object_overhead (void *ptr) 536 { 537 std::pair <T *, size_t> *entry = m_reverse_object_map->get (ptr); 538 entry->first->release_overhead (entry->second); 539 m_reverse_object_map->remove (ptr); 540 } 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 mem_alloc_description<T>::mem_alloc_description () 558 { 559 m_map = new mem_map_t (13, false, false, false); 560 m_reverse_map = new reverse_mem_map_t (13, false, false, false); 561 m_reverse_object_map = new reverse_object_map_t (13, false, false, false); 562 } 563 564 /* Default destructor. */ 565 566 template <class T> 567 inline 568 mem_alloc_description<T>::~mem_alloc_description () 569 { 570 for (typename mem_map_t::iterator it = m_map->begin (); it != m_map->end (); 571 ++it) 572 { 573 delete (*it).first; 574 delete (*it).second; 575 } 576 577 delete m_map; 578 delete m_reverse_map; 579 delete m_reverse_object_map; 580 } 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 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 size_t element_size = sizeof (mem_list_t); 595 mem_list_t *list = XCNEWVEC (mem_list_t, m_map->elements ()); 596 unsigned i = 0; 597 598 for (typename mem_map_t::iterator it = m_map->begin (); it != m_map->end (); 599 ++it) 600 if ((*it).first->m_origin == origin) 601 list[i++] = std::pair<mem_location*, T*> (*it); 602 603 qsort (list, i, element_size, T::compare); 604 *length = i; 605 606 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 mem_alloc_description<T>::get_sum (mem_alloc_origin origin) 614 { 615 unsigned length; 616 mem_list_t *list = get_list (origin, &length); 617 T sum; 618 619 for (unsigned i = 0; i < length; i++) 620 sum = sum + *list[i].second; 621 622 XDELETEVEC (list); 623 624 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 mem_alloc_description<T>::dump (mem_alloc_origin origin) 633 { 634 unsigned length; 635 636 fprintf (stderr, "\n"); 637 638 mem_list_t *list = get_list (origin, &length); 639 T total = get_sum (origin); 640 641 T::print_dash_line (); 642 T::dump_header (mem_location::get_origin_name (origin)); 643 T::print_dash_line (); 644 for (int i = length - 1; i >= 0; i--) 645 list[i].second->dump (list[i].first, total); 646 T::print_dash_line (); 647 648 T::dump_header (mem_location::get_origin_name (origin)); 649 T::print_dash_line (); 650 total.dump_footer (); 651 T::print_dash_line (); 652 653 XDELETEVEC (list); 654 655 fprintf (stderr, "\n"); 656 } 657 658 #endif // GCC_MEM_STATS_H 659