blob: 136a1b40485ee72f2500d56f69e8514cb10dae78 [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 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 *
22 */
23
24#include <stdio.h>
25#include <regex.h>
26#include <string.h>
27#include <talloc.h>
28
29#include <osmocom/vty/command.h>
30#include <osmocom/vty/vty.h>
31
32extern void *tall_vty_ctx;
33extern struct host host;
34
35enum walk_filter_type {
36 WALK_FILTER_NONE = 0,
37 WALK_FILTER_REGEXP,
38 WALK_FILTER_TREE,
39};
40
41struct walk_cb_params {
42 enum walk_filter_type filter;
43 unsigned int depth_pass;
44 const void *chunk_ptr;
45 struct vty *vty;
46 regex_t regexp;
47};
48
49/*!
50 * Print a talloc memory hierarchy to the given VTY.
51 * To be called by the talloc_report_depth_cb().
52 * If one of supported filters is specified, then
53 * only satisfying memory trees would be printed.
54 *
55 * @param chunk The talloc chunk to be printed
56 * @param depth Current depth value
57 * @param max_depth Maximal depth of report (negative means full)
58 * @param is_ref Is this chunk a reference?
59 * @param data The walk_cb_params struct instance
60 */
61static void talloc_ctx_walk_cb(const void *chunk, int depth,
62 int max_depth, int is_ref, void *data)
63{
64 struct walk_cb_params *p = (struct walk_cb_params *) data;
65 const char *chunk_name = talloc_get_name(chunk);
66 struct vty *vty = p->vty;
67 size_t chunk_blocks;
68 size_t chunk_size;
69 int rc;
70
71 if (depth > 0 && p->filter) {
72 /**
73 * A filter is being bypassed while current depth value
74 * is higher than the 'depth_pass', i.e. the callback does
75 * processing the child memory chunks. As soon as this
76 * condition becomes false, we need to 'enable' a filter,
77 * and resume the processing other chunks.
78 */
79 if (p->depth_pass && depth > p->depth_pass)
80 goto filter_bypass;
81 else
82 p->depth_pass = 0;
83
84 switch (p->filter) {
85 case WALK_FILTER_REGEXP:
86 /* Filter chunks using a regular expression */
87 rc = regexec(&p->regexp, chunk_name, 0, NULL, 0);
88 if (rc)
89 return;
90 break;
91 case WALK_FILTER_TREE:
92 /* Print a specific memory tree only */
93 if (chunk != p->chunk_ptr)
94 return;
95 break;
96 default:
97 /* Unsupported filter or incorrect value */
98 return;
99 }
100
101 /**
102 * As soon as a filter passes any chunk, all the memory
103 * tree starting from one would be printed. To do that,
104 * we need to temporary 'disable' a filter for child
105 * chunks (current_depth > depth_pass).
106 */
107 p->depth_pass = depth;
108 }
109
110filter_bypass:
111
112 if (is_ref) {
113 vty_out(vty, "%*sreference to: %s%s",
114 depth * 2, "", chunk_name, VTY_NEWLINE);
115 return;
116 }
117
118 chunk_blocks = talloc_total_blocks(chunk);
119 chunk_size = talloc_total_size(chunk);
120
121 if (depth == 0) {
122 vty_out(vty, "%stalloc report on '%s' "
123 "(total %6zu bytes in %3zu blocks)%s",
124 (max_depth < 0 ? "full " : ""), chunk_name,
125 chunk_size, chunk_blocks, VTY_NEWLINE);
126 return;
127 }
128
129 vty_out(vty, "%*s%-30s contains %6zu bytes "
130 "in %3zu blocks (ref %zu) %p%s", depth * 2, "",
131 chunk_name, chunk_size, chunk_blocks,
132 talloc_reference_count(chunk),
133 chunk, VTY_NEWLINE);
134}
135
136/*!
137 * Parse talloc context and depth values from a VTY command.
138 *
139 * @param ctx The context to be printed (a string from argv)
140 * @param depth The report depth (a string from argv)
141 * @param params The walk_cb_params struct instance
142 */
143static void talloc_ctx_walk(const char *ctx, const char *depth,
144 struct walk_cb_params *params)
145{
146 const void *talloc_ctx = NULL;
147 int max_depth;
148
149 /* Determine a context for report */
150 if (!strncmp(ctx, "app", 3))
151 talloc_ctx = host.app_info->tall_ctx;
152 else if (!strncmp(ctx, "all", 3))
153 talloc_ctx = NULL;
154
155 /* Determine report depth */
156 if (depth[0] == 'f')
157 max_depth = -1;
158 else if (depth[0] == 'b')
159 max_depth = 1;
160 else
161 max_depth = atoi(depth);
162
163 talloc_report_depth_cb(talloc_ctx, 0, max_depth,
164 &talloc_ctx_walk_cb, params);
165}
166
167#define BASE_CMD_STR \
168 "show talloc-context (application|all) (full|brief|DEPTH)"
169
170#define BASE_CMD_DESCR \
171 SHOW_STR "Show talloc memory hierarchy\n" \
172 "Application's context\n" \
173 "All contexts, if NULL-context tracking is enabled\n" \
174 "Display a full talloc memory hierarchy\n" \
175 "Display a brief talloc memory hierarchy\n" \
176 "Specify required maximal depth value"
177
178DEFUN(show_talloc_ctx, show_talloc_ctx_cmd,
179 BASE_CMD_STR, BASE_CMD_DESCR)
180{
181 struct walk_cb_params *params;
182
183 /* Allocate memory */
184 params = talloc_zero(tall_vty_ctx, struct walk_cb_params);
185 if (!params)
186 return CMD_WARNING;
187
188 /* Set up callback parameters */
189 params->filter = WALK_FILTER_NONE;
190 params->vty = vty;
191
192 talloc_ctx_walk(argv[0], argv[1], params);
193
194 /* Free memory */
195 talloc_free(params);
196
197 return CMD_SUCCESS;
198}
199
200DEFUN(show_talloc_ctx_filter, show_talloc_ctx_filter_cmd,
201 BASE_CMD_STR " filter REGEXP", BASE_CMD_DESCR
202 "Filter chunks using regular expression\n"
203 "Regular expression")
204{
205 struct walk_cb_params *params;
206 int rc;
207
208 /* Allocate memory */
209 params = talloc_zero(tall_vty_ctx, struct walk_cb_params);
210 if (!params)
211 return CMD_WARNING;
212
213 /* Attempt to compile a regular expression */
214 rc = regcomp(&params->regexp, argv[2], 0);
215 if (rc) {
216 vty_out(vty, "Invalid expression%s", VTY_NEWLINE);
217 talloc_free(params);
218 return CMD_WARNING;
219 }
220
221 /* Set up callback parameters */
222 params->filter = WALK_FILTER_REGEXP;
223 params->vty = vty;
224
225 talloc_ctx_walk(argv[0], argv[1], params);
226
227 /* Free memory */
228 regfree(&params->regexp);
229 talloc_free(params);
230
231 return CMD_SUCCESS;
232}
233
234DEFUN(show_talloc_ctx_tree, show_talloc_ctx_tree_cmd,
235 BASE_CMD_STR " tree ADDRESS", BASE_CMD_DESCR
236 "Display only a specific memory chunk\n"
237 "Chunk address (e.g. 0xdeadbeef)")
238{
239 struct walk_cb_params *params;
240 int rc;
241
242 /* Allocate memory */
243 params = talloc_zero(tall_vty_ctx, struct walk_cb_params);
244 if (!params)
245 return CMD_WARNING;
246
247 /* Attempt to parse an address */
248 rc = sscanf(argv[2], "%p", &params->chunk_ptr);
249 if (rc != 1) {
250 vty_out(vty, "Invalid chunk address%s", VTY_NEWLINE);
251 talloc_free(params);
252 return CMD_WARNING;
253 }
254
255 /* Set up callback parameters */
256 params->filter = WALK_FILTER_TREE;
257 params->vty = vty;
258
259 talloc_ctx_walk(argv[0], argv[1], params);
260
261 /* Free memory */
262 talloc_free(params);
263
264 return CMD_SUCCESS;
265}
266
267/*!
268 * Install VTY commands for talloc context introspection.
269 *
270 * This installs a set of VTY commands for introspection of
271 * a talloc context. Call this once from your application
272 * if you want to support those commands.
273 */
274void osmo_talloc_vty_add_cmds(void)
275{
276 install_element_ve(&show_talloc_ctx_cmd);
277 install_element_ve(&show_talloc_ctx_tree_cmd);
278 install_element_ve(&show_talloc_ctx_filter_cmd);
279}