Review at  https://gerrit.osmocom.org/4018

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 <context> <depth> [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.

Change-Id: I43fc42880b22294d83c565ae600ac65e4f38b30d
---
M include/osmocom/vty/misc.h
M src/vty/Makefile.am
A src/vty/talloc_ctx_vty.c
3 files changed, 242 insertions(+), 1 deletion(-)


  git pull ssh://gerrit.osmocom.org:29418/libosmocore refs/changes/18/4018/1

diff --git a/include/osmocom/vty/misc.h b/include/osmocom/vty/misc.h
index 545955c..335558d 100644
--- a/include/osmocom/vty/misc.h
+++ b/include/osmocom/vty/misc.h
@@ -28,6 +28,7 @@
 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 e083a1c..1dc76c3 100644
--- a/src/vty/Makefile.am
+++ b/src/vty/Makefile.am
@@ -10,7 +10,8 @@
 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 0000000..6050ec3
--- /dev/null
+++ b/src/vty/talloc_ctx_vty.c
@@ -0,0 +1,239 @@
+/*
+ * Osmocom talloc context introspection via VTY
+ *
+ * (C) 2017 by Vadim Yanitskiy <axilira...@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;
+};
+
+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;
+       int rc;
+
+       if (depth > 0 && p->filter) {
+               if (p->depth_pass && depth > p->depth_pass)
+                       goto filter_bypass;
+               else
+                       p->depth_pass = 0;
+
+               if (p->filter == WALK_FILTER_REGEXP) {
+                       /* Filter chunks using a regular expression */
+                       rc = regexec(&p->regexp, chunk_name, 0, NULL, 0);
+                       if (rc)
+                               return;
+               } else if (p->filter == WALK_FILTER_TREE) {
+                       /* Print a specific memory tree only */
+                       if (chunk != p->chunk_ptr)
+                               return;
+               }
+
+               p->depth_pass = depth;
+       }
+
+filter_bypass:
+
+       if (is_ref) {
+               vty_out(vty, "%*sreference to: %s%s",
+                       depth * 2, "", chunk_name, VTY_NEWLINE);
+               return;
+       }
+
+       size_t chunk_blocks = talloc_total_blocks(chunk);
+       size_t 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);
+}
+
+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);
+}
+
+DEFUN(show_talloc_ctx, show_talloc_ctx_cmd,
+       "show talloc-context (application|all) (full|brief|DEPTH)",
+       SHOW_STR "Show talloc memory hierarchy\n"
+       "Application's context\nAll 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")
+{
+       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,
+       "show talloc-context (application|all) (full|brief|DEPTH) filter 
REGEXP",
+       SHOW_STR "Show talloc memory hierarchy\n"
+       "Application's context\nAll 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\n"
+       "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(&params->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(&params->regexp);
+       talloc_free(params);
+
+       return CMD_SUCCESS;
+}
+
+DEFUN(show_talloc_ctx_tree, show_talloc_ctx_tree_cmd,
+       "show talloc-context (application|all) (full|brief|DEPTH) tree ADDRESS",
+       SHOW_STR "Show talloc memory hierarchy\n"
+       "Application's context\nAll 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\n"
+       "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", &params->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;
+}
+
+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);
+}

-- 
To view, visit https://gerrit.osmocom.org/4018
To unsubscribe, visit https://gerrit.osmocom.org/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I43fc42880b22294d83c565ae600ac65e4f38b30d
Gerrit-PatchSet: 1
Gerrit-Project: libosmocore
Gerrit-Branch: master
Gerrit-Owner: Vadim Yanitskiy <axilira...@gmail.com>

Reply via email to