From 463deef8c209dd7eb023ac70bf41fa9893ad35ed Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Sat, 23 Sep 2017 19:30:07 +0330 Subject: VTY: implement talloc context introspection command This change introduces a new command, which could be used to inspect the application's talloc context directly from VTY. To enable this feature, an application need to provide it's context via the 'vty_app_info' struct, and register the VTY command by calling the osmo_talloc_vty_add_cmds(). The new command is a sub-command of 'show': show talloc-context [filter] Currently the following contexts may be inspected: - application - a context provided by an application; - null - all contexts, if NULL-context tracking is enabled. A report depth is defined by the next parameter, and could be: - full - full tree report, as the talloc_report_full() does; - brief - brief tree report, as the talloc_report() does; - DEPTH - user defined maximal report depth. Also, there are two optional report filters: - regexp - print only contexts, matching a regular expression; - tree - print a specific context, pointed by specified address. The command output is formatted the same way as in case of calling the talloc_report() or talloc_report_full(). Change-Id: I43fc42880b22294d83c565ae600ac65e4f38b30d --- include/osmocom/vty/misc.h | 1 + src/vty/Makefile.am | 3 +- src/vty/talloc_ctx_vty.c | 279 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 282 insertions(+), 1 deletion(-) create mode 100644 src/vty/talloc_ctx_vty.c diff --git a/include/osmocom/vty/misc.h b/include/osmocom/vty/misc.h index 545955c4..335558d8 100644 --- a/include/osmocom/vty/misc.h +++ b/include/osmocom/vty/misc.h @@ -28,6 +28,7 @@ struct osmo_fsm_inst; void vty_out_fsm(struct vty *vty, struct osmo_fsm *fsm); void vty_out_fsm_inst(struct vty *vty, struct osmo_fsm_inst *fsmi); void osmo_fsm_vty_add_cmds(void); +void osmo_talloc_vty_add_cmds(void); int osmo_vty_write_config_file(const char *filename); 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 + * + * 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 +#include +#include +#include + +#include +#include + +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); +} -- cgit v1.2.3