blob: 4198b4b0976b79e7181d86d137f806320b7d7f9b [file] [log] [blame]
Ed Warnickecb9cada2015-12-08 15:45:58 -07001/*
2 * Copyright (c) 2015 Cisco and/or its affiliates.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at:
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15/*
16 * cli.c: command line interface
17 *
18 * Copyright (c) 2008 Eliot Dresselhaus
19 *
20 * Permission is hereby granted, free of charge, to any person obtaining
21 * a copy of this software and associated documentation files (the
22 * "Software"), to deal in the Software without restriction, including
23 * without limitation the rights to use, copy, modify, merge, publish,
24 * distribute, sublicense, and/or sell copies of the Software, and to
25 * permit persons to whom the Software is furnished to do so, subject to
26 * the following conditions:
27 *
28 * The above copyright notice and this permission notice shall be
29 * included in all copies or substantial portions of the Software.
30 *
31 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
32 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
33 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
34 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
35 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
36 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
37 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
38 */
39
40#include <vlib/vlib.h>
Damjan Marion8973b072022-03-01 15:51:18 +010041#include <vlib/stats/stats.h>
Chris Luke6edd3602018-06-12 22:45:06 -040042#include <vlib/unix/unix.h>
Tom Seidenberg6c81f5a2020-07-10 15:49:03 +000043#include <vppinfra/callback.h>
Damjan Marioneccf5092016-11-18 16:59:24 +010044#include <vppinfra/cpu.h>
Dave Barachc3a06552018-10-01 09:25:32 -040045#include <vppinfra/elog.h>
Matus Fabian7561c452024-07-19 15:40:59 +020046#include <vppinfra/cJSON.h>
Damjan Marione5ef1d72017-03-02 12:33:48 +010047#include <unistd.h>
Yoann Desmouceaux3060e072017-05-18 11:00:48 +020048#include <ctype.h>
Ed Warnickecb9cada2015-12-08 15:45:58 -070049
Dave Baracha1f5a952019-11-01 11:24:43 -040050/** \file src/vlib/cli.c Debug CLI Implementation
51 */
52
Dave Barachb09f4d02019-07-15 16:00:03 -040053int vl_api_set_elog_trace_api_messages (int enable);
54int vl_api_get_elog_trace_api_messages (void);
55
Dave Barachd67a4282019-06-15 12:46:13 -040056static void *current_traced_heap;
57
Ed Warnickecb9cada2015-12-08 15:45:58 -070058/* Root of all show commands. */
59VLIB_CLI_COMMAND (vlib_cli_show_command, static) = {
60 .path = "show",
61 .short_help = "Show commands",
62};
63
64/* Root of all clear commands. */
65VLIB_CLI_COMMAND (vlib_cli_clear_command, static) = {
66 .path = "clear",
67 .short_help = "Clear commands",
68};
69
70/* Root of all set commands. */
71VLIB_CLI_COMMAND (vlib_cli_set_command, static) = {
72 .path = "set",
73 .short_help = "Set commands",
74};
75
76/* Root of all test commands. */
77VLIB_CLI_COMMAND (vlib_cli_test_command, static) = {
78 .path = "test",
79 .short_help = "Test commands",
80};
81
82/* Returns bitmap of commands which match key. */
83static uword *
84vlib_cli_sub_command_match (vlib_cli_command_t * c, unformat_input_t * input)
85{
86 int i, n;
Dave Barach9b8ffd92016-07-08 08:13:45 -040087 uword *match = 0;
88 vlib_cli_parse_position_t *p;
Ed Warnickecb9cada2015-12-08 15:45:58 -070089
90 unformat_skip_white_space (input);
91
Dave Barach9b8ffd92016-07-08 08:13:45 -040092 for (i = 0;; i++)
Ed Warnickecb9cada2015-12-08 15:45:58 -070093 {
94 uword k;
95
96 k = unformat_get_input (input);
97 switch (k)
98 {
99 case 'a' ... 'z':
100 case 'A' ... 'Z':
101 case '0' ... '9':
Dave Barach9b8ffd92016-07-08 08:13:45 -0400102 case '-':
103 case '_':
Ed Warnickecb9cada2015-12-08 15:45:58 -0700104 break;
105
Dave Barach9b8ffd92016-07-08 08:13:45 -0400106 case ' ':
107 case '\t':
108 case '\r':
109 case '\n':
Ed Warnickecb9cada2015-12-08 15:45:58 -0700110 case UNFORMAT_END_OF_INPUT:
111 /* White space or end of input removes any non-white
112 matches that were before possible. */
113 if (i < vec_len (c->sub_command_positions)
114 && clib_bitmap_count_set_bits (match) > 1)
115 {
116 p = vec_elt_at_index (c->sub_command_positions, i);
117 for (n = 0; n < vec_len (p->bitmaps); n++)
118 match = clib_bitmap_andnot (match, p->bitmaps[n]);
119 }
120 goto done;
121
122 default:
123 unformat_put_input (input);
124 goto done;
125 }
126
127 if (i >= vec_len (c->sub_command_positions))
128 {
129 no_match:
130 clib_bitmap_free (match);
131 return 0;
132 }
133
134 p = vec_elt_at_index (c->sub_command_positions, i);
135 if (vec_len (p->bitmaps) == 0)
136 goto no_match;
137
138 n = k - p->min_char;
139 if (n < 0 || n >= vec_len (p->bitmaps))
140 goto no_match;
141
142 if (i == 0)
143 match = clib_bitmap_dup (p->bitmaps[n]);
144 else
145 match = clib_bitmap_and (match, p->bitmaps[n]);
146
147 if (clib_bitmap_is_zero (match))
148 goto no_match;
149 }
150
Dave Barach9b8ffd92016-07-08 08:13:45 -0400151done:
Ed Warnickecb9cada2015-12-08 15:45:58 -0700152 return match;
153}
154
Damjan Marionc50bcbd2022-05-08 20:48:37 +0200155uword
156unformat_vlib_cli_line (unformat_input_t *i, va_list *va)
157{
158 unformat_input_t *result = va_arg (*va, unformat_input_t *);
159 u8 *line = 0;
160 uword c;
161 int skip;
162
163next_line:
164 skip = 0;
165
166 /* skip leading whitespace if any */
167 unformat_skip_white_space (i);
168
169 if (unformat_is_eof (i))
170 return 0;
171
172 while ((c = unformat_get_input (i)) != UNFORMAT_END_OF_INPUT)
173 {
174 if (c == '\\')
175 {
176 c = unformat_get_input (i);
177
178 if (c == '\n')
179 {
180 if (!skip)
181 vec_add1 (line, '\n');
182 skip = 0;
183 continue;
184 }
185
186 if (!skip)
187 vec_add1 (line, '\\');
188
189 if (c == UNFORMAT_END_OF_INPUT)
190 break;
191
192 if (!skip)
193 vec_add1 (line, c);
194 continue;
195 }
196
197 if (c == '#')
198 skip = 1;
199 else if (c == '\n')
200 break;
201
202 if (!skip)
203 vec_add1 (line, c);
204 }
205
206 if (line == 0)
207 goto next_line;
208
209 unformat_init_vector (result, line);
210 return 1;
211}
212
Ed Warnickecb9cada2015-12-08 15:45:58 -0700213/* Looks for string based sub-input formatted { SUB-INPUT }. */
Dave Barach9b8ffd92016-07-08 08:13:45 -0400214uword
215unformat_vlib_cli_sub_input (unformat_input_t * i, va_list * args)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700216{
Dave Barach9b8ffd92016-07-08 08:13:45 -0400217 unformat_input_t *sub_input = va_arg (*args, unformat_input_t *);
218 u8 *s;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700219 uword c;
220
221 while (1)
222 {
223 c = unformat_get_input (i);
224 switch (c)
225 {
Dave Barach9b8ffd92016-07-08 08:13:45 -0400226 case ' ':
227 case '\t':
228 case '\n':
229 case '\r':
Ed Warnickecb9cada2015-12-08 15:45:58 -0700230 case '\f':
231 break;
232
233 case '{':
234 default:
235 /* Put back paren. */
236 if (c != UNFORMAT_END_OF_INPUT)
237 unformat_put_input (i);
238
239 if (c == '{' && unformat (i, "%v", &s))
240 {
241 unformat_init_vector (sub_input, s);
242 return 1;
243 }
244 return 0;
245 }
246 }
247 return 0;
248}
249
250static vlib_cli_command_t *
251get_sub_command (vlib_cli_main_t * cm, vlib_cli_command_t * parent, u32 si)
252{
Dave Barach9b8ffd92016-07-08 08:13:45 -0400253 vlib_cli_sub_command_t *s = vec_elt_at_index (parent->sub_commands, si);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700254 return vec_elt_at_index (cm->commands, s->index);
255}
256
Dave Barach9b8ffd92016-07-08 08:13:45 -0400257static uword
258unformat_vlib_cli_sub_command (unformat_input_t * i, va_list * args)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700259{
Damjan Marionfd8deb42021-03-06 12:26:28 +0100260 vlib_main_t __clib_unused *vm = va_arg (*args, vlib_main_t *);
261 vlib_global_main_t *vgm = vlib_get_global_main ();
Dave Barach9b8ffd92016-07-08 08:13:45 -0400262 vlib_cli_command_t *c = va_arg (*args, vlib_cli_command_t *);
263 vlib_cli_command_t **result = va_arg (*args, vlib_cli_command_t **);
Damjan Marionfd8deb42021-03-06 12:26:28 +0100264 vlib_cli_main_t *cm = &vgm->cli_main;
Dave Barach9b8ffd92016-07-08 08:13:45 -0400265 uword *match_bitmap, is_unique, index;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700266
Ed Warnickecb9cada2015-12-08 15:45:58 -0700267 match_bitmap = vlib_cli_sub_command_match (c, i);
268 is_unique = clib_bitmap_count_set_bits (match_bitmap) == 1;
269 index = ~0;
270 if (is_unique)
271 {
272 index = clib_bitmap_first_set (match_bitmap);
273 *result = get_sub_command (cm, c, index);
274 }
275 clib_bitmap_free (match_bitmap);
276
277 return is_unique;
278}
279
Yoann Desmouceaux3060e072017-05-18 11:00:48 +0200280static int
281vlib_cli_cmp_strings (void *a1, void *a2)
282{
283 u8 *c1 = *(u8 **) a1;
284 u8 *c2 = *(u8 **) a2;
285
286 return vec_cmp (c1, c2);
287}
288
289u8 **
290vlib_cli_get_possible_completions (u8 * str)
291{
292 vlib_cli_command_t *c;
293 vlib_cli_sub_command_t *sc;
Damjan Marionfd8deb42021-03-06 12:26:28 +0100294 vlib_global_main_t *vgm = vlib_get_global_main ();
295 vlib_cli_main_t *vcm = &vgm->cli_main;
Yoann Desmouceaux3060e072017-05-18 11:00:48 +0200296 uword *match_bitmap = 0;
297 uword index, is_unique, help_next_level;
298 u8 **result = 0;
299 unformat_input_t input;
300 unformat_init_vector (&input, vec_dup (str));
301 c = vec_elt_at_index (vcm->commands, 0);
302
303 /* remove trailing whitespace, except for one of them */
304 while (vec_len (input.buffer) >= 2 &&
305 isspace (input.buffer[vec_len (input.buffer) - 1]) &&
306 isspace (input.buffer[vec_len (input.buffer) - 2]))
307 {
308 vec_del1 (input.buffer, vec_len (input.buffer) - 1);
309 }
310
311 /* if input is empty, directly return list of root commands */
312 if (vec_len (input.buffer) == 0 ||
313 (vec_len (input.buffer) == 1 && isspace (input.buffer[0])))
314 {
315 vec_foreach (sc, c->sub_commands)
316 {
317 vec_add1 (result, (u8 *) sc->name);
318 }
319 goto done;
320 }
321
322 /* add a trailing '?' so that vlib_cli_sub_command_match can find
323 * all commands starting with the input string */
324 vec_add1 (input.buffer, '?');
325
326 while (1)
327 {
328 match_bitmap = vlib_cli_sub_command_match (c, &input);
329 /* no match: return no result */
330 if (match_bitmap == 0)
331 {
332 goto done;
333 }
334 is_unique = clib_bitmap_count_set_bits (match_bitmap) == 1;
335 /* unique match: try to step one subcommand level further */
336 if (is_unique)
337 {
338 /* stop if no more input */
339 if (input.index >= vec_len (input.buffer) - 1)
340 {
341 break;
342 }
343
344 index = clib_bitmap_first_set (match_bitmap);
345 c = get_sub_command (vcm, c, index);
346 clib_bitmap_free (match_bitmap);
347 continue;
348 }
349 /* multiple matches: stop here, return all matches */
350 break;
351 }
352
353 /* remove trailing '?' */
354 vec_del1 (input.buffer, vec_len (input.buffer) - 1);
355
356 /* if we have a space at the end of input, and a unique match,
357 * autocomplete the next level of subcommands */
358 help_next_level = (vec_len (str) == 0) || isspace (str[vec_len (str) - 1]);
Damjan Marionf0ca1e82020-12-13 23:26:56 +0100359 clib_bitmap_foreach (index, match_bitmap) {
Yoann Desmouceaux3060e072017-05-18 11:00:48 +0200360 if (help_next_level && is_unique) {
361 c = get_sub_command (vcm, c, index);
362 vec_foreach (sc, c->sub_commands) {
363 vec_add1 (result, (u8*) sc->name);
364 }
365 goto done; /* break doesn't work in this macro-loop */
366 }
367 sc = &c->sub_commands[index];
368 vec_add1(result, (u8*) sc->name);
Damjan Marionf0ca1e82020-12-13 23:26:56 +0100369 }
Yoann Desmouceaux3060e072017-05-18 11:00:48 +0200370
371done:
372 clib_bitmap_free (match_bitmap);
373 unformat_free (&input);
374
Yoann Desmouceaux4227eef2017-05-24 15:51:48 +0200375 if (result)
376 vec_sort_with_function (result, vlib_cli_cmp_strings);
Yoann Desmouceaux3060e072017-05-18 11:00:48 +0200377 return result;
378}
379
Dave Barach9b8ffd92016-07-08 08:13:45 -0400380static u8 *
381format_vlib_cli_command_help (u8 * s, va_list * args)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700382{
Dave Barach9b8ffd92016-07-08 08:13:45 -0400383 vlib_cli_command_t *c = va_arg (*args, vlib_cli_command_t *);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700384 int is_long = va_arg (*args, int);
385 if (is_long && c->long_help)
386 s = format (s, "%s", c->long_help);
387 else if (c->short_help)
388 s = format (s, "%s", c->short_help);
389 else
390 s = format (s, "%v commands", c->path);
391 return s;
392}
393
Dave Barach9b8ffd92016-07-08 08:13:45 -0400394static u8 *
Dave Barach9b8ffd92016-07-08 08:13:45 -0400395format_vlib_cli_path (u8 * s, va_list * args)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700396{
Dave Barach9b8ffd92016-07-08 08:13:45 -0400397 u8 *path = va_arg (*args, u8 *);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700398
Dave Barach6b3f25c2019-12-09 10:45:47 -0500399 s = format (s, "%v", path);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700400
401 return s;
402}
403
404static vlib_cli_command_t *
Dave Barach9b8ffd92016-07-08 08:13:45 -0400405all_subs (vlib_cli_main_t * cm, vlib_cli_command_t * subs, u32 command_index)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700406{
Dave Barach9b8ffd92016-07-08 08:13:45 -0400407 vlib_cli_command_t *c = vec_elt_at_index (cm->commands, command_index);
408 vlib_cli_sub_command_t *sc;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700409
410 if (c->function)
411 vec_add1 (subs, c[0]);
412
Dave Barach9b8ffd92016-07-08 08:13:45 -0400413 vec_foreach (sc, c->sub_commands) subs = all_subs (cm, subs, sc->index);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700414
415 return subs;
416}
417
Matus Fabiand2dc3df2015-12-14 10:31:33 -0500418static int
Dave Barach9b8ffd92016-07-08 08:13:45 -0400419vlib_cli_cmp_rule (void *a1, void *a2)
Matus Fabiand2dc3df2015-12-14 10:31:33 -0500420{
Dave Barach9b8ffd92016-07-08 08:13:45 -0400421 vlib_cli_sub_rule_t *r1 = a1;
422 vlib_cli_sub_rule_t *r2 = a2;
Matus Fabiand2dc3df2015-12-14 10:31:33 -0500423
424 return vec_cmp (r1->name, r2->name);
425}
426
427static int
Dave Barach9b8ffd92016-07-08 08:13:45 -0400428vlib_cli_cmp_command (void *a1, void *a2)
Matus Fabiand2dc3df2015-12-14 10:31:33 -0500429{
Dave Barach9b8ffd92016-07-08 08:13:45 -0400430 vlib_cli_command_t *c1 = a1;
431 vlib_cli_command_t *c2 = a2;
Matus Fabiand2dc3df2015-12-14 10:31:33 -0500432
433 return vec_cmp (c1->path, c2->path);
434}
435
Ed Warnickecb9cada2015-12-08 15:45:58 -0700436static clib_error_t *
437vlib_cli_dispatch_sub_commands (vlib_main_t * vm,
438 vlib_cli_main_t * cm,
439 unformat_input_t * input,
440 uword parent_command_index)
441{
Damjan Marionfd8deb42021-03-06 12:26:28 +0100442 vlib_global_main_t *vgm = vlib_get_global_main ();
Dave Barach9b8ffd92016-07-08 08:13:45 -0400443 vlib_cli_command_t *parent, *c;
444 clib_error_t *error = 0;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700445 unformat_input_t sub_input;
Dave Barach9b8ffd92016-07-08 08:13:45 -0400446 u8 *string;
Damjan Marionfd8deb42021-03-06 12:26:28 +0100447 uword is_main_dispatch = cm == &vgm->cli_main;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700448
449 parent = vec_elt_at_index (cm->commands, parent_command_index);
450 if (is_main_dispatch && unformat (input, "help"))
451 {
452 uword help_at_end_of_line, i;
453
Dave Barach9b8ffd92016-07-08 08:13:45 -0400454 help_at_end_of_line =
455 unformat_check_input (input) == UNFORMAT_END_OF_INPUT;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700456 while (1)
Dave Barach9b8ffd92016-07-08 08:13:45 -0400457 {
Ed Warnickecb9cada2015-12-08 15:45:58 -0700458 c = parent;
Dave Barach9b8ffd92016-07-08 08:13:45 -0400459 if (unformat_user
460 (input, unformat_vlib_cli_sub_command, vm, c, &parent))
Ed Warnickecb9cada2015-12-08 15:45:58 -0700461 ;
462
Dave Barach9b8ffd92016-07-08 08:13:45 -0400463 else if (!(unformat_check_input (input) == UNFORMAT_END_OF_INPUT))
Ed Warnickecb9cada2015-12-08 15:45:58 -0700464 goto unknown;
465
466 else
467 break;
Dave Barach9b8ffd92016-07-08 08:13:45 -0400468 }
469
Ed Warnickecb9cada2015-12-08 15:45:58 -0700470 /* help SUB-COMMAND => long format help.
Dave Barach9b8ffd92016-07-08 08:13:45 -0400471 "help" at end of line: show all commands. */
472 if (!help_at_end_of_line)
473 vlib_cli_output (vm, "%U", format_vlib_cli_command_help, c,
474 /* is_long */ 1);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700475
Dave Barach6b3f25c2019-12-09 10:45:47 -0500476 else if (vec_len (c->sub_commands) == 0)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700477 vlib_cli_output (vm, "%v: no sub-commands", c->path);
478
479 else
480 {
Dave Barach6b3f25c2019-12-09 10:45:47 -0500481 vlib_cli_sub_rule_t *sr, *subs = 0;
Dave Barach6d5df8d2019-12-11 09:46:56 -0500482 vlib_cli_sub_command_t *sc;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700483
Dave Barach6d5df8d2019-12-11 09:46:56 -0500484 vec_foreach (sc, c->sub_commands)
485 {
486 vec_add2 (subs, sr, 1);
487 sr->name = sc->name;
488 sr->command_index = sc->index;
489 sr->rule_index = ~0;
490 }
Ed Warnickecb9cada2015-12-08 15:45:58 -0700491
Matus Fabiand2dc3df2015-12-14 10:31:33 -0500492 vec_sort_with_function (subs, vlib_cli_cmp_rule);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700493
Dave Barach9b8ffd92016-07-08 08:13:45 -0400494 for (i = 0; i < vec_len (subs); i++)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700495 {
Dave Barach9b8ffd92016-07-08 08:13:45 -0400496 vlib_cli_command_t *d;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700497
498 d = vec_elt_at_index (cm->commands, subs[i].command_index);
Dave Barach6b3f25c2019-12-09 10:45:47 -0500499 vlib_cli_output
500 (vm, " %-30v %U", subs[i].name,
501 format_vlib_cli_command_help, d, /* is_long */ 0);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700502 }
503
504 vec_free (subs);
505 }
506 }
Dave Barach9b8ffd92016-07-08 08:13:45 -0400507
508 else if (is_main_dispatch
509 && (unformat (input, "choices") || unformat (input, "?")))
Ed Warnickecb9cada2015-12-08 15:45:58 -0700510 {
Dave Barach9b8ffd92016-07-08 08:13:45 -0400511 vlib_cli_command_t *sub, *subs;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700512
513 subs = all_subs (cm, 0, parent_command_index);
Matus Fabiand2dc3df2015-12-14 10:31:33 -0500514 vec_sort_with_function (subs, vlib_cli_cmp_command);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700515 vec_foreach (sub, subs)
516 vlib_cli_output (vm, " %-40U %U",
517 format_vlib_cli_path, sub->path,
518 format_vlib_cli_command_help, sub, /* is_long */ 0);
519 vec_free (subs);
520 }
521
522 else if (unformat (input, "comment %v", &string))
523 {
524 vec_free (string);
525 }
Dave Barach9b8ffd92016-07-08 08:13:45 -0400526
Dave Barach3d0e0d12021-02-17 10:25:18 -0500527 else if (unformat (input, "vpplog %v", &string))
528 {
529 int i;
530 /*
531 * Delete leading whitespace, so "vpplog { this and that }"
532 * and "vpplog this" line up nicely.
533 */
534 for (i = 0; i < vec_len (string); i++)
535 if (string[i] != ' ')
536 break;
537 if (i > 0)
538 vec_delete (string, i, 0);
539
540 vlib_log_notice (cm->log, "CLI: %v", string);
541 vec_free (string);
542 }
543
Ed Warnickecb9cada2015-12-08 15:45:58 -0700544 else if (unformat (input, "uncomment %U",
545 unformat_vlib_cli_sub_input, &sub_input))
546 {
Dave Barach9b8ffd92016-07-08 08:13:45 -0400547 error =
548 vlib_cli_dispatch_sub_commands (vm, cm, &sub_input,
549 parent_command_index);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700550 unformat_free (&sub_input);
551 }
Dave Barach8fdde3c2019-05-17 10:46:40 -0400552 else if (unformat (input, "leak-check %U",
553 unformat_vlib_cli_sub_input, &sub_input))
554 {
555 u8 *leak_report;
Dave Barachd67a4282019-06-15 12:46:13 -0400556 if (current_traced_heap)
557 {
558 void *oldheap;
559 oldheap = clib_mem_set_heap (current_traced_heap);
560 clib_mem_trace (0);
561 clib_mem_set_heap (oldheap);
562 current_traced_heap = 0;
563 }
Dave Barach8fdde3c2019-05-17 10:46:40 -0400564 clib_mem_trace (1);
565 error =
566 vlib_cli_dispatch_sub_commands (vm, cm, &sub_input,
567 parent_command_index);
568 unformat_free (&sub_input);
569
570 /* Otherwise, the clib_error_t shows up as a leak... */
571 if (error)
572 {
573 vlib_cli_output (vm, "%v", error->what);
574 clib_error_free (error);
575 error = 0;
576 }
577
578 (void) clib_mem_trace_enable_disable (0);
Damjan Marion4537c302020-09-28 19:03:37 +0200579 leak_report = format (0, "%U", format_clib_mem_heap, 0,
Dave Barach8fdde3c2019-05-17 10:46:40 -0400580 1 /* verbose, i.e. print leaks */ );
581 clib_mem_trace (0);
582 vlib_cli_output (vm, "%v", leak_report);
583 vec_free (leak_report);
584 }
Dave Barach9b8ffd92016-07-08 08:13:45 -0400585
586 else
587 if (unformat_user (input, unformat_vlib_cli_sub_command, vm, parent, &c))
Ed Warnickecb9cada2015-12-08 15:45:58 -0700588 {
Dave Barach9b8ffd92016-07-08 08:13:45 -0400589 unformat_input_t *si;
590 uword has_sub_commands =
591 vec_len (c->sub_commands) + vec_len (c->sub_rules) > 0;
592
Ed Warnickecb9cada2015-12-08 15:45:58 -0700593 si = input;
594 if (unformat_user (input, unformat_vlib_cli_sub_input, &sub_input))
595 si = &sub_input;
Dave Barach9b8ffd92016-07-08 08:13:45 -0400596
Ed Warnickecb9cada2015-12-08 15:45:58 -0700597 if (has_sub_commands)
598 error = vlib_cli_dispatch_sub_commands (vm, cm, si, c - cm->commands);
599
Dave Barach9b8ffd92016-07-08 08:13:45 -0400600 if (has_sub_commands && !error)
601 /* Found valid sub-command. */ ;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700602
603 else if (c->function)
604 {
Dave Barach9b8ffd92016-07-08 08:13:45 -0400605 clib_error_t *c_error;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700606
607 /* Skip white space for benefit of called function. */
608 unformat_skip_white_space (si);
609
610 if (unformat (si, "?"))
611 {
Dave Barach9b8ffd92016-07-08 08:13:45 -0400612 vlib_cli_output (vm, " %-40U %U", format_vlib_cli_path, c->path, format_vlib_cli_command_help, c, /* is_long */
613 0);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700614 }
615 else
616 {
Dave Barachc3a06552018-10-01 09:25:32 -0400617 if (PREDICT_FALSE (vm->elog_trace_cli_commands))
618 {
Dave Barachc3a06552018-10-01 09:25:32 -0400619 ELOG_TYPE_DECLARE (e) =
620 {
621 .format = "cli-cmd: %s",
622 .format_args = "T4",
623 };
Dave Barachc3a06552018-10-01 09:25:32 -0400624 struct
625 {
626 u32 c;
627 } *ed;
Damjan Marionf553a2c2021-03-26 13:45:37 +0100628 ed = ELOG_DATA (vlib_get_elog_main (), e);
629 ed->c = elog_string (vlib_get_elog_main (), "%v", c->path);
Dave Barachc3a06552018-10-01 09:25:32 -0400630 }
631
Dave Barach9b8ffd92016-07-08 08:13:45 -0400632 if (!c->is_mp_safe)
633 vlib_worker_thread_barrier_sync (vm);
Tom Seidenberg6c81f5a2020-07-10 15:49:03 +0000634 if (PREDICT_FALSE (vec_len (cm->perf_counter_cbs) != 0))
635 clib_call_callbacks (cm->perf_counter_cbs, cm,
636 c - cm->commands, 0 /* before */ );
Ed Warnickecb9cada2015-12-08 15:45:58 -0700637
Dave Baracha1f5a952019-11-01 11:24:43 -0400638 c->hit_counter++;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700639 c_error = c->function (vm, si, c);
640
Tom Seidenberg6c81f5a2020-07-10 15:49:03 +0000641 if (PREDICT_FALSE (vec_len (cm->perf_counter_cbs) != 0))
642 clib_call_callbacks (cm->perf_counter_cbs, cm,
643 c - cm->commands, 1 /* after */ );
Dave Barach9b8ffd92016-07-08 08:13:45 -0400644 if (!c->is_mp_safe)
645 vlib_worker_thread_barrier_release (vm);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700646
Dave Barachc3a06552018-10-01 09:25:32 -0400647 if (PREDICT_FALSE (vm->elog_trace_cli_commands))
648 {
Dave Barachc3a06552018-10-01 09:25:32 -0400649 ELOG_TYPE_DECLARE (e) =
650 {
651 .format = "cli-cmd: %s %s",
652 .format_args = "T4T4",
653 };
Dave Barachc3a06552018-10-01 09:25:32 -0400654 struct
655 {
656 u32 c, err;
657 } *ed;
Damjan Marionf553a2c2021-03-26 13:45:37 +0100658 ed = ELOG_DATA (vlib_get_elog_main (), e);
659 ed->c = elog_string (vlib_get_elog_main (), "%v", c->path);
Dave Barachc3a06552018-10-01 09:25:32 -0400660 if (c_error)
661 {
662 vec_add1 (c_error->what, 0);
Damjan Marionf553a2c2021-03-26 13:45:37 +0100663 ed->err = elog_string (vlib_get_elog_main (),
664 (char *) c_error->what);
Damjan Marion8bea5892022-04-04 22:40:45 +0200665 vec_dec_len (c_error->what, 1);
Dave Barachc3a06552018-10-01 09:25:32 -0400666 }
667 else
Damjan Marionf553a2c2021-03-26 13:45:37 +0100668 ed->err = elog_string (vlib_get_elog_main (), "OK");
Dave Barachc3a06552018-10-01 09:25:32 -0400669 }
670
Ed Warnickecb9cada2015-12-08 15:45:58 -0700671 if (c_error)
672 {
Dave Barach9b8ffd92016-07-08 08:13:45 -0400673 error =
674 clib_error_return (0, "%v: %v", c->path, c_error->what);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700675 clib_error_free (c_error);
676 /* Free sub input. */
677 if (si != input)
678 unformat_free (si);
679
680 return error;
681 }
682 }
683
684 /* Free any previous error. */
685 clib_error_free (error);
686 }
687
Dave Barach9b8ffd92016-07-08 08:13:45 -0400688 else if (!error)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700689 error = clib_error_return (0, "%v: no sub-commands", c->path);
690
691 /* Free sub input. */
692 if (si != input)
693 unformat_free (si);
694 }
695
696 else
697 goto unknown;
698
699 return error;
700
Dave Barach9b8ffd92016-07-08 08:13:45 -0400701unknown:
Ed Warnickecb9cada2015-12-08 15:45:58 -0700702 if (parent->path)
Dave Barach9b8ffd92016-07-08 08:13:45 -0400703 return clib_error_return (0, "%v: unknown input `%U'", parent->path,
704 format_unformat_error, input);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700705 else
Dave Barach9b8ffd92016-07-08 08:13:45 -0400706 return clib_error_return (0, "unknown input `%U'", format_unformat_error,
707 input);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700708}
709
710
Dave Barach9b8ffd92016-07-08 08:13:45 -0400711void vlib_unix_error_report (vlib_main_t *, clib_error_t *)
712 __attribute__ ((weak));
Ed Warnickecb9cada2015-12-08 15:45:58 -0700713
Dave Barach9b8ffd92016-07-08 08:13:45 -0400714void
715vlib_unix_error_report (vlib_main_t * vm, clib_error_t * error)
716{
717}
Ed Warnickecb9cada2015-12-08 15:45:58 -0700718
719/* Process CLI input. */
Ole Troan72d87582019-05-10 12:01:10 +0200720int
Dave Barach9b8ffd92016-07-08 08:13:45 -0400721vlib_cli_input (vlib_main_t * vm,
722 unformat_input_t * input,
723 vlib_cli_output_function_t * function, uword function_arg)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700724{
Damjan Marionfd8deb42021-03-06 12:26:28 +0100725 vlib_global_main_t *vgm = vlib_get_global_main ();
Dave Barach9b8ffd92016-07-08 08:13:45 -0400726 vlib_process_t *cp = vlib_get_current_process (vm);
Dave Barach9b8ffd92016-07-08 08:13:45 -0400727 clib_error_t *error;
728 vlib_cli_output_function_t *save_function;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700729 uword save_function_arg;
Ole Troan72d87582019-05-10 12:01:10 +0200730 int rv = 0;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700731
Andrew Yourtchenko716d9592016-05-10 10:51:34 +0000732 save_function = cp->output_function;
733 save_function_arg = cp->output_function_arg;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700734
Andrew Yourtchenko716d9592016-05-10 10:51:34 +0000735 cp->output_function = function;
736 cp->output_function_arg = function_arg;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700737
Dave Barach9b8ffd92016-07-08 08:13:45 -0400738 do
739 {
Damjan Marionfd8deb42021-03-06 12:26:28 +0100740 error = vlib_cli_dispatch_sub_commands (vm, &vgm->cli_main, input,
Dave Barach6b3f25c2019-12-09 10:45:47 -0500741 /* parent */ 0);
Dave Barach9b8ffd92016-07-08 08:13:45 -0400742 }
743 while (!error && !unformat (input, "%U", unformat_eof));
Ed Warnickecb9cada2015-12-08 15:45:58 -0700744
745 if (error)
746 {
747 vlib_cli_output (vm, "%v", error->what);
748 vlib_unix_error_report (vm, error);
Ole Troan72d87582019-05-10 12:01:10 +0200749 /* clib_error_return is unfortunately often called with a '0'
750 return code */
751 rv = error->code != 0 ? error->code : -1;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700752 clib_error_free (error);
753 }
754
Andrew Yourtchenko716d9592016-05-10 10:51:34 +0000755 cp->output_function = save_function;
756 cp->output_function_arg = save_function_arg;
Ole Troan72d87582019-05-10 12:01:10 +0200757 return rv;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700758}
759
760/* Output to current CLI connection. */
Dave Barach9b8ffd92016-07-08 08:13:45 -0400761void
762vlib_cli_output (vlib_main_t * vm, char *fmt, ...)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700763{
Dave Barach9b8ffd92016-07-08 08:13:45 -0400764 vlib_process_t *cp = vlib_get_current_process (vm);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700765 va_list va;
Dave Barach9b8ffd92016-07-08 08:13:45 -0400766 u8 *s;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700767
768 va_start (va, fmt);
769 s = va_format (0, fmt, &va);
770 va_end (va);
771
Nathan Skrzypczak19ce5022020-11-23 17:56:32 +0100772 /* some format functions might return 0
773 * e.g. show int addr */
774 if (NULL == s)
775 return;
776
Ed Warnickecb9cada2015-12-08 15:45:58 -0700777 /* Terminate with \n if not present. */
Dave Barach9b8ffd92016-07-08 08:13:45 -0400778 if (vec_len (s) > 0 && s[vec_len (s) - 1] != '\n')
Ed Warnickecb9cada2015-12-08 15:45:58 -0700779 vec_add1 (s, '\n');
780
Dave Barach9b8ffd92016-07-08 08:13:45 -0400781 if ((!cp) || (!cp->output_function))
Ed Warnickecb9cada2015-12-08 15:45:58 -0700782 fformat (stdout, "%v", s);
783 else
Andrew Yourtchenko716d9592016-05-10 10:51:34 +0000784 cp->output_function (cp->output_function_arg, s, vec_len (s));
Ed Warnickecb9cada2015-12-08 15:45:58 -0700785
786 vec_free (s);
787}
788
Ole Troan73710c72018-06-04 22:27:49 +0200789void *vl_msg_push_heap (void) __attribute__ ((weak));
Dave Barachd67a4282019-06-15 12:46:13 -0400790void *
791vl_msg_push_heap (void)
792{
793 return 0;
794}
795
Ole Troan73710c72018-06-04 22:27:49 +0200796void vl_msg_pop_heap (void *oldheap) __attribute__ ((weak));
Dave Barachd67a4282019-06-15 12:46:13 -0400797void
798vl_msg_pop_heap (void *oldheap)
799{
800}
801
Ed Warnickecb9cada2015-12-08 15:45:58 -0700802static clib_error_t *
803show_memory_usage (vlib_main_t * vm,
Dave Barach9b8ffd92016-07-08 08:13:45 -0400804 unformat_input_t * input, vlib_cli_command_t * cmd)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700805{
Damjan Marion57d1ec02020-09-16 21:15:44 +0200806 clib_mem_main_t *mm = &clib_mem_main;
Dave Barachd67a4282019-06-15 12:46:13 -0400807 int verbose __attribute__ ((unused)) = 0;
Dave Baracha690fdb2020-01-21 12:34:55 -0500808 int api_segment = 0, stats_segment = 0, main_heap = 0, numa_heaps = 0;
Damjan Marion6bfd0762020-09-11 22:16:53 +0200809 int map = 0;
Dave Barach9b8ffd92016-07-08 08:13:45 -0400810 clib_error_t *error;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700811 u32 index = 0;
Dave Baracha690fdb2020-01-21 12:34:55 -0500812 int i;
Dave Barachd67a4282019-06-15 12:46:13 -0400813 uword clib_mem_trace_enable_disable (uword enable);
814 uword was_enabled;
815
Ed Warnickecb9cada2015-12-08 15:45:58 -0700816
Dave Barach9b8ffd92016-07-08 08:13:45 -0400817 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700818 {
Dave Barach9b8ffd92016-07-08 08:13:45 -0400819 if (unformat (input, "verbose"))
820 verbose = 1;
Ole Troan73710c72018-06-04 22:27:49 +0200821 else if (unformat (input, "api-segment"))
822 api_segment = 1;
Dave Barachd67a4282019-06-15 12:46:13 -0400823 else if (unformat (input, "stats-segment"))
824 stats_segment = 1;
825 else if (unformat (input, "main-heap"))
826 main_heap = 1;
Dave Baracha690fdb2020-01-21 12:34:55 -0500827 else if (unformat (input, "numa-heaps"))
828 numa_heaps = 1;
Damjan Marion6bfd0762020-09-11 22:16:53 +0200829 else if (unformat (input, "map"))
830 map = 1;
Dave Barach9b8ffd92016-07-08 08:13:45 -0400831 else
832 {
833 error = clib_error_return (0, "unknown input `%U'",
834 format_unformat_error, input);
835 return error;
836 }
837 }
Ed Warnickecb9cada2015-12-08 15:45:58 -0700838
Damjan Marion6bfd0762020-09-11 22:16:53 +0200839 if ((api_segment + stats_segment + main_heap + numa_heaps + map) == 0)
Dave Barachd67a4282019-06-15 12:46:13 -0400840 return clib_error_return
Damjan Marion6bfd0762020-09-11 22:16:53 +0200841 (0, "Need one of api-segment, stats-segment, main-heap, numa-heaps "
842 "or map");
Dave Barachd67a4282019-06-15 12:46:13 -0400843
Ole Troan73710c72018-06-04 22:27:49 +0200844 if (api_segment)
845 {
846 void *oldheap = vl_msg_push_heap ();
Dave Barachd67a4282019-06-15 12:46:13 -0400847 was_enabled = clib_mem_trace_enable_disable (0);
Damjan Marion4537c302020-09-28 19:03:37 +0200848 u8 *s_in_svm = format (0, "%U\n", format_clib_mem_heap, 0, 1);
Ole Troan73710c72018-06-04 22:27:49 +0200849 vl_msg_pop_heap (oldheap);
850 u8 *s = vec_dup (s_in_svm);
851
852 oldheap = vl_msg_push_heap ();
853 vec_free (s_in_svm);
Dave Barachd67a4282019-06-15 12:46:13 -0400854 clib_mem_trace_enable_disable (was_enabled);
Ole Troan73710c72018-06-04 22:27:49 +0200855 vl_msg_pop_heap (oldheap);
Dave Barachd67a4282019-06-15 12:46:13 -0400856 vlib_cli_output (vm, "API segment");
Ole Troan73710c72018-06-04 22:27:49 +0200857 vlib_cli_output (vm, "%v", s);
Dave Barachd67a4282019-06-15 12:46:13 -0400858 vec_free (s);
859 }
860 if (stats_segment)
861 {
Damjan Mariondd298e82022-10-12 16:02:18 +0200862 void *oldheap = vlib_stats_set_heap ();
Dave Barachd67a4282019-06-15 12:46:13 -0400863 was_enabled = clib_mem_trace_enable_disable (0);
Damjan Marion4537c302020-09-28 19:03:37 +0200864 u8 *s_in_svm = format (0, "%U\n", format_clib_mem_heap, 0, 1);
Dave Barachd67a4282019-06-15 12:46:13 -0400865 if (oldheap)
866 clib_mem_set_heap (oldheap);
867 u8 *s = vec_dup (s_in_svm);
868
Damjan Mariondd298e82022-10-12 16:02:18 +0200869 oldheap = vlib_stats_set_heap ();
Dave Barachd67a4282019-06-15 12:46:13 -0400870 vec_free (s_in_svm);
871 if (oldheap)
872 {
873 clib_mem_trace_enable_disable (was_enabled);
874 clib_mem_set_heap (oldheap);
875 }
876 vlib_cli_output (vm, "Stats segment");
877 vlib_cli_output (vm, "%v", s);
Ole Troan73710c72018-06-04 22:27:49 +0200878 vec_free (s);
879 }
880
Dave Baracha690fdb2020-01-21 12:34:55 -0500881
Dave Barach6a5adc32018-07-04 10:56:23 -0400882 {
Dave Barachd67a4282019-06-15 12:46:13 -0400883 if (main_heap)
884 {
885 /*
886 * Note: the foreach_vlib_main causes allocator traffic,
887 * so shut off tracing before we go there...
888 */
889 was_enabled = clib_mem_trace_enable_disable (0);
Dave Barach6a5adc32018-07-04 10:56:23 -0400890
Damjan Marion92ccf9b2021-03-26 11:38:01 +0100891 foreach_vlib_main ()
892 {
893 vlib_cli_output (vm, "%sThread %d %s\n", index ? "\n" : "", index,
894 vlib_worker_threads[index].name);
895 vlib_cli_output (vm, " %U\n", format_clib_mem_heap,
896 mm->per_cpu_mheaps[index], verbose);
897 index++;
898 }
Dave Barach6a5adc32018-07-04 10:56:23 -0400899
Dave Barachd67a4282019-06-15 12:46:13 -0400900 /* Restore the trace flag */
901 clib_mem_trace_enable_disable (was_enabled);
902 }
Dave Baracha690fdb2020-01-21 12:34:55 -0500903 if (numa_heaps)
904 {
Damjan Marion57d1ec02020-09-16 21:15:44 +0200905 for (i = 0; i < ARRAY_LEN (mm->per_numa_mheaps); i++)
Dave Baracha690fdb2020-01-21 12:34:55 -0500906 {
Damjan Marion57d1ec02020-09-16 21:15:44 +0200907 if (mm->per_numa_mheaps[i] == 0)
Dave Baracha690fdb2020-01-21 12:34:55 -0500908 continue;
Damjan Marion57d1ec02020-09-16 21:15:44 +0200909 if (mm->per_numa_mheaps[i] == mm->per_cpu_mheaps[i])
Dave Baracha690fdb2020-01-21 12:34:55 -0500910 {
911 vlib_cli_output (vm, "Numa %d uses the main heap...", i);
912 continue;
913 }
914 was_enabled = clib_mem_trace_enable_disable (0);
Dave Baracha690fdb2020-01-21 12:34:55 -0500915
Dave Baracha690fdb2020-01-21 12:34:55 -0500916 vlib_cli_output (vm, "Numa %d:", i);
Damjan Marion4537c302020-09-28 19:03:37 +0200917 vlib_cli_output (vm, " %U\n", format_clib_mem_heap,
Damjan Marion57d1ec02020-09-16 21:15:44 +0200918 mm->per_numa_mheaps[index], verbose);
Dave Baracha690fdb2020-01-21 12:34:55 -0500919 }
920 }
Damjan Marion6bfd0762020-09-11 22:16:53 +0200921 if (map)
922 {
923 clib_mem_page_stats_t stats = { };
924 clib_mem_vm_map_hdr_t *hdr = 0;
925 u8 *s = 0;
926 int numa = -1;
927
Damjan Marion5ef25162020-09-17 13:29:33 +0200928 s = format (s, "\n%-16s%7s%5s%7s%7s",
929 "StartAddr", "size", "FD", "PageSz", "Pages");
Damjan Marion6bfd0762020-09-11 22:16:53 +0200930 while ((numa = vlib_mem_get_next_numa_node (numa)) != -1)
931 s = format (s, " Numa%u", numa);
932 s = format (s, " NotMap");
933 s = format (s, " Name");
934 vlib_cli_output (vm, "%v", s);
935 vec_reset_length (s);
936
937 while ((hdr = clib_mem_vm_get_next_map_hdr (hdr)))
938 {
939 clib_mem_get_page_stats ((void *) hdr->base_addr,
940 hdr->log2_page_sz, hdr->num_pages,
941 &stats);
Damjan Marion5ef25162020-09-17 13:29:33 +0200942 s = format (s, "%016lx%7U",
Damjan Marion6bfd0762020-09-11 22:16:53 +0200943 hdr->base_addr, format_memory_size,
Damjan Marion5ef25162020-09-17 13:29:33 +0200944 hdr->num_pages << hdr->log2_page_sz);
945
946 if (hdr->fd != -1)
947 s = format (s, "%5d", hdr->fd);
948 else
949 s = format (s, "%5s", " ");
950
951 s = format (s, "%7U%7lu",
Damjan Marion6bfd0762020-09-11 22:16:53 +0200952 format_log2_page_size, hdr->log2_page_sz,
953 hdr->num_pages);
954 while ((numa = vlib_mem_get_next_numa_node (numa)) != -1)
955 s = format (s, "%6lu", stats.per_numa[numa]);
956 s = format (s, "%7lu", stats.not_mapped);
957 s = format (s, " %s", hdr->name);
958 vlib_cli_output (vm, "%v", s);
959 vec_reset_length (s);
960 }
961 vec_free (s);
962 }
Dave Barach6a5adc32018-07-04 10:56:23 -0400963 }
Ed Warnickecb9cada2015-12-08 15:45:58 -0700964 return 0;
965}
966
967VLIB_CLI_COMMAND (show_memory_usage_command, static) = {
968 .path = "show memory",
Dave Baracha690fdb2020-01-21 12:34:55 -0500969 .short_help = "show memory [api-segment][stats-segment][verbose]\n"
Benoît Ganneaf62f932023-02-08 18:54:30 +0100970 " [numa-heaps][map][main-heap]",
Ed Warnickecb9cada2015-12-08 15:45:58 -0700971 .function = show_memory_usage,
972};
973
974static clib_error_t *
Damjan Marioneccf5092016-11-18 16:59:24 +0100975show_cpu (vlib_main_t * vm, unformat_input_t * input,
976 vlib_cli_command_t * cmd)
977{
978#define _(a,b,c) vlib_cli_output (vm, "%-25s " b, a ":", c);
979 _("Model name", "%U", format_cpu_model_name);
Paul Vinciguerrad6897c12018-12-30 11:07:36 -0800980 _("Microarch model (family)", "%U", format_cpu_uarch);
Damjan Marioneccf5092016-11-18 16:59:24 +0100981 _("Flags", "%U", format_cpu_flags);
982 _("Base frequency", "%.2f GHz",
983 ((f64) vm->clib_time.clocks_per_second) * 1e-9);
984#undef _
985 return 0;
986}
987
988/*?
989 * Displays various information about the CPU.
990 *
991 * @cliexpar
992 * @cliexstart{show cpu}
993 * Model name: Intel(R) Xeon(R) CPU E5-2667 v4 @ 3.20GHz
994 * Microarchitecture: Broadwell (Broadwell-EP/EX)
995 * Flags: sse3 ssse3 sse41 sse42 avx avx2 aes
996 * Base Frequency: 3.20 GHz
997 * @cliexend
998?*/
Damjan Marioneccf5092016-11-18 16:59:24 +0100999VLIB_CLI_COMMAND (show_cpu_command, static) = {
1000 .path = "show cpu",
1001 .short_help = "Show cpu information",
1002 .function = show_cpu,
1003};
Ole Troan73710c72018-06-04 22:27:49 +02001004
Damjan Marioneccf5092016-11-18 16:59:24 +01001005static clib_error_t *
Ed Warnickecb9cada2015-12-08 15:45:58 -07001006enable_disable_memory_trace (vlib_main_t * vm,
1007 unformat_input_t * input,
1008 vlib_cli_command_t * cmd)
1009{
Damjan Marion57d1ec02020-09-16 21:15:44 +02001010 clib_mem_main_t *mm = &clib_mem_main;
Ole Troan73710c72018-06-04 22:27:49 +02001011 unformat_input_t _line_input, *line_input = &_line_input;
Dave Barachd67a4282019-06-15 12:46:13 -04001012 int enable = 1;
Ole Troan73710c72018-06-04 22:27:49 +02001013 int api_segment = 0;
Dave Barachd67a4282019-06-15 12:46:13 -04001014 int stats_segment = 0;
1015 int main_heap = 0;
Dave Baracha690fdb2020-01-21 12:34:55 -05001016 u32 numa_id = ~0;
Ole Troan73710c72018-06-04 22:27:49 +02001017 void *oldheap;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001018
Ole Troan73710c72018-06-04 22:27:49 +02001019 if (!unformat_user (input, unformat_line_input, line_input))
1020 return 0;
1021
1022 while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001023 {
Dave Barach1ddbc012018-06-13 09:26:05 -04001024 if (unformat (line_input, "%U", unformat_vlib_enable_disable, &enable))
Ole Troan73710c72018-06-04 22:27:49 +02001025 ;
1026 else if (unformat (line_input, "api-segment"))
1027 api_segment = 1;
Dave Barachd67a4282019-06-15 12:46:13 -04001028 else if (unformat (line_input, "stats-segment"))
1029 stats_segment = 1;
1030 else if (unformat (line_input, "main-heap"))
1031 main_heap = 1;
Dave Baracha690fdb2020-01-21 12:34:55 -05001032 else if (unformat (line_input, "numa-heap %d", &numa_id))
1033 ;
Ole Troan73710c72018-06-04 22:27:49 +02001034 else
1035 {
1036 unformat_free (line_input);
1037 return clib_error_return (0, "invalid input");
1038 }
Ed Warnickecb9cada2015-12-08 15:45:58 -07001039 }
Ole Troan73710c72018-06-04 22:27:49 +02001040 unformat_free (line_input);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001041
Dave Baracha690fdb2020-01-21 12:34:55 -05001042 if ((api_segment + stats_segment + main_heap + (enable == 0)
1043 + (numa_id != ~0)) == 0)
Dave Barachd67a4282019-06-15 12:46:13 -04001044 {
1045 return clib_error_return
Dave Baracha690fdb2020-01-21 12:34:55 -05001046 (0, "Need one of main-heap, stats-segment, api-segment,\n"
1047 "numa-heap <nn> or disable");
Dave Barachd67a4282019-06-15 12:46:13 -04001048 }
1049
1050 /* Turn off current trace, if any */
1051 if (current_traced_heap)
1052 {
1053 void *oldheap;
1054 oldheap = clib_mem_set_heap (current_traced_heap);
1055 clib_mem_trace (0);
1056 clib_mem_set_heap (oldheap);
1057 current_traced_heap = 0;
1058 }
1059
1060 if (enable == 0)
1061 return 0;
1062
1063 /* API segment */
Ole Troan73710c72018-06-04 22:27:49 +02001064 if (api_segment)
Dave Barachd67a4282019-06-15 12:46:13 -04001065 {
1066 oldheap = vl_msg_push_heap ();
1067 current_traced_heap = clib_mem_get_heap ();
1068 clib_mem_trace (1);
1069 vl_msg_pop_heap (oldheap);
1070
1071 }
1072
1073 /* Stats segment */
1074 if (stats_segment)
1075 {
Damjan Marion8973b072022-03-01 15:51:18 +01001076 oldheap = vlib_stats_set_heap ();
Dave Barachd67a4282019-06-15 12:46:13 -04001077 current_traced_heap = clib_mem_get_heap ();
1078 clib_mem_trace (stats_segment);
1079 /* We don't want to call vlib_stats_pop_heap... */
1080 if (oldheap)
1081 clib_mem_set_heap (oldheap);
1082 }
1083
1084 /* main_heap */
1085 if (main_heap)
1086 {
1087 current_traced_heap = clib_mem_get_heap ();
1088 clib_mem_trace (main_heap);
1089 }
Ed Warnickecb9cada2015-12-08 15:45:58 -07001090
Dave Baracha690fdb2020-01-21 12:34:55 -05001091 if (numa_id != ~0)
1092 {
Damjan Marion57d1ec02020-09-16 21:15:44 +02001093 if (numa_id >= ARRAY_LEN (mm->per_numa_mheaps))
Dave Baracha690fdb2020-01-21 12:34:55 -05001094 return clib_error_return (0, "Numa %d out of range", numa_id);
Damjan Marion57d1ec02020-09-16 21:15:44 +02001095 if (mm->per_numa_mheaps[numa_id] == 0)
Dave Baracha690fdb2020-01-21 12:34:55 -05001096 return clib_error_return (0, "Numa %d heap not active", numa_id);
1097
Damjan Marion57d1ec02020-09-16 21:15:44 +02001098 if (mm->per_numa_mheaps[numa_id] == clib_mem_get_heap ())
Dave Baracha690fdb2020-01-21 12:34:55 -05001099 return clib_error_return (0, "Numa %d uses the main heap...",
1100 numa_id);
Damjan Marion57d1ec02020-09-16 21:15:44 +02001101 current_traced_heap = mm->per_numa_mheaps[numa_id];
Dave Baracha690fdb2020-01-21 12:34:55 -05001102 oldheap = clib_mem_set_heap (current_traced_heap);
1103 clib_mem_trace (1);
1104 clib_mem_set_heap (oldheap);
1105 }
1106
1107
Ole Troan73710c72018-06-04 22:27:49 +02001108 return 0;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001109}
1110
1111VLIB_CLI_COMMAND (enable_disable_memory_trace_command, static) = {
1112 .path = "memory-trace",
Dave Baracha690fdb2020-01-21 12:34:55 -05001113 .short_help = "memory-trace on|off [api-segment][stats-segment][main-heap]\n"
1114 " [numa-heap <numa-id>]\n",
Ed Warnickecb9cada2015-12-08 15:45:58 -07001115 .function = enable_disable_memory_trace,
1116};
1117
Damjan Marione5ef1d72017-03-02 12:33:48 +01001118static clib_error_t *
Matus Fabian7561c452024-07-19 15:40:59 +02001119save_memory_trace (vlib_main_t *vm, unformat_input_t *input,
1120 vlib_cli_command_t *cmd)
1121{
1122 char *file, *chroot_file;
1123 uword was_enabled;
1124 mheap_trace_t *t, *mem_traces = 0;
1125 u8 *tmp;
1126 cJSON *traces, *trace, *traceback, *symbol;
1127 int i;
1128 FILE *fp;
1129 char *json_str = 0;
1130
1131 cJSON_Hooks cjson_hooks = {
1132 .malloc_fn = clib_mem_alloc,
1133 .free_fn = clib_mem_free,
1134 .realloc_fn = clib_mem_realloc,
1135 };
1136 cJSON_InitHooks (&cjson_hooks);
1137
1138 if (!unformat (input, "%s", &file))
1139 {
1140 vlib_cli_output (vm, "expected file name, got `%U'",
1141 format_unformat_error, input);
1142 return 0;
1143 }
1144
1145 /* It's fairly hard to get "../oopsie" through unformat; just in case */
1146 if (strstr (file, "..") || strchr (file, '/'))
1147 {
1148 vlib_cli_output (vm, "illegal characters in filename '%s'", file);
1149 return 0;
1150 }
1151 chroot_file = (char *) format (0, "/tmp/%s%c", file, 0);
1152 vec_free (file);
1153 fp = fopen ((char *) chroot_file, "w");
1154 if (fp == NULL)
1155 {
1156 vlib_cli_output (vm, "couldn't open output file %s '%s'", chroot_file);
1157 vec_free (chroot_file);
1158 return 0;
1159 }
1160
1161 was_enabled = clib_mem_trace_enable_disable (0);
1162 vlib_cli_output (vm, "Saving trace to '%s'", chroot_file);
1163 mem_traces = clib_mem_trace_dup (current_traced_heap);
1164 traces = cJSON_CreateArray ();
1165 vec_foreach (t, mem_traces)
1166 {
1167 /* Skip over free elements. */
1168 if (t->n_allocations == 0)
1169 continue;
1170
1171 trace = cJSON_CreateObject ();
1172 cJSON_AddNumberToObject (trace, "count", t->n_allocations);
1173 cJSON_AddNumberToObject (trace, "bytes", t->n_bytes);
1174 tmp = format (0, "%p%c", t->offset, 0);
1175 cJSON_AddStringToObject (trace, "sample", (char *) tmp);
1176 vec_free (tmp);
1177 traceback = cJSON_AddArrayToObject (trace, "traceback");
1178 for (i = 0; i < ARRAY_LEN (t->callers) && t->callers[i]; i++)
1179 {
1180#if defined(CLIB_UNIX) && !defined(__APPLE__)
1181 tmp = format (0, "%U%c\n", format_clib_elf_symbol_with_address,
1182 t->callers[i], 0);
1183 symbol = cJSON_CreateString ((char *) tmp);
1184 cJSON_AddItemToArray (traceback, symbol);
1185 vec_free (tmp);
1186#else
1187 tmp = format (0, "%p%c\n", t->callers[i], 0);
1188 symbol = cJSON_CreateString ((char *) tmp);
1189 cJSON_AddItemToArray (traceback, symbol);
1190 vec_free (tmp);
1191#endif
1192 }
1193
1194 cJSON_AddItemToArray (traces, trace);
1195 }
1196 json_str = cJSON_PrintUnformatted (traces);
1197 cJSON_Delete (traces);
1198 fputs (json_str, fp);
1199 fclose (fp);
1200 clib_mem_free (json_str);
1201
1202 vec_free (mem_traces);
1203 clib_mem_trace_enable_disable (was_enabled);
1204
1205 vec_free (chroot_file);
1206
1207 return 0;
1208}
1209
1210/*?
1211 * Save memory traces of the currently traced heap in JSON format to file.
1212 * Only filename can be specified, path is fixed (/tmp/<filename>).
1213 *
1214 * @cliexpar
1215 * @cliexcmd{save memory-trace mem_trace.json}
1216?*/
1217VLIB_CLI_COMMAND (save_memory_trace_command, static) = {
1218 .path = "save memory-trace",
1219 .short_help = "save memory-trace <filename>",
1220 .function = save_memory_trace,
1221};
1222
1223static clib_error_t *
Damjan Marione5ef1d72017-03-02 12:33:48 +01001224restart_cmd_fn (vlib_main_t * vm, unformat_input_t * input,
1225 vlib_cli_command_t * cmd)
1226{
Damjan Marionfd8deb42021-03-06 12:26:28 +01001227 vlib_global_main_t *vgm = vlib_get_global_main ();
Chris Luke6edd3602018-06-12 22:45:06 -04001228 clib_file_main_t *fm = &file_main;
1229 clib_file_t *f;
Damjan Marione5ef1d72017-03-02 12:33:48 +01001230
Chris Luke6edd3602018-06-12 22:45:06 -04001231 /* environ(7) does not indicate a header for this */
1232 extern char **environ;
1233
1234 /* Close all known open files */
Damjan Marionb2c31b62020-12-13 21:47:40 +01001235 pool_foreach (f, fm->file_pool)
1236 {
Chris Luke6edd3602018-06-12 22:45:06 -04001237 if (f->file_descriptor > 2)
1238 close(f->file_descriptor);
Damjan Marionb2c31b62020-12-13 21:47:40 +01001239 }
Chris Luke6edd3602018-06-12 22:45:06 -04001240
1241 /* Exec ourself */
Damjan Marion2e90b292022-04-04 18:48:11 +02001242 execve (vgm->name, (char **) vgm->argv, environ);
Damjan Marione5ef1d72017-03-02 12:33:48 +01001243
1244 return 0;
1245}
1246
Damjan Marione5ef1d72017-03-02 12:33:48 +01001247VLIB_CLI_COMMAND (restart_cmd,static) = {
1248 .path = "restart",
1249 .short_help = "restart process",
1250 .function = restart_cmd_fn,
1251};
Damjan Marione5ef1d72017-03-02 12:33:48 +01001252
Andrew Yourtchenko716d9592016-05-10 10:51:34 +00001253#ifdef TEST_CODE
1254/*
1255 * A trivial test harness to verify the per-process output_function
1256 * is working correcty.
1257 */
1258
1259static clib_error_t *
1260sleep_ten_seconds (vlib_main_t * vm,
Dave Barach9b8ffd92016-07-08 08:13:45 -04001261 unformat_input_t * input, vlib_cli_command_t * cmd)
Andrew Yourtchenko716d9592016-05-10 10:51:34 +00001262{
1263 u16 i;
Dave Barach9b8ffd92016-07-08 08:13:45 -04001264 u16 my_id = rand ();
Andrew Yourtchenko716d9592016-05-10 10:51:34 +00001265
Dave Barach9b8ffd92016-07-08 08:13:45 -04001266 vlib_cli_output (vm, "Starting 10 seconds sleep with id %u\n", my_id);
Andrew Yourtchenko716d9592016-05-10 10:51:34 +00001267
Dave Barach9b8ffd92016-07-08 08:13:45 -04001268 for (i = 0; i < 10; i++)
Andrew Yourtchenko716d9592016-05-10 10:51:34 +00001269 {
Dave Barach9b8ffd92016-07-08 08:13:45 -04001270 vlib_process_wait_for_event_or_clock (vm, 1.0);
1271 vlib_cli_output (vm, "Iteration number %u, my id: %u\n", i, my_id);
Andrew Yourtchenko716d9592016-05-10 10:51:34 +00001272 }
Dave Barach9b8ffd92016-07-08 08:13:45 -04001273 vlib_cli_output (vm, "Done with sleep with id %u\n", my_id);
Andrew Yourtchenko716d9592016-05-10 10:51:34 +00001274 return 0;
1275}
1276
1277VLIB_CLI_COMMAND (ping_command, static) = {
1278 .path = "test sleep",
1279 .function = sleep_ten_seconds,
1280 .short_help = "Sleep for 10 seconds",
1281};
1282#endif /* ifdef TEST_CODE */
Ed Warnickecb9cada2015-12-08 15:45:58 -07001283
Dave Barach9b8ffd92016-07-08 08:13:45 -04001284static uword
1285vlib_cli_normalize_path (char *input, char **result)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001286{
Dave Barach9b8ffd92016-07-08 08:13:45 -04001287 char *i = input;
1288 char *s = 0;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001289 uword l = 0;
1290 uword index_of_last_space = ~0;
1291
1292 while (*i != 0)
1293 {
1294 u8 c = *i++;
1295 /* Multiple white space -> single space. */
1296 switch (c)
1297 {
1298 case ' ':
1299 case '\t':
1300 case '\n':
1301 case '\r':
Dave Barach9b8ffd92016-07-08 08:13:45 -04001302 if (l > 0 && s[l - 1] != ' ')
Ed Warnickecb9cada2015-12-08 15:45:58 -07001303 {
1304 vec_add1 (s, ' ');
1305 l++;
1306 }
1307 break;
1308
1309 default:
Dave Barach9b8ffd92016-07-08 08:13:45 -04001310 if (l > 0 && s[l - 1] == ' ')
Ed Warnickecb9cada2015-12-08 15:45:58 -07001311 index_of_last_space = vec_len (s);
1312 vec_add1 (s, c);
1313 l++;
1314 break;
1315 }
1316 }
1317
1318 /* Remove any extra space at end. */
Dave Barach9b8ffd92016-07-08 08:13:45 -04001319 if (l > 0 && s[l - 1] == ' ')
Damjan Marion8bea5892022-04-04 22:40:45 +02001320 vec_dec_len (s, 1);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001321
1322 *result = s;
1323 return index_of_last_space;
1324}
1325
1326always_inline uword
Dave Barach9b8ffd92016-07-08 08:13:45 -04001327parent_path_len (char *path)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001328{
1329 word i;
1330 for (i = vec_len (path) - 1; i >= 0; i--)
1331 {
1332 if (path[i] == ' ')
1333 return i;
1334 }
1335 return ~0;
1336}
1337
Dave Barach9b8ffd92016-07-08 08:13:45 -04001338static void
1339add_sub_command (vlib_cli_main_t * cm, uword parent_index, uword child_index)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001340{
Dave Barach9b8ffd92016-07-08 08:13:45 -04001341 vlib_cli_command_t *p, *c;
1342 vlib_cli_sub_command_t *sub_c;
1343 u8 *sub_name;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001344 word i, l;
1345
1346 p = vec_elt_at_index (cm->commands, parent_index);
1347 c = vec_elt_at_index (cm->commands, child_index);
1348
1349 l = parent_path_len (c->path);
1350 if (l == ~0)
1351 sub_name = vec_dup ((u8 *) c->path);
1352 else
1353 {
1354 ASSERT (l + 1 < vec_len (c->path));
1355 sub_name = 0;
1356 vec_add (sub_name, c->path + l + 1, vec_len (c->path) - (l + 1));
1357 }
1358
Dave Barachbfb9a662021-06-21 10:31:35 -04001359 /* "Can't happen," check mainly to shut up coverity */
1360 ALWAYS_ASSERT (sub_name != 0);
1361
Ed Warnickecb9cada2015-12-08 15:45:58 -07001362 if (sub_name[0] == '%')
1363 {
Dave Barach9b8ffd92016-07-08 08:13:45 -04001364 uword *q;
1365 vlib_cli_sub_rule_t *sr;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001366
1367 /* Remove %. */
1368 vec_delete (sub_name, 1, 0);
1369
Dave Barach9b8ffd92016-07-08 08:13:45 -04001370 if (!p->sub_rule_index_by_name)
1371 p->sub_rule_index_by_name = hash_create_vec ( /* initial length */ 32,
1372 sizeof (sub_name[0]),
1373 sizeof (uword));
Ed Warnickecb9cada2015-12-08 15:45:58 -07001374 q = hash_get_mem (p->sub_rule_index_by_name, sub_name);
1375 if (q)
1376 {
1377 sr = vec_elt_at_index (p->sub_rules, q[0]);
1378 ASSERT (sr->command_index == child_index);
1379 return;
1380 }
1381
Dave Barach9b8ffd92016-07-08 08:13:45 -04001382 hash_set_mem (p->sub_rule_index_by_name, sub_name,
1383 vec_len (p->sub_rules));
Ed Warnickecb9cada2015-12-08 15:45:58 -07001384 vec_add2 (p->sub_rules, sr, 1);
1385 sr->name = sub_name;
Dave Barach5c944ee2020-01-07 12:29:10 -05001386 sr->rule_index = sr - p->sub_rules;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001387 sr->command_index = child_index;
1388 return;
1389 }
1390
Dave Barach9b8ffd92016-07-08 08:13:45 -04001391 if (!p->sub_command_index_by_name)
1392 p->sub_command_index_by_name = hash_create_vec ( /* initial length */ 32,
1393 sizeof (c->path[0]),
1394 sizeof (uword));
Ed Warnickecb9cada2015-12-08 15:45:58 -07001395
1396 /* Check if sub-command has already been created. */
1397 if (hash_get_mem (p->sub_command_index_by_name, sub_name))
1398 {
1399 vec_free (sub_name);
1400 return;
1401 }
1402
1403 vec_add2 (p->sub_commands, sub_c, 1);
1404 sub_c->index = child_index;
1405 sub_c->name = sub_name;
Dave Barach9b8ffd92016-07-08 08:13:45 -04001406 hash_set_mem (p->sub_command_index_by_name, sub_c->name,
1407 sub_c - p->sub_commands);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001408
1409 vec_validate (p->sub_command_positions, vec_len (sub_c->name) - 1);
1410 for (i = 0; i < vec_len (sub_c->name); i++)
1411 {
1412 int n;
Dave Barach9b8ffd92016-07-08 08:13:45 -04001413 vlib_cli_parse_position_t *pos;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001414
1415 pos = vec_elt_at_index (p->sub_command_positions, i);
1416
Dave Barach9b8ffd92016-07-08 08:13:45 -04001417 if (!pos->bitmaps)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001418 pos->min_char = sub_c->name[i];
1419
1420 n = sub_c->name[i] - pos->min_char;
1421 if (n < 0)
1422 {
1423 pos->min_char = sub_c->name[i];
1424 vec_insert (pos->bitmaps, -n, 0);
1425 n = 0;
1426 }
1427
1428 vec_validate (pos->bitmaps, n);
Dave Barach9b8ffd92016-07-08 08:13:45 -04001429 pos->bitmaps[n] =
1430 clib_bitmap_ori (pos->bitmaps[n], sub_c - p->sub_commands);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001431 }
1432}
1433
1434static void
1435vlib_cli_make_parent (vlib_cli_main_t * cm, uword ci)
1436{
Dave Barach9b8ffd92016-07-08 08:13:45 -04001437 uword p_len, pi, *p;
1438 char *p_path;
1439 vlib_cli_command_t *c, *parent;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001440
1441 /* Root command (index 0) should have already been added. */
1442 ASSERT (vec_len (cm->commands) > 0);
1443
1444 c = vec_elt_at_index (cm->commands, ci);
1445 p_len = parent_path_len (c->path);
1446
Dave Barach9b8ffd92016-07-08 08:13:45 -04001447 /* No space? Parent is root command. */
Ed Warnickecb9cada2015-12-08 15:45:58 -07001448 if (p_len == ~0)
1449 {
1450 add_sub_command (cm, 0, ci);
1451 return;
1452 }
1453
1454 p_path = 0;
1455 vec_add (p_path, c->path, p_len);
1456
1457 p = hash_get_mem (cm->command_index_by_path, p_path);
1458
1459 /* Parent exists? */
Dave Barach9b8ffd92016-07-08 08:13:45 -04001460 if (!p)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001461 {
1462 /* Parent does not exist; create it. */
1463 vec_add2 (cm->commands, parent, 1);
1464 parent->path = p_path;
Dave Barach9b8ffd92016-07-08 08:13:45 -04001465 hash_set_mem (cm->command_index_by_path, parent->path,
1466 parent - cm->commands);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001467 pi = parent - cm->commands;
1468 }
1469 else
1470 {
1471 pi = p[0];
1472 vec_free (p_path);
1473 }
1474
1475 add_sub_command (cm, pi, ci);
1476
1477 /* Create parent's parent. */
Dave Barach9b8ffd92016-07-08 08:13:45 -04001478 if (!p)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001479 vlib_cli_make_parent (cm, pi);
1480}
1481
1482always_inline uword
1483vlib_cli_command_is_empty (vlib_cli_command_t * c)
1484{
Dave Barach9b8ffd92016-07-08 08:13:45 -04001485 return (c->long_help == 0 && c->short_help == 0 && c->function == 0);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001486}
1487
Dave Barach9b8ffd92016-07-08 08:13:45 -04001488clib_error_t *
1489vlib_cli_register (vlib_main_t * vm, vlib_cli_command_t * c)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001490{
Damjan Marionfd8deb42021-03-06 12:26:28 +01001491 vlib_global_main_t *vgm = vlib_get_global_main ();
1492 vlib_cli_main_t *cm = &vgm->cli_main;
Dave Barach9b8ffd92016-07-08 08:13:45 -04001493 clib_error_t *error = 0;
1494 uword ci, *p;
1495 char *normalized_path;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001496
1497 if ((error = vlib_call_init_function (vm, vlib_cli_init)))
1498 return error;
1499
1500 (void) vlib_cli_normalize_path (c->path, &normalized_path);
1501
Dave Barach9b8ffd92016-07-08 08:13:45 -04001502 if (!cm->command_index_by_path)
1503 cm->command_index_by_path = hash_create_vec ( /* initial length */ 32,
Ed Warnickecb9cada2015-12-08 15:45:58 -07001504 sizeof (c->path[0]),
1505 sizeof (uword));
1506
1507 /* See if command already exists with given path. */
1508 p = hash_get_mem (cm->command_index_by_path, normalized_path);
1509 if (p)
1510 {
Dave Barach9b8ffd92016-07-08 08:13:45 -04001511 vlib_cli_command_t *d;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001512
1513 ci = p[0];
1514 d = vec_elt_at_index (cm->commands, ci);
1515
1516 /* If existing command was created via vlib_cli_make_parent
Dave Barach9b8ffd92016-07-08 08:13:45 -04001517 replaced it with callers data. */
Ed Warnickecb9cada2015-12-08 15:45:58 -07001518 if (vlib_cli_command_is_empty (d))
1519 {
1520 vlib_cli_command_t save = d[0];
1521
Dave Barach9b8ffd92016-07-08 08:13:45 -04001522 ASSERT (!vlib_cli_command_is_empty (c));
Ed Warnickecb9cada2015-12-08 15:45:58 -07001523
1524 /* Copy callers fields. */
1525 d[0] = c[0];
1526
1527 /* Save internal fields. */
1528 d->path = save.path;
1529 d->sub_commands = save.sub_commands;
1530 d->sub_command_index_by_name = save.sub_command_index_by_name;
1531 d->sub_command_positions = save.sub_command_positions;
1532 d->sub_rules = save.sub_rules;
1533 }
1534 else
Dave Barach9b8ffd92016-07-08 08:13:45 -04001535 error =
1536 clib_error_return (0, "duplicate command name with path %v",
1537 normalized_path);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001538
1539 vec_free (normalized_path);
1540 if (error)
1541 return error;
1542 }
1543 else
1544 {
1545 /* Command does not exist: create it. */
1546
1547 /* Add root command (index 0). */
1548 if (vec_len (cm->commands) == 0)
1549 {
1550 /* Create command with index 0; path is empty string. */
1551 vec_resize (cm->commands, 1);
1552 }
1553
1554 ci = vec_len (cm->commands);
1555 hash_set_mem (cm->command_index_by_path, normalized_path, ci);
1556 vec_add1 (cm->commands, c[0]);
1557
1558 c = vec_elt_at_index (cm->commands, ci);
1559 c->path = normalized_path;
1560
1561 /* Don't inherit from registration. */
1562 c->sub_commands = 0;
1563 c->sub_command_index_by_name = 0;
1564 c->sub_command_positions = 0;
1565 }
1566
1567 vlib_cli_make_parent (cm, ci);
1568 return 0;
1569}
1570
Dave Barach6b3f25c2019-12-09 10:45:47 -05001571#if 0
1572/* $$$ turn back on again someday, maybe */
Ed Warnickecb9cada2015-12-08 15:45:58 -07001573clib_error_t *
1574vlib_cli_register_parse_rule (vlib_main_t * vm, vlib_cli_parse_rule_t * r_reg)
1575{
Dave Barach9b8ffd92016-07-08 08:13:45 -04001576 vlib_cli_main_t *cm = &vm->cli_main;
1577 vlib_cli_parse_rule_t *r;
1578 clib_error_t *error = 0;
1579 u8 *r_name;
1580 uword *p;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001581
Dave Barach9b8ffd92016-07-08 08:13:45 -04001582 if (!cm->parse_rule_index_by_name)
1583 cm->parse_rule_index_by_name = hash_create_vec ( /* initial length */ 32,
Ed Warnickecb9cada2015-12-08 15:45:58 -07001584 sizeof (r->name[0]),
1585 sizeof (uword));
1586
1587 /* Make vector copy of name. */
1588 r_name = format (0, "%s", r_reg->name);
1589
1590 if ((p = hash_get_mem (cm->parse_rule_index_by_name, r_name)))
1591 {
1592 vec_free (r_name);
Dave Barach9b8ffd92016-07-08 08:13:45 -04001593 return clib_error_return (0, "duplicate parse rule name `%s'",
1594 r_reg->name);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001595 }
1596
1597 vec_add2 (cm->parse_rules, r, 1);
1598 r[0] = r_reg[0];
1599 r->name = (char *) r_name;
1600 hash_set_mem (cm->parse_rule_index_by_name, r->name, r - cm->parse_rules);
1601
1602 return error;
1603}
1604
Dave Barach9b8ffd92016-07-08 08:13:45 -04001605static clib_error_t *vlib_cli_register_parse_rules (vlib_main_t * vm,
1606 vlib_cli_parse_rule_t *
1607 lo,
1608 vlib_cli_parse_rule_t *
1609 hi)
1610 __attribute__ ((unused))
Ed Warnickecb9cada2015-12-08 15:45:58 -07001611{
Dave Barach9b8ffd92016-07-08 08:13:45 -04001612 clib_error_t *error = 0;
1613 vlib_cli_parse_rule_t *r;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001614
1615 for (r = lo; r < hi; r = clib_elf_section_data_next (r, 0))
1616 {
Dave Barach9b8ffd92016-07-08 08:13:45 -04001617 if (!r->name || strlen (r->name) == 0)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001618 {
1619 error = clib_error_return (0, "parse rule with no name");
1620 goto done;
1621 }
1622
1623 error = vlib_cli_register_parse_rule (vm, r);
1624 if (error)
1625 goto done;
1626 }
1627
Dave Barach9b8ffd92016-07-08 08:13:45 -04001628done:
Ed Warnickecb9cada2015-12-08 15:45:58 -07001629 return error;
1630}
1631#endif
1632
Dave Barach9b8ffd92016-07-08 08:13:45 -04001633static clib_error_t *
Dave Barachaa676742020-11-25 07:23:42 -05001634event_logger_trace_command_fn (vlib_main_t * vm,
1635 unformat_input_t * input,
1636 vlib_cli_command_t * cmd)
Dave Barachc3a06552018-10-01 09:25:32 -04001637{
1638 unformat_input_t _line_input, *line_input = &_line_input;
1639 int enable = 1;
Dave Barach900cbad2019-01-31 19:12:51 -05001640 int api = 0, cli = 0, barrier = 0, dispatch = 0, circuit = 0;
1641 u32 circuit_node_index;
Dave Barachc3a06552018-10-01 09:25:32 -04001642
1643 if (!unformat_user (input, unformat_line_input, line_input))
1644 goto print_status;
1645
1646 while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
1647 {
1648 if (unformat (line_input, "api"))
1649 api = 1;
Dave Barach900cbad2019-01-31 19:12:51 -05001650 else if (unformat (line_input, "dispatch"))
1651 dispatch = 1;
1652 else if (unformat (line_input, "circuit-node %U",
1653 unformat_vlib_node, vm, &circuit_node_index))
1654 circuit = 1;
Dave Barachc3a06552018-10-01 09:25:32 -04001655 else if (unformat (line_input, "cli"))
1656 cli = 1;
1657 else if (unformat (line_input, "barrier"))
1658 barrier = 1;
1659 else if (unformat (line_input, "disable"))
1660 enable = 0;
1661 else if (unformat (line_input, "enable"))
1662 enable = 1;
1663 else
1664 break;
1665 }
1666 unformat_free (line_input);
1667
Dave Barachb09f4d02019-07-15 16:00:03 -04001668 vl_api_set_elog_trace_api_messages
1669 (api ? enable : vl_api_get_elog_trace_api_messages ());
Dave Barachc3a06552018-10-01 09:25:32 -04001670 vm->elog_trace_cli_commands = cli ? enable : vm->elog_trace_cli_commands;
Dave Barach900cbad2019-01-31 19:12:51 -05001671 vm->elog_trace_graph_dispatch = dispatch ?
1672 enable : vm->elog_trace_graph_dispatch;
1673 vm->elog_trace_graph_circuit = circuit ?
1674 enable : vm->elog_trace_graph_circuit;
Dave Barachc3a06552018-10-01 09:25:32 -04001675 vlib_worker_threads->barrier_elog_enabled =
1676 barrier ? enable : vlib_worker_threads->barrier_elog_enabled;
Dave Barach900cbad2019-01-31 19:12:51 -05001677 vm->elog_trace_graph_circuit_node_index = circuit_node_index;
1678
1679 /*
1680 * Set up start-of-buffer logic-analyzer trigger
1681 * for main loop event logs, which are fairly heavyweight.
1682 * See src/vlib/main/vlib_elog_main_loop_event(...), which
1683 * will fully disable the scheme when the elog buffer fills.
1684 */
1685 if (dispatch || circuit)
1686 {
Damjan Marionf553a2c2021-03-26 13:45:37 +01001687 elog_main_t *em = &vlib_global_main.elog_main;
Dave Barach900cbad2019-01-31 19:12:51 -05001688
1689 em->n_total_events_disable_limit =
1690 em->n_total_events + vec_len (em->event_ring);
1691 }
1692
Dave Barachc3a06552018-10-01 09:25:32 -04001693
1694print_status:
1695 vlib_cli_output (vm, "Current status:");
1696
1697 vlib_cli_output
1698 (vm, " Event log API message trace: %s\n CLI command trace: %s",
Dave Barachb09f4d02019-07-15 16:00:03 -04001699 vl_api_get_elog_trace_api_messages ()? "on" : "off",
Dave Barachc3a06552018-10-01 09:25:32 -04001700 vm->elog_trace_cli_commands ? "on" : "off");
1701 vlib_cli_output
1702 (vm, " Barrier sync trace: %s",
1703 vlib_worker_threads->barrier_elog_enabled ? "on" : "off");
Dave Barach900cbad2019-01-31 19:12:51 -05001704 vlib_cli_output
1705 (vm, " Graph Dispatch: %s",
1706 vm->elog_trace_graph_dispatch ? "on" : "off");
1707 vlib_cli_output
1708 (vm, " Graph Circuit: %s",
1709 vm->elog_trace_graph_circuit ? "on" : "off");
1710 if (vm->elog_trace_graph_circuit)
1711 vlib_cli_output
1712 (vm, " node %U",
1713 format_vlib_node_name, vm, vm->elog_trace_graph_circuit_node_index);
Dave Barachc3a06552018-10-01 09:25:32 -04001714
1715 return 0;
1716}
1717
1718/*?
1719 * Control event logging of api, cli, and thread barrier events
1720 * With no arguments, displays the current trace status.
1721 * Name the event groups you wish to trace or stop tracing.
1722 *
1723 * @cliexpar
1724 * @clistart
Dave Barachaa676742020-11-25 07:23:42 -05001725 * event-logger trace api cli barrier
1726 * event-logger trace api cli barrier disable
1727 * event-logger trace dispatch
1728 * event-logger trace circuit-node ethernet-input
Dave Barachc3a06552018-10-01 09:25:32 -04001729 * @cliend
Dave Barachaa676742020-11-25 07:23:42 -05001730 * @cliexcmd{event-logger trace [api][cli][barrier][disable]}
Dave Barachc3a06552018-10-01 09:25:32 -04001731?*/
Dave Barachaa676742020-11-25 07:23:42 -05001732VLIB_CLI_COMMAND (event_logger_trace_command, static) =
Dave Barachc3a06552018-10-01 09:25:32 -04001733{
Dave Barachaa676742020-11-25 07:23:42 -05001734 .path = "event-logger trace",
1735 .short_help = "event-logger trace [api][cli][barrier][dispatch]\n"
Dave Barach900cbad2019-01-31 19:12:51 -05001736 "[circuit-node <name> e.g. ethernet-input][disable]",
Dave Barachaa676742020-11-25 07:23:42 -05001737 .function = event_logger_trace_command_fn,
Dave Barachc3a06552018-10-01 09:25:32 -04001738};
Dave Barachc3a06552018-10-01 09:25:32 -04001739
1740static clib_error_t *
Dave Barachc4abafd2019-09-04 12:09:32 -04001741suspend_command_fn (vlib_main_t * vm,
1742 unformat_input_t * input, vlib_cli_command_t * cmd)
1743{
1744 vlib_process_suspend (vm, 30e-3);
1745 return 0;
1746}
1747
Dave Barachc4abafd2019-09-04 12:09:32 -04001748VLIB_CLI_COMMAND (suspend_command, static) =
1749{
1750 .path = "suspend",
1751 .short_help = "suspend debug CLI for 30ms",
1752 .function = suspend_command_fn,
Dave Baracha1f5a952019-11-01 11:24:43 -04001753 .is_mp_safe = 1,
1754};
Dave Baracha1f5a952019-11-01 11:24:43 -04001755
1756
1757static int
1758sort_cmds_by_path (void *a1, void *a2)
1759{
1760 u32 *index1 = a1;
1761 u32 *index2 = a2;
Damjan Marionfd8deb42021-03-06 12:26:28 +01001762 vlib_global_main_t *vgm = vlib_get_global_main ();
1763 vlib_cli_main_t *cm = &vgm->cli_main;
Dave Baracha1f5a952019-11-01 11:24:43 -04001764 vlib_cli_command_t *c1, *c2;
1765 int i, lmin;
1766
1767 c1 = vec_elt_at_index (cm->commands, *index1);
1768 c2 = vec_elt_at_index (cm->commands, *index2);
1769
1770 lmin = vec_len (c1->path);
1771 lmin = (vec_len (c2->path) >= lmin) ? lmin : vec_len (c2->path);
1772
1773 for (i = 0; i < lmin; i++)
1774 {
1775 if (c1->path[i] < c2->path[i])
1776 return -1;
1777 else if (c1->path[i] > c2->path[i])
1778 return 1;
1779 }
1780
1781 return 0;
1782}
1783
1784typedef struct
1785{
1786 vlib_cli_main_t *cm;
1787 u32 parent_command_index;
1788 int show_mp_safe;
1789 int show_not_mp_safe;
1790 int show_hit;
1791 int clear_hit;
1792} vlib_cli_walk_args_t;
1793
1794static void
1795cli_recursive_walk (vlib_cli_walk_args_t * aa)
1796{
1797 vlib_cli_command_t *parent;
1798 vlib_cli_sub_command_t *sub;
1799 vlib_cli_walk_args_t _a, *a = &_a;
1800 vlib_cli_main_t *cm;
1801 int i;
1802
1803 /* Copy args into this stack frame */
1804 *a = *aa;
1805 cm = a->cm;
1806
1807 parent = vec_elt_at_index (cm->commands, a->parent_command_index);
1808
1809 if (parent->function)
1810 {
1811 if (((a->show_mp_safe && parent->is_mp_safe)
1812 || (a->show_not_mp_safe && !parent->is_mp_safe))
1813 && (a->show_hit == 0 || parent->hit_counter))
1814 {
1815 vec_add1 (cm->sort_vector, a->parent_command_index);
1816 }
1817
1818 if (a->clear_hit)
1819 parent->hit_counter = 0;
1820 }
1821
1822 for (i = 0; i < vec_len (parent->sub_commands); i++)
1823 {
1824 sub = vec_elt_at_index (parent->sub_commands, i);
1825 a->parent_command_index = sub->index;
1826 cli_recursive_walk (a);
1827 }
1828}
1829
1830static u8 *
1831format_mp_safe (u8 * s, va_list * args)
1832{
1833 vlib_cli_main_t *cm = va_arg (*args, vlib_cli_main_t *);
1834 int show_mp_safe = va_arg (*args, int);
1835 int show_not_mp_safe = va_arg (*args, int);
1836 int show_hit = va_arg (*args, int);
1837 int clear_hit = va_arg (*args, int);
1838 vlib_cli_command_t *c;
1839 vlib_cli_walk_args_t _a, *a = &_a;
1840 int i;
1841 char *format_string = "\n%v";
1842
1843 if (show_hit)
1844 format_string = "\n%v: %u";
1845
1846 vec_reset_length (cm->sort_vector);
1847
1848 a->cm = cm;
1849 a->parent_command_index = 0;
1850 a->show_mp_safe = show_mp_safe;
1851 a->show_not_mp_safe = show_not_mp_safe;
1852 a->show_hit = show_hit;
1853 a->clear_hit = clear_hit;
1854
1855 cli_recursive_walk (a);
1856
1857 vec_sort_with_function (cm->sort_vector, sort_cmds_by_path);
1858
1859 for (i = 0; i < vec_len (cm->sort_vector); i++)
1860 {
1861 c = vec_elt_at_index (cm->commands, cm->sort_vector[i]);
1862 s = format (s, format_string, c->path, c->hit_counter);
1863 }
1864
1865 return s;
1866}
1867
1868
1869static clib_error_t *
1870show_cli_command_fn (vlib_main_t * vm,
1871 unformat_input_t * input, vlib_cli_command_t * cmd)
1872{
Damjan Marionfd8deb42021-03-06 12:26:28 +01001873 vlib_global_main_t *vgm = vlib_get_global_main ();
Dave Baracha1f5a952019-11-01 11:24:43 -04001874 int show_mp_safe = 0;
1875 int show_not_mp_safe = 0;
1876 int show_hit = 0;
1877 int clear_hit = 0;
1878
1879 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1880 {
1881 if (unformat (input, "mp-safe"))
1882 show_mp_safe = 1;
1883 if (unformat (input, "not-mp-safe"))
1884 show_not_mp_safe = 1;
1885 else if (unformat (input, "hit"))
1886 show_hit = 1;
1887 else if (unformat (input, "clear-hit"))
1888 clear_hit = 1;
1889 else
1890 break;
1891 }
1892
1893 /* default set: all cli commands */
1894 if (clear_hit == 0 && (show_mp_safe + show_not_mp_safe) == 0)
1895 show_mp_safe = show_not_mp_safe = 1;
1896
Damjan Marionfd8deb42021-03-06 12:26:28 +01001897 vlib_cli_output (vm, "%U", format_mp_safe, &vgm->cli_main, show_mp_safe,
1898 show_not_mp_safe, show_hit, clear_hit);
Dave Baracha1f5a952019-11-01 11:24:43 -04001899 if (clear_hit)
1900 vlib_cli_output (vm, "hit counters cleared...");
1901
1902 return 0;
1903}
1904
1905/*?
1906 * Displays debug cli command information
1907 *
1908 * @cliexpar
1909 * @cliexstart{show cli [mp-safe][not-mp-safe][hit][clear-hit]}
1910 *
1911 * "show cli" displays the entire debug cli:
1912 *
1913 * abf attach
1914 * abf policy
1915 * adjacency counters
1916 * api trace
1917 * app ns
1918 * bfd key del
1919 * ... and so on ...
1920 *
1921 * "show cli mp-safe" displays mp-safe debug CLI commands:
1922 *
1923 * abf policy
1924 * binary-api
1925 * create vhost-user
1926 * exec
1927 * ip container
1928 * ip mroute
1929 * ip probe-neighbor
1930 * ip route
1931 * ip scan-neighbor
1932 * ip table
1933 * ip6 table
1934 *
1935 * "show cli not-mp-safe" displays debug CLI commands
1936 * which cause worker thread barrier synchronization
1937 *
1938 * "show cli hit" displays commands which have been executed. Qualify
1939 * as desired with "mp-safe" or "not-mp-safe".
1940 *
1941 * "show cli clear-hit" clears the per-command hit counters.
1942 * @cliexend
1943?*/
1944
Dave Baracha1f5a952019-11-01 11:24:43 -04001945VLIB_CLI_COMMAND (show_cli_command, static) =
1946{
1947 .path = "show cli",
1948 .short_help = "show cli [mp-safe][not-mp-safe][hit][clear-hit]",
1949 .function = show_cli_command_fn,
1950 .is_mp_safe = 1,
Dave Barachc4abafd2019-09-04 12:09:32 -04001951};
Dave Barachc4abafd2019-09-04 12:09:32 -04001952
1953static clib_error_t *
Dave Barach9b8ffd92016-07-08 08:13:45 -04001954vlib_cli_init (vlib_main_t * vm)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001955{
Damjan Marionfd8deb42021-03-06 12:26:28 +01001956 vlib_global_main_t *vgm = vlib_get_global_main ();
1957 vlib_cli_main_t *cm = &vgm->cli_main;
Dave Barach9b8ffd92016-07-08 08:13:45 -04001958 clib_error_t *error = 0;
1959 vlib_cli_command_t *cmd;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001960
1961 cmd = cm->cli_command_registrations;
1962
1963 while (cmd)
1964 {
1965 error = vlib_cli_register (vm, cmd);
1966 if (error)
Dave Barach9b8ffd92016-07-08 08:13:45 -04001967 return error;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001968 cmd = cmd->next_cli_command;
1969 }
Dave Barach3d0e0d12021-02-17 10:25:18 -05001970
1971 cm->log = vlib_log_register_class_rate_limit (
1972 "cli", "log", 0x7FFFFFFF /* aka no rate limit */);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001973 return error;
1974}
1975
1976VLIB_INIT_FUNCTION (vlib_cli_init);
Dave Barach9b8ffd92016-07-08 08:13:45 -04001977
1978/*
1979 * fd.io coding-style-patch-verification: ON
1980 *
1981 * Local Variables:
1982 * eval: (c-set-style "gnu")
1983 * End:
1984 */