Line data Source code
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 :
32 : static void alloc_inc (int);
33 : static void alloc_dec (int);
34 : static void log_memstats(int log_priority);
35 :
36 : static 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. */
47 : static void __attribute__ ((noreturn))
48 0 : zerror (const char *fname, int type, size_t size)
49 : {
50 0 : zlog_err ("%s : can't allocate memory for `%s' size %d: %s\n",
51 0 : fname, lookup (mstr, type), (int) size, safe_strerror(errno));
52 0 : 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 0 : zlog_backtrace(LOG_WARNING);
58 0 : 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 : */
66 : void *
67 220777 : zmalloc (int type, size_t size)
68 : {
69 : void *memory;
70 :
71 220777 : memory = malloc (size);
72 :
73 220777 : if (memory == NULL)
74 0 : zerror ("malloc", type, size);
75 :
76 220777 : alloc_inc (type);
77 :
78 220777 : return memory;
79 : }
80 :
81 : /*
82 : * Allocate memory as in zmalloc, and also clear the memory.
83 : */
84 : void *
85 238503 : zcalloc (int type, size_t size)
86 : {
87 : void *memory;
88 :
89 238503 : memory = calloc (1, size);
90 :
91 238503 : if (memory == NULL)
92 0 : zerror ("calloc", type, size);
93 :
94 238503 : alloc_inc (type);
95 :
96 238503 : 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 : */
106 : void *
107 74769 : zrealloc (int type, void *ptr, size_t size)
108 : {
109 : void *memory;
110 :
111 74769 : memory = realloc (ptr, size);
112 74769 : if (memory == NULL)
113 0 : zerror ("realloc", type, size);
114 74769 : if (ptr == NULL)
115 155 : alloc_inc (type);
116 :
117 74769 : 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 : */
126 : void
127 57111 : zfree (int type, void *ptr)
128 : {
129 57111 : if (ptr != NULL)
130 : {
131 56481 : alloc_dec (type);
132 56481 : free (ptr);
133 : }
134 57111 : }
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 : */
142 : char *
143 840 : zstrdup (int type, const char *str)
144 : {
145 : void *dup;
146 :
147 840 : dup = strdup (str);
148 840 : if (dup == NULL)
149 0 : zerror ("strdup", type, strlen (str));
150 840 : alloc_inc (type);
151 840 : return dup;
152 : }
153 :
154 : #ifdef MEMORY_LOG
155 : static 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 :
168 : static void
169 : mtype_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 :
174 : void *
175 : mtype_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 :
188 : void *
189 : mtype_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 :
202 : void *
203 : mtype_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. */
218 : void
219 : mtype_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 :
228 : char *
229 : mtype_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
242 : static struct
243 : {
244 : char *name;
245 : long alloc;
246 : } mstat [MTYPE_MAX];
247 : #endif /* MEMORY_LOG */
248 :
249 : /* Increment allocation counter. */
250 : static void
251 460275 : alloc_inc (int type)
252 : {
253 460275 : mstat[type].alloc++;
254 460275 : }
255 :
256 : /* Decrement allocation counter. */
257 : static void
258 56481 : alloc_dec (int type)
259 : {
260 56481 : mstat[type].alloc--;
261 56481 : }
262 :
263 : /* Looking up memory status from vty interface. */
264 : #include "vector.h"
265 : #include "vty.h"
266 : #include "command.h"
267 :
268 : static void
269 0 : log_memstats(int pri)
270 : {
271 : struct mlist *ml;
272 :
273 0 : for (ml = mlists; ml->list; ml++)
274 : {
275 : struct memory_list *m;
276 :
277 0 : zlog (NULL, pri, "Memory utilization in module %s:", ml->name);
278 0 : for (m = ml->list; m->index >= 0; m++)
279 0 : if (m->index && mstat[m->index].alloc)
280 0 : zlog (NULL, pri, " %-30s: %10ld", m->format, mstat[m->index].alloc);
281 : }
282 0 : }
283 :
284 : void
285 0 : log_memstats_stderr (const char *prefix)
286 : {
287 : struct mlist *ml;
288 : struct memory_list *m;
289 : int i;
290 0 : int j = 0;
291 :
292 0 : for (ml = mlists; ml->list; ml++)
293 : {
294 0 : i = 0;
295 :
296 0 : for (m = ml->list; m->index >= 0; m++)
297 0 : if (m->index && mstat[m->index].alloc)
298 : {
299 0 : if (!i)
300 0 : fprintf (stderr,
301 : "%s: memstats: Current memory utilization in module %s:\n",
302 : prefix,
303 : ml->name);
304 0 : fprintf (stderr,
305 : "%s: memstats: %-30s: %10ld%s\n",
306 : prefix,
307 : m->format,
308 0 : mstat[m->index].alloc,
309 0 : mstat[m->index].alloc < 0 ? " (REPORT THIS BUG!)" : "");
310 0 : i = j = 1;
311 : }
312 : }
313 :
314 0 : if (j)
315 0 : fprintf (stderr,
316 : "%s: memstats: NOTE: If configuration exists, utilization may be "
317 : "expected.\n",
318 : prefix);
319 : else
320 0 : fprintf (stderr,
321 : "%s: memstats: No remaining tracked memory utilization.\n",
322 : prefix);
323 0 : }
324 :
325 : static void
326 0 : show_separator(struct vty *vty)
327 : {
328 0 : vty_out (vty, "-----------------------------\r\n");
329 0 : }
330 :
331 : static int
332 0 : show_memory_vty (struct vty *vty, struct memory_list *list)
333 : {
334 : struct memory_list *m;
335 0 : int needsep = 0;
336 :
337 0 : for (m = list; m->index >= 0; m++)
338 0 : if (m->index == 0)
339 : {
340 0 : if (needsep)
341 : {
342 0 : show_separator (vty);
343 0 : needsep = 0;
344 : }
345 : }
346 0 : else if (mstat[m->index].alloc)
347 : {
348 0 : vty_out (vty, "%-30s: %10ld\r\n", m->format, mstat[m->index].alloc);
349 0 : needsep = 1;
350 : }
351 0 : return needsep;
352 : }
353 :
354 : #ifdef HAVE_MALLINFO
355 : static int
356 0 : show_memory_mallinfo (struct vty *vty)
357 : {
358 0 : struct mallinfo minfo = mallinfo();
359 : char buf[MTYPE_MEMSTR_LEN];
360 :
361 0 : vty_out (vty, "System allocator statistics:%s", VTY_NEWLINE);
362 0 : vty_out (vty, " Total heap allocated: %s%s",
363 0 : mtype_memstr (buf, MTYPE_MEMSTR_LEN, minfo.arena),
364 0 : VTY_NEWLINE);
365 0 : vty_out (vty, " Holding block headers: %s%s",
366 0 : mtype_memstr (buf, MTYPE_MEMSTR_LEN, minfo.hblkhd),
367 0 : VTY_NEWLINE);
368 0 : vty_out (vty, " Used small blocks: %s%s",
369 0 : mtype_memstr (buf, MTYPE_MEMSTR_LEN, minfo.usmblks),
370 0 : VTY_NEWLINE);
371 0 : vty_out (vty, " Used ordinary blocks: %s%s",
372 0 : mtype_memstr (buf, MTYPE_MEMSTR_LEN, minfo.uordblks),
373 0 : VTY_NEWLINE);
374 0 : vty_out (vty, " Free small blocks: %s%s",
375 0 : mtype_memstr (buf, MTYPE_MEMSTR_LEN, minfo.fsmblks),
376 0 : VTY_NEWLINE);
377 0 : vty_out (vty, " Free ordinary blocks: %s%s",
378 0 : mtype_memstr (buf, MTYPE_MEMSTR_LEN, minfo.fordblks),
379 0 : VTY_NEWLINE);
380 0 : vty_out (vty, " Ordinary blocks: %ld%s",
381 0 : (unsigned long)minfo.ordblks,
382 0 : VTY_NEWLINE);
383 0 : vty_out (vty, " Small blocks: %ld%s",
384 0 : (unsigned long)minfo.smblks,
385 0 : VTY_NEWLINE);
386 0 : vty_out (vty, " Holding blocks: %ld%s",
387 0 : (unsigned long)minfo.hblks,
388 0 : VTY_NEWLINE);
389 0 : vty_out (vty, "(see system documentation for 'mallinfo' for meaning)%s",
390 0 : VTY_NEWLINE);
391 0 : return 1;
392 : }
393 : #endif /* HAVE_MALLINFO */
394 :
395 0 : DEFUN (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 0 : int needsep = 0;
404 :
405 : #ifdef HAVE_MALLINFO
406 0 : needsep = show_memory_mallinfo (vty);
407 : #endif /* HAVE_MALLINFO */
408 :
409 0 : for (ml = mlists; ml->list; ml++)
410 : {
411 0 : if (needsep)
412 0 : show_separator (vty);
413 0 : needsep = show_memory_vty (vty, ml->list);
414 : }
415 :
416 0 : return CMD_SUCCESS;
417 : }
418 :
419 : ALIAS (show_memory_all,
420 : show_memory_cmd,
421 : "show memory",
422 : "Show running system information\n"
423 : "Memory statistics\n")
424 :
425 0 : DEFUN (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 0 : show_memory_vty (vty, memory_list_lib);
433 0 : return CMD_SUCCESS;
434 : }
435 :
436 0 : DEFUN (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 0 : show_memory_vty (vty, memory_list_zebra);
444 0 : return CMD_SUCCESS;
445 : }
446 :
447 0 : DEFUN (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 0 : show_memory_vty (vty, memory_list_rip);
455 0 : return CMD_SUCCESS;
456 : }
457 :
458 0 : DEFUN (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 0 : show_memory_vty (vty, memory_list_ripng);
466 0 : return CMD_SUCCESS;
467 : }
468 :
469 0 : DEFUN (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 0 : show_memory_vty (vty, memory_list_babel);
477 0 : return CMD_SUCCESS;
478 : }
479 :
480 0 : DEFUN (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 0 : show_memory_vty (vty, memory_list_bgp);
488 0 : return CMD_SUCCESS;
489 : }
490 :
491 0 : DEFUN (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 0 : show_memory_vty (vty, memory_list_ospf);
499 0 : return CMD_SUCCESS;
500 : }
501 :
502 0 : DEFUN (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 0 : show_memory_vty (vty, memory_list_ospf6);
510 0 : return CMD_SUCCESS;
511 : }
512 :
513 0 : DEFUN (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 0 : show_memory_vty (vty, memory_list_isis);
521 0 : return CMD_SUCCESS;
522 : }
523 :
524 : void
525 45 : memory_init (void)
526 : {
527 45 : install_element (RESTRICTED_NODE, &show_memory_cmd);
528 45 : install_element (RESTRICTED_NODE, &show_memory_all_cmd);
529 45 : install_element (RESTRICTED_NODE, &show_memory_lib_cmd);
530 45 : install_element (RESTRICTED_NODE, &show_memory_rip_cmd);
531 45 : install_element (RESTRICTED_NODE, &show_memory_ripng_cmd);
532 45 : install_element (RESTRICTED_NODE, &show_memory_babel_cmd);
533 45 : install_element (RESTRICTED_NODE, &show_memory_bgp_cmd);
534 45 : install_element (RESTRICTED_NODE, &show_memory_ospf_cmd);
535 45 : install_element (RESTRICTED_NODE, &show_memory_ospf6_cmd);
536 45 : install_element (RESTRICTED_NODE, &show_memory_isis_cmd);
537 :
538 45 : install_element (VIEW_NODE, &show_memory_cmd);
539 45 : install_element (VIEW_NODE, &show_memory_all_cmd);
540 45 : install_element (VIEW_NODE, &show_memory_lib_cmd);
541 45 : install_element (VIEW_NODE, &show_memory_rip_cmd);
542 45 : install_element (VIEW_NODE, &show_memory_ripng_cmd);
543 45 : install_element (VIEW_NODE, &show_memory_babel_cmd);
544 45 : install_element (VIEW_NODE, &show_memory_bgp_cmd);
545 45 : install_element (VIEW_NODE, &show_memory_ospf_cmd);
546 45 : install_element (VIEW_NODE, &show_memory_ospf6_cmd);
547 45 : install_element (VIEW_NODE, &show_memory_isis_cmd);
548 :
549 45 : install_element (ENABLE_NODE, &show_memory_cmd);
550 45 : install_element (ENABLE_NODE, &show_memory_all_cmd);
551 45 : install_element (ENABLE_NODE, &show_memory_lib_cmd);
552 45 : install_element (ENABLE_NODE, &show_memory_zebra_cmd);
553 45 : install_element (ENABLE_NODE, &show_memory_rip_cmd);
554 45 : install_element (ENABLE_NODE, &show_memory_ripng_cmd);
555 45 : install_element (ENABLE_NODE, &show_memory_babel_cmd);
556 45 : install_element (ENABLE_NODE, &show_memory_bgp_cmd);
557 45 : install_element (ENABLE_NODE, &show_memory_ospf_cmd);
558 45 : install_element (ENABLE_NODE, &show_memory_ospf6_cmd);
559 45 : install_element (ENABLE_NODE, &show_memory_isis_cmd);
560 45 : }
561 :
562 : /* Stats querying from users */
563 : /* Return a pointer to a human friendly string describing
564 : * the byte count passed in. E.g:
565 : * "0 bytes", "2048 bytes", "110kB", "500MiB", "11GiB", etc.
566 : * Up to 4 significant figures will be given.
567 : * The pointer returned may be NULL (indicating an error)
568 : * or point to the given buffer, or point to static storage.
569 : */
570 : const char *
571 0 : mtype_memstr (char *buf, size_t len, unsigned long bytes)
572 : {
573 : unsigned int t, g, m, k;
574 :
575 : /* easy cases */
576 0 : if (!bytes)
577 0 : return "0 bytes";
578 0 : if (bytes == 1)
579 0 : return "1 byte";
580 :
581 : if (sizeof (unsigned long) >= 8)
582 : /* Hacked to make it not warn on ILP32 machines
583 : * Shift will always be 40 at runtime. See below too */
584 0 : t = bytes >> (sizeof (unsigned long) >= 8 ? 40 : 0);
585 : else
586 : t = 0;
587 0 : g = bytes >> 30;
588 0 : m = bytes >> 20;
589 0 : k = bytes >> 10;
590 :
591 0 : if (t > 10)
592 : {
593 : /* The shift will always be 39 at runtime.
594 : * Just hacked to make it not warn on 'smaller' machines.
595 : * Static compiler analysis should mean no extra code
596 : */
597 0 : if (bytes & (1UL << (sizeof (unsigned long) >= 8 ? 39 : 0)))
598 0 : t++;
599 0 : snprintf (buf, len, "%4d TiB", t);
600 : }
601 0 : else if (g > 10)
602 : {
603 0 : if (bytes & (1 << 29))
604 0 : g++;
605 0 : snprintf (buf, len, "%d GiB", g);
606 : }
607 0 : else if (m > 10)
608 : {
609 0 : if (bytes & (1 << 19))
610 0 : m++;
611 0 : snprintf (buf, len, "%d MiB", m);
612 : }
613 0 : else if (k > 10)
614 : {
615 0 : if (bytes & (1 << 9))
616 0 : k++;
617 0 : snprintf (buf, len, "%d KiB", k);
618 : }
619 : else
620 0 : snprintf (buf, len, "%ld bytes", bytes);
621 :
622 0 : return buf;
623 : }
624 :
625 : unsigned long
626 0 : mtype_stats_alloc (int type)
627 : {
628 0 : return mstat[type].alloc;
629 : }
|