xref: /illumos-gate/usr/src/cmd/mdb/common/mdb/mdb_fmt.c (revision 7c478bd9)
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  * Format String Decoder
31  *
32  * This file provides the core engine for converting strings of format
33  * characters into formatted output.  The various format dcmds invoke the
34  * mdb_fmt_print() function below with a target, address space identifier,
35  * address, count, and format character, and it reads the required data from
36  * the target and prints the formatted output to stdout.  Since nearly two
37  * thirds of the format characters can be expressed as simple printf format
38  * strings, we implement the engine using the lookup table below.  Each entry
39  * provides either a pointer to a printf format string or a pointer to a
40  * function to perform special processing.  For the printf case, the
41  * corresponding data size in bytes is also supplied.  The printf processing
42  * code handles 1, 2, 4, and 8-byte reads into an unsigned integer container
43  * of the given size, and then simply calls mdb_iob_printf with the integer
44  * and format string. This handles all printf cases, except when unsigned
45  * promotion of an integer type in the varargs list does not perform the
46  * conversion we require to get the proper result.  With the current set of
47  * format characters, this case only occurs twice: we need a 4-byte float
48  * to get promoted to 8-byte double for the 'f' format so it can be
49  * correctly formatted by %f, and we need a 1-byte int8_t to get promoted
50  * with sign extension to a 4-byte int32_t for the 'v' format so it can be
51  * correctly formatted by %d.  We provide explicit functions to handle these
52  * cases, as well as to handle special format characters such as 'i', etc.
53  * We also provide a cmd_formats() dcmd function below which prints a table
54  * of the output formats and their sizes.  Format characters that provide
55  * custom functions provide their help description string explicitly.  All
56  * the printf formats have their help strings generated automatically by
57  * our printf "unparser" mdb_iob_format2str().
58  */
59 
60 #include <mdb/mdb_types.h>
61 #include <mdb/mdb_target.h>
62 #include <mdb/mdb_io.h>
63 #include <mdb/mdb_err.h>
64 #include <mdb/mdb_string.h>
65 #include <mdb/mdb_modapi.h>
66 #include <mdb/mdb.h>
67 
68 #define	FUNCP(p)	((void *)(p))	/* Cast to f_ptr type */
69 #define	SZ_NONE		((size_t)-1L)	/* Format does not change dot */
70 
71 typedef mdb_tgt_addr_t mdb_fmt_func_f(mdb_tgt_t *,
72     mdb_tgt_as_t, mdb_tgt_addr_t, size_t);
73 
74 /*
75  * There are several 'special' characters that are handled outside of
76  * mdb_fmt_print().  These are characters that write (vwWZ) and characters that
77  * match (lLM).  We include them here so that ::formats can display an
78  * appropriate message, but they are handled specially by write_arglist() and
79  * match_arglist() in mdb_cmds.c.
80  */
81 #define	FMT_NONE	0x0	/* Format character is not supported */
82 #define	FMT_FUNC	0x1	/* f_ptr is a mdb_fmt_func_f to call */
83 #define	FMT_PRINTF	0x2	/* f_ptr is a const char * format string */
84 #define	FMT_MATCH	0x4	/* Match command (not supported here) */
85 #define	FMT_WRITE	0x8	/* Command writes to address space */
86 
87 #define	FMT_TYPE(x)	((x) & 0x7) /* Excludes modifying flags (FMT_WRITE) */
88 
89 typedef struct mdb_fmt_desc {
90 	int f_type;		/* Type of format (see above) */
91 	void *f_ptr;		/* Data pointer (see above) */
92 	const char *f_help;	/* Additional help string */
93 	size_t f_size;		/* Size of type in bytes, or SZ_NONE */
94 } mdb_fmt_desc_t;
95 
96 static const char help_plus[] = "increment dot by the count";
97 static const char help_minus[] = "decrement dot by the count";
98 static const char help_escchr[] = "character using C character notation";
99 static const char help_swapint[] = "swap bytes and shorts";
100 static const char help_dotinstr[] = "address and disassembled instruction";
101 static const char help_instr[] = "disassembled instruction";
102 static const char help_escstr[] = "string using C string notation";
103 static const char help_time32[] = "decoded time32_t";
104 static const char help_carat[] = "decrement dot by increment * count";
105 static const char help_dot[] = "dot as symbol+offset";
106 #ifndef _KMDB
107 static const char help_f[] = "float";
108 #endif
109 static const char help_swapshort[] = "swap bytes";
110 static const char help_nl[] = "newline";
111 static const char help_ws[] = "whitespace";
112 static const char help_rawstr[] = "raw string";
113 static const char help_tab[] = "horizontal tab";
114 static const char help_sdbyte[] = "decimal signed int";
115 static const char help_time64[] = "decoded time64_t";
116 static const char help_binary[] = "binary unsigned long long";
117 static const char help_hex64[] = "hexadecimal long long";
118 static const char help_match32[] = "int";
119 static const char help_match64[] = "long long";
120 static const char help_match16[] = "short";
121 static const char help_uintptr[] = "hexadecimal uintptr_t";
122 
123 /*ARGSUSED*/
124 static mdb_tgt_addr_t
125 fmt_dot(mdb_tgt_t *t, mdb_tgt_as_t as, mdb_tgt_addr_t addr, size_t cnt)
126 {
127 	uint_t oflags = mdb_iob_getflags(mdb.m_out) & MDB_IOB_INDENT;
128 	char buf[24];
129 
130 	mdb_iob_clrflags(mdb.m_out, oflags);
131 
132 	if (mdb.m_flags & MDB_FL_PSYM) {
133 		while (cnt-- != 0)
134 			mdb_iob_printf(mdb.m_out, "%-#16lla%16T", addr);
135 	} else {
136 		(void) mdb_iob_snprintf(buf, sizeof (buf),
137 		    "%#llx:", (u_longlong_t)addr);
138 		while (cnt-- != 0)
139 			mdb_iob_printf(mdb.m_out, "%-16s%16T", buf);
140 	}
141 
142 	mdb_iob_setflags(mdb.m_out, oflags);
143 	mdb_nv_set_value(mdb.m_rvalue, addr);
144 	return (addr);
145 }
146 
147 #ifndef _KMDB
148 static mdb_tgt_addr_t
149 fmt_float(mdb_tgt_t *t, mdb_tgt_as_t as, mdb_tgt_addr_t addr, size_t cnt)
150 {
151 	float f;
152 	/*
153 	 * We need to handle float as a special case because we need it to be
154 	 * promoted to a double by virtue of appearing as a parameter, and all
155 	 * our generic format handling below is based on integer types.
156 	 */
157 	while (cnt-- != 0) {
158 		if (mdb_tgt_aread(t, as, &f, sizeof (f), addr) != sizeof (f)) {
159 			warn("failed to read data from target");
160 			break;
161 		}
162 		mdb_iob_printf(mdb.m_out, "%e", f);
163 		addr += sizeof (f);
164 	}
165 	return (addr);
166 }
167 #endif
168 
169 /*ARGSUSED*/
170 static mdb_tgt_addr_t
171 fmt_plus(mdb_tgt_t *t, mdb_tgt_as_t as, mdb_tgt_addr_t addr, size_t cnt)
172 {
173 	return (addr + cnt);
174 }
175 
176 /*ARGSUSED*/
177 static mdb_tgt_addr_t
178 fmt_minus(mdb_tgt_t *t, mdb_tgt_as_t as, mdb_tgt_addr_t addr, size_t cnt)
179 {
180 	return (addr - cnt);
181 }
182 
183 /*ARGSUSED*/
184 static mdb_tgt_addr_t
185 fmt_carat(mdb_tgt_t *t, mdb_tgt_as_t as, mdb_tgt_addr_t addr, size_t cnt)
186 {
187 	return (addr - (mdb.m_incr * cnt));
188 }
189 
190 /*ARGSUSED*/
191 static mdb_tgt_addr_t
192 fmt_nl(mdb_tgt_t *t, mdb_tgt_as_t as, mdb_tgt_addr_t addr, size_t cnt)
193 {
194 	while (cnt-- != 0)
195 		mdb_iob_nl(mdb.m_out);
196 
197 	return (addr);
198 }
199 
200 /*ARGSUSED*/
201 static mdb_tgt_addr_t
202 fmt_ws(mdb_tgt_t *t, mdb_tgt_as_t as, mdb_tgt_addr_t addr, size_t cnt)
203 {
204 	mdb_iob_ws(mdb.m_out, cnt);
205 	return (addr);
206 }
207 
208 /*ARGSUSED*/
209 static mdb_tgt_addr_t
210 fmt_tab(mdb_tgt_t *t, mdb_tgt_as_t as, mdb_tgt_addr_t addr, size_t cnt)
211 {
212 	size_t ts = mdb_iob_gettabstop(mdb.m_out);
213 
214 	mdb_iob_tabstop(mdb.m_out, cnt);
215 	mdb_iob_tab(mdb.m_out);
216 	mdb_iob_tabstop(mdb.m_out, ts);
217 
218 	return (addr);
219 }
220 
221 static mdb_tgt_addr_t
222 fmt_rawstr(mdb_tgt_t *t, mdb_tgt_as_t as, mdb_tgt_addr_t addr, size_t cnt)
223 {
224 	uint_t oflags = mdb_iob_getflags(mdb.m_out) & MDB_IOB_INDENT;
225 	char buf[BUFSIZ];
226 	ssize_t nbytes;
227 
228 	mdb_iob_clrflags(mdb.m_out, oflags);
229 
230 	for (; cnt-- != 0; addr++) {
231 		do {
232 			nbytes = mdb_tgt_readstr(t, as, buf, BUFSIZ, addr);
233 			if (nbytes > 0) {
234 				mdb_iob_puts(mdb.m_out, buf);
235 				addr += MIN(nbytes, BUFSIZ - 1);
236 			} else if (nbytes < 0) {
237 				warn("failed to read data from target");
238 				goto out;
239 			}
240 		} while (nbytes == BUFSIZ);
241 
242 		if (cnt != 0)
243 			mdb_iob_puts(mdb.m_out, "\\0");
244 	}
245 out:
246 	mdb_iob_setflags(mdb.m_out, oflags);
247 	return (addr);
248 }
249 
250 static mdb_tgt_addr_t
251 fmt_escstr(mdb_tgt_t *t, mdb_tgt_as_t as, mdb_tgt_addr_t addr, size_t cnt)
252 {
253 	uint_t oflags = mdb_iob_getflags(mdb.m_out) & MDB_IOB_INDENT;
254 	char buf[BUFSIZ];
255 	ssize_t nbytes;
256 	char *s;
257 
258 	mdb_iob_clrflags(mdb.m_out, oflags);
259 
260 	for (; cnt-- != 0; addr++) {
261 		do {
262 			nbytes = mdb_tgt_readstr(t, as, buf, BUFSIZ, addr);
263 			if (nbytes > 0) {
264 				s = strchr2esc(buf, strlen(buf));
265 				mdb_iob_puts(mdb.m_out, s);
266 				strfree(s);
267 				addr += MIN(nbytes, BUFSIZ - 1);
268 			} else if (nbytes < 0) {
269 				warn("failed to read data from target");
270 				goto out;
271 			}
272 		} while (nbytes == BUFSIZ);
273 
274 		if (cnt != 0)
275 			mdb_iob_puts(mdb.m_out, "\\0");
276 	}
277 out:
278 	mdb_iob_setflags(mdb.m_out, oflags);
279 	return (addr);
280 }
281 
282 static mdb_tgt_addr_t
283 fmt_escchr(mdb_tgt_t *t, mdb_tgt_as_t as, mdb_tgt_addr_t addr, size_t cnt)
284 {
285 	char *(*convert)(const char *, size_t);
286 	ssize_t nbytes;
287 	char *buf, *s;
288 
289 	if (mdb.m_flags & MDB_FL_ADB)
290 		convert = &strchr2adb;
291 	else
292 		convert = &strchr2esc;
293 
294 	buf = mdb_alloc(cnt + 1, UM_SLEEP);
295 	buf[cnt] = 0;
296 
297 	if ((nbytes = mdb_tgt_aread(t, as, buf, cnt, addr)) > 0) {
298 		s = convert(buf, nbytes);
299 		mdb_iob_puts(mdb.m_out, s);
300 		strfree(s);
301 		addr += nbytes;
302 	}
303 
304 	mdb_free(buf, cnt + 1);
305 	return (addr);
306 }
307 
308 static mdb_tgt_addr_t
309 fmt_swapshort(mdb_tgt_t *t, mdb_tgt_as_t as, mdb_tgt_addr_t addr, size_t cnt)
310 {
311 	ushort_t x;
312 
313 	while (cnt-- != 0) {
314 		if (mdb_tgt_aread(t, as, &x, sizeof (x), addr) == sizeof (x)) {
315 			x = (x << 8) | (x >> 8);
316 			mdb_iob_printf(mdb.m_out, "%-8x", x);
317 			mdb_nv_set_value(mdb.m_rvalue, x);
318 			addr += sizeof (x);
319 		} else {
320 			warn("failed to read data from target");
321 			break;
322 		}
323 	}
324 	return (addr);
325 }
326 
327 static mdb_tgt_addr_t
328 fmt_swapint(mdb_tgt_t *t, mdb_tgt_as_t as, mdb_tgt_addr_t addr, size_t cnt)
329 {
330 	uint_t x;
331 
332 	while (cnt-- != 0) {
333 		if (mdb_tgt_aread(t, as, &x, sizeof (x), addr) == sizeof (x)) {
334 			x = ((x << 24) | ((x << 8) & 0xff0000) |
335 			    ((x >> 8) & 0xff00) | ((x >> 24) & 0xff));
336 			mdb_iob_printf(mdb.m_out, "%-16x", x);
337 			mdb_nv_set_value(mdb.m_rvalue, x);
338 			addr += sizeof (x);
339 		} else {
340 			warn("failed to read data from target");
341 			break;
342 		}
343 	}
344 	return (addr);
345 }
346 
347 static mdb_tgt_addr_t
348 fmt_time32(mdb_tgt_t *t, mdb_tgt_as_t as, mdb_tgt_addr_t addr, size_t cnt)
349 {
350 	uint32_t x;
351 
352 	while (cnt-- != 0) {
353 		if (mdb_tgt_aread(t, as, &x, sizeof (x), addr) == sizeof (x)) {
354 			mdb_iob_printf(mdb.m_out, "%-24Y", (time_t)x);
355 			mdb_nv_set_value(mdb.m_rvalue, x);
356 			addr += sizeof (x);
357 		} else {
358 			warn("failed to read data from target");
359 			break;
360 		}
361 	}
362 	return (addr);
363 }
364 
365 static mdb_tgt_addr_t
366 fmt_time64(mdb_tgt_t *t, mdb_tgt_as_t as, mdb_tgt_addr_t addr, size_t cnt)
367 {
368 	uint64_t x;
369 
370 	while (cnt-- != 0) {
371 		if (mdb_tgt_aread(t, as, &x, sizeof (x), addr) == sizeof (x)) {
372 			mdb_iob_printf(mdb.m_out, "%-24Y", (time_t)x);
373 			mdb_nv_set_value(mdb.m_rvalue, x);
374 			addr += sizeof (x);
375 		} else {
376 			warn("failed to read data from target");
377 			break;
378 		}
379 	}
380 	return (addr);
381 }
382 
383 static mdb_tgt_addr_t
384 fmt_sdbyte(mdb_tgt_t *t, mdb_tgt_as_t as, mdb_tgt_addr_t addr, size_t cnt)
385 {
386 	int8_t x;
387 
388 	while (cnt-- != 0) {
389 		if (mdb_tgt_aread(t, as, &x, sizeof (x), addr) == sizeof (x)) {
390 			mdb_iob_printf(mdb.m_out, "%-8d", (int32_t)x);
391 			mdb_nv_set_value(mdb.m_rvalue, (uint8_t)x);
392 			addr += sizeof (x);
393 		} else {
394 			warn("failed to read data from target");
395 			break;
396 		}
397 	}
398 	return (addr);
399 }
400 
401 static mdb_tgt_addr_t
402 fmt_instr(mdb_tgt_t *t, mdb_tgt_as_t as, mdb_tgt_addr_t addr, size_t cnt)
403 {
404 	char buf[BUFSIZ];
405 	uintptr_t naddr;
406 
407 	while (cnt-- != 0) {
408 		naddr = mdb_dis_ins2str(mdb.m_disasm, t, as,
409 		    buf, sizeof (buf), addr);
410 		if (naddr == addr)
411 			return (addr); /* If we didn't move, we failed */
412 		mdb_iob_printf(mdb.m_out, "%s\n", buf);
413 		addr = naddr;
414 	}
415 	return (addr);
416 }
417 
418 static mdb_tgt_addr_t
419 fmt_dotinstr(mdb_tgt_t *t, mdb_tgt_as_t as, mdb_tgt_addr_t addr, size_t cnt)
420 {
421 	uint_t oflags = mdb_iob_getflags(mdb.m_out) & MDB_IOB_INDENT;
422 
423 	char buf[BUFSIZ];
424 	uintptr_t naddr;
425 	uint32_t i;
426 
427 	for (mdb_iob_clrflags(mdb.m_out, oflags); cnt-- != 0; addr = naddr) {
428 		if (mdb_tgt_aread(t, as, &i, sizeof (i), addr) != sizeof (i)) {
429 			warn("failed to read data from target");
430 			break; /* Fail if we can't read instruction */
431 		}
432 		naddr = mdb_dis_ins2str(mdb.m_disasm, t, as,
433 		    buf, sizeof (buf), addr);
434 		if (naddr == addr)
435 			break; /* Fail if we didn't advance */
436 		mdb_iob_printf(mdb.m_out, "%lx %x: %s\n", (long)addr, i, buf);
437 	}
438 
439 	mdb_iob_setflags(mdb.m_out, oflags);
440 	return (addr);
441 }
442 
443 static mdb_tgt_addr_t
444 fmt_binary(mdb_tgt_t *t, mdb_tgt_as_t as, mdb_tgt_addr_t addr, size_t cnt)
445 {
446 	uint64_t x;
447 
448 	while (cnt-- != 0) {
449 		if (mdb_tgt_aread(t, as, &x, sizeof (x), addr) == sizeof (x)) {
450 			mdb_iob_printf(mdb.m_out, "%-64s",
451 			    numtostr(x, 2, NTOS_UNSIGNED));
452 			mdb_nv_set_value(mdb.m_rvalue, x);
453 			addr += sizeof (x);
454 		} else {
455 			warn("failed to read data from target");
456 			break;
457 		}
458 	}
459 	return (addr);
460 }
461 
462 static mdb_tgt_addr_t
463 fmt_hex64(mdb_tgt_t *t, mdb_tgt_as_t as, mdb_tgt_addr_t addr, size_t cnt)
464 {
465 	const char *fmts[] = { "%-16llx", "%-17llx" };
466 	const uint64_t mask = 0xf000000000000000ull;
467 	uint64_t x;
468 
469 	while (cnt-- != 0) {
470 		if (mdb_tgt_aread(t, as, &x, sizeof (x), addr) == sizeof (x)) {
471 			mdb_iob_printf(mdb.m_out, fmts[(x & mask) != 0], x);
472 			mdb_nv_set_value(mdb.m_rvalue, x);
473 			addr += sizeof (x);
474 		} else {
475 			warn("failed to read data from target");
476 			break;
477 		}
478 	}
479 	return (addr);
480 }
481 
482 static const mdb_fmt_desc_t fmttab[] = {
483 	{ FMT_NONE, NULL, NULL, 0 },				/* 0 = NUL */
484 	{ FMT_NONE, NULL, NULL, 0 },				/* 1 = SOH */
485 	{ FMT_NONE, NULL, NULL, 0 },				/* 2 = STX */
486 	{ FMT_NONE, NULL, NULL, 0 },				/* 3 = ETX */
487 	{ FMT_NONE, NULL, NULL, 0 },				/* 4 = EOT */
488 	{ FMT_NONE, NULL, NULL, 0 },				/* 5 = ENQ */
489 	{ FMT_NONE, NULL, NULL, 0 },				/* 6 = ACK */
490 	{ FMT_NONE, NULL, NULL, 0 },				/* 7 = BEL */
491 	{ FMT_NONE, NULL, NULL, 0 },				/* 8 = BS */
492 	{ FMT_NONE, NULL, NULL, 0 },				/* 9 = \t */
493 	{ FMT_NONE, NULL, NULL, 0 },				/* 10 = \n */
494 	{ FMT_NONE, NULL, NULL, 0 },				/* 11 = VT */
495 	{ FMT_NONE, NULL, NULL, 0 },				/* 12 = FF */
496 	{ FMT_NONE, NULL, NULL, 0 },				/* 13 = \r */
497 	{ FMT_NONE, NULL, NULL, 0 },				/* 14 = SO */
498 	{ FMT_NONE, NULL, NULL, 0 },				/* 15 = SI */
499 	{ FMT_NONE, NULL, NULL, 0 },				/* 16 = DLE */
500 	{ FMT_NONE, NULL, NULL, 0 },				/* 17 = DC1 */
501 	{ FMT_NONE, NULL, NULL, 0 },				/* 18 = DC2 */
502 	{ FMT_NONE, NULL, NULL, 0 },				/* 19 = DC3 */
503 	{ FMT_NONE, NULL, NULL, 0 },				/* 20 = DC4 */
504 	{ FMT_NONE, NULL, NULL, 0 },				/* 21 = NAK */
505 	{ FMT_NONE, NULL, NULL, 0 },				/* 22 = EYC */
506 	{ FMT_NONE, NULL, NULL, 0 },				/* 23 = ETB */
507 	{ FMT_NONE, NULL, NULL, 0 },				/* 24 = CAN */
508 	{ FMT_NONE, NULL, NULL, 0 },				/* 25 = EM */
509 	{ FMT_NONE, NULL, NULL, 0 },				/* 26 = SUB */
510 	{ FMT_NONE, NULL, NULL, 0 },				/* 27 = ESC */
511 	{ FMT_NONE, NULL, NULL, 0 },				/* 28 = FS */
512 	{ FMT_NONE, NULL, NULL, 0 },				/* 29 = GS */
513 	{ FMT_NONE, NULL, NULL, 0 },				/* 30 = RS */
514 	{ FMT_NONE, NULL, NULL, 0 },				/* 31 = US */
515 	{ FMT_NONE, NULL, NULL, 0 },				/* 32 = SPACE */
516 	{ FMT_NONE, NULL, NULL, 0 },				/* 33 = ! */
517 	{ FMT_NONE, NULL, NULL, 0 },				/* 34 = " */
518 	{ FMT_NONE, NULL, NULL, 0 },				/* 35 = # */
519 	{ FMT_NONE, NULL, NULL, 0 },				/* 36 = $ */
520 	{ FMT_NONE, NULL, NULL, 0 },				/* 37 = % */
521 	{ FMT_NONE, NULL, NULL, 0 },				/* 38 = & */
522 	{ FMT_NONE, NULL, NULL, 0 },				/* 39 = ' */
523 	{ FMT_NONE, NULL, NULL, 0 },				/* 40 = ( */
524 	{ FMT_NONE, NULL, NULL, 0 },				/* 41 = ) */
525 	{ FMT_NONE, NULL, NULL, 0 },				/* 42 = * */
526 	{ FMT_FUNC, FUNCP(fmt_plus), help_plus, 0 },		/* 43 = + */
527 	{ FMT_NONE, NULL, NULL, 0 },				/* 44 = , */
528 	{ FMT_FUNC, FUNCP(fmt_minus), help_minus, 0 },		/* 45 = - */
529 	{ FMT_NONE, NULL, NULL, 0 },				/* 46 = . */
530 	{ FMT_NONE, NULL, NULL, 0 },				/* 47 = / */
531 	{ FMT_NONE, NULL, NULL, 0 },				/* 48 = 0 */
532 	{ FMT_NONE, NULL, NULL, 0 },				/* 49 = 1 */
533 	{ FMT_NONE, NULL, NULL, 0 },				/* 50 = 2 */
534 	{ FMT_NONE, NULL, NULL, 0 },				/* 51 = 3 */
535 	{ FMT_NONE, NULL, NULL, 0 },				/* 52 = 4 */
536 	{ FMT_NONE, NULL, NULL, 0 },				/* 53 = 5 */
537 	{ FMT_NONE, NULL, NULL, 0 },				/* 54 = 6 */
538 	{ FMT_NONE, NULL, NULL, 0 },				/* 55 = 7 */
539 	{ FMT_NONE, NULL, NULL, 0 },				/* 56 = 8 */
540 	{ FMT_NONE, NULL, NULL, 0 },				/* 57 = 9 */
541 	{ FMT_NONE, NULL, NULL, 0 },				/* 58 = : */
542 	{ FMT_NONE, NULL, NULL, 0 },				/* 59 = ; */
543 	{ FMT_NONE, NULL, NULL, 0 },				/* 60 = < */
544 	{ FMT_NONE, NULL, NULL, 0 },				/* 61 = = */
545 	{ FMT_NONE, NULL, NULL, 0 },				/* 62 = > */
546 	{ FMT_NONE, NULL, NULL, 0 },				/* 63 = ? */
547 	{ FMT_NONE, NULL, NULL, 0 },				/* 64 = @ */
548 	{ FMT_NONE, NULL, NULL, 0 },				/* 65 = A */
549 	{ FMT_PRINTF, "%-8x", NULL, 1 },			/* 66 = B */
550 	{ FMT_FUNC, FUNCP(fmt_escchr), help_escchr, 1 },	/* 67 = C */
551 	{ FMT_PRINTF, "%-16d", NULL, 4 },			/* 68 = D */
552 	{ FMT_PRINTF, "%-16llu", NULL, 8 },			/* 69 = E */
553 #ifdef _KMDB
554 	{ FMT_NONE, NULL, NULL, 0 },				/* 70 = F */
555 #else
556 	{ FMT_PRINTF, "%g", NULL, sizeof (double) },		/* 70 = F */
557 #endif
558 	{ FMT_PRINTF, "%-16llo", NULL, 8 },			/* 71 = G */
559 	{ FMT_FUNC, FUNCP(fmt_swapint), help_swapint, 4 },	/* 72 = H */
560 	{ FMT_FUNC, FUNCP(fmt_dotinstr), help_dotinstr, 0 },	/* 73 = I */
561 	{ FMT_FUNC, FUNCP(fmt_hex64), help_hex64, 8 },		/* 74 = J */
562 #ifdef _LP64
563 	{ FMT_FUNC, FUNCP(fmt_hex64), help_uintptr, 8 },	/* 75 = K (J) */
564 #else
565 	{ FMT_PRINTF, "%-16x", help_uintptr, 4 },		/* 75 = K (X) */
566 #endif
567 	{ FMT_MATCH, NULL, help_match32, 4 },			/* 76 = L */
568 	{ FMT_MATCH, NULL, help_match64, 8 },			/* 77 = M */
569 	{ FMT_FUNC, FUNCP(fmt_nl), help_nl, SZ_NONE },		/* 78 = N */
570 	{ FMT_PRINTF, "%-#16o", NULL, 4 },			/* 79 = O */
571 	{ FMT_PRINTF, "%-16a", NULL, sizeof (uintptr_t) },	/* 80 = P */
572 	{ FMT_PRINTF, "%-#16q", NULL, 4 },			/* 81 = Q */
573 	{ FMT_FUNC, FUNCP(fmt_binary), help_binary, 8 },	/* 82 = R */
574 	{ FMT_FUNC, FUNCP(fmt_escstr), help_escstr, 0 },	/* 83 = S */
575 	{ FMT_FUNC, FUNCP(fmt_tab), help_tab, SZ_NONE },	/* 84 = T */
576 	{ FMT_PRINTF, "%-16u", NULL, 4 },			/* 85 = U */
577 	{ FMT_PRINTF, "%-8u", NULL, 1 },			/* 86 = V */
578 	{ FMT_PRINTF|FMT_WRITE, "%-16r", NULL, 4 },		/* 87 = W */
579 	{ FMT_PRINTF, "%-16x", NULL, 4 },			/* 88 = X */
580 	{ FMT_FUNC, FUNCP(fmt_time32), help_time32, 4 },	/* 89 = Y */
581 	{ FMT_FUNC|FMT_WRITE, FUNCP(fmt_hex64), help_hex64, 8 }, /* 90 = Z */
582 	{ FMT_NONE, NULL, NULL, 0 },				/* 91 = [ */
583 	{ FMT_NONE, NULL, NULL, 0 },				/* 92 = \ */
584 	{ FMT_NONE, NULL, NULL, 0 },				/* 93 = ] */
585 	{ FMT_FUNC, FUNCP(fmt_carat), help_carat, 0 },		/* 94 = ^ */
586 	{ FMT_NONE, NULL, NULL, 0 },				/* 95 = _ */
587 	{ FMT_NONE, NULL, NULL, 0 },				/* 96 = ` */
588 	{ FMT_FUNC, FUNCP(fmt_dot), help_dot, SZ_NONE },	/* 97 = a */
589 	{ FMT_PRINTF, "%-#8o", NULL, 1 },			/* 98 = b */
590 	{ FMT_PRINTF, "%c", NULL, 1 },				/* 99 = c */
591 	{ FMT_PRINTF, "%-8hd", NULL, 2 },			/* 100 = d */
592 	{ FMT_PRINTF, "%-16lld", NULL, 8 },			/* 101 = e */
593 #ifdef _KMDB
594 	{ FMT_NONE, NULL, NULL, 0 },				/* 102 = f */
595 #else
596 	{ FMT_FUNC, FUNCP(fmt_float), help_f, sizeof (float) },	/* 102 = f */
597 #endif
598 	{ FMT_PRINTF, "%-16llq", NULL, 8 },			/* 103 = g */
599 	{ FMT_FUNC, FUNCP(fmt_swapshort), help_swapshort, 2 },	/* 104 = h */
600 	{ FMT_FUNC, FUNCP(fmt_instr), help_instr, 0 },		/* 105 = i */
601 	{ FMT_NONE, NULL, NULL, 0 },				/* 106 = j */
602 	{ FMT_NONE, NULL, NULL, 0 },				/* 107 = k */
603 	{ FMT_MATCH, NULL, help_match16, 2 },			/* 108 = l */
604 	{ FMT_NONE, NULL, NULL, 0 },				/* 109 = m */
605 	{ FMT_FUNC, FUNCP(fmt_nl), help_nl, SZ_NONE },		/* 110 = n */
606 	{ FMT_PRINTF, "%-#8ho", NULL, 2 },			/* 111 = o */
607 	{ FMT_PRINTF, "%-16a", NULL, sizeof (uintptr_t) },	/* 112 = p */
608 	{ FMT_PRINTF, "%-#8hq", NULL, 2 },			/* 113 = q */
609 	{ FMT_FUNC, FUNCP(fmt_ws), help_ws, SZ_NONE },		/* 114 = r */
610 	{ FMT_FUNC, FUNCP(fmt_rawstr), help_rawstr, 0 },	/* 115 = s */
611 	{ FMT_FUNC, FUNCP(fmt_tab), help_tab, SZ_NONE },	/* 116 = t */
612 	{ FMT_PRINTF, "%-8hu", NULL, 2 },			/* 117 = u */
613 	{ FMT_FUNC|FMT_WRITE, FUNCP(fmt_sdbyte), help_sdbyte, 1 }, /* 118 = v */
614 	{ FMT_PRINTF|FMT_WRITE, "%-8hr", NULL, 2 },		/* 119 = w */
615 	{ FMT_PRINTF, "%-8hx", NULL, 2 },			/* 120 = x */
616 	{ FMT_FUNC, FUNCP(fmt_time64), help_time64, 8 },	/* 121 = y */
617 	{ FMT_NONE, NULL, NULL, 0 },				/* 122 = z */
618 };
619 
620 mdb_tgt_addr_t
621 mdb_fmt_print(mdb_tgt_t *t, mdb_tgt_as_t as,
622     mdb_tgt_addr_t addr, size_t cnt, char fmt)
623 {
624 	const mdb_fmt_desc_t *fp = &fmttab[fmt];
625 	mdb_fmt_func_f *funcp;
626 	uintmax_t rvalue;
627 	void *buf;
628 
629 	union {
630 		uint64_t i8;
631 		uint32_t i4;
632 		uint16_t i2;
633 		uint8_t i1;
634 	} u;
635 
636 	if (fmt < 0 || fmt > (sizeof (fmttab) / sizeof (fmttab[0]))) {
637 		warn("invalid format character -- '%c'\n", fmt);
638 		return (addr);
639 	}
640 
641 	switch (FMT_TYPE(fp->f_type)) {
642 	case FMT_FUNC:
643 		funcp = (mdb_fmt_func_f *)fp->f_ptr;
644 		addr = funcp(t, as, addr, cnt);
645 		break;
646 
647 	case FMT_PRINTF:
648 		switch (fp->f_size) {
649 		case 1:
650 			buf = &u.i1;
651 			break;
652 		case 2:
653 			buf = &u.i2;
654 			break;
655 		case 4:
656 			buf = &u.i4;
657 			break;
658 		case 8:
659 			buf = &u.i8;
660 			break;
661 		default:
662 			fail("format %c is defined using illegal size\n", fmt);
663 		}
664 
665 		while (cnt-- != 0) {
666 			if (mdb_tgt_aread(t, as, buf, fp->f_size, addr) !=
667 			    fp->f_size) {
668 				warn("failed to read data from target");
669 				return (addr);
670 			}
671 
672 			switch (fp->f_size) {
673 			case 1:
674 				mdb_iob_printf(mdb.m_out, fp->f_ptr, u.i1);
675 				rvalue = u.i1;
676 				break;
677 			case 2:
678 				mdb_iob_printf(mdb.m_out, fp->f_ptr, u.i2);
679 				rvalue = u.i2;
680 				break;
681 			case 4:
682 				mdb_iob_printf(mdb.m_out, fp->f_ptr, u.i4);
683 				rvalue = u.i4;
684 				break;
685 			case 8:
686 				mdb_iob_printf(mdb.m_out, fp->f_ptr, u.i8);
687 				rvalue = u.i8;
688 				break;
689 			}
690 
691 			mdb_nv_set_value(mdb.m_rvalue, rvalue);
692 			addr += fp->f_size;
693 		}
694 		break;
695 
696 	default:
697 		warn("invalid format character -- '%c'\n", fmt);
698 	}
699 
700 	return (addr);
701 }
702 
703 /*ARGSUSED*/
704 int
705 cmd_formats(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
706 {
707 	const mdb_fmt_desc_t *fp = &fmttab[0];
708 	int i;
709 	const char *write;
710 
711 	if ((flags & DCMD_ADDRSPEC) || argc != 0)
712 		return (DCMD_USAGE);
713 
714 	for (i = 0; i < (sizeof (fmttab) / sizeof (fmttab[0])); i++, fp++) {
715 		if (fp->f_type == FMT_NONE)
716 			continue;
717 
718 		write = (fp->f_type & FMT_WRITE) ? "write " : "";
719 
720 		if (fp->f_type & FMT_FUNC)
721 			mdb_printf("%c - %s%s", i, write, fp->f_help);
722 		else if (fp->f_type & FMT_MATCH)
723 			mdb_printf("%c - match %s", i, fp->f_help);
724 		else if (fp->f_help != NULL)
725 			mdb_printf("%c - %s%s", i, write, fp->f_help);
726 		else
727 			mdb_printf("%c - %s%s", i, write,
728 			    mdb_iob_format2str(fp->f_ptr));
729 
730 		switch (fp->f_size) {
731 		case SZ_NONE:
732 			mdb_printf("\n");
733 			break;
734 		case 0:
735 			mdb_printf(" (variable size)\n");
736 			break;
737 		case 1:
738 			mdb_printf(" (1 byte)\n");
739 			break;
740 		default:
741 			mdb_printf(" (%lu bytes)\n", fp->f_size);
742 		}
743 	}
744 
745 	return (DCMD_OK);
746 }
747