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