blob: ea8ebe708799d1cabaacb86a84f7c3e641e57365 [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 *
Vadim Yanitskiy463deef2017-09-23 19:30:07 +033020 */
21
22#include <stdio.h>
23#include <regex.h>
24#include <string.h>
Vadim Yanitskiy463deef2017-09-23 19:30:07 +033025
Vadim Yanitskiybe5e8382022-04-23 02:16:07 +030026#include <osmocom/core/talloc.h>
Vadim Yanitskiy463deef2017-09-23 19:30:07 +033027#include <osmocom/vty/command.h>
28#include <osmocom/vty/vty.h>
29
30extern void *tall_vty_ctx;
31extern struct host host;
32
33enum walk_filter_type {
34 WALK_FILTER_NONE = 0,
35 WALK_FILTER_REGEXP,
36 WALK_FILTER_TREE,
37};
38
39struct walk_cb_params {
40 enum walk_filter_type filter;
41 unsigned int depth_pass;
42 const void *chunk_ptr;
43 struct vty *vty;
44 regex_t regexp;
45};
46
47/*!
48 * Print a talloc memory hierarchy to the given VTY.
49 * To be called by the talloc_report_depth_cb().
50 * If one of supported filters is specified, then
51 * only satisfying memory trees would be printed.
52 *
53 * @param chunk The talloc chunk to be printed
54 * @param depth Current depth value
55 * @param max_depth Maximal depth of report (negative means full)
56 * @param is_ref Is this chunk a reference?
57 * @param data The walk_cb_params struct instance
58 */
59static void talloc_ctx_walk_cb(const void *chunk, int depth,
60 int max_depth, int is_ref, void *data)
61{
62 struct walk_cb_params *p = (struct walk_cb_params *) data;
63 const char *chunk_name = talloc_get_name(chunk);
64 struct vty *vty = p->vty;
65 size_t chunk_blocks;
66 size_t chunk_size;
67 int rc;
68
69 if (depth > 0 && p->filter) {
70 /**
71 * A filter is being bypassed while current depth value
72 * is higher than the 'depth_pass', i.e. the callback does
73 * processing the child memory chunks. As soon as this
74 * condition becomes false, we need to 'enable' a filter,
75 * and resume the processing other chunks.
76 */
77 if (p->depth_pass && depth > p->depth_pass)
78 goto filter_bypass;
79 else
80 p->depth_pass = 0;
81
82 switch (p->filter) {
83 case WALK_FILTER_REGEXP:
84 /* Filter chunks using a regular expression */
85 rc = regexec(&p->regexp, chunk_name, 0, NULL, 0);
86 if (rc)
87 return;
88 break;
89 case WALK_FILTER_TREE:
90 /* Print a specific memory tree only */
91 if (chunk != p->chunk_ptr)
92 return;
93 break;
94 default:
95 /* Unsupported filter or incorrect value */
96 return;
97 }
98
99 /**
100 * As soon as a filter passes any chunk, all the memory
101 * tree starting from one would be printed. To do that,
102 * we need to temporary 'disable' a filter for child
103 * chunks (current_depth > depth_pass).
104 */
105 p->depth_pass = depth;
106 }
107
108filter_bypass:
109
110 if (is_ref) {
111 vty_out(vty, "%*sreference to: %s%s",
112 depth * 2, "", chunk_name, VTY_NEWLINE);
113 return;
114 }
115
116 chunk_blocks = talloc_total_blocks(chunk);
117 chunk_size = talloc_total_size(chunk);
118
119 if (depth == 0) {
120 vty_out(vty, "%stalloc report on '%s' "
121 "(total %6zu bytes in %3zu blocks)%s",
122 (max_depth < 0 ? "full " : ""), chunk_name,
123 chunk_size, chunk_blocks, VTY_NEWLINE);
124 return;
125 }
126
127 vty_out(vty, "%*s%-30s contains %6zu bytes "
128 "in %3zu blocks (ref %zu) %p%s", depth * 2, "",
129 chunk_name, chunk_size, chunk_blocks,
130 talloc_reference_count(chunk),
131 chunk, VTY_NEWLINE);
132}
133
134/*!
135 * Parse talloc context and depth values from a VTY command.
136 *
137 * @param ctx The context to be printed (a string from argv)
138 * @param depth The report depth (a string from argv)
139 * @param params The walk_cb_params struct instance
140 */
141static void talloc_ctx_walk(const char *ctx, const char *depth,
142 struct walk_cb_params *params)
143{
144 const void *talloc_ctx = NULL;
145 int max_depth;
146
147 /* Determine a context for report */
148 if (!strncmp(ctx, "app", 3))
149 talloc_ctx = host.app_info->tall_ctx;
Vadim Yanitskiye6987e92021-12-07 18:11:22 +0300150 else if (!strcmp(ctx, "global"))
151 talloc_ctx = OTC_GLOBAL;
Vadim Yanitskiy463deef2017-09-23 19:30:07 +0330152 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 \
Vadim Yanitskiye6987e92021-12-07 18:11:22 +0300168 "show talloc-context (application|global|all) (full|brief|DEPTH)"
Vadim Yanitskiy463deef2017-09-23 19:30:07 +0330169
170#define BASE_CMD_DESCR \
171 SHOW_STR "Show talloc memory hierarchy\n" \
172 "Application's context\n" \
Vadim Yanitskiye6987e92021-12-07 18:11:22 +0300173 "Global context (OTC_GLOBAL)\n" \
Vadim Yanitskiy463deef2017-09-23 19:30:07 +0330174 "All contexts, if NULL-context tracking is enabled\n" \
175 "Display a full talloc memory hierarchy\n" \
176 "Display a brief talloc memory hierarchy\n" \
Harald Welte408a9002018-02-14 01:29:19 +0100177 "Specify required maximal depth value\n"
Vadim Yanitskiy463deef2017-09-23 19:30:07 +0330178
179DEFUN(show_talloc_ctx, show_talloc_ctx_cmd,
180 BASE_CMD_STR, BASE_CMD_DESCR)
181{
Vadim Yanitskiybd6968a2019-04-06 19:16:59 +0700182 struct walk_cb_params params = { 0 };
Vadim Yanitskiy463deef2017-09-23 19:30:07 +0330183
184 /* Set up callback parameters */
Vadim Yanitskiybd6968a2019-04-06 19:16:59 +0700185 params.filter = WALK_FILTER_NONE;
186 params.vty = vty;
Vadim Yanitskiy463deef2017-09-23 19:30:07 +0330187
Vadim Yanitskiybd6968a2019-04-06 19:16:59 +0700188 talloc_ctx_walk(argv[0], argv[1], &params);
Vadim Yanitskiy463deef2017-09-23 19:30:07 +0330189 return CMD_SUCCESS;
190}
191
192DEFUN(show_talloc_ctx_filter, show_talloc_ctx_filter_cmd,
193 BASE_CMD_STR " filter REGEXP", BASE_CMD_DESCR
194 "Filter chunks using regular expression\n"
Harald Welte408a9002018-02-14 01:29:19 +0100195 "Regular expression\n")
Vadim Yanitskiy463deef2017-09-23 19:30:07 +0330196{
Vadim Yanitskiybd6968a2019-04-06 19:16:59 +0700197 struct walk_cb_params params = { 0 };
Vadim Yanitskiy463deef2017-09-23 19:30:07 +0330198 int rc;
199
Vadim Yanitskiy463deef2017-09-23 19:30:07 +0330200 /* Attempt to compile a regular expression */
Vadim Yanitskiy3d811472019-04-06 20:17:57 +0700201 rc = regcomp(&params.regexp, argv[2], REG_NOSUB);
Vadim Yanitskiy463deef2017-09-23 19:30:07 +0330202 if (rc) {
203 vty_out(vty, "Invalid expression%s", VTY_NEWLINE);
Vadim Yanitskiy463deef2017-09-23 19:30:07 +0330204 return CMD_WARNING;
205 }
206
207 /* Set up callback parameters */
Vadim Yanitskiybd6968a2019-04-06 19:16:59 +0700208 params.filter = WALK_FILTER_REGEXP;
209 params.vty = vty;
Vadim Yanitskiy463deef2017-09-23 19:30:07 +0330210
Vadim Yanitskiybd6968a2019-04-06 19:16:59 +0700211 talloc_ctx_walk(argv[0], argv[1], &params);
212 regfree(&params.regexp);
Vadim Yanitskiy463deef2017-09-23 19:30:07 +0330213
214 return CMD_SUCCESS;
215}
216
217DEFUN(show_talloc_ctx_tree, show_talloc_ctx_tree_cmd,
218 BASE_CMD_STR " tree ADDRESS", BASE_CMD_DESCR
219 "Display only a specific memory chunk\n"
Harald Welte408a9002018-02-14 01:29:19 +0100220 "Chunk address (e.g. 0xdeadbeef)\n")
Vadim Yanitskiy463deef2017-09-23 19:30:07 +0330221{
Vadim Yanitskiybd6968a2019-04-06 19:16:59 +0700222 struct walk_cb_params params = { 0 };
Vadim Yanitskiy463deef2017-09-23 19:30:07 +0330223 int rc;
224
Vadim Yanitskiy463deef2017-09-23 19:30:07 +0330225 /* Attempt to parse an address */
Vadim Yanitskiybd6968a2019-04-06 19:16:59 +0700226 rc = sscanf(argv[2], "%p", &params.chunk_ptr);
Vadim Yanitskiy463deef2017-09-23 19:30:07 +0330227 if (rc != 1) {
228 vty_out(vty, "Invalid chunk address%s", VTY_NEWLINE);
Vadim Yanitskiy463deef2017-09-23 19:30:07 +0330229 return CMD_WARNING;
230 }
231
232 /* Set up callback parameters */
Vadim Yanitskiybd6968a2019-04-06 19:16:59 +0700233 params.filter = WALK_FILTER_TREE;
234 params.vty = vty;
Vadim Yanitskiy463deef2017-09-23 19:30:07 +0330235
Vadim Yanitskiybd6968a2019-04-06 19:16:59 +0700236 talloc_ctx_walk(argv[0], argv[1], &params);
Vadim Yanitskiy463deef2017-09-23 19:30:07 +0330237 return CMD_SUCCESS;
238}
239
240/*!
241 * Install VTY commands for talloc context introspection.
242 *
243 * This installs a set of VTY commands for introspection of
244 * a talloc context. Call this once from your application
245 * if you want to support those commands.
246 */
247void osmo_talloc_vty_add_cmds(void)
248{
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +0700249 install_lib_element_ve(&show_talloc_ctx_cmd);
250 install_lib_element_ve(&show_talloc_ctx_tree_cmd);
251 install_lib_element_ve(&show_talloc_ctx_filter_cmd);
Vadim Yanitskiy463deef2017-09-23 19:30:07 +0330252}