diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/vty/Makefile.am | 3 | ||||
-rw-r--r-- | src/vty/talloc_ctx_vty.c | 279 |
2 files changed, 281 insertions, 1 deletions
diff --git a/src/vty/Makefile.am b/src/vty/Makefile.am index e083a1ce..1dc76c34 100644 --- a/src/vty/Makefile.am +++ b/src/vty/Makefile.am @@ -10,7 +10,8 @@ if ENABLE_VTY lib_LTLIBRARIES = libosmovty.la libosmovty_la_SOURCES = buffer.c command.c vty.c vector.c utils.c \ - telnet_interface.c logging_vty.c stats_vty.c fsm_vty.c + telnet_interface.c logging_vty.c stats_vty.c \ + fsm_vty.c talloc_ctx_vty.c libosmovty_la_LDFLAGS = -version-info $(LIBVERSION) -no-undefined $(TALLOC_LIBS) libosmovty_la_LIBADD = $(top_builddir)/src/libosmocore.la endif diff --git a/src/vty/talloc_ctx_vty.c b/src/vty/talloc_ctx_vty.c new file mode 100644 index 00000000..136a1b40 --- /dev/null +++ b/src/vty/talloc_ctx_vty.c @@ -0,0 +1,279 @@ +/*! \file talloc_ctx_vty.c + * Osmocom talloc context introspection via VTY. */ +/* + * (C) 2017 by Vadim Yanitskiy <axilirator@gmail.com> + * + * All Rights Reserved + * + * This program 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 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdio.h> +#include <regex.h> +#include <string.h> +#include <talloc.h> + +#include <osmocom/vty/command.h> +#include <osmocom/vty/vty.h> + +extern void *tall_vty_ctx; +extern struct host host; + +enum walk_filter_type { + WALK_FILTER_NONE = 0, + WALK_FILTER_REGEXP, + WALK_FILTER_TREE, +}; + +struct walk_cb_params { + enum walk_filter_type filter; + unsigned int depth_pass; + const void *chunk_ptr; + struct vty *vty; + regex_t regexp; +}; + +/*! + * Print a talloc memory hierarchy to the given VTY. + * To be called by the talloc_report_depth_cb(). + * If one of supported filters is specified, then + * only satisfying memory trees would be printed. + * + * @param chunk The talloc chunk to be printed + * @param depth Current depth value + * @param max_depth Maximal depth of report (negative means full) + * @param is_ref Is this chunk a reference? + * @param data The walk_cb_params struct instance + */ +static void talloc_ctx_walk_cb(const void *chunk, int depth, + int max_depth, int is_ref, void *data) +{ + struct walk_cb_params *p = (struct walk_cb_params *) data; + const char *chunk_name = talloc_get_name(chunk); + struct vty *vty = p->vty; + size_t chunk_blocks; + size_t chunk_size; + int rc; + + if (depth > 0 && p->filter) { + /** + * A filter is being bypassed while current depth value + * is higher than the 'depth_pass', i.e. the callback does + * processing the child memory chunks. As soon as this + * condition becomes false, we need to 'enable' a filter, + * and resume the processing other chunks. + */ + if (p->depth_pass && depth > p->depth_pass) + goto filter_bypass; + else + p->depth_pass = 0; + + switch (p->filter) { + case WALK_FILTER_REGEXP: + /* Filter chunks using a regular expression */ + rc = regexec(&p->regexp, chunk_name, 0, NULL, 0); + if (rc) + return; + break; + case WALK_FILTER_TREE: + /* Print a specific memory tree only */ + if (chunk != p->chunk_ptr) + return; + break; + default: + /* Unsupported filter or incorrect value */ + return; + } + + /** + * As soon as a filter passes any chunk, all the memory + * tree starting from one would be printed. To do that, + * we need to temporary 'disable' a filter for child + * chunks (current_depth > depth_pass). + */ + p->depth_pass = depth; + } + +filter_bypass: + + if (is_ref) { + vty_out(vty, "%*sreference to: %s%s", + depth * 2, "", chunk_name, VTY_NEWLINE); + return; + } + + chunk_blocks = talloc_total_blocks(chunk); + chunk_size = talloc_total_size(chunk); + + if (depth == 0) { + vty_out(vty, "%stalloc report on '%s' " + "(total %6zu bytes in %3zu blocks)%s", + (max_depth < 0 ? "full " : ""), chunk_name, + chunk_size, chunk_blocks, VTY_NEWLINE); + return; + } + + vty_out(vty, "%*s%-30s contains %6zu bytes " + "in %3zu blocks (ref %zu) %p%s", depth * 2, "", + chunk_name, chunk_size, chunk_blocks, + talloc_reference_count(chunk), + chunk, VTY_NEWLINE); +} + +/*! + * Parse talloc context and depth values from a VTY command. + * + * @param ctx The context to be printed (a string from argv) + * @param depth The report depth (a string from argv) + * @param params The walk_cb_params struct instance + */ +static void talloc_ctx_walk(const char *ctx, const char *depth, + struct walk_cb_params *params) +{ + const void *talloc_ctx = NULL; + int max_depth; + + /* Determine a context for report */ + if (!strncmp(ctx, "app", 3)) + talloc_ctx = host.app_info->tall_ctx; + else if (!strncmp(ctx, "all", 3)) + talloc_ctx = NULL; + + /* Determine report depth */ + if (depth[0] == 'f') + max_depth = -1; + else if (depth[0] == 'b') + max_depth = 1; + else + max_depth = atoi(depth); + + talloc_report_depth_cb(talloc_ctx, 0, max_depth, + &talloc_ctx_walk_cb, params); +} + +#define BASE_CMD_STR \ + "show talloc-context (application|all) (full|brief|DEPTH)" + +#define BASE_CMD_DESCR \ + SHOW_STR "Show talloc memory hierarchy\n" \ + "Application's context\n" \ + "All contexts, if NULL-context tracking is enabled\n" \ + "Display a full talloc memory hierarchy\n" \ + "Display a brief talloc memory hierarchy\n" \ + "Specify required maximal depth value" + +DEFUN(show_talloc_ctx, show_talloc_ctx_cmd, + BASE_CMD_STR, BASE_CMD_DESCR) +{ + struct walk_cb_params *params; + + /* Allocate memory */ + params = talloc_zero(tall_vty_ctx, struct walk_cb_params); + if (!params) + return CMD_WARNING; + + /* Set up callback parameters */ + params->filter = WALK_FILTER_NONE; + params->vty = vty; + + talloc_ctx_walk(argv[0], argv[1], params); + + /* Free memory */ + talloc_free(params); + + return CMD_SUCCESS; +} + +DEFUN(show_talloc_ctx_filter, show_talloc_ctx_filter_cmd, + BASE_CMD_STR " filter REGEXP", BASE_CMD_DESCR + "Filter chunks using regular expression\n" + "Regular expression") +{ + struct walk_cb_params *params; + int rc; + + /* Allocate memory */ + params = talloc_zero(tall_vty_ctx, struct walk_cb_params); + if (!params) + return CMD_WARNING; + + /* Attempt to compile a regular expression */ + rc = regcomp(¶ms->regexp, argv[2], 0); + if (rc) { + vty_out(vty, "Invalid expression%s", VTY_NEWLINE); + talloc_free(params); + return CMD_WARNING; + } + + /* Set up callback parameters */ + params->filter = WALK_FILTER_REGEXP; + params->vty = vty; + + talloc_ctx_walk(argv[0], argv[1], params); + + /* Free memory */ + regfree(¶ms->regexp); + talloc_free(params); + + return CMD_SUCCESS; +} + +DEFUN(show_talloc_ctx_tree, show_talloc_ctx_tree_cmd, + BASE_CMD_STR " tree ADDRESS", BASE_CMD_DESCR + "Display only a specific memory chunk\n" + "Chunk address (e.g. 0xdeadbeef)") +{ + struct walk_cb_params *params; + int rc; + + /* Allocate memory */ + params = talloc_zero(tall_vty_ctx, struct walk_cb_params); + if (!params) + return CMD_WARNING; + + /* Attempt to parse an address */ + rc = sscanf(argv[2], "%p", ¶ms->chunk_ptr); + if (rc != 1) { + vty_out(vty, "Invalid chunk address%s", VTY_NEWLINE); + talloc_free(params); + return CMD_WARNING; + } + + /* Set up callback parameters */ + params->filter = WALK_FILTER_TREE; + params->vty = vty; + + talloc_ctx_walk(argv[0], argv[1], params); + + /* Free memory */ + talloc_free(params); + + return CMD_SUCCESS; +} + +/*! + * Install VTY commands for talloc context introspection. + * + * This installs a set of VTY commands for introspection of + * a talloc context. Call this once from your application + * if you want to support those commands. + */ +void osmo_talloc_vty_add_cmds(void) +{ + install_element_ve(&show_talloc_ctx_cmd); + install_element_ve(&show_talloc_ctx_tree_cmd); + install_element_ve(&show_talloc_ctx_filter_cmd); +} |