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