blob: 16cb76353b59e5eef039f1075235bff8e4a22ac4 [file] [log] [blame]
Vadim Yanitskiy463deef2017-09-23 19:30:07 +03301/*! \file talloc_ctx_vty.c
2 * Osmocom talloc context introspection via VTY. */
3/*
4 * (C) 2017 by Vadim Yanitskiy <axilirator@gmail.com>
5 *
6 * All Rights Reserved
7 *
Harald Weltee08da972017-11-13 01:00:26 +09008 * SPDX-License-Identifier: GPL-2.0+
9 *
Vadim Yanitskiy463deef2017-09-23 19:30:07 +033010 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License along
21 * with this program; if not, write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 *
24 */
25
26#include <stdio.h>
27#include <regex.h>
28#include <string.h>
29#include <talloc.h>
30
31#include <osmocom/vty/command.h>
32#include <osmocom/vty/vty.h>
33
34extern void *tall_vty_ctx;
35extern struct host host;
36
37enum walk_filter_type {
38 WALK_FILTER_NONE = 0,
39 WALK_FILTER_REGEXP,
40 WALK_FILTER_TREE,
41};
42
43struct walk_cb_params {
44 enum walk_filter_type filter;
45 unsigned int depth_pass;
46 const void *chunk_ptr;
47 struct vty *vty;
48 regex_t regexp;
49};
50
51/*!
52 * Print a talloc memory hierarchy to the given VTY.
53 * To be called by the talloc_report_depth_cb().
54 * If one of supported filters is specified, then
55 * only satisfying memory trees would be printed.
56 *
57 * @param chunk The talloc chunk to be printed
58 * @param depth Current depth value
59 * @param max_depth Maximal depth of report (negative means full)
60 * @param is_ref Is this chunk a reference?
61 * @param data The walk_cb_params struct instance
62 */
63static void talloc_ctx_walk_cb(const void *chunk, int depth,
64 int max_depth, int is_ref, void *data)
65{
66 struct walk_cb_params *p = (struct walk_cb_params *) data;
67 const char *chunk_name = talloc_get_name(chunk);
68 struct vty *vty = p->vty;
69 size_t chunk_blocks;
70 size_t chunk_size;
71 int rc;
72
73 if (depth > 0 && p->filter) {
74 /**
75 * A filter is being bypassed while current depth value
76 * is higher than the 'depth_pass', i.e. the callback does
77 * processing the child memory chunks. As soon as this
78 * condition becomes false, we need to 'enable' a filter,
79 * and resume the processing other chunks.
80 */
81 if (p->depth_pass && depth > p->depth_pass)
82 goto filter_bypass;
83 else
84 p->depth_pass = 0;
85
86 switch (p->filter) {
87 case WALK_FILTER_REGEXP:
88 /* Filter chunks using a regular expression */
89 rc = regexec(&p->regexp, chunk_name, 0, NULL, 0);
90 if (rc)
91 return;
92 break;
93 case WALK_FILTER_TREE:
94 /* Print a specific memory tree only */
95 if (chunk != p->chunk_ptr)
96 return;
97 break;
98 default:
99 /* Unsupported filter or incorrect value */
100 return;
101 }
102
103 /**
104 * As soon as a filter passes any chunk, all the memory
105 * tree starting from one would be printed. To do that,
106 * we need to temporary 'disable' a filter for child
107 * chunks (current_depth > depth_pass).
108 */
109 p->depth_pass = depth;
110 }
111
112filter_bypass:
113
114 if (is_ref) {
115 vty_out(vty, "%*sreference to: %s%s",
116 depth * 2, "", chunk_name, VTY_NEWLINE);
117 return;
118 }
119
120 chunk_blocks = talloc_total_blocks(chunk);
121 chunk_size = talloc_total_size(chunk);
122
123 if (depth == 0) {
124 vty_out(vty, "%stalloc report on '%s' "
125 "(total %6zu bytes in %3zu blocks)%s",
126 (max_depth < 0 ? "full " : ""), chunk_name,
127 chunk_size, chunk_blocks, VTY_NEWLINE);
128 return;
129 }
130
131 vty_out(vty, "%*s%-30s contains %6zu bytes "
132 "in %3zu blocks (ref %zu) %p%s", depth * 2, "",
133 chunk_name, chunk_size, chunk_blocks,
134 talloc_reference_count(chunk),
135 chunk, VTY_NEWLINE);
136}
137
138/*!
139 * Parse talloc context and depth values from a VTY command.
140 *
141 * @param ctx The context to be printed (a string from argv)
142 * @param depth The report depth (a string from argv)
143 * @param params The walk_cb_params struct instance
144 */
145static void talloc_ctx_walk(const char *ctx, const char *depth,
146 struct walk_cb_params *params)
147{
148 const void *talloc_ctx = NULL;
149 int max_depth;
150
151 /* Determine a context for report */
152 if (!strncmp(ctx, "app", 3))
153 talloc_ctx = host.app_info->tall_ctx;
154 else if (!strncmp(ctx, "all", 3))
155 talloc_ctx = NULL;
156
157 /* Determine report depth */
158 if (depth[0] == 'f')
159 max_depth = -1;
160 else if (depth[0] == 'b')
161 max_depth = 1;
162 else
163 max_depth = atoi(depth);
164
165 talloc_report_depth_cb(talloc_ctx, 0, max_depth,
166 &talloc_ctx_walk_cb, params);
167}
168
169#define BASE_CMD_STR \
170 "show talloc-context (application|all) (full|brief|DEPTH)"
171
172#define BASE_CMD_DESCR \
173 SHOW_STR "Show talloc memory hierarchy\n" \
174 "Application's context\n" \
175 "All contexts, if NULL-context tracking is enabled\n" \
176 "Display a full talloc memory hierarchy\n" \
177 "Display a brief talloc memory hierarchy\n" \
Harald Welte408a9002018-02-14 01:29:19 +0100178 "Specify required maximal depth value\n"
Vadim Yanitskiy463deef2017-09-23 19:30:07 +0330179
180DEFUN(show_talloc_ctx, show_talloc_ctx_cmd,
181 BASE_CMD_STR, BASE_CMD_DESCR)
182{
Vadim Yanitskiybd6968a2019-04-06 19:16:59 +0700183 struct walk_cb_params params = { 0 };
Vadim Yanitskiy463deef2017-09-23 19:30:07 +0330184
185 /* Set up callback parameters */
Vadim Yanitskiybd6968a2019-04-06 19:16:59 +0700186 params.filter = WALK_FILTER_NONE;
187 params.vty = vty;
Vadim Yanitskiy463deef2017-09-23 19:30:07 +0330188
Vadim Yanitskiybd6968a2019-04-06 19:16:59 +0700189 talloc_ctx_walk(argv[0], argv[1], &params);
Vadim Yanitskiy463deef2017-09-23 19:30:07 +0330190 return CMD_SUCCESS;
191}
192
193DEFUN(show_talloc_ctx_filter, show_talloc_ctx_filter_cmd,
194 BASE_CMD_STR " filter REGEXP", BASE_CMD_DESCR
195 "Filter chunks using regular expression\n"
Harald Welte408a9002018-02-14 01:29:19 +0100196 "Regular expression\n")
Vadim Yanitskiy463deef2017-09-23 19:30:07 +0330197{
Vadim Yanitskiybd6968a2019-04-06 19:16:59 +0700198 struct walk_cb_params params = { 0 };
Vadim Yanitskiy463deef2017-09-23 19:30:07 +0330199 int rc;
200
Vadim Yanitskiy463deef2017-09-23 19:30:07 +0330201 /* Attempt to compile a regular expression */
Vadim Yanitskiy3d811472019-04-06 20:17:57 +0700202 rc = regcomp(&params.regexp, argv[2], REG_NOSUB);
Vadim Yanitskiy463deef2017-09-23 19:30:07 +0330203 if (rc) {
204 vty_out(vty, "Invalid expression%s", VTY_NEWLINE);
Vadim Yanitskiy463deef2017-09-23 19:30:07 +0330205 return CMD_WARNING;
206 }
207
208 /* Set up callback parameters */
Vadim Yanitskiybd6968a2019-04-06 19:16:59 +0700209 params.filter = WALK_FILTER_REGEXP;
210 params.vty = vty;
Vadim Yanitskiy463deef2017-09-23 19:30:07 +0330211
Vadim Yanitskiybd6968a2019-04-06 19:16:59 +0700212 talloc_ctx_walk(argv[0], argv[1], &params);
213 regfree(&params.regexp);
Vadim Yanitskiy463deef2017-09-23 19:30:07 +0330214
215 return CMD_SUCCESS;
216}
217
218DEFUN(show_talloc_ctx_tree, show_talloc_ctx_tree_cmd,
219 BASE_CMD_STR " tree ADDRESS", BASE_CMD_DESCR
220 "Display only a specific memory chunk\n"
Harald Welte408a9002018-02-14 01:29:19 +0100221 "Chunk address (e.g. 0xdeadbeef)\n")
Vadim Yanitskiy463deef2017-09-23 19:30:07 +0330222{
Vadim Yanitskiybd6968a2019-04-06 19:16:59 +0700223 struct walk_cb_params params = { 0 };
Vadim Yanitskiy463deef2017-09-23 19:30:07 +0330224 int rc;
225
Vadim Yanitskiy463deef2017-09-23 19:30:07 +0330226 /* Attempt to parse an address */
Vadim Yanitskiybd6968a2019-04-06 19:16:59 +0700227 rc = sscanf(argv[2], "%p", &params.chunk_ptr);
Vadim Yanitskiy463deef2017-09-23 19:30:07 +0330228 if (rc != 1) {
229 vty_out(vty, "Invalid chunk address%s", VTY_NEWLINE);
Vadim Yanitskiy463deef2017-09-23 19:30:07 +0330230 return CMD_WARNING;
231 }
232
233 /* Set up callback parameters */
Vadim Yanitskiybd6968a2019-04-06 19:16:59 +0700234 params.filter = WALK_FILTER_TREE;
235 params.vty = vty;
Vadim Yanitskiy463deef2017-09-23 19:30:07 +0330236
Vadim Yanitskiybd6968a2019-04-06 19:16:59 +0700237 talloc_ctx_walk(argv[0], argv[1], &params);
Vadim Yanitskiy463deef2017-09-23 19:30:07 +0330238 return CMD_SUCCESS;
239}
240
241/*!
242 * Install VTY commands for talloc context introspection.
243 *
244 * This installs a set of VTY commands for introspection of
245 * a talloc context. Call this once from your application
246 * if you want to support those commands.
247 */
248void osmo_talloc_vty_add_cmds(void)
249{
250 install_element_ve(&show_talloc_ctx_cmd);
251 install_element_ve(&show_talloc_ctx_tree_cmd);
252 install_element_ve(&show_talloc_ctx_filter_cmd);
253}