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
cd_init(mdb_walk_state_t * wsp)44 cd_init(mdb_walk_state_t *wsp)
45 {
46 wsp->walk_addr = 0xf;
47 return (WALK_NEXT);
48 }
49
50 static int
cd_step(mdb_walk_state_t * wsp)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
cmd_praddr(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)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
compare(const void * lp,const void * rp)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
cmd_qsort(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)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
cmd_runtest(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)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
cmd_vread(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)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
cmd_pread(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)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
cmd_readsym(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)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
cmd_call_dcmd(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)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
cmd_getsetdot(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)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
do_nputs_tests(const char * banner,uint_t flags,size_t rows,size_t cols,size_t ocols)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
cmd_nputs(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)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
cmd_printf(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)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
cmd_abort(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)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 *
_mdb_init(void)405 _mdb_init(void)
406 {
407 return (&modinfo);
408 }
409