mirror of https://gerrit.osmocom.org/libosmocore
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. The command output is formatted the same way as in case of calling the talloc_report() or talloc_report_full(). Change-Id: I43fc42880b22294d83c565ae600ac65e4f38b30d
This commit is contained in:
parent
5584a14a31
commit
463deef8c2
|
@ -28,6 +28,7 @@ struct osmo_fsm_inst;
|
||||||
void vty_out_fsm(struct vty *vty, struct osmo_fsm *fsm);
|
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 vty_out_fsm_inst(struct vty *vty, struct osmo_fsm_inst *fsmi);
|
||||||
void osmo_fsm_vty_add_cmds(void);
|
void osmo_fsm_vty_add_cmds(void);
|
||||||
|
void osmo_talloc_vty_add_cmds(void);
|
||||||
|
|
||||||
|
|
||||||
int osmo_vty_write_config_file(const char *filename);
|
int osmo_vty_write_config_file(const char *filename);
|
||||||
|
|
|
@ -10,7 +10,8 @@ if ENABLE_VTY
|
||||||
lib_LTLIBRARIES = libosmovty.la
|
lib_LTLIBRARIES = libosmovty.la
|
||||||
|
|
||||||
libosmovty_la_SOURCES = buffer.c command.c vty.c vector.c utils.c \
|
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_LDFLAGS = -version-info $(LIBVERSION) -no-undefined $(TALLOC_LIBS)
|
||||||
libosmovty_la_LIBADD = $(top_builddir)/src/libosmocore.la
|
libosmovty_la_LIBADD = $(top_builddir)/src/libosmocore.la
|
||||||
endif
|
endif
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
Loading…
Reference in New Issue