diff options
author | Chris Hall <GMCH@hestia.halldom.com> | 2010-03-16 01:35:19 +0000 |
---|---|---|
committer | Chris Hall <GMCH@hestia.halldom.com> | 2010-03-16 01:35:19 +0000 |
commit | d87a9d74eab06082ea49313083ffa0aa41f666f9 (patch) | |
tree | 7c6f7ae0be39683b7c90ea298454ec28d49406cb /lib/mem_tracker.c | |
parent | 05fb7fd0421b395c089bb08dd0e0d78d3746b8cf (diff) | |
download | quagga-d87a9d74eab06082ea49313083ffa0aa41f666f9.tar.bz2 quagga-d87a9d74eab06082ea49313083ffa0aa41f666f9.tar.xz |
Major update
bgpd/bgp_advertise.c
bgpd/bgp_advertise.h
The adj_in and adj_out objects are now put on a list based
on the peer to whom the route belongs.
The adj_in and adj_out objects also now point to the bgp_node
which they are routes for.
This substantially reduces the work needed to shut down a peer.
bgpd/bgp_damp.c
Changes to adj_in and adj_out forced small change to macros
used in bgp_damp.c to manage its lists.
bgpd/bgp_debug.c
Replaced direct access to vty->node by the required vty_get_node().
bgpd/bgp_dump.c
Changes to the names of fields in bgp_info structures.
bgpd/bgp_engine.h
Modified the debug and trace functions.
bgpd/bgp_fsm.c
Make use of sockunion2str() consistent with common usage.
Improved some documentation.
bgpd/bgp_main.c
Use the newly extended qpn_add_hook_function() facility.
bgpd/bgp_mplsvpn.c
Changes to the names of fields in bgp_info structures.
bgpd/bgp_msg_read.c
Bug fix: correct handling of capability code length.
Improvement: better casting in calculation of message length.
bgpd/bgp_msg_write.c
Bug fix: correct byte ordering of bgp_id in open message.
bgpd/bgp_network.c
Bug fix: correct handling of incoming connections.
Takes advantage of improvements in sockunion.c.
bgpd/bgp_nexthop.c
Changes to the names of fields in bgp_info structures.
bgpd/bgp_open_state.c
Remove mistaken #include of memtypes.h
bgpd/bgp_packet.c
Improvements to handling of withdrawing routes for peers.
bgpd/bgp_peer.c
Tidying up the state of peers as they are enabled and disabled.
Improvements to handling of withdrawing routes for peers.
bgpd/bgp_peer.h
Adding list bases for lists of routes originated by the peer.
bgpd/bgp_peer_index.c
Bug fix: correct freeing of peer indexes.
bgpd/bgp_route.c
Implement lists of bgp_info based in the owning peer. Adjust
for name changes to bgp_info fields.
Reimplemented all the clearing functions to use the lists of
items that belong to the peer -- rather than searching route
tables for stuff to withdraw.
Changed work queue handling for added/changed routes, so
that queues run through existing items, rather than having
queues of auxiliary items -- lower memory overhead.
bgpd/bgp_route.h
Added fields to bgp_info to allow all bgp_info originated by
each peer to live on lists based in the peer. And changed the
name of existing fields to avoid confusion.
bgpd/bgp_routemap.c
Removing redundant code and fixing a memory leak.
bgpd/bgp_table.h
Based work queue for added/changed routes directly in the
table, rather than having auxiliary structures.
bgpd/bgp_vty.c
Use vty_get_node() and vty_set_node() rather than direct
access to the vty field.
bgpd/bgpd.c
Implement changes to route clearing.
bgpd/bgpd.h
Changes to work queue handling.
lib/buffer.c
Changes to allow embedded buffer structures.
lib/buffer.h
Moved struct buffer here so that could have embedded buffer
structurs.
lib/command.c
Substantial tidy up and document exercise.
Restructured the top level command processing and finding
of descriptions and command completion.
Removal of unpleasant messing around with the insides of
vector structures.
Movement of some command actions to vty.c.
Uses uty.h to pick up the "private" functions from vty.c
et al.
lib/command.h
Moved the "node" values to node_type.h, so that can use an
enum node_type in places where cannot include command.h.
lib/command_queue.c
Updated to cope with the called command changing the node
value.
Improved handling of revoked commands, so the the command
line handler does not get stuck waiting for a command to
complete which has been revoked !
lib/command_queue.h
Improved message format.
lib/if.c
Use vty_set_node().
lib/keychain.c
Use vty_set_node().
new lib/keystroke.c
new lib/keystroke.h
New code to implement a keystroke FIFO.
This moves some complexity out of the command handler.
The handling of mixtures of escapes and Telnet IACs is
tightened up.
It would be possible to extend this to, say, UTF-8.
Regularises the "stealing" of keystrokes for the "--more--"
output handling... which was a bit hit and miss.
new lib/list_util.c
new lib/list_util.h
New code to implement various forms of linked list,
where the list pointers are embedded in structures.
lib/log.c
Changed the handling of log messages, so that all types
of log output (except syslog) use the same message buffer
scheme, and the message is constructed once and once
only.
Changes to the handling of VTY_LOCK() etc.
Uses uty.h to pick up the "private" functions from vty.c
et al.
lib/log.h
Changes to the buffering of log messages.
new lib/mem_tracker.c
New code to track memory allocation/deallocation, for
debug purposes.
lib/memory.c
lib/memory.h
Updated to allow the use of the mem_tracker.
lib/memtypes.awk
Made the memtypes into a named enum MTYPE.
lib/memtypes.c
Various new memory types.
lib/mqueue.c
lib/mqueue.h
Add mqueue_finish function for close-down.
lib/network.c
lib/network.h
Added non-blocking read_nb() and write_nb().
new lib/node_type.h
As above.
lib/plist.c
Remove vty_puts() which wasn't a good idea.
lib/qlib_init.c
Added qps_init() to first stage and mqueue_finish to
finish.
lib/qpnexus.c
lib/qpnexus.h
More flexible hooks for in_thread_init and in_thread_final.
lib/qpselect.c
lib/qpselect.h
Added qps_start_up() to build the required maps once and
for all.
Added qdebug to control the debug checks and validation.
Improved validation and test functions.
new lib/qstring.c
new lib/qstring.h
New code for limited flexible string handling.
lib/qtimers.c
Added qdebug to control the debug checks and validation.
lib/routemap.c
Use vty_set_node().
lib/sockunion.c
lib/sockunion.h
Tidied up and regularised the handling of sin_len and
sin6_len.
Created common function for setting port into socket.
Created common function for initialisation/allocation of
new sockunion.
Reduced various functions by using common sub-functions.
Rationalised some code.
Added sockunion_listen() and sockunion_new_sockaddr().
Renamed sockunion_new() to sockunion_new_prefix().
Improved some logging messages.
Added documentation.
new lib/uty.h
Functions etc. used only by vty/command/log/vty_io and
vty_cli.
lib/vector.c
lib/vector.h
Added vector_t type.
Removed VECTOR_INDEX, vector_only_wrapper_free() and
vector_only_index_free() -- following improvement of
code in command.c.
Added vector_set_min_length(), vector_set_new_min_length()
and vector_length() functions.
new lib/vio_fifo.c
new lib/vio_fifo.h
New code to manage simple FIFO of indefinite length.
lib/vty.c
lib/vty.h
Reworked. Broken into vty.c, vty_io.c and vty_cli.c.
new lib/vty_cli.c
new lib/vty_cli.h
CLI handling parts of the vty family.
new lib/vty_io.c
new lib/vty_io.h
I/O parts of the vty family.
lib/workqueue.h
Introduced tyedefs for the various call-back entries.
new tests/test-list_util.c
Tests for the list-util stuff.
vtysh/vtysh.c
Small change to interface for cmd_execute_command()
Diffstat (limited to 'lib/mem_tracker.c')
-rw-r--r-- | lib/mem_tracker.c | 583 |
1 files changed, 583 insertions, 0 deletions
diff --git a/lib/mem_tracker.c b/lib/mem_tracker.c new file mode 100644 index 00000000..a7ee430f --- /dev/null +++ b/lib/mem_tracker.c @@ -0,0 +1,583 @@ +/* Memory Allocation Tracker + * Copyright (C) 2010 Chris Hall (GMCH), Highwayman + * + * This file is part of GNU Zebra. + * + * GNU Zebra is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * GNU Zebra is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Zebra; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include "vty.h" + +/*============================================================================== + * Memory Tracker + */ +typedef struct mem_descriptor* mem_descriptor ; +struct mem_descriptor +{ + void* addr ; + const char* name ; + + uint32_t next ; /* MS Type is encoded as MS 4 bits */ + uint32_t size ; /* LS Type is encoded as MS 4 bits */ +} ; + +typedef uint32_t md_index ; + +enum +{ + md_next_bits = 28, /* up to 256M allocated objects */ + md_next_mask = (1 << md_next_bits) - 1, + + md_index_max = md_next_mask + 1, + + md_size_bits = 28, /* up to 256M individual item */ + md_size_mask = (1 << md_size_bits) - 1, + + md_size_max = md_size_mask, + + md_next_type_bits = 32 - md_next_bits, + md_next_type_mask = (1 << md_next_type_bits) - 1, + md_size_type_bits = 32 - md_size_bits, + md_size_type_mask = (1 << md_size_type_bits) - 1, + + md_i_index_bits = 16, + md_i_index_count = 1 << md_i_index_bits, + md_i_index_mask = md_i_index_count - 1, + + md_page_bits = md_next_bits - md_i_index_bits, + md_page_count = 1 << md_page_bits, + md_page_mask = md_page_count - 1, +} ; + +CONFIRM(MTYPE_MAX < (1 << (md_next_type_bits + md_size_type_bits))) ; + +static struct mem_type_tracker +{ + struct mem_tracker mt[MTYPE_MAX] ; +} mem_type_tracker ; + +static mem_descriptor mem_page_table[md_page_count] ; + +static mem_descriptor mem_free_descriptors ; +static md_index mem_next_index ; + +static struct mem_tracker mem ; + +uint32_t mem_base_count ; + +md_index* mem_bases ; + +inline static void +mem_md_set_type(mem_descriptor md, enum MTYPE mtype) +{ + uint32_t t_ms ; + uint32_t t_ls ; + + t_ms = mtype >> md_size_type_bits ; + t_ls = mtype ; + + t_ms = (t_ms & md_next_type_mask) << md_next_bits ; + t_ls = (t_ls & md_size_type_mask) << md_size_bits ; + + md->next = (md->next & md_next_mask) | t_ms ; + md->size = (md->size & md_size_mask) | t_ls ; +} ; + +inline static void +mem_md_set_next(mem_descriptor md, md_index next) +{ + md->next = (md->next & ~md_next_mask) | (next & md_next_mask) ; +} ; + +inline static void +mem_md_set_size(mem_descriptor md, size_t size) +{ + md->size = (md->size & ~md_size_mask) | (size & md_size_mask) ; +} ; + +inline static uint8_t +mem_md_type(mem_descriptor md) +{ + return ( (md->next >> (md_next_bits - md_size_type_bits)) + & (md_next_type_mask << md_size_type_bits) ) + | ( (md->size >> md_size_bits) & md_size_type_mask ) ; +} ; + +inline static md_index +mem_md_next(mem_descriptor md) +{ + return md->next & md_next_mask ; +} ; + +inline static size_t +mem_md_size(mem_descriptor md) +{ + return md->size & md_size_mask ; +} ; + +inline static mem_descriptor +mem_md_ptr(md_index mdi) +{ + mem_descriptor page ; + + if (mdi == 0) + return NULL ; + + page = mem_page_table[(mdi >> md_i_index_bits) & md_page_mask] ; + passert(page != NULL) ; + return page + (mdi & md_i_index_mask) ; +} ; + +static void mem_md_make_bases(void) ; + +inline static md_index* +mem_md_base(void* address) +{ + if (mem_bases == NULL) + mem_md_make_bases() ; + + return mem_bases + ((uintptr_t)address % mem_base_count) ; +} ; + +static void +mem_md_make_bases(void) +{ + md_index* bases_was = mem_bases ; + uint32_t count_was = mem_base_count ; + + mem_base_count += 256 * 1024 ; + mem_base_count |= 1 ; + mem_bases = calloc(mem_base_count, sizeof(md_index)) ; + + if (bases_was == NULL) + passert(count_was == 0) ; + else + { + md_index* base = bases_was ; + md_index* new_base ; + md_index this ; + md_index next ; + mem_descriptor md ; + + while (count_was) + { + next = *base++ ; + while (next != 0) + { + this = next ; + md = mem_md_ptr(this) ; + next = mem_md_next(md) ; + + new_base = mem_md_base(md->addr) ; + mem_md_set_next(md, *new_base) ; + *new_base = this ; + } ; + --count_was ; + } ; + + free(bases_was) ; + } ; +} ; + +static void +mem_md_make_descriptors(void) +{ + mem_descriptor md ; + md_index mdi ; + + mdi = mem_next_index ; + passert(mdi < md_index_max) ; + + mem_free_descriptors + = mem_page_table[(mdi >> md_i_index_bits) & md_page_mask] + = calloc(md_i_index_count, sizeof(struct mem_descriptor)) ; + + mem_next_index += md_i_index_count ; + + if (mdi == 0) + { + ++mem_free_descriptors ; /* don't use index == 0 */ + ++mdi ; + } ; + + md = mem_free_descriptors ; + while (mdi < mem_next_index) + { + md->addr = md + 1 ; /* point at next entry */ + md->next = mdi ; /* set to point at self */ + ++md ; + ++mdi ; + } ; + (md-1)->addr = NULL ; /* set end of list */ +} ; + +inline static void +mem_md_malloc(enum MTYPE mtype, void* address, size_t size, const char* name) +{ + mem_tracker mtt ; + md_index* base ; + mem_descriptor md ; + md_index mdi ; + + passert(size <= md_size_max) ; + + if (mem_free_descriptors == NULL) + mem_md_make_descriptors() ; + + md = mem_free_descriptors ; + mem_free_descriptors = md->addr ; + mdi = md->next ; + + if (mem.tracked_count >= (mem_base_count * 4)) + mem_md_make_bases() ; + + base = mem_md_base(address) ; + + md->addr = address ; + md->name = name ; + md->size = size ; + md->next = *base ; + mem_md_set_type(md, mtype) ; + + *base = mdi ; + + ++mem.malloc_count ; + ++mem.tracked_count ; + + mem.tracked_size += size ; + + if (mem.tracked_max_count < mem.tracked_count) + mem.tracked_max_count = mem.tracked_count ; + + if (mem.tracked_max_size < mem.tracked_size) + mem.tracked_max_size = mem.tracked_size ; + + mtt = &(mem_type_tracker.mt[mtype]) ; + + ++(mtt->malloc_count) ; + ++(mtt->tracked_count) ; + mtt->tracked_size += size ; + + if (mtt->tracked_max_count < mtt->tracked_count) + mtt->tracked_max_count = mtt->tracked_count ; + + if (mtt->tracked_max_size < mtt->tracked_size) + mtt->tracked_max_size = mtt->tracked_size ; +} ; + +inline static void +mem_md_free(enum MTYPE mtype, void* address) +{ + mem_tracker mtt ; + md_index* base ; + mem_descriptor md, prev_md ; + md_index this, next ; + + base = mem_md_base(address) ; + + prev_md = NULL ; + this = *base ; + while (this != 0) + { + md = mem_md_ptr(this) ; + next = mem_md_next(md) ; + + if (md->addr == address) + { + if (mem_md_type(md) != mtype) + zabort("memory type mismatch in free") ; + + ++mem.free_count ; + --mem.tracked_count ; + + mem.tracked_size -= mem_md_size(md) ; + + mtt = &(mem_type_tracker.mt[mtype]) ; + + ++(mtt->free_count) ; + --(mtt->tracked_count) ; + mtt->tracked_size -= mem_md_size(md) ; + + if (prev_md == NULL) + *base = next ; + else + mem_md_set_next(prev_md, next) ; + + md->addr = mem_free_descriptors ; + mem_free_descriptors = md ; + md->next = this ; + + return ; + } + else + { + prev_md = md ; + this = next ; + } ; + } ; + + zabort("Failed to find memory being freed") ; +} ; + +inline static void +mem_md_realloc(enum MTYPE mtype, void* old_address, void* new_address, + size_t size, const char* name) +{ + mem_tracker mtt ; + md_index* base ; + mem_descriptor md, prev_md ; + md_index this, next ; + + if (old_address == NULL) + { + mem_md_malloc(mtype, new_address, size, name) ; + return ; + } ; + + passert(size <= md_size_max) ; + + base = mem_md_base(old_address) ; + + prev_md = NULL ; + this = *base ; + while (this != 0) + { + md = mem_md_ptr(this) ; + next = mem_md_next(md) ; + + if (md->addr == old_address) + { + if (mem_md_type(md) != mtype) + zabort("memory type mismatch in realloc") ; + + ++mem.realloc_count ; + + mem.tracked_size += size - mem_md_size(md) ; + + if (mem.tracked_max_size < mem.tracked_size) + mem.tracked_max_size = mem.tracked_size ; + + mtt = &(mem_type_tracker.mt[mtype]) ; + + ++(mtt->realloc_count) ; + mtt->tracked_size += size - mem_md_size(md) ; + + if (mtt->tracked_max_size < mtt->tracked_size) + mtt->tracked_max_size = mtt->tracked_size ; + + md->name = name ; + mem_md_set_size(md, size) ; + + if (old_address == new_address) + return ; + + if (prev_md == NULL) + *base = next ; + else + mem_md_set_next(prev_md, next) ; + + base = mem_md_base(new_address) ; + mem_md_set_next(md, *base) ; + *base = this ; + + md->addr = new_address ; + + return ; + } + else + { + prev_md = md ; + this = next ; + } ; + } ; + + zabort("Failed to find memory being realloced") ; +} ; + +/*============================================================================== + * Memory Tracker Display + */ + +static const char* scale_d_tags [] = +{ + [0] = " " , + [1] = "k", + [2] = "m", + [3] = "g", +} ; + +static const char* scale_b_tags [] = +{ + [0] = " " , + [1] = "KiB", + [2] = "MiB", + [3] = "GiB", +} ; + +static char* +mem_show_commas(char* buff, size_t size, uint64_t val, const char* tag) +{ + char* p ; + const char* q ; + int n ; + + passert(size > 10) ; + + p = buff + size ; + *(--p) = '\0' ; + + q = tag + strlen(tag) ; + while ((p > buff) && (q > tag)) + *(--p) = *(--q) ; + + n = 3 ; + while (p > buff) + { + *(--p) = '0' + (val % 10) ; + val /= 10 ; + if (val == 0) + break ; + + if ((--n == 0) && (p > buff)) + { + *(--p) = ',' ; + n = 3 ; + } ; + } ; + + return p ; +} ; + +static char* +mem_show_count(char* buff, size_t size, uint64_t val, int scale) +{ + int i, r ; + + i = 0 ; + if (scale) + { + r = 0 ; + while ((i < 3) && (val >= 10000)) + { + r = (val % 1000) ; + val /= 1000 ; + ++i ; + } ; + if (r >= 500) { + val += 1 ; + if ((val == 10000) && (i < 3)) + { + val /= 1000 ; + ++i ; + } ; + } ; + } ; + + return mem_show_commas(buff, size, val, scale_d_tags[i]) ; +} ; + +static char* +mem_show_byte_count(char* buff, size_t size, uint64_t val, int scale) +{ + int i, r ; + + i = 0 ; + if (scale) + { + r = 0 ; + while ((i < 3) && (val >= 10000)) + { + r = (val % 1024) ; + val /= 1024 ; + ++i ; + } ; + if (r >= 512) { + val += 1 ; + if ((val == 10000) && (i < 3)) + { + val /= 1024 ; + ++i ; + } ; + } ; + } ; + + return mem_show_commas(buff, size, val, scale_b_tags[i]) ; +} ; + +static int +show_memory_tracker_summary(struct vty *vty) +{ + struct mem_tracker mt ; + enum { sbs = 100 } ; + char buf[sbs]; + size_t overhead ; + + LOCK ; + overhead = (sizeof(struct mem_descriptor) * mem_next_index) + + (sizeof(md_index) * mem_base_count) + + (sizeof(mem_descriptor) * md_page_count) ; + + mt = mem ; /* copy the overall memory information */ + UNLOCK ; + + vty_out (vty, "Memory Tracker Statistics:%s", VTY_NEWLINE); + vty_out (vty, " Current memory allocated: %10s%s", + mem_show_byte_count(buf, sbs, mt.tracked_size, 1), + VTY_NEWLINE); + vty_out (vty, " Current allocated objects: %8s%s", + mem_show_count (buf, sbs, mt.tracked_count, 1), + VTY_NEWLINE); + vty_out (vty, " Maximum memory allocated: %10s%s", + mem_show_byte_count(buf, sbs, mt.tracked_max_size, 1), + VTY_NEWLINE); + vty_out (vty, " Maximum allocated objects: %8s%s", + mem_show_count (buf, sbs, mt.tracked_max_count, 1), + VTY_NEWLINE); + vty_out (vty, " malloc/calloc call count: %8s%s", + mem_show_count (buf, sbs, mt.malloc_count, 1), + VTY_NEWLINE); + vty_out (vty, " realloc_call_count: %8s%s", + mem_show_count (buf, sbs, mt.realloc_count, 1), + VTY_NEWLINE); + vty_out (vty, " free call count: %8s%s", + mem_show_count (buf, sbs, mt.free_count, 1), + VTY_NEWLINE); + vty_out (vty, " Memory Tracker overhead: %10s%s", + mem_show_byte_count(buf, sbs, overhead, 1), + VTY_NEWLINE); + return 1; +} ; + +static int +show_memory_tracker_detail(struct vty *vty, struct mem_tracker* mt, + unsigned long alloc) +{ + enum { sbs = 100 } ; + char buf[sbs]; + + vty_out(vty, "%8s", mem_show_count(buf, sbs, mt->tracked_count, 1)) ; + vty_out(vty, "%10s", mem_show_byte_count(buf, sbs, mt->tracked_size, 1)) ; + vty_out(vty, "%8s", mem_show_count(buf, sbs, mt->tracked_max_count, 1)) ; + vty_out(vty, "%10s", mem_show_byte_count(buf, sbs, mt->tracked_max_size, 1)) ; + vty_out(vty, "%8s", mem_show_count(buf, sbs, mt->malloc_count, 1)) ; + vty_out(vty, "%8s", mem_show_count(buf, sbs, mt->realloc_count, 1)) ; + vty_out(vty, "%8s", mem_show_count(buf, sbs, mt->free_count, 1)) ; + + if (alloc != mt->tracked_count) + vty_out(vty, " %8s!!", mem_show_count(buf, sbs, alloc, 1)) ; + + return 1; +} ; |