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 /*
30  * MDB Regression Test Module
31  *
32  * This module contains dcmds and walkers that exercise various aspects of
33  * MDB and the MDB Module API.  It can be manually loaded and executed to
34  * verify that MDB is still working properly.
35  */
36 
37 #include <mdb/mdb_modapi.h>
38 #define	_MDB
39 #include <mdb/mdb_io_impl.h>
40 #include <mdb/mdb.h>
41 #undef _MDB
42 
43 static int
44 cd_init(mdb_walk_state_t *wsp)
45 {
46 	wsp->walk_addr = 0xf;
47 	return (WALK_NEXT);
48 }
49 
50 static int
51 cd_step(mdb_walk_state_t *wsp)
52 {
53 	int status = wsp->walk_callback(wsp->walk_addr, NULL, wsp->walk_cbdata);
54 
55 	if (wsp->walk_addr-- == 0)
56 		return (WALK_DONE);
57 
58 	return (status);
59 }
60 
61 /*ARGSUSED*/
62 static int
63 cmd_praddr(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
64 {
65 	if ((flags != (DCMD_ADDRSPEC|DCMD_LOOP|DCMD_PIPE)) &&
66 	    (flags != (DCMD_ADDRSPEC|DCMD_LOOP|DCMD_PIPE|DCMD_LOOPFIRST))) {
67 		mdb_warn("ERROR: praddr invoked with flags = 0x%x\n", flags);
68 		return (DCMD_ERR);
69 	}
70 
71 	if (argc != 0) {
72 		mdb_warn("ERROR: praddr invoked with argc = %lu\n", argc);
73 		return (DCMD_ERR);
74 	}
75 
76 	mdb_printf("%lr\n", addr);
77 	return (DCMD_OK);
78 }
79 
80 static int
81 compare(const void *lp, const void *rp)
82 {
83 	uintptr_t lhs = *((const uintptr_t *)lp);
84 	uintptr_t rhs = *((const uintptr_t *)rp);
85 	return (lhs - rhs);
86 }
87 
88 /*ARGSUSED*/
89 static int
90 cmd_qsort(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
91 {
92 	mdb_pipe_t p;
93 	size_t i;
94 
95 	if (flags != (DCMD_ADDRSPEC | DCMD_LOOP |
96 	    DCMD_LOOPFIRST | DCMD_PIPE | DCMD_PIPE_OUT)) {
97 		mdb_warn("ERROR: qsort invoked with flags = 0x%x\n", flags);
98 		return (DCMD_ERR);
99 	}
100 
101 	if (argc != 0) {
102 		mdb_warn("ERROR: qsort invoked with argc = %lu\n", argc);
103 		return (DCMD_ERR);
104 	}
105 
106 	mdb_get_pipe(&p);
107 
108 	if (p.pipe_data == NULL || p.pipe_len != 16) {
109 		mdb_warn("ERROR: qsort got bad results from mdb_get_pipe\n");
110 		return (DCMD_ERR);
111 	}
112 
113 	if (p.pipe_data[0] != addr) {
114 		mdb_warn("ERROR: qsort pipe_data[0] != addr\n");
115 		return (DCMD_ERR);
116 	}
117 
118 	qsort(p.pipe_data, p.pipe_len, sizeof (uintptr_t), compare);
119 	mdb_set_pipe(&p);
120 
121 	for (i = 0; i < 16; i++) {
122 		if (p.pipe_data[i] != i) {
123 			mdb_warn("ERROR: qsort got bad data in slot %lu\n", i);
124 			return (DCMD_ERR);
125 		}
126 	}
127 
128 	return (DCMD_OK);
129 }
130 
131 /*ARGSUSED*/
132 static int
133 cmd_runtest(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
134 {
135 	mdb_walker_t w = { "count", "count", cd_init, cd_step, NULL };
136 	int state, i;
137 
138 	mdb_printf("- adding countdown walker\n");
139 	if (mdb_add_walker(&w) != 0) {
140 		mdb_warn("ERROR: failed to add walker");
141 		return (DCMD_ERR);
142 	}
143 
144 	mdb_printf("- executing countdown pipeline\n");
145 	if (mdb_eval("::walk mdb_test`count |::mdb_test`qsort |::praddr")) {
146 		mdb_warn("ERROR: failed to eval command");
147 		return (DCMD_ERR);
148 	}
149 
150 	mdb_printf("- removing countdown walker\n");
151 	if (mdb_remove_walker("count") != 0) {
152 		mdb_warn("ERROR: failed to remove walker");
153 		return (DCMD_ERR);
154 	}
155 
156 	state = mdb_get_state();
157 	mdb_printf("- kernel=%d state=%d\n", mdb_prop_kernel, state);
158 
159 	if (mdb_prop_kernel && (state == MDB_STATE_DEAD ||
160 	    state == MDB_STATE_RUNNING)) {
161 		mdb_printf("- exercising pipelines\n");
162 		for (i = 0; i < 100; i++) {
163 			if (mdb_eval("::walk proc p | ::map *. | ::grep .==0 "
164 			    "| ::map <p | ::ps") != 0) {
165 				mdb_warn("ERROR: failed to eval pipeline");
166 				return (DCMD_ERR);
167 			}
168 		}
169 	}
170 
171 	return (DCMD_OK);
172 }
173 
174 static int
175 cmd_vread(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
176 {
177 	size_t nbytes;
178 	ssize_t rbytes;
179 	void *buf;
180 
181 	if (!(flags & DCMD_ADDRSPEC) || argc != 1)
182 		return (DCMD_USAGE);
183 
184 	if (argv->a_type == MDB_TYPE_STRING)
185 		nbytes = (size_t)mdb_strtoull(argv->a_un.a_str);
186 	else
187 		nbytes = (size_t)argv->a_un.a_val;
188 
189 	buf = mdb_alloc(nbytes, UM_SLEEP | UM_GC);
190 	rbytes = mdb_vread(buf, nbytes, addr);
191 
192 	if (rbytes >= 0) {
193 		mdb_printf("mdb_vread of %lu bytes returned %ld\n",
194 		    nbytes, rbytes);
195 	} else
196 		mdb_warn("mdb_vread returned %ld", rbytes);
197 
198 	return (DCMD_OK);
199 }
200 
201 /*ARGSUSED*/
202 static int
203 cmd_pread(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
204 {
205 	size_t nbytes;
206 	ssize_t rbytes;
207 	void *buf;
208 
209 	if (!(flags & DCMD_ADDRSPEC) || argc != 1)
210 		return (DCMD_USAGE);
211 
212 	if (argv->a_type == MDB_TYPE_STRING)
213 		nbytes = (size_t)mdb_strtoull(argv->a_un.a_str);
214 	else
215 		nbytes = (size_t)argv->a_un.a_val;
216 
217 	buf = mdb_alloc(nbytes, UM_SLEEP | UM_GC);
218 	rbytes = mdb_pread(buf, nbytes, mdb_get_dot());
219 
220 	if (rbytes >= 0) {
221 		mdb_printf("mdb_pread of %lu bytes returned %ld\n",
222 		    nbytes, rbytes);
223 	} else
224 		mdb_warn("mdb_pread returned %ld", rbytes);
225 
226 	return (DCMD_OK);
227 }
228 
229 /*ARGSUSED*/
230 static int
231 cmd_readsym(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
232 {
233 	size_t nbytes;
234 	ssize_t rbytes;
235 	void *buf;
236 
237 	if ((flags & DCMD_ADDRSPEC) || argc != 2 ||
238 	    argv->a_type != MDB_TYPE_STRING)
239 		return (DCMD_USAGE);
240 
241 	if (argv[1].a_type == MDB_TYPE_STRING)
242 		nbytes = (size_t)mdb_strtoull(argv[1].a_un.a_str);
243 	else
244 		nbytes = (size_t)argv[1].a_un.a_val;
245 
246 	buf = mdb_alloc(nbytes, UM_SLEEP | UM_GC);
247 	rbytes = mdb_readsym(buf, nbytes, argv->a_un.a_str);
248 
249 	if (rbytes >= 0) {
250 		mdb_printf("mdb_readsym of %lu bytes returned %ld\n",
251 		    nbytes, rbytes);
252 	} else
253 		mdb_warn("mdb_readsym returned %ld", rbytes);
254 
255 	return (DCMD_OK);
256 }
257 
258 static int
259 cmd_call_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
260 {
261 	const char *dcmd;
262 
263 	if (argc < 1 || argv->a_type != MDB_TYPE_STRING)
264 		return (DCMD_USAGE);
265 
266 	dcmd = argv->a_un.a_str;
267 	argv++;
268 	argc--;
269 
270 	if (mdb_call_dcmd(dcmd, addr, flags, argc, argv) == -1) {
271 		mdb_warn("failed to execute %s", dcmd);
272 		return (DCMD_ERR);
273 	}
274 
275 	return (DCMD_OK);
276 }
277 
278 /*ARGSUSED*/
279 static int
280 cmd_getsetdot(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
281 {
282 	if (argc != 0)
283 		return (DCMD_USAGE);
284 
285 	mdb_set_dot(0x12345678feedbeefULL);
286 
287 	if (mdb_get_dot() != 0x12345678feedbeefULL) {
288 		mdb_warn("mdb_get_dot() returned wrong value!\n");
289 		return (DCMD_ERR);
290 	}
291 
292 	return (DCMD_OK);
293 }
294 
295 /*
296  * kmdb doesn't export some of the symbols used by these tests - namely mdb and
297  * mdb_iob_.*.  We therefore can't use these tests with kmdb.
298  */
299 #ifndef _KMDB
300 static void
301 do_nputs_tests(const char *banner, uint_t flags,
302     size_t rows, size_t cols, size_t ocols)
303 {
304 	uint_t oflags;
305 	int i;
306 
307 	oflags = mdb_iob_getflags(mdb.m_out) &
308 	    (MDB_IOB_AUTOWRAP | MDB_IOB_INDENT);
309 
310 	mdb_printf("%s:\n", banner);
311 	for (i = 0; i < 8; i++)
312 		mdb_printf("0123456789");
313 	mdb_printf("\n");
314 
315 	mdb_iob_clrflags(mdb.m_out, MDB_IOB_AUTOWRAP | MDB_IOB_INDENT);
316 	mdb_iob_setflags(mdb.m_out, flags);
317 	mdb_iob_resize(mdb.m_out, rows, cols);
318 
319 	for (i = 0; i < 50; i++)
320 		mdb_printf(" xx");
321 	mdb_printf("\n");
322 
323 	mdb_iob_clrflags(mdb.m_out, flags);
324 	mdb_iob_setflags(mdb.m_out, oflags);
325 	mdb_iob_resize(mdb.m_out, rows, ocols);
326 }
327 
328 /*ARGSUSED*/
329 static int
330 cmd_nputs(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
331 {
332 	size_t rows = mdb.m_out->iob_rows;
333 	size_t cols = mdb.m_out->iob_cols;
334 
335 	if (argc != 0)
336 		return (DCMD_USAGE);
337 
338 	if (!(flags & DCMD_ADDRSPEC))
339 		addr = cols;
340 
341 	do_nputs_tests("tests with (~WRAP, ~INDENT)",
342 	    0, rows, addr, cols);
343 
344 	do_nputs_tests("tests with (WRAP, ~INDENT)",
345 	    MDB_IOB_AUTOWRAP, rows, addr, cols);
346 
347 	do_nputs_tests("tests with (~WRAP, INDENT)",
348 	    MDB_IOB_INDENT, rows, addr, cols);
349 
350 	do_nputs_tests("tests with (WRAP, INDENT)",
351 	    MDB_IOB_AUTOWRAP | MDB_IOB_INDENT, rows, addr, cols);
352 
353 	return (DCMD_OK);
354 }
355 #endif
356 
357 /*ARGSUSED*/
358 static int
359 cmd_printf(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
360 {
361 	if (argc != 2 || argv[0].a_type != MDB_TYPE_STRING)
362 		return (DCMD_USAGE);
363 
364 	if (argv[1].a_type == MDB_TYPE_STRING)
365 		mdb_printf(argv[0].a_un.a_str, argv[1].a_un.a_str);
366 	else
367 		mdb_printf(argv[0].a_un.a_str, argv[1].a_un.a_val);
368 
369 	return (DCMD_OK);
370 }
371 
372 /*ARGSUSED*/
373 static int
374 cmd_abort(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
375 {
376 	mdb_printf("hello"); /* stuff something in stdout's buffer */
377 	return (*((volatile int *)NULL));
378 }
379 
380 static const mdb_dcmd_t dcmds[] = {
381 	{ "runtest", NULL, "run MDB regression tests", cmd_runtest },
382 	{ "qsort", NULL, "qsort addresses", cmd_qsort },
383 	{ "praddr", NULL, "print addresses", cmd_praddr },
384 	{ "vread", ":nbytes", "call mdb_vread", cmd_vread },
385 	{ "pread", ":nbytes", "call mdb_pread", cmd_pread },
386 	{ "readsym", "symbol nbytes", "call mdb_readsym", cmd_readsym },
387 	{ "call_dcmd", "dcmd [ args ... ]", "call dcmd", cmd_call_dcmd },
388 	{ "getsetdot", NULL, "test get and set dot", cmd_getsetdot },
389 #ifndef _KMDB
390 	{ "nputs", "?", "test iob nputs engine", cmd_nputs },
391 #endif
392 	{ "printf", "fmt arg", "test printf engine", cmd_printf },
393 	{ "abort", NULL, "test unexpected dcmd abort", cmd_abort },
394 	{ NULL }
395 };
396 
397 static const mdb_walker_t walkers[] = {
398 	{ "countdown", "count down from 16 to 0", cd_init, cd_step, NULL },
399 	{ NULL }
400 };
401 
402 static const mdb_modinfo_t modinfo = { MDB_API_VERSION, dcmds, walkers };
403 
404 const mdb_modinfo_t *
405 _mdb_init(void)
406 {
407 	return (&modinfo);
408 }
409