1/* 2 * Memory management routine 3 * Copyright (C) 1998 Kunihiro Ishiguro 4 * 5 * This file is part of GNU Zebra. 6 * 7 * GNU Zebra is free software; you can redistribute it and/or modify it 8 * under the terms of the GNU General Public License as published by the 9 * Free Software Foundation; either version 2, or (at your option) any 10 * later version. 11 * 12 * GNU Zebra is distributed in the hope that it will be useful, but 13 * WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with GNU Zebra; see the file COPYING. If not, write to the Free 19 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 20 * 02111-1307, USA. 21 */ 22 23#include <zebra.h> 24/* malloc.h is generally obsolete, however GNU Libc mallinfo wants it. */ 25#if !defined(HAVE_STDLIB_H) || (defined(GNU_LINUX) && defined(HAVE_MALLINFO)) 26#include <malloc.h> 27#endif /* !HAVE_STDLIB_H || HAVE_MALLINFO */ 28 29#include "log.h" 30#include "memory.h" 31 32static void alloc_inc (int); 33static void alloc_dec (int); 34static void log_memstats(int log_priority); 35 36static const struct message mstr [] = 37{ 38 { MTYPE_THREAD, "thread" }, 39 { MTYPE_THREAD_MASTER, "thread_master" }, 40 { MTYPE_VECTOR, "vector" }, 41 { MTYPE_VECTOR_INDEX, "vector_index" }, 42 { MTYPE_IF, "interface" }, 43 { 0, NULL }, 44}; 45 46/* Fatal memory allocation error occured. */ 47static void __attribute__ ((noreturn)) 48zerror (const char *fname, int type, size_t size) 49{ 50 zlog_err ("%s : can't allocate memory for `%s' size %d: %s\n", 51 fname, lookup (mstr, type), (int) size, safe_strerror(errno)); 52 log_memstats(LOG_WARNING); 53 /* N.B. It might be preferable to call zlog_backtrace_sigsafe here, since 54 that function should definitely be safe in an OOM condition. But 55 unfortunately zlog_backtrace_sigsafe does not support syslog logging at 56 this time... */ 57 zlog_backtrace(LOG_WARNING); 58 abort(); 59} 60 61/* 62 * Allocate memory of a given size, to be tracked by a given type. 63 * Effects: Returns a pointer to usable memory. If memory cannot 64 * be allocated, aborts execution. 65 */ 66void * 67zmalloc (int type, size_t size) 68{ 69 void *memory; 70 71 memory = malloc (size); 72 73 if (memory == NULL) 74 zerror ("malloc", type, size); 75 76 alloc_inc (type); 77 78 return memory; 79} 80 81/* 82 * Allocate memory as in zmalloc, and also clear the memory. 83 */ 84void * 85zcalloc (int type, size_t size) 86{ 87 void *memory; 88 89 memory = calloc (1, size); 90 91 if (memory == NULL) 92 zerror ("calloc", type, size); 93 94 alloc_inc (type); 95 96 return memory; 97} 98 99/* 100 * Given a pointer returned by zmalloc or zcalloc, free it and 101 * return a pointer to a new size, basically acting like realloc(). 102 * Requires: ptr was returned by zmalloc, zcalloc, or zrealloc with the 103 * same type. 104 * Effects: Returns a pointer to the new memory, or aborts. 105 */ 106void * 107zrealloc (int type, void *ptr, size_t size) 108{ 109 void *memory; 110 111 memory = realloc (ptr, size); 112 if (memory == NULL) 113 zerror ("realloc", type, size); 114 if (ptr == NULL) 115 alloc_inc (type); 116 117 return memory; 118} 119 120/* 121 * Free memory allocated by z*alloc or zstrdup. 122 * Requires: ptr was returned by zmalloc, zcalloc, or zrealloc with the 123 * same type. 124 * Effects: The memory is freed and may no longer be referenced. 125 */ 126void 127zfree (int type, void *ptr) 128{ 129 if (ptr != NULL) 130 { 131 alloc_dec (type); 132 free (ptr); 133 } 134} 135 136/* 137 * Duplicate a string, counting memory usage by type. 138 * Effects: The string is duplicated, and the return value must 139 * eventually be passed to zfree with the same type. The function will 140 * succeed or abort. 141 */ 142char * 143zstrdup (int type, const char *str) 144{ 145 void *dup; 146 147 dup = strdup (str); 148 if (dup == NULL) 149 zerror ("strdup", type, strlen (str)); 150 alloc_inc (type); 151 return dup; 152} 153 154#ifdef MEMORY_LOG 155static struct 156{ 157 const char *name; 158 long alloc; 159 unsigned long t_malloc; 160 unsigned long c_malloc; 161 unsigned long t_calloc; 162 unsigned long c_calloc; 163 unsigned long t_realloc; 164 unsigned long t_free; 165 unsigned long c_strdup; 166} mstat [MTYPE_MAX]; 167 168static void 169mtype_log (char *func, void *memory, const char *file, int line, int type) 170{ 171 zlog_debug ("%s: %s %p %s %d", func, lookup (mstr, type), memory, file, line); 172} 173 174void * 175mtype_zmalloc (const char *file, int line, int type, size_t size) 176{ 177 void *memory; 178 179 mstat[type].c_malloc++; 180 mstat[type].t_malloc++; 181 182 memory = zmalloc (type, size); 183 mtype_log ("zmalloc", memory, file, line, type); 184 185 return memory; 186} 187 188void * 189mtype_zcalloc (const char *file, int line, int type, size_t size) 190{ 191 void *memory; 192 193 mstat[type].c_calloc++; 194 mstat[type].t_calloc++; 195 196 memory = zcalloc (type, size); 197 mtype_log ("xcalloc", memory, file, line, type); 198 199 return memory; 200} 201 202void * 203mtype_zrealloc (const char *file, int line, int type, void *ptr, size_t size) 204{ 205 void *memory; 206 207 /* Realloc need before allocated pointer. */ 208 mstat[type].t_realloc++; 209 210 memory = zrealloc (type, ptr, size); 211 212 mtype_log ("xrealloc", memory, file, line, type); 213 214 return memory; 215} 216 217/* Important function. */ 218void 219mtype_zfree (const char *file, int line, int type, void *ptr) 220{ 221 mstat[type].t_free++; 222 223 mtype_log ("xfree", ptr, file, line, type); 224 225 zfree (type, ptr); 226} 227 228char * 229mtype_zstrdup (const char *file, int line, int type, const char *str) 230{ 231 char *memory; 232 233 mstat[type].c_strdup++; 234 235 memory = zstrdup (type, str); 236 237 mtype_log ("xstrdup", memory, file, line, type); 238 239 return memory; 240} 241#else 242static struct 243{ 244 char *name; 245 long alloc; 246} mstat [MTYPE_MAX]; 247#endif /* MEMORY_LOG */ 248 249/* Increment allocation counter. */ 250static void 251alloc_inc (int type) 252{ 253 mstat[type].alloc++; 254} 255 256/* Decrement allocation counter. */ 257static void 258alloc_dec (int type) 259{ 260 mstat[type].alloc--; 261} 262 263/* Looking up memory status from vty interface. */ 264#include "vector.h" 265#include "vty.h" 266#include "command.h" 267 268static void 269log_memstats(int pri) 270{ 271 struct mlist *ml; 272 273 for (ml = mlists; ml->list; ml++) 274 { 275 struct memory_list *m; 276 277 zlog (NULL, pri, "Memory utilization in module %s:", ml->name); 278 for (m = ml->list; m->index >= 0; m++) 279 if (m->index && mstat[m->index].alloc) 280 zlog (NULL, pri, " %-30s: %10ld", m->format, mstat[m->index].alloc); 281 } 282} 283 284void 285log_memstats_stderr (const char *prefix) 286{ 287 struct mlist *ml; 288 struct memory_list *m; 289 int i; 290 int j = 0; 291 292 for (ml = mlists; ml->list; ml++) 293 { 294 i = 0; 295 296 for (m = ml->list; m->index >= 0; m++) 297 if (m->index && mstat[m->index].alloc) 298 { 299 if (!i) 300 fprintf (stderr, 301 "%s: memstats: Current memory utilization in module %s:\n", 302 prefix, 303 ml->name); 304 fprintf (stderr, 305 "%s: memstats: %-30s: %10ld%s\n", 306 prefix, 307 m->format, 308 mstat[m->index].alloc, 309 mstat[m->index].alloc < 0 ? " (REPORT THIS BUG!)" : ""); 310 i = j = 1; 311 } 312 } 313 314 if (j) 315 fprintf (stderr, 316 "%s: memstats: NOTE: If configuration exists, utilization may be " 317 "expected.\n", 318 prefix); 319 else 320 fprintf (stderr, 321 "%s: memstats: No remaining tracked memory utilization.\n", 322 prefix); 323} 324 325static void 326show_separator(struct vty *vty) 327{ 328 vty_out (vty, "-----------------------------\r\n"); 329} 330 331static int 332show_memory_vty (struct vty *vty, struct memory_list *list) 333{ 334 struct memory_list *m; 335 int needsep = 0; 336 337 for (m = list; m->index >= 0; m++) 338 if (m->index == 0) 339 { 340 if (needsep) 341 { 342 show_separator (vty); 343 needsep = 0; 344 } 345 } 346 else if (mstat[m->index].alloc) 347 { 348 vty_out (vty, "%-30s: %10ld\r\n", m->format, mstat[m->index].alloc); 349 needsep = 1; 350 } 351 return needsep; 352} 353 354#ifdef HAVE_MALLINFO 355static int 356show_memory_mallinfo (struct vty *vty) 357{ 358 struct mallinfo minfo = mallinfo(); 359 char buf[MTYPE_MEMSTR_LEN]; 360 361 vty_out (vty, "System allocator statistics:%s", VTY_NEWLINE); 362 vty_out (vty, " Total heap allocated: %s%s", 363 mtype_memstr (buf, MTYPE_MEMSTR_LEN, minfo.arena), 364 VTY_NEWLINE); 365 vty_out (vty, " Holding block headers: %s%s", 366 mtype_memstr (buf, MTYPE_MEMSTR_LEN, minfo.hblkhd), 367 VTY_NEWLINE); 368 vty_out (vty, " Used small blocks: %s%s", 369 mtype_memstr (buf, MTYPE_MEMSTR_LEN, minfo.usmblks), 370 VTY_NEWLINE); 371 vty_out (vty, " Used ordinary blocks: %s%s", 372 mtype_memstr (buf, MTYPE_MEMSTR_LEN, minfo.uordblks), 373 VTY_NEWLINE); 374 vty_out (vty, " Free small blocks: %s%s", 375 mtype_memstr (buf, MTYPE_MEMSTR_LEN, minfo.fsmblks), 376 VTY_NEWLINE); 377 vty_out (vty, " Free ordinary blocks: %s%s", 378 mtype_memstr (buf, MTYPE_MEMSTR_LEN, minfo.fordblks), 379 VTY_NEWLINE); 380 vty_out (vty, " Ordinary blocks: %ld%s", 381 (unsigned long)minfo.ordblks, 382 VTY_NEWLINE); 383 vty_out (vty, " Small blocks: %ld%s", 384 (unsigned long)minfo.smblks, 385 VTY_NEWLINE); 386 vty_out (vty, " Holding blocks: %ld%s", 387 (unsigned long)minfo.hblks, 388 VTY_NEWLINE); 389 vty_out (vty, "(see system documentation for 'mallinfo' for meaning)%s", 390 VTY_NEWLINE); 391 return 1; 392} 393#endif /* HAVE_MALLINFO */ 394 395DEFUN (show_memory_all, 396 show_memory_all_cmd, 397 "show memory all", 398 "Show running system information\n" 399 "Memory statistics\n" 400 "All memory statistics\n") 401{ 402 struct mlist *ml; 403 int needsep = 0; 404 405#ifdef HAVE_MALLINFO 406 needsep = show_memory_mallinfo (vty); 407#endif /* HAVE_MALLINFO */ 408 409 for (ml = mlists; ml->list; ml++) 410 { 411 if (needsep) 412 show_separator (vty); 413 needsep = show_memory_vty (vty, ml->list); 414 } 415 416 return CMD_SUCCESS; 417} 418 419ALIAS (show_memory_all, 420 show_memory_cmd, 421 "show memory", 422 "Show running system information\n" 423 "Memory statistics\n") 424 425DEFUN (show_memory_lib, 426 show_memory_lib_cmd, 427 "show memory lib", 428 SHOW_STR 429 "Memory statistics\n" 430 "Library memory\n") 431{ 432 show_memory_vty (vty, memory_list_lib); 433 return CMD_SUCCESS; 434} 435 436DEFUN (show_memory_zebra, 437 show_memory_zebra_cmd, 438 "show memory zebra", 439 SHOW_STR 440 "Memory statistics\n" 441 "Zebra memory\n") 442{ 443 show_memory_vty (vty, memory_list_zebra); 444 return CMD_SUCCESS; 445} 446 447DEFUN (show_memory_rip, 448 show_memory_rip_cmd, 449 "show memory rip", 450 SHOW_STR 451 "Memory statistics\n" 452 "RIP memory\n") 453{ 454 show_memory_vty (vty, memory_list_rip); 455 return CMD_SUCCESS; 456} 457 458DEFUN (show_memory_ripng, 459 show_memory_ripng_cmd, 460 "show memory ripng", 461 SHOW_STR 462 "Memory statistics\n" 463 "RIPng memory\n") 464{ 465 show_memory_vty (vty, memory_list_ripng); 466 return CMD_SUCCESS; 467} 468 469DEFUN (show_memory_babel, 470 show_memory_babel_cmd, 471 "show memory babel", 472 SHOW_STR 473 "Memory statistics\n" 474 "Babel memory\n") 475{ 476 show_memory_vty (vty, memory_list_babel); 477 return CMD_SUCCESS; 478} 479 480DEFUN (show_memory_bgp, 481 show_memory_bgp_cmd, 482 "show memory bgp", 483 SHOW_STR 484 "Memory statistics\n" 485 "BGP memory\n") 486{ 487 show_memory_vty (vty, memory_list_bgp); 488 return CMD_SUCCESS; 489} 490 491DEFUN (show_memory_ospf, 492 show_memory_ospf_cmd, 493 "show memory ospf", 494 SHOW_STR 495 "Memory statistics\n" 496 "OSPF memory\n") 497{ 498 show_memory_vty (vty, memory_list_ospf); 499 return CMD_SUCCESS; 500} 501 502DEFUN (show_memory_ospf6, 503 show_memory_ospf6_cmd, 504 "show memory ospf6", 505 SHOW_STR 506 "Memory statistics\n" 507 "OSPF6 memory\n") 508{ 509 show_memory_vty (vty, memory_list_ospf6); 510 return CMD_SUCCESS; 511} 512 513DEFUN (show_memory_isis, 514 show_memory_isis_cmd, 515 "show memory isis", 516 SHOW_STR 517 "Memory statistics\n" 518 "ISIS memory\n") 519{ 520 show_memory_vty (vty, memory_list_isis); 521 return CMD_SUCCESS; 522} 523 524DEFUN (show_memory_pim, 525 show_memory_pim_cmd, 526 "show memory pim", 527 SHOW_STR 528 "Memory statistics\n" 529 "PIM memory\n") 530{ 531 show_memory_vty (vty, memory_list_pim); 532 return CMD_SUCCESS; 533} 534 535void 536memory_init (void) 537{ 538 install_element (RESTRICTED_NODE, &show_memory_cmd); 539 install_element (RESTRICTED_NODE, &show_memory_all_cmd); 540 install_element (RESTRICTED_NODE, &show_memory_lib_cmd); 541 install_element (RESTRICTED_NODE, &show_memory_rip_cmd); 542 install_element (RESTRICTED_NODE, &show_memory_ripng_cmd); 543 install_element (RESTRICTED_NODE, &show_memory_babel_cmd); 544 install_element (RESTRICTED_NODE, &show_memory_bgp_cmd); 545 install_element (RESTRICTED_NODE, &show_memory_ospf_cmd); 546 install_element (RESTRICTED_NODE, &show_memory_ospf6_cmd); 547 install_element (RESTRICTED_NODE, &show_memory_isis_cmd); 548 549 install_element (VIEW_NODE, &show_memory_cmd); 550 install_element (VIEW_NODE, &show_memory_all_cmd); 551 install_element (VIEW_NODE, &show_memory_lib_cmd); 552 install_element (VIEW_NODE, &show_memory_rip_cmd); 553 install_element (VIEW_NODE, &show_memory_ripng_cmd); 554 install_element (VIEW_NODE, &show_memory_babel_cmd); 555 install_element (VIEW_NODE, &show_memory_bgp_cmd); 556 install_element (VIEW_NODE, &show_memory_ospf_cmd); 557 install_element (VIEW_NODE, &show_memory_ospf6_cmd); 558 install_element (VIEW_NODE, &show_memory_isis_cmd); 559 install_element (VIEW_NODE, &show_memory_pim_cmd); 560 561 install_element (ENABLE_NODE, &show_memory_cmd); 562 install_element (ENABLE_NODE, &show_memory_all_cmd); 563 install_element (ENABLE_NODE, &show_memory_lib_cmd); 564 install_element (ENABLE_NODE, &show_memory_zebra_cmd); 565 install_element (ENABLE_NODE, &show_memory_rip_cmd); 566 install_element (ENABLE_NODE, &show_memory_ripng_cmd); 567 install_element (ENABLE_NODE, &show_memory_babel_cmd); 568 install_element (ENABLE_NODE, &show_memory_bgp_cmd); 569 install_element (ENABLE_NODE, &show_memory_ospf_cmd); 570 install_element (ENABLE_NODE, &show_memory_ospf6_cmd); 571 install_element (ENABLE_NODE, &show_memory_isis_cmd); 572 install_element (ENABLE_NODE, &show_memory_pim_cmd); 573} 574 575/* Stats querying from users */ 576/* Return a pointer to a human friendly string describing 577 * the byte count passed in. E.g: 578 * "0 bytes", "2048 bytes", "110kB", "500MiB", "11GiB", etc. 579 * Up to 4 significant figures will be given. 580 * The pointer returned may be NULL (indicating an error) 581 * or point to the given buffer, or point to static storage. 582 */ 583const char * 584mtype_memstr (char *buf, size_t len, unsigned long bytes) 585{ 586 unsigned int t, g, m, k; 587 588 /* easy cases */ 589 if (!bytes) 590 return "0 bytes"; 591 if (bytes == 1) 592 return "1 byte"; 593 594 if (sizeof (unsigned long) >= 8) 595 /* Hacked to make it not warn on ILP32 machines 596 * Shift will always be 40 at runtime. See below too */ 597 t = bytes >> (sizeof (unsigned long) >= 8 ? 40 : 0); 598 else 599 t = 0; 600 g = bytes >> 30; 601 m = bytes >> 20; 602 k = bytes >> 10; 603 604 if (t > 10) 605 { 606 /* The shift will always be 39 at runtime. 607 * Just hacked to make it not warn on 'smaller' machines. 608 * Static compiler analysis should mean no extra code 609 */ 610 if (bytes & (1UL << (sizeof (unsigned long) >= 8 ? 39 : 0))) 611 t++; 612 snprintf (buf, len, "%4d TiB", t); 613 } 614 else if (g > 10) 615 { 616 if (bytes & (1 << 29)) 617 g++; 618 snprintf (buf, len, "%d GiB", g); 619 } 620 else if (m > 10) 621 { 622 if (bytes & (1 << 19)) 623 m++; 624 snprintf (buf, len, "%d MiB", m); 625 } 626 else if (k > 10) 627 { 628 if (bytes & (1 << 9)) 629 k++; 630 snprintf (buf, len, "%d KiB", k); 631 } 632 else 633 snprintf (buf, len, "%ld bytes", bytes); 634 635 return buf; 636} 637 638unsigned long 639mtype_stats_alloc (int type) 640{ 641 return mstat[type].alloc; 642} 643