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