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