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