xref: /illumos-gate/usr/src/cmd/mdb/common/mdb/mdb_evset.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <mdb/mdb_target.h>
30 #include <mdb/mdb_modapi.h>
31 #include <mdb/mdb_string.h>
32 #include <mdb/mdb.h>
33 
34 #include <libproc.h>
35 #include <string.h>
36 
37 /*ARGSUSED*/
38 void
39 cmd_event(mdb_tgt_t *t, int vid, void *s)
40 {
41 	if (s != NULL && mdb_eval(s) == -1)
42 		mdb_warn("failed to eval [ %d ] command \"%s\"", vid, s);
43 }
44 
45 int
46 cmd_evset(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
47 {
48 	uint_t setb = 0, clrb = 0;
49 	const char *opt_c = NULL;
50 	uint_t opt_F = FALSE;
51 	uintptr_t opt_n = 0;
52 
53 	int *idv = mdb_zalloc(sizeof (int) * (argc + 1), UM_SLEEP | UM_GC);
54 	int idc = 0;
55 
56 	int status = DCMD_OK;
57 	const char *p;
58 	void *data;
59 	int argi;
60 
61 	if (flags & DCMD_ADDRSPEC)
62 		idv[idc++] = (int)(intptr_t)addr;
63 
64 	/*
65 	 * Perform an initial pass through argv: we accumulate integer ids into
66 	 * idv, and compute a group of bits to set and a group to clear.
67 	 */
68 	while (argc != 0 && (argi = mdb_getopts(argc, argv,
69 	    'c', MDB_OPT_STR, &opt_c,
70 	    'd', MDB_OPT_SETBITS, MDB_TGT_SPEC_AUTODIS, &setb,
71 	    'D', MDB_OPT_SETBITS, MDB_TGT_SPEC_AUTODEL, &setb,
72 	    'e', MDB_OPT_SETBITS, MDB_TGT_SPEC_DISABLED, &clrb,
73 	    'F', MDB_OPT_SETBITS, TRUE, &opt_F,
74 	    'n', MDB_OPT_UINTPTR, &opt_n,
75 	    's', MDB_OPT_SETBITS, MDB_TGT_SPEC_AUTOSTOP, &setb,
76 	    't', MDB_OPT_SETBITS, MDB_TGT_SPEC_TEMPORARY, &setb,
77 	    'T', MDB_OPT_SETBITS, MDB_TGT_SPEC_STICKY, &setb,
78 	    NULL)) != argc) {
79 
80 		argv += argi; /* advance past elements processed by getopts */
81 		argc -= argi; /* decrement argc by number of args processed */
82 
83 		if (argv->a_type == MDB_TYPE_STRING) {
84 			if (argv->a_un.a_str[0] == '+') {
85 				for (p = argv->a_un.a_str + 1; *p != '\0'; ) {
86 					switch (*p++) {
87 					case 'd':
88 						clrb |= MDB_TGT_SPEC_AUTODIS;
89 						break;
90 					case 'D':
91 						clrb |= MDB_TGT_SPEC_AUTODEL;
92 						break;
93 					case 'e':
94 						setb |= MDB_TGT_SPEC_DISABLED;
95 						break;
96 					case 's':
97 						clrb |= MDB_TGT_SPEC_AUTOSTOP;
98 						break;
99 					case 't':
100 						clrb |= MDB_TGT_SPEC_TEMPORARY;
101 						break;
102 					case 'T':
103 						clrb |= MDB_TGT_SPEC_STICKY;
104 						break;
105 					default:
106 						mdb_warn("illegal option -- "
107 						    "+%c\n", p[-1]);
108 						return (DCMD_USAGE);
109 					}
110 				}
111 			} else if (argv->a_un.a_str[0] != '-') {
112 				idv[idc++] = (int)(intmax_t)
113 				    strtonum(argv->a_un.a_str, 10);
114 			} else
115 				return (DCMD_USAGE);
116 		} else
117 			idv[idc++] = (int)(intmax_t)argv->a_un.a_val;
118 
119 		argc--;
120 		argv++;
121 	}
122 
123 	if (idc == 0) {
124 		mdb_warn("expected one or more event IDs to be specified\n");
125 		return (DCMD_USAGE);
126 	}
127 
128 	/*
129 	 * If -n was not specified, then -d means "disable now" instead of
130 	 * meaning "set auto-disable after n hits".
131 	 */
132 	if (opt_n == 0 && (setb & MDB_TGT_SPEC_AUTODIS))
133 		setb = (setb & ~MDB_TGT_SPEC_AUTODIS) | MDB_TGT_SPEC_DISABLED;
134 
135 	while (idc-- != 0) {
136 		mdb_tgt_spec_desc_t sp;
137 		int id = *idv++;
138 
139 		bzero(&sp, sizeof (mdb_tgt_spec_desc_t));
140 		(void) mdb_tgt_vespec_info(mdb.m_target, id, &sp, NULL, 0);
141 		data = sp.spec_data;
142 
143 		if (opt_F == FALSE && (sp.spec_flags & MDB_TGT_SPEC_HIDDEN)) {
144 			mdb_warn("cannot modify event %d: internal "
145 			    "debugger event\n", id);
146 			status = DCMD_ERR;
147 			continue;
148 		}
149 
150 		sp.spec_flags |= setb;
151 		sp.spec_flags &= ~clrb;
152 
153 		if (opt_c && !(sp.spec_flags & MDB_TGT_SPEC_HIDDEN)) {
154 			if (opt_c[0] != '\0')
155 				sp.spec_data = strdup(opt_c);
156 			else
157 				sp.spec_data = NULL;
158 		}
159 
160 		if (opt_n)
161 			sp.spec_limit = opt_n;
162 
163 		if (mdb_tgt_vespec_modify(mdb.m_target, id, sp.spec_flags,
164 		    sp.spec_limit, sp.spec_data) == -1) {
165 			mdb_warn("failed to modify event %d", id);
166 			data = sp.spec_data;
167 			status = DCMD_ERR;
168 		}
169 
170 		if (opt_c && data && !(sp.spec_flags & MDB_TGT_SPEC_HIDDEN))
171 			strfree(data);
172 	}
173 
174 	return (status);
175 }
176 
177 /*
178  * Utility routine for performing the stock argument processing that is common
179  * among the dcmds that create event specifiers.  We parse out the standard set
180  * of event property options from the command-line, and return a copy of the
181  * argument list to the caller that consists solely of the remaining non-option
182  * arguments.  If a parsing error occurs, NULL is returned.
183  */
184 static const mdb_arg_t *
185 ev_getopts(uintmax_t addr, uint_t flags, int argc, const mdb_arg_t *argv,
186     uint_t *evflags, char **opt_c, uint_t *opt_i, uint_t *opt_l,
187     uint64_t *opt_L, uintptr_t *opt_n, uint_t *opt_o, uint_t *opt_p,
188     uint_t *rwx)
189 {
190 	uint_t setb = 0, clrb = 0;
191 	const char *p;
192 	int argi;
193 
194 	mdb_arg_t *av;
195 	int ac = 0;
196 
197 	/* keep lint happy */
198 	*opt_p = FALSE;
199 
200 	av = mdb_alloc(sizeof (mdb_arg_t) * (argc + 2), UM_SLEEP | UM_GC);
201 
202 	/*
203 	 * If an address was specified, take it as an additional immediate
204 	 * value argument by adding it to the argument list.
205 	 */
206 	if (flags & DCMD_ADDRSPEC) {
207 		av[ac].a_type = MDB_TYPE_IMMEDIATE;
208 		av[ac++].a_un.a_val = addr;
209 	}
210 
211 	/*
212 	 * Now call mdb_getopts repeatedly to parse the argument list.  We need
213 	 * to handle '+[a-z]' processing manually, and we also manually copy
214 	 * each non-option argument into the av[] array as we encounter them.
215 	 */
216 	while (argc != 0 && (argi = mdb_getopts(argc, argv,
217 	    'c', MDB_OPT_STR, opt_c,
218 	    'd', MDB_OPT_SETBITS, MDB_TGT_SPEC_AUTODIS, &setb,
219 	    'D', MDB_OPT_SETBITS, MDB_TGT_SPEC_AUTODEL, &setb,
220 	    'e', MDB_OPT_SETBITS, MDB_TGT_SPEC_DISABLED, &clrb,
221 	    'i', MDB_OPT_SETBITS, TRUE, opt_i,
222 	    'n', MDB_OPT_UINTPTR, opt_n,
223 	    'o', MDB_OPT_SETBITS, TRUE, opt_o,
224 #ifdef _KMDB
225 	    'p', MDB_OPT_SETBITS, TRUE, opt_p,
226 #endif
227 	    'r', MDB_OPT_SETBITS, MDB_TGT_WA_R, rwx,
228 	    's', MDB_OPT_SETBITS, MDB_TGT_SPEC_AUTOSTOP, &setb,
229 	    'l', MDB_OPT_SETBITS, TRUE, opt_l,
230 	    'L', MDB_OPT_UINT64, opt_L,
231 	    't', MDB_OPT_SETBITS, MDB_TGT_SPEC_TEMPORARY, &setb,
232 	    'T', MDB_OPT_SETBITS, MDB_TGT_SPEC_STICKY, &setb,
233 	    'w', MDB_OPT_SETBITS, MDB_TGT_WA_W, rwx,
234 	    'x', MDB_OPT_SETBITS, MDB_TGT_WA_X, rwx, NULL)) != argc) {
235 
236 		argv += argi; /* advance past elements processed by getopts */
237 		argc -= argi; /* decrement argc by number of args processed */
238 
239 		if (argv->a_type == MDB_TYPE_STRING) {
240 			if (argv->a_un.a_str[0] == '+') {
241 				for (p = argv->a_un.a_str + 1; *p != '\0'; ) {
242 					switch (*p++) {
243 					case 'd':
244 						clrb |= MDB_TGT_SPEC_AUTODIS;
245 						break;
246 					case 'D':
247 						clrb |= MDB_TGT_SPEC_AUTODEL;
248 						break;
249 					case 'e':
250 						setb |= MDB_TGT_SPEC_DISABLED;
251 						break;
252 					case 's':
253 						clrb |= MDB_TGT_SPEC_AUTOSTOP;
254 						break;
255 					case 't':
256 						clrb |= MDB_TGT_SPEC_TEMPORARY;
257 						break;
258 					case 'T':
259 						clrb |= MDB_TGT_SPEC_STICKY;
260 						break;
261 					default:
262 						mdb_warn("illegal option -- "
263 						    "+%c\n", p[-1]);
264 						return (NULL);
265 					}
266 				}
267 			} else if (argv->a_un.a_str[0] != '-') {
268 				av[ac++] = *argv;
269 			} else
270 				return (NULL);
271 		} else
272 			av[ac++] = *argv;
273 
274 		argc--;
275 		argv++;
276 	}
277 
278 	/*
279 	 * If no arguments were found on the command-line, return NULL to
280 	 * indicate that the caller should return DCMD_USAGE.
281 	 */
282 	if (ac == 0)
283 		return (NULL);
284 
285 	/*
286 	 * If -n was not specified, then -d means "disable now" instead of
287 	 * meaning "set auto-disable after n hits".
288 	 */
289 	if (opt_n == 0 && (setb & MDB_TGT_SPEC_AUTODIS))
290 		setb = (setb & ~MDB_TGT_SPEC_AUTODIS) | MDB_TGT_SPEC_DISABLED;
291 
292 	/*
293 	 * Return the final set of flags, and terminate the argument array
294 	 * with a NULL string argument.
295 	 */
296 	*evflags = setb & ~clrb;
297 
298 	av[ac].a_type = MDB_TYPE_STRING;
299 	av[ac].a_un.a_str = NULL;
300 
301 	return (av);
302 }
303 
304 /*
305  * Utility function for modifying the spec_data and spec_limit properties of an
306  * event specifier.  We use this for handling the -c and -n options below.
307  */
308 static void
309 ev_setopts(mdb_tgt_t *t, int id, const char *opt_c, uintptr_t opt_n)
310 {
311 	mdb_tgt_spec_desc_t sp;
312 
313 	(void) mdb_tgt_vespec_info(t, id, &sp, NULL, 0);
314 
315 	if (opt_c != NULL)
316 		sp.spec_data = strdup(opt_c);
317 	if (opt_n != 0)
318 		sp.spec_limit = opt_n;
319 
320 	if (mdb_tgt_vespec_modify(t, id, sp.spec_flags,
321 	    sp.spec_limit, sp.spec_data) == -1) {
322 		mdb_warn("failed to modify event %d", id);
323 		if (opt_c != NULL)
324 			strfree(sp.spec_data);
325 	}
326 }
327 
328 int
329 cmd_bp(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
330 {
331 	char *opt_c = NULL;
332 	uint_t opt_i = FALSE;
333 	uint_t opt_l = FALSE;
334 	uint64_t opt_L = 0;
335 	uintptr_t opt_n = 0;
336 	uint_t opt_o = FALSE;
337 	uint_t opt_p = FALSE;
338 	uint_t opt_rwx = 0;
339 	int status = DCMD_OK;
340 	int id;
341 
342 	if ((argv = ev_getopts(addr, flags, argc, argv, &flags, &opt_c, &opt_i,
343 	    &opt_l, &opt_L, &opt_n, &opt_o, &opt_p, &opt_rwx)) == NULL ||
344 	    opt_i || opt_o || opt_rwx != 0 || opt_l || opt_L != 0 || opt_p)
345 		return (DCMD_USAGE);
346 
347 	while (argv->a_type != MDB_TYPE_STRING || argv->a_un.a_str != NULL) {
348 		if (argv->a_type == MDB_TYPE_STRING) {
349 			id = mdb_tgt_add_sbrkpt(mdb.m_target, argv->a_un.a_str,
350 			    flags, cmd_event, NULL);
351 		} else {
352 			id = mdb_tgt_add_vbrkpt(mdb.m_target, argv->a_un.a_val,
353 			    flags, cmd_event, NULL);
354 		}
355 
356 		if (id == 0) {
357 			mdb_warn("failed to add breakpoint at %s",
358 			    argv->a_type == MDB_TYPE_STRING ? argv->a_un.a_str :
359 			    numtostr(argv->a_un.a_val, mdb.m_radix,
360 			    NTOS_UNSIGNED | NTOS_SHOWBASE));
361 			status = DCMD_ERR;
362 
363 		} else if (opt_c || opt_n)
364 			ev_setopts(mdb.m_target, id, opt_c, opt_n);
365 
366 		argv++;
367 	}
368 
369 	return (status);
370 }
371 
372 
373 int
374 cmd_sigbp(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
375 {
376 	char *opt_c = NULL;
377 	uint_t opt_i = FALSE;
378 	uint_t opt_l = FALSE;
379 	uint64_t opt_L = 0;
380 	uintptr_t opt_n = 0;
381 	uint_t opt_o = FALSE;
382 	uint_t opt_p = FALSE;
383 	uint_t opt_rwx = 0;
384 	int status = DCMD_OK;
385 	int id, sig;
386 
387 	if ((argv = ev_getopts(addr, flags, argc, argv, &flags, &opt_c, &opt_i,
388 	    &opt_l, &opt_L, &opt_n, &opt_o, &opt_p, &opt_rwx)) == NULL ||
389 	    opt_i || opt_l || opt_L != 0 || opt_o || opt_p || opt_rwx != 0)
390 		return (DCMD_USAGE);
391 
392 	while (argv->a_type != MDB_TYPE_STRING || argv->a_un.a_str != NULL) {
393 		if (argv->a_type == MDB_TYPE_STRING) {
394 			if (proc_str2sig(argv->a_un.a_str, &sig) == -1) {
395 				mdb_warn("invalid signal name -- %s\n",
396 				    argv->a_un.a_str);
397 				status = DCMD_ERR;
398 				argv++;
399 				continue;
400 			}
401 		} else
402 			sig = (int)(intmax_t)argv->a_un.a_val;
403 
404 		if ((id = mdb_tgt_add_signal(mdb.m_target, sig, flags,
405 		    cmd_event, NULL)) == 0) {
406 			mdb_warn("failed to trace signal %d", sig);
407 			status = DCMD_ERR;
408 		} else if (opt_c || opt_n)
409 			ev_setopts(mdb.m_target, id, opt_c, opt_n);
410 
411 		argv++;
412 	}
413 
414 	return (status);
415 }
416 
417 int
418 cmd_sysbp(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
419 {
420 	char *opt_c = NULL;
421 	uint_t opt_i = FALSE;
422 	uint_t opt_l = FALSE;
423 	uint64_t opt_L = 0;
424 	uintptr_t opt_n = 0;
425 	uint_t opt_o = FALSE;
426 	uint_t opt_p = FALSE;
427 	uint_t opt_rwx = 0;
428 	int status = DCMD_OK;
429 	int id, sysnum;
430 
431 	if ((argv = ev_getopts(addr, flags, argc, argv, &flags, &opt_c, &opt_i,
432 	    &opt_l, &opt_L, &opt_n, &opt_o, &opt_p, &opt_rwx)) == NULL ||
433 	    (opt_i && opt_o) || opt_l || opt_L != 0 || opt_p || opt_rwx != 0)
434 		return (DCMD_USAGE);
435 
436 	while (argv->a_type != MDB_TYPE_STRING || argv->a_un.a_str != NULL) {
437 		if (argv->a_type == MDB_TYPE_STRING) {
438 			if (proc_str2sys(argv->a_un.a_str, &sysnum) == -1) {
439 				mdb_warn("invalid system call name -- %s\n",
440 				    argv->a_un.a_str);
441 				status = DCMD_ERR;
442 				argv++;
443 				continue;
444 			}
445 		} else
446 			sysnum = (int)(intmax_t)argv->a_un.a_val;
447 
448 		if (opt_o) {
449 			id = mdb_tgt_add_sysexit(mdb.m_target, sysnum,
450 			    flags, cmd_event, NULL);
451 		} else {
452 			id = mdb_tgt_add_sysenter(mdb.m_target, sysnum,
453 			    flags, cmd_event, NULL);
454 		}
455 
456 		if (id == 0) {
457 			mdb_warn("failed to trace system call %d", sysnum);
458 			status = DCMD_ERR;
459 		} else if (opt_c || opt_n)
460 			ev_setopts(mdb.m_target, id, opt_c, opt_n);
461 
462 		argv++;
463 	}
464 
465 	return (status);
466 }
467 
468 int
469 cmd_fltbp(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
470 {
471 	char *opt_c = NULL;
472 	uint_t opt_i = FALSE;
473 	uint_t opt_l = FALSE;
474 	uint64_t opt_L = 0;
475 	uintptr_t opt_n = 0;
476 	uint_t opt_o = FALSE;
477 	uint_t opt_p = FALSE;
478 	uint_t opt_rwx = 0;
479 	int status = DCMD_OK;
480 	int id, fltnum;
481 
482 	if ((argv = ev_getopts(addr, flags, argc, argv, &flags, &opt_c,
483 	    &opt_i, &opt_l, &opt_L, &opt_n, &opt_o, &opt_p,
484 	    &opt_rwx)) == NULL || opt_i || opt_l || opt_L != 0 || opt_o ||
485 	    opt_p || opt_rwx != 0)
486 		return (DCMD_USAGE);
487 
488 	while (argv->a_type != MDB_TYPE_STRING || argv->a_un.a_str != NULL) {
489 		if (argv->a_type == MDB_TYPE_STRING) {
490 			if (proc_str2flt(argv->a_un.a_str, &fltnum) == -1) {
491 				mdb_warn("invalid fault name -- %s\n",
492 				    argv->a_un.a_str);
493 				status = DCMD_ERR;
494 				argv++;
495 				continue;
496 			}
497 		} else
498 			fltnum = (int)(intmax_t)argv->a_un.a_val;
499 
500 		id = mdb_tgt_add_fault(mdb.m_target, fltnum,
501 		    flags, cmd_event, NULL);
502 
503 		if (id == 0) {
504 			mdb_warn("failed to trace fault %d", fltnum);
505 			status = DCMD_ERR;
506 		} else if (opt_c || opt_n)
507 			ev_setopts(mdb.m_target, id, opt_c, opt_n);
508 
509 		argv++;
510 	}
511 
512 	return (status);
513 }
514 
515 /*ARGSUSED*/
516 int
517 cmd_wp(uintptr_t x, uint_t flags, int argc, const mdb_arg_t *argv)
518 {
519 	mdb_tgt_addr_t addr = mdb_get_dot();
520 	char *opt_c = NULL;
521 	uint_t opt_i = FALSE;
522 	uint_t opt_l = FALSE;
523 	uint64_t opt_L = 0;
524 	uintptr_t opt_n = 0;
525 	uint_t opt_o = FALSE;
526 	uint_t opt_p = FALSE;
527 	uint_t opt_rwx = 0;
528 	int id;
529 	char buf[MDB_SYM_NAMLEN];
530 	GElf_Sym gsym;
531 	int size;
532 
533 	if ((argv = ev_getopts(addr, flags, argc, argv, &flags, &opt_c, &opt_i,
534 	    &opt_l, &opt_L, &opt_n, &opt_o, &opt_p, &opt_rwx)) == NULL ||
535 	    opt_o || (opt_p && opt_i))
536 		return (DCMD_USAGE);
537 
538 #ifndef _KMDB
539 	if (opt_i)
540 		return (DCMD_USAGE);
541 #endif
542 
543 	if (argv->a_type != MDB_TYPE_IMMEDIATE)
544 		return (DCMD_USAGE);
545 
546 	if (opt_rwx == 0) {
547 		mdb_warn("at least one of -r, -w, or -x must be specified\n");
548 		return (DCMD_USAGE);
549 	}
550 
551 	if ((opt_l) + (opt_L > 0) + (mdb.m_dcount != 1) > 1) {
552 		mdb_warn("only one of -l, -L, or command count can be "
553 		    "specified\n");
554 		return (DCMD_ABORT);
555 	}
556 
557 	if (opt_l) {
558 		if (mdb_lookup_by_addr(addr, MDB_SYM_EXACT, buf,
559 		    sizeof (buf), &gsym) == -1) {
560 			mdb_warn("failed to lookup symbol at %p", addr);
561 			return (DCMD_ERR);
562 		}
563 
564 		if (gsym.st_size == 0) {
565 			mdb_warn("cannot set watchpoint: symbol '%s' has zero "
566 			    "size\n", buf);
567 			return (DCMD_ERR);
568 		}
569 		size = gsym.st_size;
570 	} else if (opt_L != 0) {
571 		size = opt_L;
572 	} else
573 		size = mdb.m_dcount;
574 
575 	if (opt_p) {
576 		id = mdb_tgt_add_pwapt(mdb.m_target, addr, size, opt_rwx,
577 		    flags, cmd_event, NULL);
578 	} else if (opt_i) {
579 		id = mdb_tgt_add_iowapt(mdb.m_target, addr, size, opt_rwx,
580 		    flags, cmd_event, NULL);
581 	} else {
582 		id = mdb_tgt_add_vwapt(mdb.m_target, addr, size, opt_rwx,
583 		    flags, cmd_event, NULL);
584 	}
585 
586 	if (id == 0) {
587 		mdb_warn("failed to set watchpoint at %p", addr);
588 		return ((opt_l || opt_L) ? DCMD_ERR : DCMD_ABORT);
589 	}
590 
591 	if (opt_c || opt_n)
592 		ev_setopts(mdb.m_target, id, opt_c, opt_n);
593 
594 	/*
595 	 * We use m_dcount as an argument; don't loop. We ignore this
596 	 * restriction with the -l and -L options, since we read the size from
597 	 * the symbol and don't rely on the count.
598 	 */
599 	return ((opt_l || opt_L) ? DCMD_OK : DCMD_ABORT);
600 }
601 
602 /*ARGSUSED*/
603 int
604 cmd_oldbp(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
605 {
606 	char *s = mdb_argv_to_str(argc, argv);
607 
608 	if (mdb_tgt_add_vbrkpt(mdb.m_target, addr, 0, cmd_event, s) == 0) {
609 		mdb_warn("failed to add breakpoint");
610 		if (s != NULL)
611 			strfree(s);
612 		return (DCMD_ERR);
613 	}
614 
615 	return (DCMD_OK);
616 }
617 
618 /*ARGSUSED*/
619 static int
620 oldwp(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv, uint_t rwx)
621 {
622 	char *s = mdb_argv_to_str(argc, argv);
623 
624 	if (mdb_tgt_add_vwapt(mdb.m_target, addr, mdb.m_dcount, rwx, 0,
625 	    cmd_event, s) == 0) {
626 		mdb_warn("failed to add watchpoint");
627 		if (s != NULL)
628 			strfree(s);
629 		return (DCMD_ABORT);
630 	}
631 
632 	return (DCMD_ABORT); /* we use m_dcount as an argument; don't loop */
633 }
634 
635 int
636 cmd_oldwpr(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
637 {
638 	return (oldwp(addr, flags, argc, argv, MDB_TGT_WA_R));
639 }
640 
641 int
642 cmd_oldwpw(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
643 {
644 	return (oldwp(addr, flags, argc, argv, MDB_TGT_WA_W));
645 }
646 
647 int
648 cmd_oldwpx(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
649 {
650 	return (oldwp(addr, flags, argc, argv, MDB_TGT_WA_X));
651 }
652 
653 static const char _evset_help[] =
654 "+/-d     disable specifier when hit count reaches limit (+d to unset);\n"
655 "         if -n is not present with -d, specifier is disabled immediately\n\n"
656 "+/-D     delete specifier when hit count reaches limit (+D to unset);\n"
657 "+/-e     enable specifier (+e or -d to disable)\n"
658 "+/-s     stop target when hit count reaches limit (+s to unset)\n"
659 "+/-t     delete specifier the next time the target stops (+t to unset)\n"
660 "+/-T     sticky bit: ::delete all will not remove specifier (+T to unset)\n\n"
661 "-c cmd   execute \"cmd\" each time the corresponding event occurs\n"
662 "-n count set limit for -D, -d, or -s to \"count\" (default 1)\n\n";
663 
664 void
665 bp_help(void)
666 {
667 	mdb_printf(_evset_help);
668 	mdb_printf("addr     set breakpoint at specified virtual address\n");
669 	mdb_printf("sym      set deferred breakpoint at specified symbol\n");
670 }
671 
672 void
673 evset_help(void)
674 {
675 	mdb_printf(_evset_help);
676 	mdb_printf("addr/id  set properties of specified event ids\n");
677 }
678 
679 void
680 fltbp_help(void)
681 {
682 	mdb_printf(_evset_help);
683 	mdb_printf("flt      fault name (see <sys/fault.h>) or number\n");
684 }
685 
686 void
687 sigbp_help(void)
688 {
689 	mdb_printf(_evset_help);
690 	mdb_printf("SIG      signal name (see signal(3HEAD)) or number\n");
691 }
692 
693 void
694 sysbp_help(void)
695 {
696 	mdb_printf(_evset_help);
697 	mdb_printf("-i       trace system call on entry into kernel (default)\n"
698 	    "-o       trace system call on exit from kernel\n\n"
699 	    "syscall  system call name (see <sys/syscall.h>) or number\n");
700 }
701 
702 void
703 wp_help(void)
704 {
705 	mdb_printf(_evset_help);
706 	mdb_printf(
707 #ifdef _KMDB
708 	    "-p       treat addr as a physical address\n"
709 	    "-i       treat addr as an I/O port address\n"
710 #endif
711 	    "-l       use size of addr's type for watched region\n"
712 	    "-L size  set size of watched region (default 1)\n"
713 	    "-r       trace read access to watched region\n"
714 	    "-w       trace write access to watched region\n"
715 	    "-x       trace execute access to watched region\n\n"
716 	    "addr     address for base of watched region\n"
717 	    "repeat   size of watched region (equivalent to -L)\n");
718 }
719