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