Home | History | Annotate | Download | only in btt
      1 /*
      2  * blktrace output analysis: generate a timeline & gather statistics
      3  *
      4  * Copyright (C) 2006 Alan D. Brunelle <Alan.Brunelle (at) 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 
     22 #include <stdio.h>
     23 #include <stdlib.h>
     24 #include <getopt.h>
     25 #include <sys/types.h>
     26 #include <sys/stat.h>
     27 #include <fcntl.h>
     28 #include "globals.h"
     29 
     30 #define SETBUFFER_SIZE	(64 * 1024)
     31 
     32 #define S_OPTS	"aAB:d:D:e:hi:I:l:L:m:M:o:p:P:q:Q:rs:S:t:T:u:VvXz:"
     33 static struct option l_opts[] = {
     34 	{
     35 		.name = "seek-absolute",
     36 		.has_arg = no_argument,
     37 		.flag = NULL,
     38 		.val = 'a'
     39 	},
     40 	{
     41 		.name = "all-data",
     42 		.has_arg = no_argument,
     43 		.flag = NULL,
     44 		.val = 'A'
     45 	},
     46 	{
     47 		.name = "dump-blocknos",
     48 		.has_arg = required_argument,
     49 		.flag = NULL,
     50 		.val = 'B'
     51 	},
     52 	{
     53 		.name = "range-delta",
     54 		.has_arg = required_argument,
     55 		.flag = NULL,
     56 		.val = 'd'
     57 	},
     58 	{
     59 		.name = "devices",
     60 		.has_arg = required_argument,
     61 		.flag = NULL,
     62 		.val = 'D'
     63 	},
     64 	{
     65 		.name = "exes",
     66 		.has_arg = required_argument,
     67 		.flag = NULL,
     68 		.val = 'e'
     69 	},
     70 	{
     71 		.name = "help",
     72 		.has_arg = no_argument,
     73 		.flag = NULL,
     74 		.val = 'h'
     75 	},
     76 	{
     77 		.name = "input-file",
     78 		.has_arg = required_argument,
     79 		.flag = NULL,
     80 		.val = 'i'
     81 	},
     82 	{
     83 		.name = "iostat",
     84 		.has_arg = required_argument,
     85 		.flag = NULL,
     86 		.val = 'I'
     87 	},
     88 	{
     89 		.name = "d2c-latencies",
     90 		.has_arg = required_argument,
     91 		.flag = NULL,
     92 		.val = 'l'
     93 	},
     94 	{
     95 		.name = "periodic-latencies",
     96 		.has_arg = required_argument,
     97 		.flag = NULL,
     98 		.val = 'L'
     99 	},
    100 	{
    101 		.name = "seeks-per-second",
    102 		.has_arg = required_argument,
    103 		.flag = NULL,
    104 		.val = 'm'
    105 	},
    106 	{
    107 		.name = "dev-maps",
    108 		.has_arg = required_argument,
    109 		.flag = NULL,
    110 		.val = 'M'
    111 	},
    112 	{
    113 		.name = "output-file",
    114 		.has_arg = required_argument,
    115 		.flag = NULL,
    116 		.val = 'o'
    117 	},
    118 	{
    119 		.name = "per-io-dump",
    120 		.has_arg = required_argument,
    121 		.flag = NULL,
    122 		.val = 'p'
    123 	},
    124 	{
    125 		.name = "per-io-trees",
    126 		.has_arg = required_argument,
    127 		.flag = NULL,
    128 		.val = 'P'
    129 	},
    130 	{
    131 		.name = "q2c-latencies",
    132 		.has_arg = required_argument,
    133 		.flag = NULL,
    134 		.val = 'q'
    135 	},
    136 	{
    137 		.name = "active-queue-depth",
    138 		.has_arg = required_argument,
    139 		.flag = NULL,
    140 		.val = 'Q'
    141 	},
    142 	{
    143 		.name = "no-remaps",
    144 		.has_arg = no_argument,
    145 		.flag = NULL,
    146 		.val = 'r'
    147 	},
    148 	{
    149 		.name = "seeks",
    150 		.has_arg = required_argument,
    151 		.flag = NULL,
    152 		.val = 's'
    153 	},
    154 	{
    155 		.name = "iostat-interval",
    156 		.has_arg = required_argument,
    157 		.flag = NULL,
    158 		.val = 'S'
    159 	},
    160 	{
    161 		.name = "time-start",
    162 		.has_arg = required_argument,
    163 		.flag = NULL,
    164 		.val = 't'
    165 	},
    166 	{
    167 		.name = "time-end",
    168 		.has_arg = required_argument,
    169 		.flag = NULL,
    170 		.val = 'T'
    171 	},
    172 	{
    173 		.name = "unplug-hist",
    174 		.has_arg = required_argument,
    175 		.flag = NULL,
    176 		.val = 'u'
    177 	},
    178 	{
    179 		.name = "version",
    180 		.has_arg = no_argument,
    181 		.flag = NULL,
    182 		.val = 'V'
    183 	},
    184 	{
    185 		.name = "verbose",
    186 		.has_arg = no_argument,
    187 		.flag = NULL,
    188 		.val = 'v'
    189 	},
    190 	{
    191 		.name = "easy-parse-avgs",
    192 		.has_arg = no_argument,
    193 		.flag = NULL,
    194 		.val = 'X'
    195 	},
    196 	{
    197 		.name = "q2d-latencies",
    198 		.has_arg = required_argument,
    199 		.flag = NULL,
    200 		.val = 'z'
    201 	},
    202 	{
    203 		.name = NULL,
    204 	}
    205 };
    206 
    207 static char usage_str[] = \
    208 	"\n[ -a               | --seek-absolute ]\n" \
    209 	"[ -A               | --all-data ]\n" \
    210 	"[ -B <output name> | --dump-blocknos=<output name> ]\n" \
    211 	"[ -d <seconds>     | --range-delta=<seconds> ]\n" \
    212 	"[ -D <dev;...>     | --devices=<dev;...> ]\n" \
    213 	"[ -e <exe,...>     | --exes=<exe,...>  ]\n" \
    214 	"[ -h               | --help ]\n" \
    215 	"[ -i <input name>  | --input-file=<input name> ]\n" \
    216 	"[ -I <output name> | --iostat=<output name> ]\n" \
    217 	"[ -l <output name> | --d2c-latencies=<output name> ]\n" \
    218 	"[ -L <freq>        | --periodic-latencies=<freq> ]\n" \
    219 	"[ -m <output name> | --seeks-per-second=<output name> ]\n" \
    220 	"[ -M <dev map>     | --dev-maps=<dev map>\n" \
    221 	"[ -o <output name> | --output-file=<output name> ]\n" \
    222 	"[ -p <output name> | --per-io-dump=<output name> ]\n" \
    223 	"[ -P <output name> | --per-io-trees=<output name> ]\n" \
    224 	"[ -q <output name> | --q2c-latencies=<output name> ]\n" \
    225 	"[ -Q <output name> | --active-queue-depth=<output name> ]\n" \
    226 	"[ -r               | --no-remaps ]\n" \
    227 	"[ -s <output name> | --seeks=<output name> ]\n" \
    228 	"[ -S <interval>    | --iostat-interval=<interval> ]\n" \
    229 	"[ -t <sec>         | --time-start=<sec> ]\n" \
    230 	"[ -T <sec>         | --time-end=<sec> ]\n" \
    231 	"[ -u <output name> | --unplug-hist=<output name> ]\n" \
    232 	"[ -V               | --version ]\n" \
    233 	"[ -v               | --verbose ]\n" \
    234 	"[ -X               | --easy-parse-avgs ]\n" \
    235 	"[ -z <output name> | --q2d-latencies=<output name> ]\n" \
    236 	"\n";
    237 
    238 static void usage(char *prog)
    239 {
    240 	fprintf(stderr, "Usage: %s %s %s", prog, bt_timeline_version,
    241 		usage_str);
    242 }
    243 
    244 static FILE *setup_ofile(char *fname)
    245 {
    246 	if (fname) {
    247 		char *buf;
    248 		FILE *ofp = my_fopen(fname, "w");
    249 
    250 		if (!ofp) {
    251 			perror(fname);
    252 			exit(1);
    253 		}
    254 
    255 		buf = malloc(SETBUFFER_SIZE);
    256 		setbuffer(ofp, buf, SETBUFFER_SIZE);
    257 
    258 		add_file(ofp, fname);
    259 		add_buf(buf);
    260 
    261 		return ofp;
    262 	}
    263 
    264 	return NULL;
    265 }
    266 
    267 static FILE *std_open(char *output_name, char *sfx, char *msg)
    268 {
    269 	FILE *fp;
    270 	char fname[strlen(output_name) + 32];
    271 
    272 	sprintf(fname, "%s.%s", output_name, sfx);
    273 	fp = my_fopen(fname, "w");
    274 	if (fp == NULL) {
    275 		perror(fname);
    276 		exit(1);
    277 	}
    278 	if (verbose)
    279 		printf("Sending %s to %s\n", msg, fname);
    280 
    281 	return fp;
    282 }
    283 
    284 void handle_args(int argc, char *argv[])
    285 {
    286 	int c;
    287 
    288 	while ((c = getopt_long(argc, argv, S_OPTS, l_opts, NULL)) != -1) {
    289 		switch (c) {
    290 		case 'a':
    291 			seek_absolute = 1;
    292 			break;
    293 		case 'A':
    294 			output_all_data = 1;
    295 			break;
    296 		case 'B':
    297 			bno_dump_name = optarg;
    298 			break;
    299 		case 'd':
    300 			sscanf(optarg, "%lf", &range_delta);
    301 			break;
    302 		case 'D':
    303 			devices = optarg;
    304 			break;
    305 		case 'e':
    306 			exes = optarg;
    307 			break;
    308 		case 'h':
    309 			usage(argv[0]);
    310 			exit(0);
    311 		case 'i':
    312 			input_name = optarg;
    313 			break;
    314 		case 'l':
    315 			d2c_name = optarg;
    316 			break;
    317 		case 'L':
    318 			plat_freq = atof(optarg);
    319 			break;
    320 		case 'I':
    321 			iostat_name = strdup(optarg);
    322 			break;
    323 		case 'm':
    324 			sps_name = optarg;
    325 			break;
    326 		case 'M':
    327 			if (dev_map_read(optarg))
    328 				exit(1);
    329 			break;
    330 		case 'o':
    331 			output_name = optarg;
    332 			break;
    333 		case 'p':
    334 			per_io_name = strdup(optarg);
    335 			break;
    336 		case 'P':
    337 			per_io_trees = optarg;
    338 			break;
    339 		case 'q':
    340 			q2c_name = optarg;
    341 			break;
    342 		case 'Q':
    343 			aqd_name = optarg;
    344 			break;
    345 		case 'r':
    346 			ignore_remaps = 1;
    347 			break;
    348 		case 's':
    349 			seek_name = optarg;
    350 			break;
    351 		case 'S': {
    352 			unsigned int interval;
    353 
    354 			sscanf(optarg, "%u", &interval);
    355 			iostat_interval = (__u64)interval * 1000000000LL;
    356 			break;
    357 		}
    358 		case 't':
    359 			sscanf(optarg, "%lf", &t_astart);
    360 			time_bounded = 1;
    361 			break;
    362 		case 'T':
    363 			sscanf(optarg, "%lf", &t_aend);
    364 			time_bounded = 1;
    365 			break;
    366 		case 'u':
    367 			unplug_hist_name = optarg;
    368 			break;
    369 		case 'v':
    370 			verbose = 1;
    371 			break;
    372 		case 'V':
    373 			printf("%s version %s\n", argv[0], bt_timeline_version);
    374 			exit(0);
    375 		case 'X':
    376 			easy_parse_avgs++;
    377 			break;
    378 		case 'z':
    379 			q2d_name = optarg;
    380 			break;
    381 		default:
    382 			usage(argv[0]);
    383 			exit(1);
    384 		}
    385 	}
    386 
    387 	if (input_name == NULL) {
    388 		usage(argv[0]);
    389 		exit(1);
    390 	}
    391 
    392 	if (sps_name && !seek_name) {
    393 		fprintf(stderr, "FATAL: -m option requires -s options\n");
    394 		exit(1);
    395 	}
    396 
    397 	setup_ifile(input_name);
    398 
    399 	if (output_name == NULL) {
    400 		rngs_ofp = avgs_ofp = msgs_ofp = stdout;
    401 		easy_parse_avgs = 0;
    402 	} else {
    403 		rngs_ofp = std_open(output_name, "dat", "range data");
    404 		avgs_ofp = std_open(output_name, "avg", "stats data");
    405 		msgs_ofp = std_open(output_name, "msg", "K messages");
    406 		if (easy_parse_avgs) {
    407 			xavgs_ofp = std_open(output_name, "xvg",
    408 					     "EZ stats data");
    409 		}
    410 	}
    411 
    412 	iostat_ofp = setup_ofile(iostat_name);
    413 	per_io_ofp = setup_ofile(per_io_name);
    414 }
    415