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