xref: /illumos-gate/usr/src/cmd/mdb/common/mdb/mdb_dump.c (revision 2a8bcb4e)
1*7c478bd9Sstevel@tonic-gate /*
2*7c478bd9Sstevel@tonic-gate  * CDDL HEADER START
3*7c478bd9Sstevel@tonic-gate  *
4*7c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*7c478bd9Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*7c478bd9Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*7c478bd9Sstevel@tonic-gate  * with the License.
8*7c478bd9Sstevel@tonic-gate  *
9*7c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*7c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*7c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*7c478bd9Sstevel@tonic-gate  * and limitations under the License.
13*7c478bd9Sstevel@tonic-gate  *
14*7c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*7c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*7c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*7c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*7c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*7c478bd9Sstevel@tonic-gate  *
20*7c478bd9Sstevel@tonic-gate  * CDDL HEADER END
21*7c478bd9Sstevel@tonic-gate  */
22*7c478bd9Sstevel@tonic-gate /*
23*7c478bd9Sstevel@tonic-gate  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24*7c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
25*7c478bd9Sstevel@tonic-gate  */
26*7c478bd9Sstevel@tonic-gate 
27*7c478bd9Sstevel@tonic-gate #include <mdb/mdb_dump.h>
28*7c478bd9Sstevel@tonic-gate #include <mdb/mdb_modapi.h>
29*7c478bd9Sstevel@tonic-gate #include <mdb/mdb_nv.h>
30*7c478bd9Sstevel@tonic-gate #include <mdb/mdb_err.h>
31*7c478bd9Sstevel@tonic-gate #include <mdb/mdb.h>
32*7c478bd9Sstevel@tonic-gate #include <limits.h>
33*7c478bd9Sstevel@tonic-gate 
34*7c478bd9Sstevel@tonic-gate #define	DUMP_PARAGRAPH	16
35*7c478bd9Sstevel@tonic-gate #define	DUMP_WIDTH(x)	(DUMP_PARAGRAPH * ((((x) >> 16) & 0xf) + 1))
36*7c478bd9Sstevel@tonic-gate #define	DUMP_GROUP(x)	((((x) >> 20) & 0xff) + 1)
37*7c478bd9Sstevel@tonic-gate #define	DUMP_MAXWIDTH	DUMP_WIDTH(MDB_DUMP_WIDTH(0x10))
38*7c478bd9Sstevel@tonic-gate 
39*7c478bd9Sstevel@tonic-gate /*
40*7c478bd9Sstevel@tonic-gate  * This is the implementation of mdb's generic hexdump facility (though
41*7c478bd9Sstevel@tonic-gate  * not named such in case we decide to add support for other radices).
42*7c478bd9Sstevel@tonic-gate  * While it is possible to call mdb_dump_internal directly, it is
43*7c478bd9Sstevel@tonic-gate  * recommended that you use mdb_dumpptr or mdb_dump64 instead.
44*7c478bd9Sstevel@tonic-gate  */
45*7c478bd9Sstevel@tonic-gate 
46*7c478bd9Sstevel@tonic-gate 
47*7c478bd9Sstevel@tonic-gate /*
48*7c478bd9Sstevel@tonic-gate  * Output the header for the dump.  pad is the width of the address
49*7c478bd9Sstevel@tonic-gate  * field, and offset is the index of the byte that we want highlighted.
50*7c478bd9Sstevel@tonic-gate  * If the output isn't MDB_DUMP_ALIGNed, we use offset to adjust the
51*7c478bd9Sstevel@tonic-gate  * labels to reflect the true least significant address nibble.
52*7c478bd9Sstevel@tonic-gate  */
53*7c478bd9Sstevel@tonic-gate 
54*7c478bd9Sstevel@tonic-gate static void
mdb_dump_header(int flags,int pad,int offset)55*7c478bd9Sstevel@tonic-gate mdb_dump_header(int flags, int pad, int offset)
56*7c478bd9Sstevel@tonic-gate {
57*7c478bd9Sstevel@tonic-gate 	int	nalign = !(flags & MDB_DUMP_ALIGN);
58*7c478bd9Sstevel@tonic-gate 	int	group = DUMP_GROUP(flags);
59*7c478bd9Sstevel@tonic-gate 	int	width = DUMP_WIDTH(flags);
60*7c478bd9Sstevel@tonic-gate 	int	i;
61*7c478bd9Sstevel@tonic-gate 
62*7c478bd9Sstevel@tonic-gate 	mdb_printf("%*s  ", pad, "");
63*7c478bd9Sstevel@tonic-gate 	for (i = 0; i < width; i++) {
64*7c478bd9Sstevel@tonic-gate 		if (!(i % group))
65*7c478bd9Sstevel@tonic-gate 			mdb_printf((group == 1 && i && !(i % 8)) ? "  " : " ");
66*7c478bd9Sstevel@tonic-gate 		if (i == offset && !nalign)
67*7c478bd9Sstevel@tonic-gate 			mdb_printf("\\/");
68*7c478bd9Sstevel@tonic-gate 		else
69*7c478bd9Sstevel@tonic-gate 			mdb_printf("%2x", (i + (nalign * offset)) & 0xf);
70*7c478bd9Sstevel@tonic-gate 	}
71*7c478bd9Sstevel@tonic-gate 
72*7c478bd9Sstevel@tonic-gate 	if (flags & MDB_DUMP_ASCII) {
73*7c478bd9Sstevel@tonic-gate 		mdb_printf("  ");
74*7c478bd9Sstevel@tonic-gate 		for (i = 0; i < width; i++) {
75*7c478bd9Sstevel@tonic-gate 			if (i == offset && !nalign)
76*7c478bd9Sstevel@tonic-gate 				mdb_printf("v");
77*7c478bd9Sstevel@tonic-gate 			else
78*7c478bd9Sstevel@tonic-gate 				mdb_printf("%x", (i + (nalign * offset)) & 0xf);
79*7c478bd9Sstevel@tonic-gate 		}
80*7c478bd9Sstevel@tonic-gate 	}
81*7c478bd9Sstevel@tonic-gate 
82*7c478bd9Sstevel@tonic-gate 	mdb_printf("\n");
83*7c478bd9Sstevel@tonic-gate }
84*7c478bd9Sstevel@tonic-gate 
85*7c478bd9Sstevel@tonic-gate 
86*7c478bd9Sstevel@tonic-gate /*
87*7c478bd9Sstevel@tonic-gate  * Output a line of data.  pad is as defined above.  A non-zero lmargin
88*7c478bd9Sstevel@tonic-gate  * and/or rmargin indicate a set of bytes that shouldn't be printed.
89*7c478bd9Sstevel@tonic-gate  */
90*7c478bd9Sstevel@tonic-gate 
91*7c478bd9Sstevel@tonic-gate static void
mdb_dump_data(uint64_t addr,uchar_t * buf,int flags,int pad,int lmargin,int rmargin)92*7c478bd9Sstevel@tonic-gate mdb_dump_data(uint64_t addr, uchar_t *buf, int flags, int pad,
93*7c478bd9Sstevel@tonic-gate 	int lmargin, int rmargin)
94*7c478bd9Sstevel@tonic-gate {
95*7c478bd9Sstevel@tonic-gate 	uchar_t	abuf[DUMP_MAXWIDTH + 1];
96*7c478bd9Sstevel@tonic-gate 	int	group = DUMP_GROUP(flags);
97*7c478bd9Sstevel@tonic-gate 	int	width = DUMP_WIDTH(flags);
98*7c478bd9Sstevel@tonic-gate 	int	i;
99*7c478bd9Sstevel@tonic-gate #ifdef	_LITTLE_ENDIAN
100*7c478bd9Sstevel@tonic-gate 	int	flip = FALSE;
101*7c478bd9Sstevel@tonic-gate 
102*7c478bd9Sstevel@tonic-gate 	if (flags & MDB_DUMP_ENDIAN)
103*7c478bd9Sstevel@tonic-gate 		flip = TRUE;
104*7c478bd9Sstevel@tonic-gate #endif
105*7c478bd9Sstevel@tonic-gate 
106*7c478bd9Sstevel@tonic-gate 	mdb_printf("%0*llx: ", pad, addr);
107*7c478bd9Sstevel@tonic-gate 
108*7c478bd9Sstevel@tonic-gate 	for (i = 0; i < width; i++) {
109*7c478bd9Sstevel@tonic-gate 		if (!(i % group))
110*7c478bd9Sstevel@tonic-gate 			mdb_printf((group == 1 && i && !(i % 8)) ? "  " : " ");
111*7c478bd9Sstevel@tonic-gate 		if (i < lmargin || (width - i) <= rmargin) {
112*7c478bd9Sstevel@tonic-gate 			mdb_printf("  ");
113*7c478bd9Sstevel@tonic-gate #ifdef	_LITTLE_ENDIAN
114*7c478bd9Sstevel@tonic-gate 		} else if (flip) {
115*7c478bd9Sstevel@tonic-gate 			int j = group * ((i / group) + 1) - (i % group) - 1;
116*7c478bd9Sstevel@tonic-gate 			mdb_printf("%02x", buf[j]);
117*7c478bd9Sstevel@tonic-gate #endif
118*7c478bd9Sstevel@tonic-gate 		} else {
119*7c478bd9Sstevel@tonic-gate 			mdb_printf("%02x", buf[i]);
120*7c478bd9Sstevel@tonic-gate 		}
121*7c478bd9Sstevel@tonic-gate 	}
122*7c478bd9Sstevel@tonic-gate 
123*7c478bd9Sstevel@tonic-gate 	if (flags & MDB_DUMP_ASCII) {
124*7c478bd9Sstevel@tonic-gate 		for (i = 0; i < width; i++)
125*7c478bd9Sstevel@tonic-gate 			if (i < lmargin || (width - i) <= rmargin)
126*7c478bd9Sstevel@tonic-gate 				abuf[i] = ' ';
127*7c478bd9Sstevel@tonic-gate 			else if (buf[i] < ' ' || buf[i] > '~')
128*7c478bd9Sstevel@tonic-gate 				abuf[i] = '.';
129*7c478bd9Sstevel@tonic-gate 			else
130*7c478bd9Sstevel@tonic-gate 				abuf[i] = buf[i];
131*7c478bd9Sstevel@tonic-gate 		abuf[width] = '\0';
132*7c478bd9Sstevel@tonic-gate 		mdb_printf("  %s", abuf);
133*7c478bd9Sstevel@tonic-gate 	}
134*7c478bd9Sstevel@tonic-gate 
135*7c478bd9Sstevel@tonic-gate 	mdb_printf("\n");
136*7c478bd9Sstevel@tonic-gate }
137*7c478bd9Sstevel@tonic-gate 
138*7c478bd9Sstevel@tonic-gate 
139*7c478bd9Sstevel@tonic-gate /*
140*7c478bd9Sstevel@tonic-gate  * Given an address and a length, compute the number of characters
141*7c478bd9Sstevel@tonic-gate  * needed to display addresses within that range.
142*7c478bd9Sstevel@tonic-gate  */
143*7c478bd9Sstevel@tonic-gate 
144*7c478bd9Sstevel@tonic-gate static int
mdb_dump_pad(uint64_t addr,uint64_t len,int flags,int bytes)145*7c478bd9Sstevel@tonic-gate mdb_dump_pad(uint64_t addr, uint64_t len, int flags, int bytes)
146*7c478bd9Sstevel@tonic-gate {
147*7c478bd9Sstevel@tonic-gate 	uint64_t x;
148*7c478bd9Sstevel@tonic-gate 	int bits;
149*7c478bd9Sstevel@tonic-gate 
150*7c478bd9Sstevel@tonic-gate 	if (flags & MDB_DUMP_PEDANT) {
151*7c478bd9Sstevel@tonic-gate 		/*
152*7c478bd9Sstevel@tonic-gate 		 * Assume full width pointers
153*7c478bd9Sstevel@tonic-gate 		 */
154*7c478bd9Sstevel@tonic-gate 		bits = NBBY * bytes;
155*7c478bd9Sstevel@tonic-gate 	} else {
156*7c478bd9Sstevel@tonic-gate 		/*
157*7c478bd9Sstevel@tonic-gate 		 * Vary width based on address and length, but first
158*7c478bd9Sstevel@tonic-gate 		 * check to see if the address is relevant.
159*7c478bd9Sstevel@tonic-gate 		 */
160*7c478bd9Sstevel@tonic-gate 		if (len > 1 || (addr && len == 1))
161*7c478bd9Sstevel@tonic-gate 			len--;
162*7c478bd9Sstevel@tonic-gate 		if (flags & MDB_DUMP_RELATIVE)
163*7c478bd9Sstevel@tonic-gate 			x = len;
164*7c478bd9Sstevel@tonic-gate 		else
165*7c478bd9Sstevel@tonic-gate 			x = len + addr;
166*7c478bd9Sstevel@tonic-gate 
167*7c478bd9Sstevel@tonic-gate 		bits = 0;
168*7c478bd9Sstevel@tonic-gate 		while (x) {
169*7c478bd9Sstevel@tonic-gate 			bits++;
170*7c478bd9Sstevel@tonic-gate 			x >>= 1;
171*7c478bd9Sstevel@tonic-gate 		}
172*7c478bd9Sstevel@tonic-gate 	}
173*7c478bd9Sstevel@tonic-gate 
174*7c478bd9Sstevel@tonic-gate 	return ((bits + 3) / 4);
175*7c478bd9Sstevel@tonic-gate }
176*7c478bd9Sstevel@tonic-gate 
177*7c478bd9Sstevel@tonic-gate 
178*7c478bd9Sstevel@tonic-gate /*
179*7c478bd9Sstevel@tonic-gate  * The main dump routine, called by mdb_dump64 and (indirectly) by
180*7c478bd9Sstevel@tonic-gate  * mdb_dumpptr.  Arguments:
181*7c478bd9Sstevel@tonic-gate  *   addr  - the address to start dumping at
182*7c478bd9Sstevel@tonic-gate  *   len   - the amount of data to dump
183*7c478bd9Sstevel@tonic-gate  *   flags - to tune operation (see mdb_modapi.h)
184*7c478bd9Sstevel@tonic-gate  *   func  - callback function used to obtain data
185*7c478bd9Sstevel@tonic-gate  *   arg   - argument to pass to callback function
186*7c478bd9Sstevel@tonic-gate  *   bytes - size of pointer type
187*7c478bd9Sstevel@tonic-gate  */
188*7c478bd9Sstevel@tonic-gate 
189*7c478bd9Sstevel@tonic-gate int
mdb_dump_internal(uint64_t addr,uint64_t len,int flags,mdb_dump64_cb_t func,void * arg,int bytes)190*7c478bd9Sstevel@tonic-gate mdb_dump_internal(uint64_t addr, uint64_t len, int flags, mdb_dump64_cb_t func,
191*7c478bd9Sstevel@tonic-gate 	void *arg, int bytes)
192*7c478bd9Sstevel@tonic-gate {
193*7c478bd9Sstevel@tonic-gate 	uchar_t	buffers[2][DUMP_MAXWIDTH];
194*7c478bd9Sstevel@tonic-gate 	uchar_t	*buf, *pbuf;
195*7c478bd9Sstevel@tonic-gate 	uint64_t i;
196*7c478bd9Sstevel@tonic-gate 	ssize_t	j;
197*7c478bd9Sstevel@tonic-gate 	uint64_t addrmax;
198*7c478bd9Sstevel@tonic-gate 	uint64_t offset;	/* bytes between first position and addr */
199*7c478bd9Sstevel@tonic-gate 	uint64_t reqlen = len;	/* requested length */
200*7c478bd9Sstevel@tonic-gate 	int	l, r;		/* left and right margins */
201*7c478bd9Sstevel@tonic-gate 	int	pskip;		/* previous line was skipped */
202*7c478bd9Sstevel@tonic-gate 	int	pvalid;		/* previous line was valid (we may skip) */
203*7c478bd9Sstevel@tonic-gate 	int	bread, bwanted;	/* used to handle partial reads */
204*7c478bd9Sstevel@tonic-gate 	int	pad, n;
205*7c478bd9Sstevel@tonic-gate 	int	group, width;
206*7c478bd9Sstevel@tonic-gate 	int	err = 0;
207*7c478bd9Sstevel@tonic-gate 
208*7c478bd9Sstevel@tonic-gate 	addrmax = (1LL << (bytes * NBBY - 1)) - 1 + (1LL << (bytes * NBBY - 1));
209*7c478bd9Sstevel@tonic-gate 
210*7c478bd9Sstevel@tonic-gate 	/*
211*7c478bd9Sstevel@tonic-gate 	 * Ensure that len doesn't wrap around the end of addressable
212*7c478bd9Sstevel@tonic-gate 	 * memory.  Note that because we take an address and a length,
213*7c478bd9Sstevel@tonic-gate 	 * it isn't possible to dump from 0 to UINT64_MAX if
214*7c478bd9Sstevel@tonic-gate 	 * MDB_DUMP_TRIM is set.
215*7c478bd9Sstevel@tonic-gate 	 */
216*7c478bd9Sstevel@tonic-gate 	if (len && (len - 1 > addrmax - addr)) {
217*7c478bd9Sstevel@tonic-gate 		len = addrmax - addr;
218*7c478bd9Sstevel@tonic-gate 		if (addr || (addrmax < UINT64_MAX))
219*7c478bd9Sstevel@tonic-gate 			len++;
220*7c478bd9Sstevel@tonic-gate 	}
221*7c478bd9Sstevel@tonic-gate 
222*7c478bd9Sstevel@tonic-gate 	/*
223*7c478bd9Sstevel@tonic-gate 	 * If a) the grouping isn't a power of two, or
224*7c478bd9Sstevel@tonic-gate 	 *    b) the display width is not evenly divisible by the grouping
225*7c478bd9Sstevel@tonic-gate 	 * we ignore the specified grouping (and default to 4).
226*7c478bd9Sstevel@tonic-gate 	 */
227*7c478bd9Sstevel@tonic-gate 	group = DUMP_GROUP(flags);
228*7c478bd9Sstevel@tonic-gate 	width = DUMP_WIDTH(flags);
229*7c478bd9Sstevel@tonic-gate 	if (((group - 1) & group) || (width % group)) {
230*7c478bd9Sstevel@tonic-gate 		group = 4;
231*7c478bd9Sstevel@tonic-gate 		flags = (flags & 0xfffff) | MDB_DUMP_GROUP(group);
232*7c478bd9Sstevel@tonic-gate 	}
233*7c478bd9Sstevel@tonic-gate 
234*7c478bd9Sstevel@tonic-gate 	/*
235*7c478bd9Sstevel@tonic-gate 	 * If we are reordering bytes to adjust for endianness, turn
236*7c478bd9Sstevel@tonic-gate 	 * off text output, headers, and alignment to cut down on the
237*7c478bd9Sstevel@tonic-gate 	 * number of special cases (and confusing output).  For
238*7c478bd9Sstevel@tonic-gate 	 * correctness, we will continue to observe MDB_DUMP_TRIM, but
239*7c478bd9Sstevel@tonic-gate 	 * will truncate output if the specified length isn't a
240*7c478bd9Sstevel@tonic-gate 	 * multiple of the grouping.
241*7c478bd9Sstevel@tonic-gate 	 */
242*7c478bd9Sstevel@tonic-gate 	if (flags & MDB_DUMP_ENDIAN) {
243*7c478bd9Sstevel@tonic-gate 		flags &= ~(MDB_DUMP_ALIGN | MDB_DUMP_HEADER | MDB_DUMP_ASCII);
244*7c478bd9Sstevel@tonic-gate 		if (flags & MDB_DUMP_TRIM)
245*7c478bd9Sstevel@tonic-gate 			len -= len % group;
246*7c478bd9Sstevel@tonic-gate 	}
247*7c478bd9Sstevel@tonic-gate 
248*7c478bd9Sstevel@tonic-gate 	/*
249*7c478bd9Sstevel@tonic-gate 	 * If we are interested in seeing the data indexed relative to
250*7c478bd9Sstevel@tonic-gate 	 * the starting location, paragraph alignment is irrelevant.
251*7c478bd9Sstevel@tonic-gate 	 * The left margin will always be 0.
252*7c478bd9Sstevel@tonic-gate 	 */
253*7c478bd9Sstevel@tonic-gate 	if (flags & MDB_DUMP_RELATIVE) {
254*7c478bd9Sstevel@tonic-gate 		flags &= ~MDB_DUMP_ALIGN;
255*7c478bd9Sstevel@tonic-gate 		l = 0;
256*7c478bd9Sstevel@tonic-gate 	} else {
257*7c478bd9Sstevel@tonic-gate 		l = addr % DUMP_PARAGRAPH;
258*7c478bd9Sstevel@tonic-gate 	}
259*7c478bd9Sstevel@tonic-gate 
260*7c478bd9Sstevel@tonic-gate 	/*
261*7c478bd9Sstevel@tonic-gate 	 * Compute the width of our addresses, and adjust our starting
262*7c478bd9Sstevel@tonic-gate 	 * point based on the address and the state of the alignment
263*7c478bd9Sstevel@tonic-gate 	 * flag.
264*7c478bd9Sstevel@tonic-gate 	 */
265*7c478bd9Sstevel@tonic-gate 	pad = mdb_dump_pad(addr, len, flags, bytes);
266*7c478bd9Sstevel@tonic-gate 	if (flags & MDB_DUMP_ALIGN) {
267*7c478bd9Sstevel@tonic-gate 		len += l;
268*7c478bd9Sstevel@tonic-gate 		addr -= l;
269*7c478bd9Sstevel@tonic-gate 		offset = l;
270*7c478bd9Sstevel@tonic-gate 	} else {
271*7c478bd9Sstevel@tonic-gate 		offset = 0;
272*7c478bd9Sstevel@tonic-gate 	}
273*7c478bd9Sstevel@tonic-gate 
274*7c478bd9Sstevel@tonic-gate 	/*
275*7c478bd9Sstevel@tonic-gate 	 * Display the header (if appropriate), using the left margin
276*7c478bd9Sstevel@tonic-gate 	 * to determine what our column header offset should be.
277*7c478bd9Sstevel@tonic-gate 	 */
278*7c478bd9Sstevel@tonic-gate 	if (flags & MDB_DUMP_HEADER)
279*7c478bd9Sstevel@tonic-gate 		mdb_dump_header(flags, pad, l);
280*7c478bd9Sstevel@tonic-gate 
281*7c478bd9Sstevel@tonic-gate 	/*
282*7c478bd9Sstevel@tonic-gate 	 * If we aren't trimming and aligning the output, the left
283*7c478bd9Sstevel@tonic-gate 	 * margin is now irrelevant and should be zeroed.
284*7c478bd9Sstevel@tonic-gate 	 */
285*7c478bd9Sstevel@tonic-gate 	if (!(flags & MDB_DUMP_TRIM) || !(flags & MDB_DUMP_ALIGN))
286*7c478bd9Sstevel@tonic-gate 		l = 0;
287*7c478bd9Sstevel@tonic-gate 
288*7c478bd9Sstevel@tonic-gate 	/*
289*7c478bd9Sstevel@tonic-gate 	 * We haven't skipped the previous line, it isn't valid to skip
290*7c478bd9Sstevel@tonic-gate 	 * the current line, and we use buffer 0 first.  lint doesn't
291*7c478bd9Sstevel@tonic-gate 	 * realize that this implies pbuf won't be accessed until after
292*7c478bd9Sstevel@tonic-gate 	 * it is set, so we explicitly initialize that here, too.
293*7c478bd9Sstevel@tonic-gate 	 */
294*7c478bd9Sstevel@tonic-gate 	pskip = pvalid = FALSE;
295*7c478bd9Sstevel@tonic-gate 	pbuf = NULL;
296*7c478bd9Sstevel@tonic-gate 	n = 0;
297*7c478bd9Sstevel@tonic-gate 	r = 0;
298*7c478bd9Sstevel@tonic-gate 
299*7c478bd9Sstevel@tonic-gate 	for (i = 0; i < len && r == 0; i += width) {
300*7c478bd9Sstevel@tonic-gate 		/*
301*7c478bd9Sstevel@tonic-gate 		 * Select the current buffer.
302*7c478bd9Sstevel@tonic-gate 		 */
303*7c478bd9Sstevel@tonic-gate 		buf = buffers[n];
304*7c478bd9Sstevel@tonic-gate 
305*7c478bd9Sstevel@tonic-gate 		/*
306*7c478bd9Sstevel@tonic-gate 		 * We have a right margin only if we are on the last
307*7c478bd9Sstevel@tonic-gate 		 * line and either (1) MDB_DUMP_TRIM is set or (2) our
308*7c478bd9Sstevel@tonic-gate 		 * untrimmed output would require reading past the end
309*7c478bd9Sstevel@tonic-gate 		 * of addressable memory.  In either case, we clear
310*7c478bd9Sstevel@tonic-gate 		 * pvalid since we don't want to skip the last line.
311*7c478bd9Sstevel@tonic-gate 		 */
312*7c478bd9Sstevel@tonic-gate 		if ((uint64_t)width >= len - i) {
313*7c478bd9Sstevel@tonic-gate 			pvalid = FALSE;
314*7c478bd9Sstevel@tonic-gate 			if (flags & MDB_DUMP_TRIM)
315*7c478bd9Sstevel@tonic-gate 				r = width - (len - i);
316*7c478bd9Sstevel@tonic-gate 			if ((uint64_t)width - 1 > addrmax - (addr + i)) {
317*7c478bd9Sstevel@tonic-gate 				int nr = width - (addrmax - (addr + i)) - 1;
318*7c478bd9Sstevel@tonic-gate 				r = MAX(r, nr);
319*7c478bd9Sstevel@tonic-gate 			}
320*7c478bd9Sstevel@tonic-gate 		}
321*7c478bd9Sstevel@tonic-gate 
322*7c478bd9Sstevel@tonic-gate 		/*
323*7c478bd9Sstevel@tonic-gate 		 * Read data into the current buffer, obeying the left
324*7c478bd9Sstevel@tonic-gate 		 * and right margins.
325*7c478bd9Sstevel@tonic-gate 		 *
326*7c478bd9Sstevel@tonic-gate 		 * We handle read(2)-style partial results by
327*7c478bd9Sstevel@tonic-gate 		 * repeatedly calling the callback until we fill the
328*7c478bd9Sstevel@tonic-gate 		 * buffer, we get a 0 (end of file), or we get a -1
329*7c478bd9Sstevel@tonic-gate 		 * (error).  We take care to never read the same data
330*7c478bd9Sstevel@tonic-gate 		 * twice, though.
331*7c478bd9Sstevel@tonic-gate 		 *
332*7c478bd9Sstevel@tonic-gate 		 * mdb(1)-style partial results (i.e. EMDB_PARTIAL) are
333*7c478bd9Sstevel@tonic-gate 		 * treated like any other error.  If more exotic
334*7c478bd9Sstevel@tonic-gate 		 * handling is desired, the caller is free to wrap
335*7c478bd9Sstevel@tonic-gate 		 * their callback with an auxiliary function.  See
336*7c478bd9Sstevel@tonic-gate 		 * mdb_dumpptr and mdb_dump64 for examples of this.
337*7c478bd9Sstevel@tonic-gate 		 */
338*7c478bd9Sstevel@tonic-gate 		bread = l;
339*7c478bd9Sstevel@tonic-gate 		bwanted = width - r;
340*7c478bd9Sstevel@tonic-gate 		while (bread < bwanted) {
341*7c478bd9Sstevel@tonic-gate 			j = func(buf + bread, bwanted - bread,
342*7c478bd9Sstevel@tonic-gate 			    addr + i + bread, arg);
343*7c478bd9Sstevel@tonic-gate 			if (j <= 0) {
344*7c478bd9Sstevel@tonic-gate 				if (i + bread < offset) {
345*7c478bd9Sstevel@tonic-gate 					l++;
346*7c478bd9Sstevel@tonic-gate 					j = 1;
347*7c478bd9Sstevel@tonic-gate 				} else {
348*7c478bd9Sstevel@tonic-gate 					r += bwanted - bread;
349*7c478bd9Sstevel@tonic-gate 					pvalid = FALSE;
350*7c478bd9Sstevel@tonic-gate 					if (j == -1)
351*7c478bd9Sstevel@tonic-gate 						err = errno;
352*7c478bd9Sstevel@tonic-gate 					if (bread == l) {
353*7c478bd9Sstevel@tonic-gate 						i += width;
354*7c478bd9Sstevel@tonic-gate 						goto out;
355*7c478bd9Sstevel@tonic-gate 					}
356*7c478bd9Sstevel@tonic-gate 					break;
357*7c478bd9Sstevel@tonic-gate 				}
358*7c478bd9Sstevel@tonic-gate 			}
359*7c478bd9Sstevel@tonic-gate 			bread += j;
360*7c478bd9Sstevel@tonic-gate 		}
361*7c478bd9Sstevel@tonic-gate 
362*7c478bd9Sstevel@tonic-gate 		/*
363*7c478bd9Sstevel@tonic-gate 		 * If we are eliminating repeated lines, AND it is
364*7c478bd9Sstevel@tonic-gate 		 * valid to eliminate this line, AND the current line
365*7c478bd9Sstevel@tonic-gate 		 * is the same as the previous line, don't print the
366*7c478bd9Sstevel@tonic-gate 		 * current line.  If we didn't skip the previous line,
367*7c478bd9Sstevel@tonic-gate 		 * print an asterisk and set the previous-line-skipped
368*7c478bd9Sstevel@tonic-gate 		 * flag.
369*7c478bd9Sstevel@tonic-gate 		 *
370*7c478bd9Sstevel@tonic-gate 		 * Otherwise, print the line and clear the
371*7c478bd9Sstevel@tonic-gate 		 * previous-line-skipped flag.
372*7c478bd9Sstevel@tonic-gate 		 */
373*7c478bd9Sstevel@tonic-gate 		if ((flags & MDB_DUMP_SQUISH) && pvalid &&
374*7c478bd9Sstevel@tonic-gate 		    (memcmp(buf, pbuf, width) == 0)) {
375*7c478bd9Sstevel@tonic-gate 			if (!pskip) {
376*7c478bd9Sstevel@tonic-gate 				mdb_printf("*\n");
377*7c478bd9Sstevel@tonic-gate 				pskip = TRUE;
378*7c478bd9Sstevel@tonic-gate 			}
379*7c478bd9Sstevel@tonic-gate 		} else {
380*7c478bd9Sstevel@tonic-gate 			if (flags & MDB_DUMP_RELATIVE)
381*7c478bd9Sstevel@tonic-gate 				mdb_dump_data(i, buf, flags, pad, l, r);
382*7c478bd9Sstevel@tonic-gate 			else
383*7c478bd9Sstevel@tonic-gate 				mdb_dump_data(addr + i, buf, flags, pad, l, r);
384*7c478bd9Sstevel@tonic-gate 			pskip = FALSE;
385*7c478bd9Sstevel@tonic-gate 		}
386*7c478bd9Sstevel@tonic-gate 
387*7c478bd9Sstevel@tonic-gate 		/*
388*7c478bd9Sstevel@tonic-gate 		 * If we have a non-zero left margin then we don't have
389*7c478bd9Sstevel@tonic-gate 		 * a full buffer of data and we shouldn't try to skip
390*7c478bd9Sstevel@tonic-gate 		 * the next line.  It doesn't matter if the right
391*7c478bd9Sstevel@tonic-gate 		 * margin is non-zero since we'll fall out of the loop.
392*7c478bd9Sstevel@tonic-gate 		 */
393*7c478bd9Sstevel@tonic-gate 		if (!l)
394*7c478bd9Sstevel@tonic-gate 			pvalid = TRUE;
395*7c478bd9Sstevel@tonic-gate 
396*7c478bd9Sstevel@tonic-gate 		/*
397*7c478bd9Sstevel@tonic-gate 		 * Swap buffers, and zero the left margin.
398*7c478bd9Sstevel@tonic-gate 		 */
399*7c478bd9Sstevel@tonic-gate 		n = (n + 1) % 2;
400*7c478bd9Sstevel@tonic-gate 		pbuf = buf;
401*7c478bd9Sstevel@tonic-gate 		l = 0;
402*7c478bd9Sstevel@tonic-gate 	}
403*7c478bd9Sstevel@tonic-gate 
404*7c478bd9Sstevel@tonic-gate out:
405*7c478bd9Sstevel@tonic-gate 	/*
406*7c478bd9Sstevel@tonic-gate 	 * If we successfully dumped everything, update . to be the
407*7c478bd9Sstevel@tonic-gate 	 * address following that of the last byte requested.
408*7c478bd9Sstevel@tonic-gate 	 */
409*7c478bd9Sstevel@tonic-gate 	if (i - r - offset >= reqlen) {
410*7c478bd9Sstevel@tonic-gate 		if (flags & MDB_DUMP_NEWDOT)
411*7c478bd9Sstevel@tonic-gate 			mdb_set_dot(addr + offset + reqlen);
412*7c478bd9Sstevel@tonic-gate 	} else if (err) {
413*7c478bd9Sstevel@tonic-gate 		errno = err;
414*7c478bd9Sstevel@tonic-gate 		mdb_warn("failed to read data at %#llx", addr + i - r);
415*7c478bd9Sstevel@tonic-gate 		return (-1);
416*7c478bd9Sstevel@tonic-gate 	}
417*7c478bd9Sstevel@tonic-gate 
418*7c478bd9Sstevel@tonic-gate 	return (0);
419*7c478bd9Sstevel@tonic-gate }
420