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