xref: /titanic_50/usr/src/cmd/tnf/tnfxtract/tnfxtract.c (revision 7eea693d6b672899726e75993fddc4e95b52647f)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <unistd.h>
30 #include <stdlib.h>
31 #include <stdio.h>
32 #include <libintl.h>
33 #include <unistd.h>
34 #include <string.h>
35 #include <sys/types.h>
36 #include <sys/stat.h>
37 #include <kvm.h>
38 #include <fcntl.h>
39 #include <sys/mman.h>
40 #include <sys/tnf.h>
41 #include <sys/tnf_com.h>
42 #include <nlist.h>
43 #include <errno.h>
44 
45 #define	TNFDEV		"/dev/tnfmap"
46 
47 #define	MAXFWTRY	5
48 
49 typedef struct {
50 	boolean_t	ever_read;
51 	tnf_uint32_t	generation;
52 	tnf_uint16_t	bytes_valid;
53 } BLOCK_STATUS;
54 
55 static char *dumpfile;		/* Dump file if extracting from crashdump */
56 static char *namelist;		/* Symbol tbl. if extracting from crashdump */
57 static kvm_t *kvm_p;		/* Handle for kvm_open, kvm_read, ... */
58 
59 static struct nlist kvm_syms[] = {
60 	{ "tnf_buf" },
61 	{ "tnf_trace_file_size" },
62 	{ NULL }
63 };
64 
65 static uintptr_t dump_bufaddr;
66 static size_t tnf_bufsize;
67 static char *program_name;
68 static int input_fd;
69 static int output_fd;
70 static tnf_file_header_t *tnf_header;
71 
72 /*
73  * usage() - gives a description of the arguments, and exits
74  */
75 
76 static void
77 usage(char *argv[], const char *msg)
78 {
79 	if (msg)
80 		(void) fprintf(stderr,
81 			gettext("%s: %s\n"), argv[0], msg);
82 
83 	(void) fprintf(stderr, gettext(
84 	    "usage: %s [-d <dumpfile> -n <symbolfile> ] "
85 	    "<output-filename>\n"), argv[0]);
86 	exit(1);
87 }				/* end usage */
88 
89 
90 /*
91  * Write 'size' bytes at offset 'offset' from 'addr'
92  * to the output file.  Bail out unceremoniously if anything goes wrong.
93  */
94 static void
95 writeout(char *addr, int offset, int size)
96 
97 {
98 	if (lseek(output_fd, offset, SEEK_SET) < 0) {
99 		perror("lseek");
100 		exit(1);
101 	}
102 	if (write(output_fd, addr, size) != size) {
103 		perror("write");
104 		exit(1);
105 	}
106 }
107 
108 
109 static void
110 dumpfile_init()
111 
112 {
113 	kvm_p = kvm_open(namelist, dumpfile, NULL, O_RDONLY, program_name);
114 	if (kvm_p == NULL) {
115 		/* kvm_open prints an error message */
116 		exit(1);
117 	}
118 	if (kvm_nlist(kvm_p, kvm_syms) != 0) {
119 		(void) fprintf(stderr, gettext(
120 			"Symbol lookup error in %s\n"), namelist);
121 		exit(1);
122 	}
123 	if (kvm_read(kvm_p, kvm_syms[0].n_value, (char *) &dump_bufaddr,
124 	    sizeof (dump_bufaddr)) != sizeof (dump_bufaddr) ||
125 	    kvm_read(kvm_p, kvm_syms[1].n_value, (char *) &tnf_bufsize,
126 	    sizeof (tnf_bufsize)) != sizeof (tnf_bufsize)) {
127 		(void) fprintf(stderr, gettext(
128 			"kvm_read error in %s\n"), dumpfile);
129 		exit(1);
130 	}
131 	if (dump_bufaddr == NULL || tnf_bufsize == 0) {
132 		(void) fprintf(stderr, gettext(
133 			"No trace data available in the kernel.\n"));
134 		exit(1);
135 	}
136 }
137 
138 static void
139 live_kernel_init()
140 
141 {
142 	tifiocstate_t tstate;
143 
144 	if ((input_fd = open(TNFDEV, O_RDWR)) < 0) {
145 		perror(TNFDEV);
146 		exit(1);
147 	}
148 	if (ioctl(input_fd, TIFIOCGSTATE, &tstate) < 0) {
149 		perror(gettext("Error getting trace system state"));
150 		exit(1);
151 	}
152 	if (tstate.buffer_state != TIFIOCBUF_OK) {
153 		(void) fprintf(stderr, gettext(
154 		    "No trace data available in the kernel.\n"));
155 		exit(1);
156 	}
157 	tnf_bufsize = tstate.buffer_size;
158 }
159 
160 static void
161 read_tnf_header(char *addr)
162 
163 {
164 	if (dumpfile != NULL) {
165 		if (kvm_read(kvm_p, dump_bufaddr, addr, 512) != 512) {
166 			(void) fprintf(stderr, gettext(
167 			    "Error reading tnf header from dump file.\n"));
168 			exit(1);
169 		}
170 	} else {
171 		if (ioctl(input_fd, TIFIOCGHEADER, addr) != 0) {
172 			perror(gettext("Error reading tnf header from kernel"));
173 			exit(1);
174 		}
175 	}
176 }
177 
178 static int
179 read_tnf_block(tnf_block_header_t *addr, int block_num)
180 
181 {
182 	int offset;
183 	tifiocgblock_t ioctl_arg;
184 
185 	if (dumpfile != NULL) {
186 		offset = tnf_header->directory_size +
187 		    block_num * tnf_header->block_size;
188 		if (kvm_read(kvm_p, dump_bufaddr + offset, (char *) addr,
189 		    tnf_header->block_size) != tnf_header->block_size) {
190 			(void) fprintf(stderr, gettext(
191 			    "Error reading tnf block.\n"));
192 			exit(1);
193 		}
194 	} else {
195 		ioctl_arg.dst_addr = (char *) addr;
196 		ioctl_arg.block_num = block_num;
197 		if (ioctl(input_fd, TIFIOCGBLOCK, &ioctl_arg) < 0) {
198 			if (errno == EBUSY)
199 				return (EBUSY);
200 			perror(gettext("Error reading tnf block"));
201 			exit(1);
202 		}
203 	}
204 	return (0);
205 }
206 
207 static void
208 read_tnf_fwzone(tnf_ref32_t *dest, int start, int slots)
209 
210 {
211 	int offset;
212 	int len;
213 	tifiocgfw_t ioctl_arg;
214 
215 	if (dumpfile != NULL) {
216 		/* LINTED assignment of 64-bit integer to 32-bit integer */
217 		offset = tnf_header->block_size + start * sizeof (tnf_ref32_t);
218 		/* LINTED assignment of 64-bit integer to 32-bit integer */
219 		len = slots * sizeof (tnf_ref32_t);
220 		if (kvm_read(kvm_p, dump_bufaddr + offset, (char *) dest,
221 		    len) != len) {
222 			(void) fprintf(stderr, gettext(
223 			    "Error reading tnf forwarding zone.\n"));
224 			exit(1);
225 		}
226 	} else {
227 		/* LINTED pointer cast may result in improper alignment */
228 		ioctl_arg.dst_addr = (long *) dest;
229 		ioctl_arg.start = start;
230 		ioctl_arg.slots = slots;
231 		if (ioctl(input_fd, TIFIOCGFWZONE, &ioctl_arg) < 0) {
232 			perror(gettext("Error reading tnf block"));
233 			exit(1);
234 		}
235 	}
236 }
237 
238 int
239 main(int argc, char *argv[])
240 {
241 	const char *optstr = "d:n:";
242 	const char *outfile;
243 	char *local_buf;
244 	int c;
245 	tnf_uint32_t *magicp;
246 	tnf_block_header_t *block_base, *blockp;
247 	BLOCK_STATUS *block_stat, *bsp;
248 	int block_num;
249 	boolean_t any_unread, any_different, retry;
250 	tnf_ref32_t *fwzone;
251 	int fwzonesize;
252 	int i;
253 	int fwtries;
254 	int block_count;
255 
256 	program_name = argv[0];
257 	while ((c = getopt(argc, argv, optstr)) != EOF) {
258 		switch (c) {
259 		case 'd':
260 		    dumpfile = optarg;
261 		    break;
262 		case 'n':
263 		    namelist = optarg;
264 		    break;
265 		case '?':
266 			usage(argv, gettext("unrecognized argument"));
267 		}
268 	}
269 	if (optind != argc - 1) {
270 		usage(argv, gettext("too many or too few arguments"));
271 	} else {
272 		outfile = argv[optind];
273 	}
274 	if ((dumpfile != NULL) ^ (namelist != NULL)) {
275 		usage(argv, gettext("must specify both or neither of the "
276 		    "-d and -n options"));
277 	}
278 
279 	output_fd = open(outfile, O_WRONLY | O_CREAT | O_TRUNC, 0600);
280 	if (output_fd < 0) {
281 		perror(outfile);
282 		exit(1);
283 	}
284 	if (dumpfile != NULL)
285 		dumpfile_init();
286 	else
287 		live_kernel_init();
288 
289 	if ((local_buf = malloc(tnf_bufsize)) == NULL) {
290 		(void) fprintf(stderr,
291 		    gettext("tnfxtract memory allocation failure\n"));
292 		exit(1);
293 	}
294 
295 	/* Read header, get block size, check for version mismatch */
296 	read_tnf_header(local_buf);
297 	/*LINTED pointer cast may result in improper alignment*/
298 	magicp = (tnf_uint32_t *) local_buf;
299 	/*LINTED pointer cast may result in improper alignment*/
300 	tnf_header = (tnf_file_header_t *)(local_buf + sizeof (*magicp));
301 	if (*magicp != TNF_MAGIC) {
302 		(void) fprintf(stderr, gettext(
303 		    "Buffer is not in TNF format.\n"));
304 		exit(1);
305 	}
306 	if (tnf_header->file_version != TNF_FILE_VERSION) {
307 		(void) fprintf(stderr,
308 		    gettext("Version mismatch (tnfxtract: %d; buffer: %d)\n"),
309 		    TNF_FILE_VERSION, tnf_header->file_version);
310 		exit(1);
311 	}
312 	writeout(local_buf, 0, tnf_header->block_size);
313 	/* LINTED pointer cast may result in improper alignment */
314 	block_base = (tnf_block_header_t *)
315 	    (local_buf + tnf_header->directory_size);
316 	block_count = tnf_header->block_count -
317 	    tnf_header->directory_size / tnf_header->block_size;
318 	fwzonesize = tnf_header->directory_size - tnf_header->block_size;
319 
320 	block_stat = (BLOCK_STATUS *)
321 	    calloc(block_count, sizeof (BLOCK_STATUS));
322 	if (block_stat == NULL) {
323 		(void) fprintf(stderr,
324 		    gettext("tnfxtract memory allocation failure\n"));
325 		exit(1);
326 	}
327 
328 	for (bsp = block_stat; bsp != block_stat + block_count; ++bsp)
329 		bsp->ever_read = B_FALSE;
330 	/*
331 	 * Make repeated passes until we've read every non-tag block.
332 	 */
333 	do {
334 		any_unread = B_FALSE;
335 		bsp = block_stat;
336 		block_num = 0;
337 		blockp = block_base;
338 		while (block_num != block_count) {
339 			if (!bsp->ever_read) {
340 				if (read_tnf_block(blockp, block_num) != 0)
341 					any_unread = B_TRUE;
342 				else {
343 					bsp->ever_read = B_TRUE;
344 					bsp->generation = blockp->generation;
345 					bsp->bytes_valid = blockp->bytes_valid;
346 					writeout((char *) blockp,
347 					/* LINTED cast 64 to 32 bit */
348 					(int)((char *) blockp - local_buf),
349 					    tnf_header->block_size);
350 				}
351 			}
352 			++bsp;
353 			++block_num;
354 		/* LINTED pointer cast may result in improper alignment */
355 			blockp = (tnf_block_header_t *)
356 			    ((char *) blockp + tnf_header->block_size);
357 		}
358 	} while (any_unread);
359 
360 	/*
361 	 * Then read tag blocks only, until we have two consecutive,
362 	 * consistent reads.
363 	 */
364 	do {
365 		any_different = B_FALSE;
366 		bsp = block_stat;
367 		block_num = 0;
368 		blockp = block_base;
369 		while (block_num != block_count) {
370 			if (read_tnf_block(blockp, block_num) == 0 &&
371 			    blockp->generation == TNF_TAG_GENERATION_NUM &&
372 			    (bsp->generation != TNF_TAG_GENERATION_NUM ||
373 			    bsp->bytes_valid != blockp->bytes_valid)) {
374 				bsp->generation = TNF_TAG_GENERATION_NUM;
375 				bsp->bytes_valid = blockp->bytes_valid;
376 				writeout((char *) blockp,
377 				/* LINTED cast 64bit to 32 bit */
378 				    (int)((char *) blockp - local_buf),
379 				    tnf_header->block_size);
380 				any_different = B_TRUE;
381 			}
382 			++bsp;
383 			++block_num;
384 		/* LINTED pointer cast may result in improper alignment */
385 			blockp = (tnf_block_header_t *)
386 			    ((char *) blockp + tnf_header->block_size);
387 		}
388 	} while (any_different);
389 
390 	/*
391 	 * Then read the forwarding pointers.  If any are -1:
392 	 * sleep briefly, then make another pass.
393 	 */
394 	/*LINTED pointer cast may result in improper alignment*/
395 	fwzone = (tnf_ref32_t *)(local_buf + tnf_header->block_size);
396 
397 	read_tnf_fwzone(fwzone, 0,
398 		/* LINTED cast from 64-bit integer to 32-bit integer */
399 		(int)(fwzonesize / sizeof (fwzone[0])));
400 	fwtries = 0;
401 	while (fwtries != MAXFWTRY) {
402 		retry = B_FALSE;
403 		for (i = 0; i != fwzonesize / sizeof (fwzone[0]); ++i) {
404 			if (fwzone[i] == -1) {
405 				read_tnf_fwzone(&fwzone[i], i, 1);
406 				if (!retry) {
407 					retry = B_TRUE;
408 					++fwtries;
409 				}
410 			}
411 		}
412 		if (!retry)
413 			break;
414 		sleep(2);
415 	}
416 	if (fwtries == MAXFWTRY) {
417 		(void) fprintf(stderr, gettext(
418 		    "Warning:  forwarding pointers may "
419 		    "be invalid.\n"));
420 	}
421 	writeout((char *) fwzone, tnf_header->block_size, fwzonesize);
422 	return (0);
423 }
424