xref: /illumos-gate/usr/src/cmd/zstreamdump/zstreamdump.c (revision c3d26abc9ee97b4f60233556aadeb57e0bd30bb9)
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 /*
23  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*
28  * Copyright (c) 2013, 2014 by Delphix. All rights reserved.
29  * Copyright (c) 2014 Integros [integros.com]
30  */
31 
32 #include <ctype.h>
33 #include <libnvpair.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <strings.h>
37 #include <unistd.h>
38 #include <stddef.h>
39 
40 #include <sys/dmu.h>
41 #include <sys/zfs_ioctl.h>
42 #include <zfs_fletcher.h>
43 
44 /*
45  * If dump mode is enabled, the number of bytes to print per line
46  */
47 #define	BYTES_PER_LINE	16
48 /*
49  * If dump mode is enabled, the number of bytes to group together, separated
50  * by newlines or spaces
51  */
52 #define	DUMP_GROUPING	4
53 
54 uint64_t total_write_size = 0;
55 uint64_t total_stream_len = 0;
56 FILE *send_stream = 0;
57 boolean_t do_byteswap = B_FALSE;
58 boolean_t do_cksum = B_TRUE;
59 
60 static void
61 usage(void)
62 {
63 	(void) fprintf(stderr, "usage: zstreamdump [-v] [-C] [-d] < file\n");
64 	(void) fprintf(stderr, "\t -v -- verbose\n");
65 	(void) fprintf(stderr, "\t -C -- suppress checksum verification\n");
66 	(void) fprintf(stderr, "\t -d -- dump contents of blocks modified, "
67 	    "implies verbose\n");
68 	exit(1);
69 }
70 
71 static void *
72 safe_malloc(size_t size)
73 {
74 	void *rv = malloc(size);
75 	if (rv == NULL) {
76 		(void) fprintf(stderr, "ERROR; failed to allocate %zu bytes\n",
77 		    size);
78 		abort();
79 	}
80 	return (rv);
81 }
82 
83 /*
84  * ssread - send stream read.
85  *
86  * Read while computing incremental checksum
87  */
88 static size_t
89 ssread(void *buf, size_t len, zio_cksum_t *cksum)
90 {
91 	size_t outlen;
92 
93 	if ((outlen = fread(buf, len, 1, send_stream)) == 0)
94 		return (0);
95 
96 	if (do_cksum) {
97 		if (do_byteswap)
98 			fletcher_4_incremental_byteswap(buf, len, cksum);
99 		else
100 			fletcher_4_incremental_native(buf, len, cksum);
101 	}
102 	total_stream_len += len;
103 	return (outlen);
104 }
105 
106 static size_t
107 read_hdr(dmu_replay_record_t *drr, zio_cksum_t *cksum)
108 {
109 	ASSERT3U(offsetof(dmu_replay_record_t, drr_u.drr_checksum.drr_checksum),
110 	    ==, sizeof (dmu_replay_record_t) - sizeof (zio_cksum_t));
111 	size_t r = ssread(drr, sizeof (*drr) - sizeof (zio_cksum_t), cksum);
112 	if (r == 0)
113 		return (0);
114 	zio_cksum_t saved_cksum = *cksum;
115 	r = ssread(&drr->drr_u.drr_checksum.drr_checksum,
116 	    sizeof (zio_cksum_t), cksum);
117 	if (r == 0)
118 		return (0);
119 	if (!ZIO_CHECKSUM_IS_ZERO(&drr->drr_u.drr_checksum.drr_checksum) &&
120 	    !ZIO_CHECKSUM_EQUAL(saved_cksum,
121 	    drr->drr_u.drr_checksum.drr_checksum)) {
122 		fprintf(stderr, "invalid checksum\n");
123 		(void) printf("Incorrect checksum in record header.\n");
124 		(void) printf("Expected checksum = %llx/%llx/%llx/%llx\n",
125 		    saved_cksum.zc_word[0],
126 		    saved_cksum.zc_word[1],
127 		    saved_cksum.zc_word[2],
128 		    saved_cksum.zc_word[3]);
129 		return (0);
130 	}
131 	return (sizeof (*drr));
132 }
133 
134 /*
135  * Print part of a block in ASCII characters
136  */
137 static void
138 print_ascii_block(char *subbuf, int length)
139 {
140 	int i;
141 
142 	for (i = 0; i < length; i++) {
143 		char char_print = isprint(subbuf[i]) ? subbuf[i] : '.';
144 		if (i != 0 && i % DUMP_GROUPING == 0) {
145 			(void) printf(" ");
146 		}
147 		(void) printf("%c", char_print);
148 	}
149 	(void) printf("\n");
150 }
151 
152 /*
153  * print_block - Dump the contents of a modified block to STDOUT
154  *
155  * Assume that buf has capacity evenly divisible by BYTES_PER_LINE
156  */
157 static void
158 print_block(char *buf, int length)
159 {
160 	int i;
161 	/*
162 	 * Start printing ASCII characters at a constant offset, after
163 	 * the hex prints. Leave 3 characters per byte on a line (2 digit
164 	 * hex number plus 1 space) plus spaces between characters and
165 	 * groupings.
166 	 */
167 	int ascii_start = BYTES_PER_LINE * 3 +
168 	    BYTES_PER_LINE / DUMP_GROUPING + 2;
169 
170 	for (i = 0; i < length; i += BYTES_PER_LINE) {
171 		int j;
172 		int this_line_length = MIN(BYTES_PER_LINE, length - i);
173 		int print_offset = 0;
174 
175 		for (j = 0; j < this_line_length; j++) {
176 			int buf_offset = i + j;
177 
178 			/*
179 			 * Separate every DUMP_GROUPING bytes by a space.
180 			 */
181 			if (buf_offset % DUMP_GROUPING == 0) {
182 				print_offset += printf(" ");
183 			}
184 
185 			/*
186 			 * Print the two-digit hex value for this byte.
187 			 */
188 			unsigned char hex_print = buf[buf_offset];
189 			print_offset += printf("%02x ", hex_print);
190 		}
191 
192 		(void) printf("%*s", ascii_start - print_offset, " ");
193 
194 		print_ascii_block(buf + i, this_line_length);
195 	}
196 }
197 
198 int
199 main(int argc, char *argv[])
200 {
201 	char *buf = safe_malloc(SPA_MAXBLOCKSIZE);
202 	uint64_t drr_record_count[DRR_NUMTYPES] = { 0 };
203 	uint64_t total_records = 0;
204 	dmu_replay_record_t thedrr;
205 	dmu_replay_record_t *drr = &thedrr;
206 	struct drr_begin *drrb = &thedrr.drr_u.drr_begin;
207 	struct drr_end *drre = &thedrr.drr_u.drr_end;
208 	struct drr_object *drro = &thedrr.drr_u.drr_object;
209 	struct drr_freeobjects *drrfo = &thedrr.drr_u.drr_freeobjects;
210 	struct drr_write *drrw = &thedrr.drr_u.drr_write;
211 	struct drr_write_byref *drrwbr = &thedrr.drr_u.drr_write_byref;
212 	struct drr_free *drrf = &thedrr.drr_u.drr_free;
213 	struct drr_spill *drrs = &thedrr.drr_u.drr_spill;
214 	struct drr_write_embedded *drrwe = &thedrr.drr_u.drr_write_embedded;
215 	struct drr_checksum *drrc = &thedrr.drr_u.drr_checksum;
216 	char c;
217 	boolean_t verbose = B_FALSE;
218 	boolean_t very_verbose = B_FALSE;
219 	boolean_t first = B_TRUE;
220 	/*
221 	 * dump flag controls whether the contents of any modified data blocks
222 	 * are printed to the console during processing of the stream. Warning:
223 	 * for large streams, this can obviously lead to massive prints.
224 	 */
225 	boolean_t dump = B_FALSE;
226 	int err;
227 	zio_cksum_t zc = { 0 };
228 	zio_cksum_t pcksum = { 0 };
229 
230 	while ((c = getopt(argc, argv, ":vCd")) != -1) {
231 		switch (c) {
232 		case 'C':
233 			do_cksum = B_FALSE;
234 			break;
235 		case 'v':
236 			if (verbose)
237 				very_verbose = B_TRUE;
238 			verbose = B_TRUE;
239 			break;
240 		case 'd':
241 			dump = B_TRUE;
242 			verbose = B_TRUE;
243 			very_verbose = B_TRUE;
244 			break;
245 		case ':':
246 			(void) fprintf(stderr,
247 			    "missing argument for '%c' option\n", optopt);
248 			usage();
249 			break;
250 		case '?':
251 			(void) fprintf(stderr, "invalid option '%c'\n",
252 			    optopt);
253 			usage();
254 		}
255 	}
256 
257 	if (isatty(STDIN_FILENO)) {
258 		(void) fprintf(stderr,
259 		    "Error: Backup stream can not be read "
260 		    "from a terminal.\n"
261 		    "You must redirect standard input.\n");
262 		exit(1);
263 	}
264 
265 	send_stream = stdin;
266 	while (read_hdr(drr, &zc)) {
267 
268 		/*
269 		 * If this is the first DMU record being processed, check for
270 		 * the magic bytes and figure out the endian-ness based on them.
271 		 */
272 		if (first) {
273 			if (drrb->drr_magic == BSWAP_64(DMU_BACKUP_MAGIC)) {
274 				do_byteswap = B_TRUE;
275 				if (do_cksum) {
276 					ZIO_SET_CHECKSUM(&zc, 0, 0, 0, 0);
277 					/*
278 					 * recalculate header checksum now
279 					 * that we know it needs to be
280 					 * byteswapped.
281 					 */
282 					fletcher_4_incremental_byteswap(drr,
283 					    sizeof (dmu_replay_record_t), &zc);
284 				}
285 			} else if (drrb->drr_magic != DMU_BACKUP_MAGIC) {
286 				(void) fprintf(stderr, "Invalid stream "
287 				    "(bad magic number)\n");
288 				exit(1);
289 			}
290 			first = B_FALSE;
291 		}
292 		if (do_byteswap) {
293 			drr->drr_type = BSWAP_32(drr->drr_type);
294 			drr->drr_payloadlen =
295 			    BSWAP_32(drr->drr_payloadlen);
296 		}
297 
298 		/*
299 		 * At this point, the leading fields of the replay record
300 		 * (drr_type and drr_payloadlen) have been byte-swapped if
301 		 * necessary, but the rest of the data structure (the
302 		 * union of type-specific structures) is still in its
303 		 * original state.
304 		 */
305 		if (drr->drr_type >= DRR_NUMTYPES) {
306 			(void) printf("INVALID record found: type 0x%x\n",
307 			    drr->drr_type);
308 			(void) printf("Aborting.\n");
309 			exit(1);
310 		}
311 
312 		drr_record_count[drr->drr_type]++;
313 		total_records++;
314 
315 		switch (drr->drr_type) {
316 		case DRR_BEGIN:
317 			if (do_byteswap) {
318 				drrb->drr_magic = BSWAP_64(drrb->drr_magic);
319 				drrb->drr_versioninfo =
320 				    BSWAP_64(drrb->drr_versioninfo);
321 				drrb->drr_creation_time =
322 				    BSWAP_64(drrb->drr_creation_time);
323 				drrb->drr_type = BSWAP_32(drrb->drr_type);
324 				drrb->drr_flags = BSWAP_32(drrb->drr_flags);
325 				drrb->drr_toguid = BSWAP_64(drrb->drr_toguid);
326 				drrb->drr_fromguid =
327 				    BSWAP_64(drrb->drr_fromguid);
328 			}
329 
330 			(void) printf("BEGIN record\n");
331 			(void) printf("\thdrtype = %lld\n",
332 			    DMU_GET_STREAM_HDRTYPE(drrb->drr_versioninfo));
333 			(void) printf("\tfeatures = %llx\n",
334 			    DMU_GET_FEATUREFLAGS(drrb->drr_versioninfo));
335 			(void) printf("\tmagic = %llx\n",
336 			    (u_longlong_t)drrb->drr_magic);
337 			(void) printf("\tcreation_time = %llx\n",
338 			    (u_longlong_t)drrb->drr_creation_time);
339 			(void) printf("\ttype = %u\n", drrb->drr_type);
340 			(void) printf("\tflags = 0x%x\n", drrb->drr_flags);
341 			(void) printf("\ttoguid = %llx\n",
342 			    (u_longlong_t)drrb->drr_toguid);
343 			(void) printf("\tfromguid = %llx\n",
344 			    (u_longlong_t)drrb->drr_fromguid);
345 			(void) printf("\ttoname = %s\n", drrb->drr_toname);
346 			if (verbose)
347 				(void) printf("\n");
348 
349 			if (drr->drr_payloadlen != 0) {
350 				nvlist_t *nv;
351 				int sz = drr->drr_payloadlen;
352 
353 				if (sz > SPA_MAXBLOCKSIZE) {
354 					free(buf);
355 					buf = safe_malloc(sz);
356 				}
357 				(void) ssread(buf, sz, &zc);
358 				if (ferror(send_stream))
359 					perror("fread");
360 				err = nvlist_unpack(buf, sz, &nv, 0);
361 				if (err)
362 					perror(strerror(err));
363 				nvlist_print(stdout, nv);
364 				nvlist_free(nv);
365 			}
366 			break;
367 
368 		case DRR_END:
369 			if (do_byteswap) {
370 				drre->drr_checksum.zc_word[0] =
371 				    BSWAP_64(drre->drr_checksum.zc_word[0]);
372 				drre->drr_checksum.zc_word[1] =
373 				    BSWAP_64(drre->drr_checksum.zc_word[1]);
374 				drre->drr_checksum.zc_word[2] =
375 				    BSWAP_64(drre->drr_checksum.zc_word[2]);
376 				drre->drr_checksum.zc_word[3] =
377 				    BSWAP_64(drre->drr_checksum.zc_word[3]);
378 			}
379 			/*
380 			 * We compare against the *previous* checksum
381 			 * value, because the stored checksum is of
382 			 * everything before the DRR_END record.
383 			 */
384 			if (do_cksum && !ZIO_CHECKSUM_EQUAL(drre->drr_checksum,
385 			    pcksum)) {
386 				(void) printf("Expected checksum differs from "
387 				    "checksum in stream.\n");
388 				(void) printf("Expected checksum = "
389 				    "%llx/%llx/%llx/%llx\n",
390 				    pcksum.zc_word[0],
391 				    pcksum.zc_word[1],
392 				    pcksum.zc_word[2],
393 				    pcksum.zc_word[3]);
394 			}
395 			(void) printf("END checksum = %llx/%llx/%llx/%llx\n",
396 			    drre->drr_checksum.zc_word[0],
397 			    drre->drr_checksum.zc_word[1],
398 			    drre->drr_checksum.zc_word[2],
399 			    drre->drr_checksum.zc_word[3]);
400 
401 			ZIO_SET_CHECKSUM(&zc, 0, 0, 0, 0);
402 			break;
403 
404 		case DRR_OBJECT:
405 			if (do_byteswap) {
406 				drro->drr_object = BSWAP_64(drro->drr_object);
407 				drro->drr_type = BSWAP_32(drro->drr_type);
408 				drro->drr_bonustype =
409 				    BSWAP_32(drro->drr_bonustype);
410 				drro->drr_blksz = BSWAP_32(drro->drr_blksz);
411 				drro->drr_bonuslen =
412 				    BSWAP_32(drro->drr_bonuslen);
413 				drro->drr_toguid = BSWAP_64(drro->drr_toguid);
414 			}
415 			if (verbose) {
416 				(void) printf("OBJECT object = %llu type = %u "
417 				    "bonustype = %u blksz = %u bonuslen = %u\n",
418 				    (u_longlong_t)drro->drr_object,
419 				    drro->drr_type,
420 				    drro->drr_bonustype,
421 				    drro->drr_blksz,
422 				    drro->drr_bonuslen);
423 			}
424 			if (drro->drr_bonuslen > 0) {
425 				(void) ssread(buf,
426 				    P2ROUNDUP(drro->drr_bonuslen, 8), &zc);
427 				if (dump) {
428 					print_block(buf,
429 					    P2ROUNDUP(drro->drr_bonuslen, 8));
430 				}
431 			}
432 			break;
433 
434 		case DRR_FREEOBJECTS:
435 			if (do_byteswap) {
436 				drrfo->drr_firstobj =
437 				    BSWAP_64(drrfo->drr_firstobj);
438 				drrfo->drr_numobjs =
439 				    BSWAP_64(drrfo->drr_numobjs);
440 				drrfo->drr_toguid = BSWAP_64(drrfo->drr_toguid);
441 			}
442 			if (verbose) {
443 				(void) printf("FREEOBJECTS firstobj = %llu "
444 				    "numobjs = %llu\n",
445 				    (u_longlong_t)drrfo->drr_firstobj,
446 				    (u_longlong_t)drrfo->drr_numobjs);
447 			}
448 			break;
449 
450 		case DRR_WRITE:
451 			if (do_byteswap) {
452 				drrw->drr_object = BSWAP_64(drrw->drr_object);
453 				drrw->drr_type = BSWAP_32(drrw->drr_type);
454 				drrw->drr_offset = BSWAP_64(drrw->drr_offset);
455 				drrw->drr_length = BSWAP_64(drrw->drr_length);
456 				drrw->drr_toguid = BSWAP_64(drrw->drr_toguid);
457 				drrw->drr_key.ddk_prop =
458 				    BSWAP_64(drrw->drr_key.ddk_prop);
459 			}
460 			/*
461 			 * If this is verbose and/or dump output,
462 			 * print info on the modified block
463 			 */
464 			if (verbose) {
465 				(void) printf("WRITE object = %llu type = %u "
466 				    "checksum type = %u\n"
467 				    "    offset = %llu length = %llu "
468 				    "props = %llx\n",
469 				    (u_longlong_t)drrw->drr_object,
470 				    drrw->drr_type,
471 				    drrw->drr_checksumtype,
472 				    (u_longlong_t)drrw->drr_offset,
473 				    (u_longlong_t)drrw->drr_length,
474 				    (u_longlong_t)drrw->drr_key.ddk_prop);
475 			}
476 			/*
477 			 * Read the contents of the block in from STDIN to buf
478 			 */
479 			(void) ssread(buf, drrw->drr_length, &zc);
480 			/*
481 			 * If in dump mode
482 			 */
483 			if (dump) {
484 				print_block(buf, drrw->drr_length);
485 			}
486 			total_write_size += drrw->drr_length;
487 			break;
488 
489 		case DRR_WRITE_BYREF:
490 			if (do_byteswap) {
491 				drrwbr->drr_object =
492 				    BSWAP_64(drrwbr->drr_object);
493 				drrwbr->drr_offset =
494 				    BSWAP_64(drrwbr->drr_offset);
495 				drrwbr->drr_length =
496 				    BSWAP_64(drrwbr->drr_length);
497 				drrwbr->drr_toguid =
498 				    BSWAP_64(drrwbr->drr_toguid);
499 				drrwbr->drr_refguid =
500 				    BSWAP_64(drrwbr->drr_refguid);
501 				drrwbr->drr_refobject =
502 				    BSWAP_64(drrwbr->drr_refobject);
503 				drrwbr->drr_refoffset =
504 				    BSWAP_64(drrwbr->drr_refoffset);
505 				drrwbr->drr_key.ddk_prop =
506 				    BSWAP_64(drrwbr->drr_key.ddk_prop);
507 			}
508 			if (verbose) {
509 				(void) printf("WRITE_BYREF object = %llu "
510 				    "checksum type = %u props = %llx\n"
511 				    "    offset = %llu length = %llu\n"
512 				    "toguid = %llx refguid = %llx\n"
513 				    "    refobject = %llu refoffset = %llu\n",
514 				    (u_longlong_t)drrwbr->drr_object,
515 				    drrwbr->drr_checksumtype,
516 				    (u_longlong_t)drrwbr->drr_key.ddk_prop,
517 				    (u_longlong_t)drrwbr->drr_offset,
518 				    (u_longlong_t)drrwbr->drr_length,
519 				    (u_longlong_t)drrwbr->drr_toguid,
520 				    (u_longlong_t)drrwbr->drr_refguid,
521 				    (u_longlong_t)drrwbr->drr_refobject,
522 				    (u_longlong_t)drrwbr->drr_refoffset);
523 			}
524 			break;
525 
526 		case DRR_FREE:
527 			if (do_byteswap) {
528 				drrf->drr_object = BSWAP_64(drrf->drr_object);
529 				drrf->drr_offset = BSWAP_64(drrf->drr_offset);
530 				drrf->drr_length = BSWAP_64(drrf->drr_length);
531 			}
532 			if (verbose) {
533 				(void) printf("FREE object = %llu "
534 				    "offset = %llu length = %lld\n",
535 				    (u_longlong_t)drrf->drr_object,
536 				    (u_longlong_t)drrf->drr_offset,
537 				    (longlong_t)drrf->drr_length);
538 			}
539 			break;
540 		case DRR_SPILL:
541 			if (do_byteswap) {
542 				drrs->drr_object = BSWAP_64(drrs->drr_object);
543 				drrs->drr_length = BSWAP_64(drrs->drr_length);
544 			}
545 			if (verbose) {
546 				(void) printf("SPILL block for object = %llu "
547 				    "length = %llu\n", drrs->drr_object,
548 				    drrs->drr_length);
549 			}
550 			(void) ssread(buf, drrs->drr_length, &zc);
551 			if (dump) {
552 				print_block(buf, drrs->drr_length);
553 			}
554 			break;
555 		case DRR_WRITE_EMBEDDED:
556 			if (do_byteswap) {
557 				drrwe->drr_object =
558 				    BSWAP_64(drrwe->drr_object);
559 				drrwe->drr_offset =
560 				    BSWAP_64(drrwe->drr_offset);
561 				drrwe->drr_length =
562 				    BSWAP_64(drrwe->drr_length);
563 				drrwe->drr_toguid =
564 				    BSWAP_64(drrwe->drr_toguid);
565 				drrwe->drr_lsize =
566 				    BSWAP_32(drrwe->drr_lsize);
567 				drrwe->drr_psize =
568 				    BSWAP_32(drrwe->drr_psize);
569 			}
570 			if (verbose) {
571 				(void) printf("WRITE_EMBEDDED object = %llu "
572 				    "offset = %llu length = %llu\n"
573 				    "    toguid = %llx comp = %u etype = %u "
574 				    "lsize = %u psize = %u\n",
575 				    (u_longlong_t)drrwe->drr_object,
576 				    (u_longlong_t)drrwe->drr_offset,
577 				    (u_longlong_t)drrwe->drr_length,
578 				    (u_longlong_t)drrwe->drr_toguid,
579 				    drrwe->drr_compression,
580 				    drrwe->drr_etype,
581 				    drrwe->drr_lsize,
582 				    drrwe->drr_psize);
583 			}
584 			(void) ssread(buf,
585 			    P2ROUNDUP(drrwe->drr_psize, 8), &zc);
586 			break;
587 		}
588 		if (drr->drr_type != DRR_BEGIN && very_verbose) {
589 			(void) printf("    checksum = %llx/%llx/%llx/%llx\n",
590 			    (longlong_t)drrc->drr_checksum.zc_word[0],
591 			    (longlong_t)drrc->drr_checksum.zc_word[1],
592 			    (longlong_t)drrc->drr_checksum.zc_word[2],
593 			    (longlong_t)drrc->drr_checksum.zc_word[3]);
594 		}
595 		pcksum = zc;
596 	}
597 	free(buf);
598 
599 	/* Print final summary */
600 
601 	(void) printf("SUMMARY:\n");
602 	(void) printf("\tTotal DRR_BEGIN records = %lld\n",
603 	    (u_longlong_t)drr_record_count[DRR_BEGIN]);
604 	(void) printf("\tTotal DRR_END records = %lld\n",
605 	    (u_longlong_t)drr_record_count[DRR_END]);
606 	(void) printf("\tTotal DRR_OBJECT records = %lld\n",
607 	    (u_longlong_t)drr_record_count[DRR_OBJECT]);
608 	(void) printf("\tTotal DRR_FREEOBJECTS records = %lld\n",
609 	    (u_longlong_t)drr_record_count[DRR_FREEOBJECTS]);
610 	(void) printf("\tTotal DRR_WRITE records = %lld\n",
611 	    (u_longlong_t)drr_record_count[DRR_WRITE]);
612 	(void) printf("\tTotal DRR_WRITE_BYREF records = %lld\n",
613 	    (u_longlong_t)drr_record_count[DRR_WRITE_BYREF]);
614 	(void) printf("\tTotal DRR_WRITE_EMBEDDED records = %lld\n",
615 	    (u_longlong_t)drr_record_count[DRR_WRITE_EMBEDDED]);
616 	(void) printf("\tTotal DRR_FREE records = %lld\n",
617 	    (u_longlong_t)drr_record_count[DRR_FREE]);
618 	(void) printf("\tTotal DRR_SPILL records = %lld\n",
619 	    (u_longlong_t)drr_record_count[DRR_SPILL]);
620 	(void) printf("\tTotal records = %lld\n",
621 	    (u_longlong_t)total_records);
622 	(void) printf("\tTotal write size = %lld (0x%llx)\n",
623 	    (u_longlong_t)total_write_size, (u_longlong_t)total_write_size);
624 	(void) printf("\tTotal stream length = %lld (0x%llx)\n",
625 	    (u_longlong_t)total_stream_len, (u_longlong_t)total_stream_len);
626 	return (0);
627 }
628