1/*
2 * block queue tracing application
3 *
4 * Copyright (C) 2006 Alan D. Brunelle <Alan.Brunelle@hp.com>
5 *
6 *  This program is free software; you can redistribute it and/or modify
7 *  it under the terms of the GNU General Public License as published by
8 *  the Free Software Foundation; either version 2 of the License, or
9 *  (at your option) any later version.
10 *
11 *  This program is distributed in the hope that it will be useful,
12 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 *  GNU General Public License for more details.
15 *
16 *  You should have received a copy of the GNU General Public License
17 *  along with this program; if not, write to the Free Software
18 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19 *
20 */
21#include <stdio.h>
22#include <stdlib.h>
23#include <string.h>
24#include <errno.h>
25#include <sys/types.h>
26#include <sys/stat.h>
27#include <unistd.h>
28
29#include "blktrace.h"
30
31struct trace_info {
32	int bit_field;
33	char *string;
34};
35
36int data_is_native = -1;
37
38#define TRACE_TO_STRING(f)	{.bit_field = f, .string = #f}
39static struct trace_info traces[] = {
40	TRACE_TO_STRING( BLK_TC_READ ),
41	TRACE_TO_STRING( BLK_TC_WRITE ),
42	TRACE_TO_STRING( BLK_TC_BARRIER ),
43	TRACE_TO_STRING( BLK_TC_SYNC ),
44	TRACE_TO_STRING( BLK_TC_QUEUE ),
45	TRACE_TO_STRING( BLK_TC_REQUEUE ),
46	TRACE_TO_STRING( BLK_TC_ISSUE ),
47	TRACE_TO_STRING( BLK_TC_COMPLETE ),
48	TRACE_TO_STRING( BLK_TC_FS ),
49	TRACE_TO_STRING( BLK_TC_PC ),
50	TRACE_TO_STRING( BLK_TC_AHEAD ),
51	TRACE_TO_STRING( BLK_TC_META ),
52	TRACE_TO_STRING( BLK_TC_DISCARD ),
53};
54#define N_TRACES (sizeof(traces) / sizeof(struct trace_info))
55
56struct act_info {
57	__u32 val;
58	char *string;
59};
60
61#define ACT_TO_STRING(f)	{.val = f, .string = #f}
62static struct act_info acts[] = {
63	ACT_TO_STRING( __BLK_TA_QUEUE ),
64	ACT_TO_STRING( __BLK_TA_QUEUE ),
65	ACT_TO_STRING( __BLK_TA_BACKMERGE ),
66	ACT_TO_STRING( __BLK_TA_FRONTMERGE ),
67	ACT_TO_STRING( __BLK_TA_GETRQ ),
68	ACT_TO_STRING( __BLK_TA_SLEEPRQ ),
69	ACT_TO_STRING( __BLK_TA_REQUEUE ),
70	ACT_TO_STRING( __BLK_TA_ISSUE ),
71	ACT_TO_STRING( __BLK_TA_COMPLETE ),
72	ACT_TO_STRING( __BLK_TA_PLUG ),
73	ACT_TO_STRING( __BLK_TA_UNPLUG_IO ),
74	ACT_TO_STRING( __BLK_TA_UNPLUG_TIMER ),
75	ACT_TO_STRING( __BLK_TA_INSERT ),
76	ACT_TO_STRING( __BLK_TA_SPLIT ),
77	ACT_TO_STRING( __BLK_TA_BOUNCE ),
78	ACT_TO_STRING( __BLK_TA_REMAP )
79};
80#define N_ACTS (sizeof(acts) / sizeof(struct act_info))
81
82static char *act_to_str(__u32 action)
83{
84	static char buf[1024];
85	unsigned int i;
86	unsigned int act = action & 0xffff;
87	unsigned int trace = (action >> BLK_TC_SHIFT) & 0xffff;
88
89	if (act <= N_ACTS) {
90		sprintf(buf, "%s ", acts[act].string);
91		for (i = 0; i < N_TRACES; i++)
92			if (trace & (1 << i)) {
93				char buf2[1024];
94				sprintf(buf2, "| %s ", traces[i].string);
95				strcat(buf, buf2);
96			}
97	}
98	else
99		sprintf(buf, "Invalid action=%08x", action);
100
101	return buf;
102}
103
104static void dump_trace(FILE *ofp, char *prefix, struct blk_io_trace *bit)
105{
106	fprintf(ofp, "    Dump %s\n", prefix);
107	fprintf(ofp, "        %8s: %08x\n", "magic", bit->magic);
108	fprintf(ofp, "        %8s: %u\n", "sequence", bit->sequence);
109	fprintf(ofp, "        %8s: %llu\n", "time", (unsigned long long) bit->time);
110	fprintf(ofp, "        %8s: %llu\n", "sector", (unsigned long long) bit->sector);
111	fprintf(ofp, "        %8s: %u\n", "bytes", bit->bytes);
112	fprintf(ofp, "        %8s: %s\n", "action", act_to_str(bit->action));
113	fprintf(ofp, "        %8s: %u\n", "bytes", bit->bytes);
114	fprintf(ofp, "        %8s: %u\n", "cpu", bit->cpu);
115	fprintf(ofp, "        %8s: %u\n", "error", bit->error);
116	fprintf(ofp, "        %8s: %u\n", "pdu_len", bit->pdu_len);
117	fprintf(ofp, "        %8s: (%u,%u)\n\n", "device", MAJOR(bit->device),
118						           MINOR(bit->device));
119}
120
121static int process(FILE **fp, char *devname, char *file, unsigned int cpu)
122{
123#	define SWAP_BITS() do {						\
124		if (bit_save) {						\
125			struct blk_io_trace *tmp = bit_save;		\
126			bit_save = bit;					\
127			bit = tmp;					\
128		}							\
129		else {							\
130			bit_save = bit;					\
131			bit = malloc(sizeof(struct blk_io_trace));	\
132		}							\
133	} while (0)
134
135#	define INC_BAD(str) do {					\
136		nbad++;							\
137		fprintf(ofp, "    ----------------\n");			\
138		if (bit_save) dump_trace(ofp,"seq-1",bit_save);		\
139		dump_trace(ofp, str, bit);				\
140		SWAP_BITS();						\
141	} while (0)
142
143	size_t n;
144	FILE *ifp, *ofp;
145	__u32 save_device = 0, save_sequence = 0;
146	__u64 save_time = 0;
147	struct blk_io_trace *bit_save = NULL;
148	struct blk_io_trace *bit = malloc(sizeof(struct blk_io_trace));
149	unsigned int ngood = 0;
150	unsigned int nbad = 0;
151	unsigned int nbad_trace = 0, nbad_pdu = 0, nbad_cpu = 0;
152	unsigned int nbad_seq = 0, nbad_dev = 0, nbad_time = 0;
153	char ofname[1024];
154
155	ifp = fopen(file, "r");
156	if (!ifp)
157		return 0;
158
159	sprintf(ofname, "%s.verify.out", devname);
160
161	if (!*fp) {
162		*fp = fopen(ofname, "w");
163		if (*fp == NULL) {
164			fprintf(stderr,"Failed to open %s (%s), skipping\n",
165				ofname, strerror(errno));
166			fclose(ifp);
167			return 0;
168		}
169		fprintf(*fp, "\n---------------\n" );
170		fprintf(*fp, "Verifying %s\n", devname);
171	}
172
173	ofp = *fp;
174	while ((n = fread(bit, sizeof(struct blk_io_trace), 1, ifp)) == 1) {
175		if (ferror(ifp)) {
176			clearerr(ifp);
177			perror("fread");
178			break;
179		}
180		if (data_is_native == -1)
181			check_data_endianness(bit->magic);
182
183		trace_to_cpu(bit);
184
185		if (!CHECK_MAGIC(bit)) {
186			INC_BAD("bad trace");
187			continue;
188		}
189
190		if ((bit->magic & 0xff) != SUPPORTED_VERSION) {
191			fprintf(stderr, "unsupported trace version\n");
192			break;
193		}
194
195		if (bit->pdu_len) {
196			char *pdu_buf;
197
198			pdu_buf = malloc(bit->pdu_len);
199			n = fread(pdu_buf, bit->pdu_len, 1, ifp);
200			if (n == 0) {
201				INC_BAD("bad pdu");
202				nbad_seq++;
203				break;
204			}
205			free(pdu_buf);
206		}
207
208		if (bit->cpu != cpu) {
209			INC_BAD("bad cpu");
210			nbad_cpu++;
211			continue;
212		}
213
214		/*
215		 * skip notify traces, they don't have valid sequences
216		 */
217		if (bit->action & BLK_TC_ACT(BLK_TC_NOTIFY))
218			continue;
219
220		if (ngood) {
221			if (bit->sequence <= save_sequence) {
222				INC_BAD("bad seq");
223				nbad_seq++;
224				continue;
225			}
226			else if (bit->time <= save_time) {
227				INC_BAD("time regression");
228				nbad_time++;
229				continue;
230			}
231			else if (bit->device != save_device) {
232				INC_BAD("bad dev");
233				nbad_dev++;
234				continue;
235			}
236		}
237
238		save_sequence = bit->sequence;
239		save_time = bit->time;
240		save_device = bit->device;
241
242		ngood++;
243		SWAP_BITS();
244	}
245
246	if (n == 0 && !feof(ifp))
247		fprintf(stderr,"%s: fread failed %d/%s\n",
248		        file, errno, strerror(errno));
249	fclose(ifp);
250
251	fprintf(ofp, "    ---------------------\n");
252	fprintf(ofp, "    Summary for cpu %d:\n", cpu);
253	fprintf(ofp, "    %10d valid + %10d invalid (%5.1f%%) processed\n\n",
254		ngood, nbad,
255		ngood ? 100.0 * (float)ngood / (float)(ngood + nbad) : 0.0);
256
257	if (nbad) {
258		if (nbad_trace)
259			fprintf(ofp, "%8s %d traces\n", "", nbad_trace);
260		if (nbad_trace)
261			fprintf(ofp, "%8s %d pdu\n", "", nbad_pdu);
262		if (nbad_cpu)
263			fprintf(ofp, "%8s %d cpu\n", "", nbad_cpu);
264		if (nbad_seq)
265			fprintf(ofp, "%8s %d seq\n", "", nbad_seq);
266		if (nbad_dev)
267			fprintf(ofp, "%8s %d dev\n", "", nbad_dev);
268		if (nbad_time)
269			fprintf(ofp, "%8s %d time\n", "", nbad_time);
270		fprintf(ofp,"\n");
271	}
272
273	return nbad;
274}
275
276int main(int argc, char *argv[])
277{
278	char *devname;
279	struct stat st;
280	int i, cpu, nbad, rval = 0;
281	FILE *ofp;
282	char *ofname = malloc(1024);
283	char *fname = malloc(1024);
284
285	if (argc < 2) {
286		fprintf(stderr,"FATAL: Need device name(s)\n");
287		fprintf(stderr,"Usage: blkrawverify <dev> [<dev>...]\n");
288		exit(1);
289	}
290
291	for (i = 1; i < argc; i++) {
292		devname = argv[i];
293		sprintf(ofname, "%s.verify.out", devname);
294		ofp = NULL;
295
296		printf("Verifying %s\n", devname); fflush(stdout);
297		for (cpu = 0; ; cpu++) {
298			sprintf(fname, "%s.blktrace.%d", devname, cpu);
299			if (stat(fname, &st) < 0) {
300				if (cpu == 0) {
301					fprintf(stderr, "No tracefiles found for %s\n",
302						devname);
303					rval = 1;
304				}
305				break;
306			}
307			printf("    CPU %d ", cpu); fflush(stdout);
308			nbad = process(&ofp, devname, fname, cpu);
309			if (nbad) {
310				printf("-- %d bad", nbad);
311				rval = 1;
312			}
313			printf("\n");
314		}
315		if (ofp) {
316			fclose(ofp);
317			fprintf(stdout, "Wrote output to %s\n", ofname);
318		}
319	}
320
321	return rval;
322}
323