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