xref: /illumos-gate/usr/src/cmd/mdb/common/mdb/mdb_tab.c (revision ad135b5d644628e791c3188a6ecbd9c257961ef8)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright (c) 2012 by Delphix. All rights reserved.
23  * Copyright (c) 2012 Joyent, Inc. All rights reserved.
24  */
25 /*
26  * This file contains all of the interfaces for mdb's tab completion engine.
27  * Currently some interfaces are private to mdb and its internal implementation,
28  * those are in mdb_tab.h. Other pieces are public interfaces. Those are in
29  * mdb_modapi.h.
30  *
31  * Memory allocations in tab completion context have to be done very carefully.
32  * We need to think of ourselves as the same as any other command that is being
33  * executed by the user, which means we must use UM_GC to handle being
34  * interrupted.
35  */
36 
37 #include <mdb/mdb_modapi.h>
38 #include <mdb/mdb_ctf.h>
39 #include <mdb/mdb_ctf_impl.h>
40 #include <mdb/mdb_string.h>
41 #include <mdb/mdb_module.h>
42 #include <mdb/mdb_debug.h>
43 #include <mdb/mdb_print.h>
44 #include <mdb/mdb_nv.h>
45 #include <mdb/mdb_tab.h>
46 #include <mdb/mdb.h>
47 
48 #include <ctype.h>
49 
50 /*
51  * There may be another way to do this, but this works well enough.
52  */
53 #define	COMMAND_SEPARATOR "::"
54 
55 /*
56  * find_command_start --
57  *
58  * 	Given a buffer find the start of the last command.
59  */
60 static char *
61 tab_find_command_start(char *buf)
62 {
63 	char *offset = strstr(buf, COMMAND_SEPARATOR);
64 
65 	if (offset == NULL)
66 		return (NULL);
67 
68 	for (;;) {
69 		char *next = strstr(offset + strlen(COMMAND_SEPARATOR),
70 		    COMMAND_SEPARATOR);
71 
72 		if (next == NULL) {
73 			return (offset);
74 		}
75 
76 		offset = next;
77 	}
78 }
79 
80 /*
81  * get_dcmd --
82  *
83  * 	Given a buffer containing a command and its argument return
84  * 	the name of the command and the offset in the buffer where
85  * 	the command arguments start.
86  *
87  * 	Note: This will modify the buffer.
88  */
89 char *
90 tab_get_dcmd(char *buf, char **args, uint_t *flags)
91 {
92 	char *start = buf + strlen(COMMAND_SEPARATOR);
93 	char *separator = start;
94 	const char *end = buf + strlen(buf);
95 	uint_t space = 0;
96 
97 	while (separator < end && !isspace(*separator))
98 		separator++;
99 
100 	if (separator == end) {
101 		*args = NULL;
102 	} else {
103 		if (isspace(*separator))
104 			space = 1;
105 
106 		*separator++ = '\0';
107 		*args = separator;
108 	}
109 
110 	if (space)
111 		*flags |= DCMD_TAB_SPACE;
112 
113 	return (start);
114 }
115 
116 /*
117  * count_args --
118  *
119  * 	Given a buffer containing dmcd arguments return the total number
120  * 	of arguments.
121  *
122  * 	While parsing arguments we need to keep track of whether or not the last
123  * 	arguments ends with a trailing space.
124  */
125 static int
126 tab_count_args(const char *input, uint_t *flags)
127 {
128 	const char *index;
129 	int argc = 0;
130 	uint_t space = *flags & DCMD_TAB_SPACE;
131 	index = input;
132 
133 	while (*index != '\0') {
134 		while (*index != '\0' && isspace(*index)) {
135 			index++;
136 			space = 1;
137 		}
138 
139 		if (*index != '\0' && !isspace(*index)) {
140 			argc++;
141 			space = 0;
142 			while (*index != '\0' && !isspace (*index)) {
143 				index++;
144 			}
145 		}
146 	}
147 
148 	if (space)
149 		*flags |= DCMD_TAB_SPACE;
150 	else
151 		*flags &= ~DCMD_TAB_SPACE;
152 
153 	return (argc);
154 }
155 
156 /*
157  * copy_args --
158  *
159  * 	Given a buffer containing dcmd arguments and an array of mdb_arg_t's
160  * 	initialize the string value of each mdb_arg_t.
161  *
162  * 	Note: This will modify the buffer.
163  */
164 static int
165 tab_copy_args(char *input, int argc, mdb_arg_t *argv)
166 {
167 	int i = 0;
168 	char *index;
169 
170 	index = input;
171 
172 	while (*index) {
173 		while (*index && isspace(*index)) {
174 			index++;
175 		}
176 
177 		if (*index && !isspace(*index)) {
178 			char *end = index;
179 
180 			while (*end && !isspace(*end)) {
181 				end++;
182 			}
183 
184 			if (*end) {
185 				*end++ = '\0';
186 			}
187 
188 			argv[i].a_type = MDB_TYPE_STRING;
189 			argv[i].a_un.a_str = index;
190 
191 			index = end;
192 			i++;
193 		}
194 	}
195 
196 	if (i != argc)
197 		return (-1);
198 
199 	return (0);
200 }
201 
202 /*
203  * parse-buf --
204  *
205  * 	Parse the given buffer and return the specified dcmd, the number
206  * 	of arguments, and array of mdb_arg_t containing the argument
207  * 	values.
208  *
209  * 	Note: this will modify the specified buffer. Caller is responisble
210  * 	for freeing argvp.
211  */
212 static int
213 tab_parse_buf(char *buf, char **dcmdp, int *argcp, mdb_arg_t **argvp,
214     uint_t *flags)
215 {
216 	char *data = tab_find_command_start(buf);
217 	char *args_data = NULL;
218 	char *dcmd = NULL;
219 	int argc = 0;
220 	mdb_arg_t *argv = NULL;
221 
222 	if (data == NULL) {
223 		return (-1);
224 	}
225 
226 	dcmd = tab_get_dcmd(data, &args_data, flags);
227 
228 	if (dcmd == NULL) {
229 		return (-1);
230 	}
231 
232 	if (args_data != NULL) {
233 		argc = tab_count_args(args_data, flags);
234 
235 		if (argc != 0) {
236 			argv = mdb_alloc(sizeof (mdb_arg_t) * argc,
237 			    UM_SLEEP | UM_GC);
238 
239 			if (tab_copy_args(args_data, argc, argv) == -1)
240 				return (-1);
241 		}
242 	}
243 
244 	*dcmdp = dcmd;
245 	*argcp = argc;
246 	*argvp = argv;
247 
248 	return (0);
249 }
250 
251 /*
252  * tab_command --
253  *
254  * 	This function is executed anytime a tab is entered. It checks
255  * 	the current buffer to determine if there is a valid dmcd,
256  * 	if that dcmd has a tab completion handler it will invoke it.
257  *
258  *	This function returns the string (if any) that should be added to the
259  *	existing buffer to complete it.
260  */
261 int
262 mdb_tab_command(mdb_tab_cookie_t *mcp, const char *buf)
263 {
264 	char *data;
265 	char *dcmd = NULL;
266 	int argc = 0;
267 	mdb_arg_t *argv = NULL;
268 	int ret = 0;
269 	mdb_idcmd_t *cp;
270 	uint_t flags = 0;
271 
272 	/*
273 	 * Parsing the command and arguments will modify the buffer
274 	 * (replacing spaces with \0), so make a copy of the specified
275 	 * buffer first.
276 	 */
277 	data = mdb_alloc(strlen(buf) + 1, UM_SLEEP | UM_GC);
278 	(void) strcpy(data, buf);
279 
280 	/*
281 	 * Get the specified dcmd and arguments from the buffer.
282 	 */
283 	ret = tab_parse_buf(data, &dcmd, &argc, &argv, &flags);
284 
285 	if (ret != 0) {
286 		goto out;
287 	}
288 
289 	/*
290 	 * Check to see if the buffer contains a valid dcmd
291 	 */
292 	cp = mdb_dcmd_lookup(dcmd);
293 
294 	/*
295 	 * When argc is zero it indicates that we are trying to tab complete
296 	 * a dcmd. Note, that if there isn't the start of a dcmd, i.e. ::, then
297 	 * we will have already bailed in the call to tab_parse_buf.
298 	 */
299 	if (cp == NULL && argc != 0) {
300 		goto out;
301 	}
302 
303 	/*
304 	 * Invoke the command specific tab completion handler or the built in
305 	 * dcmd one if there is no dcmd.
306 	 */
307 	if (cp == NULL)
308 		(void) mdb_tab_complete_dcmd(mcp, dcmd);
309 	else
310 		mdb_call_tab(cp, mcp, flags, argc, argv);
311 
312 out:
313 	return (mdb_tab_size(mcp));
314 }
315 
316 static int
317 tab_complete_dcmd(mdb_var_t *v, void *arg)
318 {
319 	mdb_idcmd_t *idcp = mdb_nv_get_cookie(mdb_nv_get_cookie(v));
320 	mdb_tab_cookie_t *mcp = (mdb_tab_cookie_t *)arg;
321 
322 	/*
323 	 * The way that mdb is implemented, even commands like $C will show up
324 	 * here. As such, we don't want to match anything that doesn't start
325 	 * with an alpha or number. While nothing currently appears (via a
326 	 * cursory search with mdb -k) to start with a capital letter or a
327 	 * number, we'll support them anyways.
328 	 */
329 	if (!isalnum(idcp->idc_name[0]))
330 		return (0);
331 
332 	mdb_tab_insert(mcp, idcp->idc_name);
333 	return (0);
334 }
335 
336 int
337 mdb_tab_complete_dcmd(mdb_tab_cookie_t *mcp, const char *dcmd)
338 {
339 	mdb_tab_setmbase(mcp, dcmd);
340 	mdb_nv_sort_iter(&mdb.m_dcmds, tab_complete_dcmd, mcp,
341 	    UM_GC | UM_SLEEP);
342 	return (0);
343 }
344 
345 static int
346 tab_complete_walker(mdb_var_t *v, void *arg)
347 {
348 	mdb_iwalker_t *iwp = mdb_nv_get_cookie(mdb_nv_get_cookie(v));
349 	mdb_tab_cookie_t *mcp = arg;
350 
351 	mdb_tab_insert(mcp, iwp->iwlk_name);
352 	return (0);
353 }
354 
355 int
356 mdb_tab_complete_walker(mdb_tab_cookie_t *mcp, const char *walker)
357 {
358 	if (walker != NULL)
359 		mdb_tab_setmbase(mcp, walker);
360 	mdb_nv_sort_iter(&mdb.m_walkers, tab_complete_walker, mcp,
361 	    UM_GC | UM_SLEEP);
362 
363 	return (0);
364 }
365 
366 mdb_tab_cookie_t *
367 mdb_tab_init(void)
368 {
369 	mdb_tab_cookie_t *mcp;
370 
371 	mcp = mdb_zalloc(sizeof (mdb_tab_cookie_t), UM_SLEEP | UM_GC);
372 	(void) mdb_nv_create(&mcp->mtc_nv, UM_SLEEP | UM_GC);
373 
374 	return (mcp);
375 }
376 
377 size_t
378 mdb_tab_size(mdb_tab_cookie_t *mcp)
379 {
380 	return (mdb_nv_size(&mcp->mtc_nv));
381 }
382 
383 /*
384  * Determine whether the specified name is a valid tab completion for
385  * the given command. If the name is a valid tab completion then
386  * it will be saved in the mdb_tab_cookie_t.
387  */
388 void
389 mdb_tab_insert(mdb_tab_cookie_t *mcp, const char *name)
390 {
391 	size_t len, matches, index;
392 	uint_t flags;
393 	mdb_var_t *v;
394 	char *n;
395 	const char *nvn;
396 
397 	/*
398 	 * If we have a match set, then we want to verify that we actually match
399 	 * it.
400 	 */
401 	if (mcp->mtc_base != NULL &&
402 	    strncmp(name, mcp->mtc_base, strlen(mcp->mtc_base)) != 0)
403 		return;
404 
405 	v = mdb_nv_lookup(&mcp->mtc_nv, name);
406 	if (v != NULL)
407 		return;
408 
409 	/*
410 	 * Names that we get passed in may be longer than MDB_NV_NAMELEN which
411 	 * is currently 31 including the null terminator. If that is the case,
412 	 * then we're going to take care of allocating a string and holding it
413 	 * for our caller. Note that we don't need to free it, because we're
414 	 * allocating this with UM_GC.
415 	 */
416 	flags = 0;
417 	len = strlen(name);
418 	if (len > MDB_NV_NAMELEN - 1) {
419 		n = mdb_alloc(len + 1, UM_SLEEP | UM_GC);
420 		(void) strcpy(n, name);
421 		nvn = n;
422 		flags |= MDB_NV_EXTNAME;
423 	} else {
424 		nvn = name;
425 	}
426 	flags |= MDB_NV_RDONLY;
427 
428 	(void) mdb_nv_insert(&mcp->mtc_nv, nvn, NULL, 0, flags);
429 
430 	matches = mdb_tab_size(mcp);
431 	if (matches == 1) {
432 		(void) strlcpy(mcp->mtc_match, nvn, MDB_SYM_NAMLEN);
433 	} else {
434 		index = 0;
435 		while (mcp->mtc_match[index] &&
436 		    mcp->mtc_match[index] == nvn[index])
437 			index++;
438 
439 		mcp->mtc_match[index] = '\0';
440 	}
441 }
442 
443 /*ARGSUSED*/
444 static int
445 tab_print_cb(mdb_var_t *v, void *ignored)
446 {
447 	mdb_printf("%s\n", mdb_nv_get_name(v));
448 	return (0);
449 }
450 
451 void
452 mdb_tab_print(mdb_tab_cookie_t *mcp)
453 {
454 	mdb_nv_sort_iter(&mcp->mtc_nv, tab_print_cb, NULL, UM_SLEEP | UM_GC);
455 }
456 
457 const char *
458 mdb_tab_match(mdb_tab_cookie_t *mcp)
459 {
460 	size_t blen;
461 
462 	if (mcp->mtc_base == NULL)
463 		blen = 0;
464 	else
465 		blen = strlen(mcp->mtc_base);
466 	return (mcp->mtc_match + blen);
467 }
468 
469 void
470 mdb_tab_setmbase(mdb_tab_cookie_t *mcp, const char *base)
471 {
472 	(void) strlcpy(mcp->mtc_base, base, MDB_SYM_NAMLEN);
473 }
474 
475 /*
476  * This function is currently a no-op due to the fact that we have to GC because
477  * we're in command context.
478  */
479 /*ARGSUSED*/
480 void
481 mdb_tab_fini(mdb_tab_cookie_t *mcp)
482 {
483 }
484 
485 /*
486  * This function takes a ctf id and determines whether or not the associated
487  * type should be considered as a potential match for the given tab
488  * completion command. We verify that the type itself is valid
489  * for completion given the current context of the command, resolve
490  * its actual name, and then pass it off to mdb_tab_insert to determine
491  * if it's an actual match.
492  */
493 static int
494 tab_complete_type(mdb_ctf_id_t id, void *arg)
495 {
496 	int rkind;
497 	char buf[MDB_SYM_NAMLEN];
498 	mdb_ctf_id_t rid;
499 	mdb_tab_cookie_t *mcp = arg;
500 	uint_t flags = (uint_t)(uintptr_t)mcp->mtc_cba;
501 
502 	/*
503 	 * CTF data includes types that mdb commands don't understand. Before
504 	 * we resolve the actual type prune any entry that is a type we
505 	 * don't care about.
506 	 */
507 	switch (mdb_ctf_type_kind(id)) {
508 	case CTF_K_CONST:
509 	case CTF_K_RESTRICT:
510 	case CTF_K_VOLATILE:
511 		return (0);
512 	}
513 
514 	if (mdb_ctf_type_resolve(id, &rid) != 0)
515 		return (1);
516 
517 	rkind = mdb_ctf_type_kind(rid);
518 
519 	if ((flags & MDB_TABC_MEMBERS) && rkind != CTF_K_STRUCT &&
520 	    rkind != CTF_K_UNION)
521 		return (0);
522 
523 	if ((flags & MDB_TABC_NOPOINT) && rkind == CTF_K_POINTER)
524 		return (0);
525 
526 	if ((flags & MDB_TABC_NOARRAY) && rkind == CTF_K_ARRAY)
527 		return (0);
528 
529 	(void) mdb_ctf_type_name(id, buf, sizeof (buf));
530 
531 	mdb_tab_insert(mcp, buf);
532 	return (0);
533 }
534 
535 /*ARGSUSED*/
536 static int
537 mdb_tab_complete_module(void *data, const mdb_map_t *mp, const char *name)
538 {
539 	(void) mdb_ctf_type_iter(name, tab_complete_type, data);
540 	return (0);
541 }
542 
543 int
544 mdb_tab_complete_type(mdb_tab_cookie_t *mcp, const char *name, uint_t flags)
545 {
546 	mdb_tgt_t *t = mdb.m_target;
547 
548 	mcp->mtc_cba = (void *)(uintptr_t)flags;
549 	if (name != NULL)
550 		mdb_tab_setmbase(mcp, name);
551 
552 	(void) mdb_tgt_object_iter(t, mdb_tab_complete_module, mcp);
553 	return (0);
554 }
555 
556 /*ARGSUSED*/
557 static int
558 tab_complete_member(const char *name, mdb_ctf_id_t id, ulong_t off, void *arg)
559 {
560 	mdb_tab_cookie_t *mcp = arg;
561 	mdb_tab_insert(mcp, name);
562 	return (0);
563 }
564 
565 int
566 mdb_tab_complete_member_by_id(mdb_tab_cookie_t *mcp, mdb_ctf_id_t id,
567     const char *member)
568 {
569 	if (member != NULL)
570 		mdb_tab_setmbase(mcp, member);
571 	(void) mdb_ctf_member_iter(id, tab_complete_member, mcp);
572 	return (0);
573 }
574 
575 int
576 mdb_tab_complete_member(mdb_tab_cookie_t *mcp, const char *type,
577     const char *member)
578 {
579 	mdb_ctf_id_t id;
580 
581 	if (mdb_ctf_lookup_by_name(type, &id) != 0)
582 		return (-1);
583 
584 	return (mdb_tab_complete_member_by_id(mcp, id, member));
585 }
586 
587 int
588 mdb_tab_complete_mt(mdb_tab_cookie_t *mcp, uint_t flags, int argc,
589     const mdb_arg_t *argv)
590 {
591 	char tn[MDB_SYM_NAMLEN];
592 	int ret;
593 
594 	if (argc == 0 && !(flags & DCMD_TAB_SPACE))
595 		return (0);
596 
597 	if (argc == 0)
598 		return (mdb_tab_complete_type(mcp, NULL, MDB_TABC_MEMBERS));
599 
600 	if ((ret = mdb_tab_typename(&argc, &argv, tn, sizeof (tn))) < 0)
601 		return (ret);
602 
603 	if (argc == 1 && (!(flags & DCMD_TAB_SPACE) || ret == 1))
604 		return (mdb_tab_complete_type(mcp, tn, MDB_TABC_MEMBERS));
605 
606 	if (argc == 1 && (flags & DCMD_TAB_SPACE))
607 		return (mdb_tab_complete_member(mcp, tn, NULL));
608 
609 	if (argc == 2)
610 		return (mdb_tab_complete_member(mcp, tn, argv[1].a_un.a_str));
611 
612 	return (0);
613 }
614 
615 /*
616  * This is similar to mdb_print.c's args_to_typename, but it has subtle
617  * differences surrounding how the strings of one element are handled that have
618  * 'struct', 'enum', or 'union' in them and instead works with them for tab
619  * completion purposes.
620  */
621 int
622 mdb_tab_typename(int *argcp, const mdb_arg_t **argvp, char *buf, size_t len)
623 {
624 	int argc = *argcp;
625 	const mdb_arg_t *argv = *argvp;
626 
627 	if (argc < 1 || argv->a_type != MDB_TYPE_STRING)
628 		return (DCMD_USAGE);
629 
630 	if (strcmp(argv->a_un.a_str, "struct") == 0 ||
631 	    strcmp(argv->a_un.a_str, "enum") == 0 ||
632 	    strcmp(argv->a_un.a_str, "union") == 0) {
633 		if (argc == 1) {
634 			(void) mdb_snprintf(buf, len, "%s ",
635 			    argv[0].a_un.a_str);
636 			return (1);
637 		}
638 
639 		if (argv[1].a_type != MDB_TYPE_STRING)
640 			return (DCMD_USAGE);
641 
642 		(void) mdb_snprintf(buf, len, "%s %s",
643 		    argv[0].a_un.a_str, argv[1].a_un.a_str);
644 
645 		*argcp = argc - 1;
646 		*argvp = argv + 1;
647 	} else {
648 		(void) mdb_snprintf(buf, len, "%s", argv[0].a_un.a_str);
649 	}
650 
651 	return (0);
652 }
653