Memory statistics split into Effective and Overhead
This feature is intended mostly for checking that BIRD's allocation strategies don't consume much memory space. There are some cases where withdrawing routes in a specific order lead to memory fragmentation and this output should give the user at least a notion of how much memory is actually used for data storage and how much memory is "just allocated" or used for overhead. Also raising the "system allocator overhead estimation" from 8 to 16 bytes; it is probably even more. I've found 16 as a local minimum in best scenarios among reachable machines. I couldn't find any reasonable method to estimate this value when BIRD starts up. This commit also fixes the inaccurate computation of memory overhead for slabs where the "system allocater overhead estimation" was improperly added to the size of mmap-ed memory.
This commit is contained in:
parent
644e9ca94e
commit
f772afc525
5 changed files with 98 additions and 35 deletions
|
@ -45,7 +45,7 @@ struct linpool {
|
||||||
static void lp_free(resource *);
|
static void lp_free(resource *);
|
||||||
static void lp_dump(resource *);
|
static void lp_dump(resource *);
|
||||||
static resource *lp_lookup(resource *, unsigned long);
|
static resource *lp_lookup(resource *, unsigned long);
|
||||||
static size_t lp_memsize(resource *r);
|
static struct resmem lp_memsize(resource *r);
|
||||||
|
|
||||||
static struct resclass lp_class = {
|
static struct resclass lp_class = {
|
||||||
"LinPool",
|
"LinPool",
|
||||||
|
@ -287,7 +287,7 @@ lp_dump(resource *r)
|
||||||
m->total_large);
|
m->total_large);
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t
|
static struct resmem
|
||||||
lp_memsize(resource *r)
|
lp_memsize(resource *r)
|
||||||
{
|
{
|
||||||
linpool *m = (linpool *) r;
|
linpool *m = (linpool *) r;
|
||||||
|
@ -299,9 +299,11 @@ lp_memsize(resource *r)
|
||||||
for(c=m->first_large; c; c=c->next)
|
for(c=m->first_large; c; c=c->next)
|
||||||
cnt++;
|
cnt++;
|
||||||
|
|
||||||
return ALLOC_OVERHEAD + sizeof(struct linpool) +
|
return (struct resmem) {
|
||||||
cnt * (ALLOC_OVERHEAD + sizeof(struct lp_chunk)) +
|
.effective = m->total + m->total_large,
|
||||||
m->total + m->total_large;
|
.overhead = ALLOC_OVERHEAD + sizeof(struct linpool) +
|
||||||
|
cnt * (ALLOC_OVERHEAD + sizeof(struct lp_chunk)),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
* BIRD Resource Manager
|
* BIRD Resource Manager
|
||||||
*
|
*
|
||||||
* (c) 1998--2000 Martin Mares <mj@ucw.cz>
|
* (c) 1998--2000 Martin Mares <mj@ucw.cz>
|
||||||
|
* (c) 2021 Maria Matejka <mq@jmq.cz>
|
||||||
*
|
*
|
||||||
* Can be freely distributed and used under the terms of the GNU GPL.
|
* Can be freely distributed and used under the terms of the GNU GPL.
|
||||||
*/
|
*/
|
||||||
|
@ -37,7 +38,7 @@ struct pool {
|
||||||
static void pool_dump(resource *);
|
static void pool_dump(resource *);
|
||||||
static void pool_free(resource *);
|
static void pool_free(resource *);
|
||||||
static resource *pool_lookup(resource *, unsigned long);
|
static resource *pool_lookup(resource *, unsigned long);
|
||||||
static size_t pool_memsize(resource *P);
|
static struct resmem pool_memsize(resource *P);
|
||||||
|
|
||||||
static struct resclass pool_class = {
|
static struct resclass pool_class = {
|
||||||
"Pool",
|
"Pool",
|
||||||
|
@ -97,15 +98,22 @@ pool_dump(resource *P)
|
||||||
indent -= 3;
|
indent -= 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t
|
static struct resmem
|
||||||
pool_memsize(resource *P)
|
pool_memsize(resource *P)
|
||||||
{
|
{
|
||||||
pool *p = (pool *) P;
|
pool *p = (pool *) P;
|
||||||
resource *r;
|
resource *r;
|
||||||
size_t sum = sizeof(pool) + ALLOC_OVERHEAD;
|
struct resmem sum = {
|
||||||
|
.effective = 0,
|
||||||
|
.overhead = sizeof(pool) + ALLOC_OVERHEAD,
|
||||||
|
};
|
||||||
|
|
||||||
WALK_LIST(r, p->inside)
|
WALK_LIST(r, p->inside)
|
||||||
sum += rmemsize(r);
|
{
|
||||||
|
struct resmem add = rmemsize(r);
|
||||||
|
sum.effective += add.effective;
|
||||||
|
sum.overhead += add.overhead;
|
||||||
|
}
|
||||||
|
|
||||||
return sum;
|
return sum;
|
||||||
}
|
}
|
||||||
|
@ -193,14 +201,17 @@ rdump(void *res)
|
||||||
debug("NULL\n");
|
debug("NULL\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t
|
struct resmem
|
||||||
rmemsize(void *res)
|
rmemsize(void *res)
|
||||||
{
|
{
|
||||||
resource *r = res;
|
resource *r = res;
|
||||||
if (!r)
|
if (!r)
|
||||||
return 0;
|
return (struct resmem) {};
|
||||||
if (!r->class->memsize)
|
if (!r->class->memsize)
|
||||||
return r->class->size + ALLOC_OVERHEAD;
|
return (struct resmem) {
|
||||||
|
.effective = r->class->size - sizeof(resource),
|
||||||
|
.overhead = ALLOC_OVERHEAD + sizeof(resource),
|
||||||
|
};
|
||||||
return r->class->memsize(r);
|
return r->class->memsize(r);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -305,11 +316,14 @@ mbl_lookup(resource *r, unsigned long a)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t
|
static struct resmem
|
||||||
mbl_memsize(resource *r)
|
mbl_memsize(resource *r)
|
||||||
{
|
{
|
||||||
struct mblock *m = (struct mblock *) r;
|
struct mblock *m = (struct mblock *) r;
|
||||||
return ALLOC_OVERHEAD + sizeof(struct mblock) + m->size;
|
return (struct resmem) {
|
||||||
|
.effective = m->size,
|
||||||
|
.overhead = ALLOC_OVERHEAD + sizeof(struct mblock),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct resclass mb_class = {
|
static struct resclass mb_class = {
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
* BIRD Resource Manager
|
* BIRD Resource Manager
|
||||||
*
|
*
|
||||||
* (c) 1998--1999 Martin Mares <mj@ucw.cz>
|
* (c) 1998--1999 Martin Mares <mj@ucw.cz>
|
||||||
|
* (c) 2021 Maria Matejka <mq@jmq.cz>
|
||||||
*
|
*
|
||||||
* Can be freely distributed and used under the terms of the GNU GPL.
|
* Can be freely distributed and used under the terms of the GNU GPL.
|
||||||
*/
|
*/
|
||||||
|
@ -11,6 +12,11 @@
|
||||||
|
|
||||||
#include "lib/lists.h"
|
#include "lib/lists.h"
|
||||||
|
|
||||||
|
struct resmem {
|
||||||
|
size_t effective; /* Memory actually used for data storage */
|
||||||
|
size_t overhead; /* Overhead memory imposed by allocator strategies */
|
||||||
|
};
|
||||||
|
|
||||||
/* Resource */
|
/* Resource */
|
||||||
|
|
||||||
typedef struct resource {
|
typedef struct resource {
|
||||||
|
@ -26,11 +32,11 @@ struct resclass {
|
||||||
void (*free)(resource *); /* Freeing function */
|
void (*free)(resource *); /* Freeing function */
|
||||||
void (*dump)(resource *); /* Dump to debug output */
|
void (*dump)(resource *); /* Dump to debug output */
|
||||||
resource *(*lookup)(resource *, unsigned long); /* Look up address (only for debugging) */
|
resource *(*lookup)(resource *, unsigned long); /* Look up address (only for debugging) */
|
||||||
size_t (*memsize)(resource *); /* Return size of memory used by the resource, may be NULL */
|
struct resmem (*memsize)(resource *); /* Return size of memory used by the resource, may be NULL */
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Estimate of system allocator overhead per item, for memory consumtion stats */
|
/* Estimate of system allocator overhead per item, for memory consumtion stats */
|
||||||
#define ALLOC_OVERHEAD 8
|
#define ALLOC_OVERHEAD 16
|
||||||
|
|
||||||
/* Generic resource manipulation */
|
/* Generic resource manipulation */
|
||||||
|
|
||||||
|
@ -40,7 +46,7 @@ void resource_init(void);
|
||||||
pool *rp_new(pool *, const char *); /* Create new pool */
|
pool *rp_new(pool *, const char *); /* Create new pool */
|
||||||
void rfree(void *); /* Free single resource */
|
void rfree(void *); /* Free single resource */
|
||||||
void rdump(void *); /* Dump to debug output */
|
void rdump(void *); /* Dump to debug output */
|
||||||
size_t rmemsize(void *res); /* Return size of memory used by the resource */
|
struct resmem rmemsize(void *res); /* Return size of memory used by the resource */
|
||||||
void rlookup(unsigned long); /* Look up address (only for debugging) */
|
void rlookup(unsigned long); /* Look up address (only for debugging) */
|
||||||
void rmove(void *, pool *); /* Move to a different pool */
|
void rmove(void *, pool *); /* Move to a different pool */
|
||||||
|
|
||||||
|
|
33
lib/slab.c
33
lib/slab.c
|
@ -42,7 +42,7 @@
|
||||||
static void slab_free(resource *r);
|
static void slab_free(resource *r);
|
||||||
static void slab_dump(resource *r);
|
static void slab_dump(resource *r);
|
||||||
static resource *slab_lookup(resource *r, unsigned long addr);
|
static resource *slab_lookup(resource *r, unsigned long addr);
|
||||||
static size_t slab_memsize(resource *r);
|
static struct resmem slab_memsize(resource *r);
|
||||||
|
|
||||||
#ifdef FAKE_SLAB
|
#ifdef FAKE_SLAB
|
||||||
|
|
||||||
|
@ -128,7 +128,7 @@ slab_dump(resource *r)
|
||||||
debug("(%d objects per %d bytes)\n", cnt, s->size);
|
debug("(%d objects per %d bytes)\n", cnt, s->size);
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t
|
static struct resmem
|
||||||
slab_memsize(resource *r)
|
slab_memsize(resource *r)
|
||||||
{
|
{
|
||||||
slab *s = (slab *) r;
|
slab *s = (slab *) r;
|
||||||
|
@ -138,7 +138,10 @@ slab_memsize(resource *r)
|
||||||
WALK_LIST(o, s->objs)
|
WALK_LIST(o, s->objs)
|
||||||
cnt++;
|
cnt++;
|
||||||
|
|
||||||
return ALLOC_OVERHEAD + sizeof(struct slab) + cnt * (ALLOC_OVERHEAD + s->size);
|
return (struct resmem) {
|
||||||
|
.effective = cnt * s->size,
|
||||||
|
.overhead = ALLOC_OVERHEAD + sizeof(struct slab) + cnt * ALLOC_OVERHEAD,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -363,21 +366,33 @@ slab_dump(resource *r)
|
||||||
debug("(%de+%dp+%df blocks per %d objs per %d bytes)\n", ec, pc, fc, s->objs_per_slab, s->obj_size);
|
debug("(%de+%dp+%df blocks per %d objs per %d bytes)\n", ec, pc, fc, s->objs_per_slab, s->obj_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t
|
static struct resmem
|
||||||
slab_memsize(resource *r)
|
slab_memsize(resource *r)
|
||||||
{
|
{
|
||||||
slab *s = (slab *) r;
|
slab *s = (slab *) r;
|
||||||
size_t heads = 0;
|
size_t heads = 0;
|
||||||
struct sl_head *h;
|
struct sl_head *h;
|
||||||
|
|
||||||
WALK_LIST(h, s->empty_heads)
|
|
||||||
heads++;
|
|
||||||
WALK_LIST(h, s->partial_heads)
|
|
||||||
heads++;
|
|
||||||
WALK_LIST(h, s->full_heads)
|
WALK_LIST(h, s->full_heads)
|
||||||
heads++;
|
heads++;
|
||||||
|
|
||||||
return ALLOC_OVERHEAD + sizeof(struct slab) + heads * (ALLOC_OVERHEAD + get_page_size());
|
size_t items = heads * s->objs_per_slab;
|
||||||
|
|
||||||
|
WALK_LIST(h, s->partial_heads)
|
||||||
|
{
|
||||||
|
heads++;
|
||||||
|
items += h->num_full;
|
||||||
|
}
|
||||||
|
|
||||||
|
WALK_LIST(h, s->empty_heads)
|
||||||
|
heads++;
|
||||||
|
|
||||||
|
size_t eff = items * s->obj_size;
|
||||||
|
|
||||||
|
return (struct resmem) {
|
||||||
|
.effective = eff,
|
||||||
|
.overhead = ALLOC_OVERHEAD + sizeof(struct slab) + heads * get_page_size() - eff,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static resource *
|
static resource *
|
||||||
|
|
44
nest/cmds.c
44
nest/cmds.c
|
@ -67,18 +67,43 @@ cmd_show_symbols(struct sym_show_data *sd)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
#define SIZE_SUFFIX " kMGT"
|
||||||
print_size(char *dsc, size_t val)
|
#define SIZE_FORMAT "% 4u.%1u % 1cB"
|
||||||
|
#define SIZE_ARGS(a) (a).val, (a).decimal, SIZE_SUFFIX[(a).magnitude]
|
||||||
|
|
||||||
|
struct size_args {
|
||||||
|
u64 val:48;
|
||||||
|
u64 decimal:8;
|
||||||
|
u64 magnitude:8;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct size_args
|
||||||
|
get_size_args(u64 val)
|
||||||
{
|
{
|
||||||
char *px = " kMG";
|
#define VALDEC 10 /* One decimal place */
|
||||||
int i = 0;
|
val *= VALDEC;
|
||||||
while ((val >= 10000) && (i < 3))
|
|
||||||
|
uint i = 0;
|
||||||
|
while ((val >= 10000 * VALDEC) && (i < 4))
|
||||||
{
|
{
|
||||||
val = (val + 512) / 1024;
|
val = (val + 512) / 1024;
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
cli_msg(-1018, "%-17s %4u %cB", dsc, (unsigned) val, px[i]);
|
return (struct size_args) {
|
||||||
|
.val = (val / VALDEC),
|
||||||
|
.decimal = (val % VALDEC),
|
||||||
|
.magnitude = i,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
print_size(char *dsc, struct resmem vals)
|
||||||
|
{
|
||||||
|
struct size_args effective = get_size_args(vals.effective);
|
||||||
|
struct size_args overhead = get_size_args(vals.overhead);
|
||||||
|
|
||||||
|
cli_msg(-1018, "%-17s " SIZE_FORMAT " " SIZE_FORMAT, dsc, SIZE_ARGS(effective), SIZE_ARGS(overhead));
|
||||||
}
|
}
|
||||||
|
|
||||||
extern pool *rt_table_pool;
|
extern pool *rt_table_pool;
|
||||||
|
@ -88,13 +113,14 @@ void
|
||||||
cmd_show_memory(void)
|
cmd_show_memory(void)
|
||||||
{
|
{
|
||||||
cli_msg(-1018, "BIRD memory usage");
|
cli_msg(-1018, "BIRD memory usage");
|
||||||
|
cli_msg(-1018, "%-17s Effective Overhead", "");
|
||||||
print_size("Routing tables:", rmemsize(rt_table_pool));
|
print_size("Routing tables:", rmemsize(rt_table_pool));
|
||||||
print_size("Route attributes:", rmemsize(rta_pool));
|
print_size("Route attributes:", rmemsize(rta_pool));
|
||||||
print_size("Protocols:", rmemsize(proto_pool));
|
print_size("Protocols:", rmemsize(proto_pool));
|
||||||
size_t total = rmemsize(&root_pool);
|
struct resmem total = rmemsize(&root_pool);
|
||||||
#ifdef HAVE_MMAP
|
#ifdef HAVE_MMAP
|
||||||
print_size("Standby memory:", get_page_size() * pages_kept);
|
print_size("Standby memory:", (struct resmem) { .overhead = get_page_size() * pages_kept });
|
||||||
total += get_page_size() * pages_kept;
|
total.overhead += get_page_size() * pages_kept;
|
||||||
#endif
|
#endif
|
||||||
print_size("Total:", total);
|
print_size("Total:", total);
|
||||||
cli_msg(0, "");
|
cli_msg(0, "");
|
||||||
|
|
Loading…
Reference in a new issue