SCRIPTS = btrace
all: depend $(PROGS) $(SCRIPTS)
+ $(MAKE) -C btt
%.o: %.c
$(CC) -o $*.o -c $(ALL_CFLAGS) $<
clean: docsclean
-rm -f *.o $(PROGS) .depend btrace-1.0.tar.bz2
+ $(MAKE) -C btt clean
install: $(PROGS) $(SCRIPTS)
$(INSTALL) -m755 -d $(DESTDIR)$(bindir)
$(INSTALL) $(PROGS) $(SCRIPTS) $(DESTDIR)$(bindir)
+ $(INSTALL) btt/btt $(DESTDIR)$(bindir)
ifneq ($(wildcard .depend),)
include .depend
--- /dev/null
+CC = gcc
+MYFLAGS = -DLVM_REMAP_WORKAROUND
+CFLAGS = -Wall -O2 -W -I.. -DDO_INLINE ${MYFLAGS}
+#CFLAGS = -Wall -g -W -I.. -UDO_INLINE -DDEBUG ${MYFLAGS}
+ALL_CFLAGS = $(CFLAGS) -D_GNU_SOURCE -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64
+PROGS = btt
+#ELIBS = -lefence
+#PLIBS = -lpthread
+LIBS = $(PLIBS) $(ELIBS)
+OBJS = bt_timeline.o args.o output.o proc.o trace.o misc.o devs.o \
+ traverse.o iofree.o devmap.o cylist.o
+
+all: depend $(PROGS)
+
+depend:
+ @$(CC) -MM $(ALL_CFLAGS) -I.. *.c 1> .depend
+
+clean:
+ -rm -f *.o $(PROGS) .depend
+
+%.o: %.c
+ $(CC) -o $*.o -c $(ALL_CFLAGS) $<
+
+btt: $(OBJS)
+ $(CC) $(ALL_CFLAGS) -o $@ $(filter %.o,$^) $(LIBS)
+
+ifneq ($(wildcard .depend),)
+include .depend
+endif
--- /dev/null
+blktrace Timeline
+-----------------
+
+Alan D. Brunelle (initial version)
+
+Usage
+-----
+
+$ btt -i <input file> (-l | -m) [-d <range delta>] [-D <devices...>]
+ [-e <executables...>] [-h] [-M map information] [-o <output file>]
+ [-v] [-V]
+
+You are required to:
+
+o Specify an input file (-i)
+
+o Specify MD (-m) or LVM2/DM (-l) devices present (If no software RAID
+ devices, use -m.) This is a workaround for issues between LVM2 and MD
+ remap events that needs to be cleaned up. [DM devices are not working
+ as of right now, always specify -m for individual disks and/or MD
+ devices.]
+
+The -d argument allows you to specify the granularity which dtermines
+"activity" with regard to the .dat files -- this specific the time
+(in seconds) that must elapse without a particular event occuring to
+signify inactivity. The larger the number, the fewer ranges output --
+the default is 0.1 seconds.
+
+The -D argument supplies the devices which should be looked at when
+analyzing the input. This is a ":" separated list of devices, devices are
+specified by a mjr,mnr tuple (e.g.: -D "8,0:8,8" specifies two devices
+with major 8 and minor 0 and 8 respectively).
+
+The -e argument supplies the list of executables that will have I/Os
+analyzed.
+
+The -M argument takes in a file generated by the provided script
+(gen_disk_info.py), and allows for better output of device names.
+
+Overview
+--------
+
+btt will take in binary dump data from blkparse, and analyze the events,
+producing a series of output from the analysis. It will also build .dat
+files containing "range data" -- showing things like Q activity (periods
+of time while Q events are being produced), C activity (likewise for
+command completions), and etc.
+
+Resources
+---------
+
+vger hosts a mailing list dedicated to btrace discussion and development.
+The list is called linux-btrace@vger.kernel.org, subscribe by sending
+a mail to majordomo@vger.kernel.org with 'subscribe linux-btrace' in
+the mail body.
+
+2006-05-17, Alan D. Brunelle <alan.brunelle@hp.com>
--- /dev/null
+/*
+ * blktrace output analysis: generate a timeline & gather statistics
+ *
+ * Copyright (C) 2006 Alan D. Brunelle <Alan.Brunelle@hp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "globals.h"
+
+#define S_OPTS "d:D:e:hlmM:i:o:Vv"
+static struct option l_opts[] = {
+ {
+ .name = "range-delta",
+ .has_arg = required_argument,
+ .flag = NULL,
+ .val = 'd'
+ },
+ {
+ .name = "devices",
+ .has_arg = required_argument,
+ .flag = NULL,
+ .val = 'D'
+ },
+ {
+ .name = "exes",
+ .has_arg = required_argument,
+ .flag = NULL,
+ .val = 'e'
+ },
+ {
+ .name = "help",
+ .has_arg = no_argument,
+ .flag = NULL,
+ .val = 'h'
+ },
+ {
+ .name = "lvm",
+ .has_arg = no_argument,
+ .flag = NULL,
+ .val = 'l'
+ },
+ {
+ .name = "md",
+ .has_arg = no_argument,
+ .flag = NULL,
+ .val = 'm'
+ },
+ {
+ .name = "dev-maps",
+ .has_arg = required_argument,
+ .flag = NULL,
+ .val = 'M'
+ },
+ {
+ .name = "input-file",
+ .has_arg = required_argument,
+ .flag = NULL,
+ .val = 'i'
+ },
+ {
+ .name = "output-file",
+ .has_arg = required_argument,
+ .flag = NULL,
+ .val = 'o'
+ },
+ {
+ .name = "version",
+ .has_arg = no_argument,
+ .flag = NULL,
+ .val = 'V'
+ },
+ {
+ .name = "verbose",
+ .has_arg = no_argument,
+ .flag = NULL,
+ .val = 'v'
+ },
+ {
+ .name = NULL,
+ }
+};
+
+static char usage_str[] = \
+ "\n[ -d <seconds> | --range-delta=<seconds> ]\n" \
+ "[ -e <exe,...> | --exes=<exe,...> ]\n" \
+ "[ -h | --help ]\n" \
+ "[ -i <input name> | --input-file=<input name> ]\n" \
+ "(-l | -m) | (--lvm | -md)\n" \
+ "[ -o <output name> | --output-file=<output name> ]\n" \
+ "[ -V | --version ]\n" \
+ "[ -v | --verbose ]\n\n";
+
+static void usage(char *prog)
+{
+ fprintf(stderr, "Usage: %s %s %s", prog, bt_timeline_version,
+ usage_str);
+}
+
+void handle_args(int argc, char *argv[])
+{
+ int c;
+ char *dev_map_fname = NULL;
+
+ while ((c = getopt_long(argc, argv, S_OPTS, l_opts, NULL)) != -1) {
+ switch (c) {
+ case 'd':
+ sscanf(optarg, "%lf", &range_delta);
+ break;
+ case 'D':
+ devices = optarg;
+ break;
+ case 'e':
+ exes = optarg;
+ break;
+ case 'h':
+ usage(argv[0]);
+ exit(0);
+ case 'i':
+ input_name = optarg;
+ break;
+ case 'l':
+ is_lvm = 1;
+ break;
+ case 'm':
+ is_lvm = 0;
+ break;
+ case 'M':
+ dev_map_fname = optarg;
+ break;
+ case 'o':
+ output_name = optarg;
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ case 'V':
+ printf("%s version %s\n", argv[0], bt_timeline_version);
+ exit(0);
+ default:
+ usage(argv[0]);
+ exit(1);
+ }
+ }
+
+ if (input_name == NULL || is_lvm < 0) {
+ usage(argv[0]);
+ exit(1);
+ }
+
+ ifd = open(input_name, O_RDONLY);
+ if (ifd < 0) {
+ perror(input_name);
+ exit(1);
+ }
+
+ if (dev_map_fname && dev_map_read(dev_map_fname))
+ exit(1);
+
+ if (output_name == NULL)
+ ranges_ofp = avgs_ofp = stdout;
+ else {
+ char *fname = malloc(sizeof(output_name) + 20);
+
+ sprintf(fname, "%s.dat", output_name);
+ ranges_ofp = fopen(fname, "w");
+ if (ranges_ofp == NULL) {
+ perror(fname);
+ exit(1);
+ }
+ if (verbose)
+ printf("Sending range data to %s\n", output_name);
+
+ sprintf(fname, "%s.avg", output_name);
+ avgs_ofp = fopen(fname, "w");
+ if (avgs_ofp == NULL) {
+ perror(fname);
+ exit(1);
+ }
+ if (verbose)
+ printf("Sending stats data to %s\n", output_name);
+
+ free(fname);
+ }
+}
--- /dev/null
+/*
+ * blktrace output analysis: generate a timeline & gather statistics
+ *
+ * Copyright (C) 2006 Alan D. Brunelle <Alan.Brunelle@hp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "globals.h"
+
+char bt_timeline_version[] = "0.99";
+
+char *devices = NULL;
+char *exes = NULL;
+char *input_name = NULL;
+char *output_name = NULL;
+double range_delta = 0.1;
+FILE *ranges_ofp, *avgs_ofp;
+int is_lvm = -1;
+int verbose = 0;
+int ifd;
+unsigned long n_traces, n_io_allocs, n_io_frees;
+struct avgs_info all_avgs;
+__u64 last_q = (__u64)-1;
+unsigned int pending_xs;
+
+unsigned int n_devs;
+LIST_HEAD(all_devs);
+LIST_HEAD(all_ios);
+LIST_HEAD(all_procs);
+struct my_mem *free_ios = NULL;
+struct my_mem *free_bits = NULL;
+
+struct region_info all_regions = {
+ .qranges = LIST_HEAD_INIT(all_regions.qranges),
+ .cranges = LIST_HEAD_INIT(all_regions.cranges),
+ .qr_cur = NULL,
+ .cr_cur = NULL
+};
+
+char iop_map[] = { 'Q', 'X', 'A', 'M', 'I', 'D', 'C', 'Y' };
+
+struct blk_io_trace *convert_to_cpu(struct blk_io_trace *t);
+int process(void);
+
+int main(int argc, char *argv[])
+{
+ handle_args(argc, argv);
+
+ cy_init();
+ if (process() || output_avgs(avgs_ofp) || output_ranges(ranges_ofp))
+ return 1;
+
+ return 0;
+}
+
+int process(void)
+{
+ int ret = 0;
+ struct blk_io_trace *t;
+ struct io *iop = IO_ZALLOC();
+
+ while (!do_read(ifd, &iop->t, sizeof(iop->t))) {
+ t = convert_to_cpu(&iop->t);
+ if (t->pdu_len > 0) {
+ iop->pdu = malloc(t->pdu_len);
+ if (do_read(ifd, iop->pdu, t->pdu_len)) {
+ free(iop->pdu);
+ ret = 1;
+ break;
+ }
+ }
+ add_trace(iop);
+ iop = IO_ZALLOC();
+ }
+ IO_FREE(iop);
+
+ if (verbose)
+ printf("\n%10lu traces, %10lu mallocs %1lu frees\n",
+ n_traces, n_io_allocs, n_io_frees);
+
+ cy_shutdown();
+ return ret;
+}
--- /dev/null
+/*
+ * blktrace output analysis: generate a timeline & gather statistics
+ *
+ * Copyright (C) 2006 Alan D. Brunelle <Alan.Brunelle@hp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#include "globals.h"
+
+struct cy_list {
+ struct list_head head;
+ struct list_head list;
+};
+
+struct cy_list *pending_cys;
+
+static inline void __rem_cy(struct io_list *iolp)
+{
+ io_unlink(&iolp->iop);
+ if (--iolp->cy_users == 0) {
+ LIST_DEL(&iolp->head);
+ free(iolp);
+ }
+}
+
+void rem_c(struct io *iop)
+{
+ struct list_head *p, *q;
+ struct io_list *iolp;
+
+ ASSERT(iop->type == IOP_C);
+
+ list_for_each_safe(p, q, &pending_cys->list) {
+ iolp = list_entry(p, struct io_list, head);
+
+ if (iolp->iop == iop) {
+ __rem_cy(iolp);
+ break;
+ }
+ }
+}
+
+void run_cy_list(struct list_head *list)
+{
+ struct list_head *p, *q;
+ struct io_list *iolp;
+
+ list_for_each_safe(p, q, list) {
+ iolp = list_entry(p, struct io_list, head);
+ traverse(iolp->iop);
+ __rem_cy(iolp);
+ }
+}
+
+void add_cy(struct io *iop)
+{
+ struct io_list *iolp = malloc(sizeof(*iolp));
+
+ ASSERT(iop->type == IOP_C || iop->type == IOP_Y);
+
+ iolp->cy_users = 1;
+ io_link(&iolp->iop, iop);
+
+ list_add_tail(&iolp->head, &pending_cys->list);
+ if (pending_xs == 0)
+ run_cy_list(&pending_cys->list);
+}
+
+void cy_init(void)
+{
+ pending_cys = zmalloc(sizeof(*pending_cys));
+ INIT_LIST_HEAD(&pending_cys->list);
+}
+
+void cy_shutdown(void)
+{
+ ASSERT(list_empty(&pending_cys->list));
+ free(pending_cys);
+}
--- /dev/null
+/*
+ * blktrace output analysis: generate a timeline & gather statistics
+ *
+ * Copyright (C) 2006 Alan D. Brunelle <Alan.Brunelle@hp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#include <stdio.h>
+
+#include "globals.h"
+struct devmap *all_devmaps = NULL;
+void dev_map_add(struct devmap *dmp)
+{
+ struct devmap *this = zmalloc(sizeof(*this));
+
+ *this = *dmp;
+ this->next = all_devmaps;
+ all_devmaps = this;
+}
+
+struct devmap *dev_map_find(__u32 device)
+{
+ char this[128];
+ struct devmap *dmp;
+
+ sprintf(this, "%u,%u", MAJOR(device), MINOR(device));
+ for (dmp = all_devmaps; dmp != NULL; dmp = dmp->next)
+ if (!strcmp(this, dmp->devno))
+ break;
+
+ return dmp;
+}
+
+int dev_map_read(char *fname)
+{
+ char line[256];
+ struct devmap dm;
+ FILE *fp = fopen(fname, "r");
+
+ if (!fp) {
+ perror(fname);
+ return 1;
+ }
+
+ while (fscanf(fp, "%255[a-zA-Z0-9 :.,/]\n", line) == 1) {
+ if (strstr(line, "Device") != NULL) continue;
+ if (sscanf(line, "%s %s %u %u %u %u %s %s %u %u %s",
+ &dm.device[0], &dm.model[0], &dm.host, &dm.bus,
+ &dm.target, &dm.lun, &dm.node[0], &dm.pci[0],
+ &dm.irq, &dm.cpu, &dm.devno[0]) != 11)
+ break;
+ dev_map_add(&dm);
+ }
+
+ return 0;
+}
--- /dev/null
+/*
+ * blktrace output analysis: generate a timeline & gather statistics
+ *
+ * Copyright (C) 2006 Alan D. Brunelle <Alan.Brunelle@hp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#include <stdio.h>
+
+#include "globals.h"
+
+#define N_DEV_HASH 128
+#define DEV_HASH(dev) ((MAJOR(dev) ^ MINOR(dev)) & (N_DEV_HASH - 1))
+struct list_head *dev_heads = NULL;
+
+static void init_dev_heads(void)
+{
+ int i;
+
+ dev_heads = zmalloc(N_DEV_HASH * sizeof(struct list_head));
+ for (i = 0; i < N_DEV_HASH; i++)
+ INIT_LIST_HEAD(&dev_heads[i]);
+}
+
+struct d_info *__dip_find(__u32 device)
+{
+ struct list_head *p;
+ struct d_info *dip;
+
+ if (dev_heads == NULL) {
+ init_dev_heads();
+ return NULL;
+ }
+
+ __list_for_each(p, &dev_heads[DEV_HASH(device)]) {
+ dip = list_entry(p, struct d_info, hash_head);
+ if (device == dip->device)
+ return dip;
+ }
+
+ return NULL;
+}
+
+struct d_info *dip_add(__u32 device, struct io *iop)
+{
+ struct d_info *dip = __dip_find(device);
+
+ if (dip == NULL) {
+ int i;
+
+ dip = zmalloc(sizeof(*dip));
+ dip->device = device;
+ dip->last_q = (__u64)-1;
+ for (i = 0; i < N_IOP_TYPES; i++)
+ INIT_LIST_HEAD(&dip->iop_heads[i]);
+ init_region(&dip->regions);
+ dip->map = dev_map_find(device);
+
+ if (dev_heads == NULL) init_dev_heads();
+ list_add_tail(&dip->hash_head, &dev_heads[DEV_HASH(device)]);
+
+ list_add_tail(&dip->head, &all_devs);
+ n_devs++;
+ }
+
+ list_add(&iop->dev_head, dip_get_head(dip, iop->type));
+
+ return dip;
+}
--- /dev/null
+/*
+ * blktrace output analysis: generate a timeline & gather statistics
+ *
+ * Copyright (C) 2006 Alan D. Brunelle <Alan.Brunelle@hp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "blktrace.h"
+#include "list.h"
+
+#define pdu_start(t) (((void *) (t) + sizeof(struct blk_io_trace)))
+
+#define BIT_TIME(t) ((double)SECONDS(t) + ((double)NANO_SECONDS(t) / 1.0e9))
+
+#define BIT_START(iop) ((iop)->t.sector)
+#define BIT_END(iop) ((iop)->t.sector + ((iop)->t.bytes >> 9))
+
+#if defined(DEBUG)
+#define ASSERT(truth) do { \
+ if (!(truth)) { \
+ DBG_PING(); \
+ assert(truth); \
+ } \
+ } while (0)
+
+#define DBG_PING() dbg_ping()
+
+#define LIST_DEL(hp) list_del(hp)
+
+#else
+#define ASSERT(truth)
+#define DBG_PING()
+
+#define LIST_DEL(hp) do { \
+ if (((hp)->next != NULL) && \
+ ((hp)->next != LIST_POISON1)) \
+ list_del(hp); \
+ } while (0)
+#endif
+
+#define IO_ZALLOC() my_zmalloc(&free_ios, sizeof(struct io))
+#define IO_FREE(iop) my_free(&free_ios, iop)
+
+enum iop_type {
+ IOP_Q = 0,
+ IOP_X = 1,
+ IOP_A = 2,
+ IOP_M = 3,
+ IOP_I = 4,
+ IOP_D = 5,
+ IOP_C = 6,
+ IOP_Y = 7,
+};
+#define N_IOP_TYPES (IOP_Y + 1)
+
+struct my_mem {
+ struct my_mem *next;
+};
+
+struct io;
+struct io_list {
+ struct list_head head;
+ struct io *iop;
+ int cy_users;
+};
+
+struct avg_info {
+ __u64 min, max, total;
+ double avg;
+ int n;
+};
+
+struct avgs_info {
+ struct avg_info q2q;
+ struct avg_info q2c;
+ struct avg_info q2a; /* Q to (A or X) */
+ struct avg_info q2i; /* Q to (I or M) */
+ struct avg_info i2d; /* (I or M) to D */
+ struct avg_info d2c;
+
+ struct avg_info blks; /* Blocks transferred */
+};
+
+struct range_info {
+ struct list_head head; /* on: qranges OR cranges */
+ __u64 start, end;
+};
+
+struct region_info {
+ struct list_head qranges;
+ struct list_head cranges;
+ struct range_info *qr_cur, *cr_cur;
+};
+
+struct p_info;
+struct p_pid {
+ struct list_head head;
+ struct p_info *pip;
+ __u32 pid;
+};
+
+struct p_info {
+ struct list_head head;
+ struct region_info regions;
+ struct avgs_info avgs;
+ char *name;
+ __u64 last_q;
+};
+
+struct devmap {
+ struct devmap *next;
+ char device[32];
+ char model[64];
+ unsigned int host, bus, target, lun;
+ char node[32], pci[32];
+ unsigned int irq, cpu;
+ char devno[32];
+};
+
+struct d_info {
+ struct list_head head, hash_head;
+ struct list_head iop_heads[N_IOP_TYPES];
+ struct region_info regions;
+ struct avgs_info avgs;
+ __u64 last_q;
+ __u32 device;
+ __u64 n_ds;
+ struct devmap *map;
+};
+
+struct io {
+ struct list_head all_head, dev_head;
+ struct d_info *dip;
+ struct p_info *pip;
+ void *pdu;
+
+ struct blk_io_trace t;
+
+ int users, traversed;
+ enum iop_type type;
+
+ union {
+ struct {
+ union {
+ struct io *q_a;
+ struct io *q_x;
+ } qp;
+ enum {
+ Q_NONE = 10,
+ Q_A = 20,
+ Q_X = 30,
+ } qp_type;
+ } q;
+ struct { struct io *x_q; } x;
+ struct { struct io *a_q; } a;
+ struct { struct io *m_q; } m;
+ struct { struct list_head i_qs_head; } i;
+ struct { struct list_head d_im_head; } d;
+ struct { struct io *c_d; } c;
+ struct { struct io *y_c1, *y_c2; } y;
+ } u;
+};
+
+extern char bt_timeline_version[], *devices, *exes, *input_name, *output_name;
+extern double range_delta;
+extern FILE *ranges_ofp, *avgs_ofp;
+extern int is_lvm, verbose, ifd;
+extern unsigned int n_devs;
+extern unsigned long n_traces, n_io_allocs, n_io_frees;
+extern struct list_head all_devs, all_ios, all_procs;
+extern struct avgs_info all_avgs;
+extern __u64 last_q;
+extern struct region_info all_regions;
+extern struct my_mem *free_ios, *free_bits;
+extern char iop_map[];
+extern unsigned int pending_xs;
+
+void add_trace(struct io *iop);
+int in_devices(struct blk_io_trace *t);
+int output_avgs(FILE *ofp);
+int output_ranges(FILE *ofp);
+unsigned int do_read(int ifd, void *buf, int len);
+void add_process(__u32 pid, char *name);
+struct p_info *find_process(__u32 pid, char *name);
+void pip_update_q(struct io *iop);
+void handle_args(int argc, char *argv[]);
+struct devmap *dev_map_find(__u32 device);
+int dev_map_read(char *fname);
+void add_cy(struct io *iop);
+void rem_c(struct io *iop);
+void cy_init(void);
+void cy_shutdown(void);
+struct d_info *__dip_find(__u32 device);
+struct d_info *dip_add(__u32 device, struct io *iop);
+void traverse(struct io *iop);
+void io_free_resources(struct io *iop);
+
+#include "inlines.h"
--- /dev/null
+/*
+ * blktrace output analysis: generate a timeline & gather statistics
+ *
+ * Copyright (C) 2006 Alan D. Brunelle <Alan.Brunelle@hp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#if defined(DO_INLINE)
+# define INLINE_PREFIX static inline
+# define INLINE_DECLARE
+#else
+# define INLINE_PREFIX
+#endif
+
+static inline void dbg_ping(void) { }
+
+#if defined(INLINE_DECLARE)
+INLINE_PREFIX void *zmalloc(size_t len)
+{
+ return memset(malloc(len), 0, len);
+}
+
+INLINE_PREFIX int is_dev(struct io *iop, unsigned int mjr, unsigned int mnr)
+{
+ return MAJOR(iop->t.device) == mjr && MINOR(iop->t.device) == mnr;
+}
+
+INLINE_PREFIX int in_bit(struct io *in, struct io *out)
+{
+ return (BIT_START(out) <= BIT_START(in)) &&
+ (BIT_END(in) <= BIT_END(out));
+}
+
+INLINE_PREFIX int is_bit(struct io *i1, struct io *i2)
+{
+ return (BIT_START(i1) == BIT_START(i2)) && (BIT_END(i1) == BIT_END(i2));
+}
+
+INLINE_PREFIX struct range_info *new_cur(__u64 time)
+{
+ struct range_info *cur = zmalloc(sizeof(*cur));
+
+ INIT_LIST_HEAD(&cur->head);
+ cur->start = time;
+ return cur;
+}
+
+INLINE_PREFIX void update_range(struct list_head *head_p,
+ struct range_info **cur_p, __u64 time)
+{
+ if (*cur_p == NULL)
+ *cur_p = new_cur(time);
+ else {
+ __u64 my_delta = time - (*cur_p)->end;
+ if (BIT_TIME(my_delta) >= range_delta) {
+ list_add_tail(&(*cur_p)->head, head_p);
+ *cur_p = new_cur(time);
+ }
+ }
+
+ (*cur_p)->end = time;
+}
+
+INLINE_PREFIX void init_region(struct region_info *reg)
+{
+ INIT_LIST_HEAD(®->qranges);
+ INIT_LIST_HEAD(®->cranges);
+ reg->qr_cur = reg->cr_cur = NULL;
+}
+
+INLINE_PREFIX void update_qregion(struct region_info *reg, __u64 time)
+{
+ update_range(®->qranges, ®->qr_cur, time);
+}
+
+INLINE_PREFIX void update_cregion(struct region_info *reg, __u64 time)
+{
+ update_range(®->cranges, ®->cr_cur, time);
+}
+
+INLINE_PREFIX void avg_update(struct avg_info *ap, __u64 t)
+{
+ if (ap->n++ == 0)
+ ap->min = ap->total = ap->max = t;
+ else {
+ if (t < ap->min)
+ ap->min = t;
+ else if (t > ap->max)
+ ap->max = t;
+ ap->total += t;
+ }
+}
+
+INLINE_PREFIX void update_lq(__u64 *last_q, struct avg_info *avg, __u64 time)
+{
+ if (*last_q != ((__u64)-1))
+ avg_update(avg, time - *last_q);
+ *last_q = time;
+}
+
+INLINE_PREFIX struct list_head *dip_get_head(struct d_info *dip,
+ enum iop_type type)
+{
+ return &dip->iop_heads[type];
+}
+
+INLINE_PREFIX struct list_head *dip_get_head_dev(__u32 dev, enum iop_type type)
+{
+ return dip_get_head(__dip_find(dev), type);
+}
+
+INLINE_PREFIX void dip_update_q(struct d_info *dip, struct io *iop)
+{
+ update_lq(&dip->last_q, &dip->avgs.q2q, iop->t.time);
+ update_qregion(&dip->regions, iop->t.time);
+}
+
+INLINE_PREFIX void dip_rem(struct io *iop)
+{
+ LIST_DEL(&iop->dev_head);
+}
+
+INLINE_PREFIX void *my_malloc(struct my_mem **head_p, size_t len)
+{
+ struct my_mem *this = *head_p;
+
+ if (this)
+ *head_p = this->next;
+ else
+ this = malloc(len);
+
+ return this;
+}
+
+INLINE_PREFIX void *my_zmalloc(struct my_mem **head_p, size_t len)
+{
+ return memset(my_malloc(head_p, len), 0, len);
+}
+
+INLINE_PREFIX void my_free(struct my_mem **head_p, void *p)
+{
+ struct my_mem *this = p;
+
+ this->next = *head_p;
+ *head_p = this;
+}
+
+INLINE_PREFIX void io_setup(struct io *iop, enum iop_type type)
+{
+ iop->type = type;
+ iop->dip = dip_add(iop->t.device, iop);
+ iop->pip = find_process(iop->t.pid, NULL);
+
+ n_io_allocs++;
+ list_add_tail(&iop->all_head, &all_ios);
+}
+
+INLINE_PREFIX void io_free(struct io *iop)
+{
+ ASSERT(iop->users == 0);
+
+ LIST_DEL(&iop->all_head);
+ dip_rem(iop);
+ IO_FREE(iop);
+ n_io_frees++;
+}
+
+INLINE_PREFIX void io_get(struct io *iop)
+{
+ iop->users++;
+}
+
+
+INLINE_PREFIX int __io_put(struct io *iop)
+{
+ return --iop->users;
+}
+
+INLINE_PREFIX void io_put(struct io *iop)
+{
+ if (__io_put(iop) == 0) {
+ io_free_resources(iop);
+ io_free(iop);
+ }
+}
+
+INLINE_PREFIX void io_link(struct io **p_dst, struct io *iop)
+{
+ ASSERT(iop != NULL);
+ io_get(iop);
+ *p_dst = iop;
+}
+
+INLINE_PREFIX void io_unlink(struct io **p_dst)
+{
+ ASSERT(*p_dst != NULL);
+ io_put(*p_dst);
+
+#if defined(DEBUG)
+ *p_dst = NULL;
+#endif
+}
+
+#define UPDATE_AVGS(_avg, _iop, _pip, _time) do { \
+ avg_update(&all_avgs. _avg , _time); \
+ avg_update(&_iop->dip->avgs. _avg , _time); \
+ if (_pip) avg_update(&_pip->avgs. _avg , _time); \
+ } while (0)
+
+INLINE_PREFIX void update_q2c(struct io *iop, __u64 c_time)
+{
+ UPDATE_AVGS(q2c, iop, iop->pip, c_time);
+}
+
+INLINE_PREFIX void update_q2a(struct io *iop, __u64 a_time)
+{
+ UPDATE_AVGS(q2a, iop, iop->pip, a_time);
+}
+
+INLINE_PREFIX void update_q2i(struct io *iop, __u64 i_time)
+{
+ UPDATE_AVGS(q2i, iop, iop->pip, i_time);
+}
+
+INLINE_PREFIX void update_i2d(struct io *iop, __u64 d_time)
+{
+ UPDATE_AVGS(i2d, iop, iop->pip, d_time);
+}
+
+INLINE_PREFIX void update_d2c(struct io *iop, __u64 c_time)
+{
+ UPDATE_AVGS(d2c, iop, iop->pip, c_time);
+}
+
+INLINE_PREFIX void update_blks(struct io *iop)
+{
+ __u64 nblks = iop->t.bytes >> 9;
+ avg_update(&all_avgs.blks, nblks);
+ avg_update(&iop->dip->avgs.blks, nblks);
+ if (iop->pip)
+ avg_update(&iop->pip->avgs.blks, nblks);
+}
+#else
+void *zmalloc(size_t len);
+int is_dev(struct io *iop, unsigned int mjr, unsigned int mnr);
+int in_bit(struct io *in, struct io *out);
+int is_bit(struct io *i1, struct io *i2);
+struct range_info *new_cur(__u64 time);
+void update_range(struct list_head *head_p,
+ struct range_info **cur_p, __u64 time);
+void init_region(struct region_info *reg);
+void update_qregion(struct region_info *reg, __u64 time);
+void update_cregion(struct region_info *reg, __u64 time);
+void avg_update(struct avg_info *ap, __u64 t);
+void update_lq(__u64 *last_q, struct avg_info *avg, __u64 time);
+struct list_head *dip_get_head(struct d_info *dip, enum iop_type type);
+void dip_update_q(struct d_info *dip, struct io *iop);
+void dip_rem(struct io *iop);
+void io_log(char *header, struct io *iop);
+void *my_malloc(struct my_mem **head_p, size_t len);
+void *my_zmalloc(struct my_mem **head_p, size_t len);
+void my_free(struct my_mem **head_p, void *p);
+void io_setup(struct io *iop, enum iop_type type);
+void io_free(struct io *iop);
+void io_get(struct io *iop);
+int __io_put(struct io *iop);
+void io_put(struct io *iop);
+void io_link(struct io **p_dst, struct io *iop);
+void io_unlink(struct io **p_dst);
+void update_q2c(struct io *iop, __u64 d_time);
+void update_q2a(struct io *iop, __u64 d_time);
+void update_q2i(struct io *iop, __u64 d_time);
+void update_i2d(struct io *iop, __u64 d_time);
+void update_d2c(struct io *iop, __u64 d_time);
+void update_blks(struct io *iop);
+struct list_head *dip_get_head_dev(__u32 dev, enum iop_type type);
+#endif
--- /dev/null
+/*
+ * blktrace output analysis: generate a timeline & gather statistics
+ *
+ * Copyright (C) 2006 Alan D. Brunelle <Alan.Brunelle@hp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#include <stdio.h>
+
+#include "globals.h"
+
+void io_free_resources(struct io *iop)
+{
+ switch (iop->type) {
+ case IOP_X: io_unlink(&iop->u.x.x_q); break;
+ case IOP_A: io_unlink(&iop->u.a.a_q); break;
+ case IOP_M: io_unlink(&iop->u.m.m_q); break;
+ case IOP_C: io_unlink(&iop->u.c.c_d); break;
+
+ case IOP_Q:
+ switch (iop->u.q.qp_type) {
+ case Q_NONE: break;
+ case Q_A: io_unlink(&iop->u.q.qp.q_a); break;
+ case Q_X: io_unlink(&iop->u.q.qp.q_x); break;
+ default:
+ ASSERT(0);
+ /*NOTREACHED*/
+ }
+ break;
+
+ case IOP_D: {
+ struct io_list *iolp;
+ struct list_head *p, *q;
+
+ list_for_each_safe(p, q, &iop->u.d.d_im_head) {
+ iolp = list_entry(p, struct io_list, head);
+ io_unlink(&iolp->iop);
+
+ LIST_DEL(&iolp->head);
+ free(iolp);
+ }
+ break;
+ }
+
+ case IOP_I: {
+ struct io_list *iolp;
+ struct list_head *p, *q;
+
+ list_for_each_safe(p, q, &iop->u.i.i_qs_head) {
+ iolp = list_entry(p, struct io_list, head);
+ io_unlink(&iolp->iop);
+
+ LIST_DEL(&iolp->head);
+ free(iolp);
+ }
+ break;
+ }
+
+ case IOP_Y:
+ io_unlink(&iop->u.y.y_c1);
+ io_unlink(&iop->u.y.y_c2);
+ break;
+
+ default:
+ ASSERT(0);
+ /*NOTREACHED*/
+ }
+}
--- /dev/null
+#ifndef _LINUX_LIST_H
+#define _LINUX_LIST_H
+
+/*
+ * These are non-NULL pointers that will result in page faults
+ * under normal circumstances, used to verify that nobody uses
+ * non-initialized list entries.
+ */
+#define LIST_POISON1 ((void *) 0x00100100)
+#define LIST_POISON2 ((void *) 0x00200200)
+
+struct list_head {
+ struct list_head *next, *prev;
+};
+
+#define LIST_HEAD_INIT(name) { &(name), &(name) }
+
+#define LIST_HEAD(name) \
+ struct list_head name = LIST_HEAD_INIT(name)
+
+static inline void INIT_LIST_HEAD(struct list_head *list)
+{
+ list->next = list;
+ list->prev = list;
+}
+
+/*
+ * Insert a new entry between two known consecutive entries.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_add(struct list_head *new,
+ struct list_head *prev,
+ struct list_head *next)
+{
+ next->prev = new;
+ new->next = next;
+ new->prev = prev;
+ prev->next = new;
+}
+
+/**
+ * list_add - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it after
+ *
+ * Insert a new entry after the specified head.
+ * This is good for implementing stacks.
+ */
+static inline void list_add(struct list_head *new, struct list_head *head)
+{
+ __list_add(new, head, head->next);
+}
+
+/**
+ * list_add_tail - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it before
+ *
+ * Insert a new entry before the specified head.
+ * This is useful for implementing queues.
+ */
+static inline void list_add_tail(struct list_head *new, struct list_head *head)
+{
+ __list_add(new, head->prev, head);
+}
+
+/*
+ * Delete a list entry by making the prev/next entries
+ * point to each other.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_del(struct list_head * prev, struct list_head * next)
+{
+ next->prev = prev;
+ prev->next = next;
+}
+
+/**
+ * list_del - deletes entry from list.
+ * @entry: the element to delete from the list.
+ * Note: list_empty on entry does not return true after this, the entry is
+ * in an undefined state.
+ */
+static inline void list_del(struct list_head *entry)
+{
+ __list_del(entry->prev, entry->next);
+ entry->next = LIST_POISON1;
+ entry->prev = LIST_POISON2;
+}
+
+/**
+ * __list_for_each - iterate over a list
+ * @pos: the &struct list_head to use as a loop counter.
+ * @head: the head for your list.
+ *
+ * This variant differs from list_for_each() in that it's the
+ * simplest possible list iteration code, no prefetching is done.
+ * Use this for code that knows the list to be very short (empty
+ * or 1 entry) most of the time.
+ */
+#define __list_for_each(pos, head) \
+ for (pos = (head)->next; pos != (head); pos = pos->next)
+
+/**
+ * list_for_each_safe - iterate over a list safe against removal of list entry
+ * @pos: the &struct list_head to use as a loop counter.
+ * @n: another &struct list_head to use as temporary storage
+ * @head: the head for your list.
+ */
+#define list_for_each_safe(pos, n, head) \
+ for (pos = (head)->next, n = pos->next; pos != (head); \
+ pos = n, n = pos->next)
+
+/**
+ * list_entry - get the struct for this entry
+ * @ptr: the &struct list_head pointer.
+ * @type: the type of the struct this is embedded in.
+ * @member: the name of the list_struct within the struct.
+ */
+#define list_entry(ptr, type, member) \
+ container_of(ptr, type, member)
+
+static inline int list_len(struct list_head *head_p)
+{
+ struct list_head *p;
+ int n = 0;
+
+ __list_for_each(p, head_p) {
+ n++;
+ }
+
+ return n;
+}
+
+/**
+ * list_empty - tests whether a list is empty
+ * @head: the list to test.
+ */
+static inline int list_empty(const struct list_head *head)
+{
+ return head->next == head;
+}
+
+#endif
--- /dev/null
+/*
+ * blktrace output analysis: generate a timeline & gather statistics
+ *
+ * Copyright (C) 2006 Alan D. Brunelle <Alan.Brunelle@hp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#define INLINE_DECLARE
+#include "globals.h"
+
+int data_is_native = -1;
+
+struct blk_io_trace *convert_to_cpu(struct blk_io_trace *t)
+{
+ if (data_is_native == -1)
+ check_data_endianness(t->magic);
+
+ trace_to_cpu(t);
+
+ ASSERT(CHECK_MAGIC(t));
+ ASSERT((t->magic & 0xff) == SUPPORTED_VERSION);
+
+ return t;
+}
+
+int in_devices(struct blk_io_trace *t)
+{
+ int i;
+ unsigned int mjr, mnr;
+ char *p = devices;
+
+ if (p == NULL) return 1; /* Allow anything */
+
+ for (;;) {
+ i = sscanf(p, "%u,%u;", &mjr, &mnr);
+ ASSERT(i == 2);
+
+ if ((mjr == MAJOR(t->device) && (mnr == MINOR(t->device))))
+ return 1;
+
+ p = strchr(p, ';');
+ if (!p)
+ break;
+ p++;
+ }
+
+ return 0;
+}
+
+unsigned int do_read(int ifd, void *buf, int len)
+{
+ int n;
+
+ n = read(ifd, buf, len);
+ if (n < 0) {
+ perror(input_name);
+ return 1;
+ }
+ else if (0 < n && n < len) {
+ fprintf(stderr,"Short read on %s\n", input_name);
+ return 1;
+ }
+ else if (n == 0) /* EOF */
+ return 1;
+
+ return 0;
+}
--- /dev/null
+/*
+ * blktrace output analysis: generate a timeline & gather statistics
+ *
+ * Copyright (C) 2006 Alan D. Brunelle <Alan.Brunelle@hp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#include <stdio.h>
+#include "globals.h"
+
+typedef struct avg_info *ai_dip_t;
+ai_dip_t dip_q2q_avg(struct d_info *dip) { return &dip->avgs.q2q; }
+ai_dip_t dip_q2c_avg(struct d_info *dip) { return &dip->avgs.q2c; }
+ai_dip_t dip_q2a_avg(struct d_info *dip) { return &dip->avgs.q2a; }
+ai_dip_t dip_q2i_avg(struct d_info *dip) { return &dip->avgs.q2i; }
+ai_dip_t dip_i2d_avg(struct d_info *dip) { return &dip->avgs.i2d; }
+ai_dip_t dip_d2c_avg(struct d_info *dip) { return &dip->avgs.d2c; }
+
+typedef struct avg_info *ai_pip_t;
+ai_pip_t pip_q2q_avg(struct p_info *pip) { return &pip->avgs.q2q; }
+ai_pip_t pip_q2c_avg(struct p_info *pip) { return &pip->avgs.q2c; }
+ai_pip_t pip_q2a_avg(struct p_info *pip) { return &pip->avgs.q2a; }
+ai_pip_t pip_q2i_avg(struct p_info *pip) { return &pip->avgs.q2i; }
+ai_pip_t pip_i2d_avg(struct p_info *pip) { return &pip->avgs.i2d; }
+ai_pip_t pip_d2c_avg(struct p_info *pip) { return &pip->avgs.d2c; }
+
+void output_section_hdr(FILE *ofp, char *hdr)
+{
+ fprintf(ofp, "==================== ");
+ fprintf(ofp, hdr);
+ fprintf(ofp, " ====================\n\n");
+}
+
+void output_hdr(FILE *ofp, char *hdr)
+{
+ fprintf(ofp, "%12s %13s %13s %13s %11s\n",
+ hdr, "MIN", "AVG", "MAX", "N" );
+ fprintf(ofp, "------------ ------------- ------------- ------------- -----------\n");
+}
+
+void __output_avg(FILE *ofp, char *hdr, struct avg_info *ap)
+{
+ if (ap->n > 0) {
+ ap->avg = BIT_TIME(ap->total) / (double)ap->n;
+ fprintf(ofp, "%-12s %13.9f %13.9f %13.9f %11d\n", hdr,
+ BIT_TIME(ap->min), ap->avg, BIT_TIME(ap->max), ap->n);
+ }
+}
+
+void output_hdr2(FILE *ofp, char*hdr)
+{
+ fprintf(ofp, "%12s %13s %13s %13s %13s %13s %13s\n", hdr, "Q2Q", "Q2A", "Q2I", "I2D", "D2C", "Q2C");
+ fprintf(ofp, "------------ ------------- ------------- ------------- ------------- ------------- -------------\n");
+}
+
+static inline char *avg2string(struct avg_info *ap, char *string)
+{
+ if (ap->n > 0)
+ sprintf(string, "%13.9f", ap->avg);
+ else
+ sprintf(string, " ");
+ return string;
+}
+
+void __output_avg2(FILE *ofp, char *hdr, struct avgs_info *ap)
+{
+ char c1[16], c2[16], c3[16], c4[16], c5[16], c6[16];
+
+ if (ap->q2q.n > 0 || ap->q2a.n > 0 || ap->q2i.n > 0 ||
+ ap->i2d.n > 0 || ap->d2c.n > 0 || ap->q2c.n > 0) {
+ fprintf(ofp, "%-12s %13s %13s %13s %13s %13s %13s\n", hdr,
+ avg2string(&ap->q2q,c1), avg2string(&ap->q2a,c2),
+ avg2string(&ap->q2i,c3), avg2string(&ap->i2d,c4),
+ avg2string(&ap->d2c,c5), avg2string(&ap->q2c,c6));
+ }
+}
+
+char *make_dev_hdr(char *pad, size_t len, struct d_info *dip)
+{
+ if (dip->map == NULL) {
+ snprintf(pad, len, "(%3d,%3d)",
+ MAJOR(dip->device), MINOR(dip->device));
+ }
+ else {
+ snprintf(pad, len, "[%3d,%3d]",
+ dip->map->host, dip->map->target);
+ }
+
+ return pad;
+}
+
+void __output_dip_avg(FILE *ofp, struct d_info *dip, struct avg_info *ap)
+{
+ if (ap->n > 0) {
+ char dev_info[12];
+ ap->avg = BIT_TIME(ap->total) / (double)ap->n;
+ __output_avg(ofp, make_dev_hdr(dev_info, 12, dip), ap);
+ }
+}
+
+void output_dip_avg(FILE *ofp, char *hdr, ai_dip_t (*func)(struct d_info *))
+{
+ struct d_info *dip;
+
+ output_hdr(ofp, hdr);
+ if (devices == NULL) {
+ struct list_head *p;
+
+ __list_for_each(p, &all_devs) {
+ dip = list_entry(p, struct d_info, head);
+ __output_dip_avg(ofp, dip, func(dip));
+ }
+ }
+ else {
+ int i;
+ unsigned int mjr, mnr;
+ char *p = devices;
+
+ while (p && ((i = sscanf(p, "%u,%u", &mjr, &mnr)) == 2)) {
+ dip = __dip_find((__u32)((mjr << MINORBITS) | mnr));
+ __output_dip_avg(ofp, dip, func(dip));
+
+ p = strchr(p, ';');
+ if (p) p++;
+ }
+ }
+
+ fprintf(ofp, "\n");
+}
+
+void __output_dip_merge_ratio(FILE *ofp, struct d_info *dip)
+{
+ double blks_avg;
+ char scratch[12];
+ double ratio, q2c_n = dip->avgs.q2c.n, d2c_n = dip->n_ds;
+
+ if (q2c_n > 0.0 && d2c_n > 0.0) {
+ ratio = q2c_n / d2c_n;
+ blks_avg = (double)dip->avgs.blks.total /
+ (double)dip->avgs.d2c.n;
+ fprintf(ofp, "%10s | %8llu %8llu %7.1lf | %8llu %8llu %8llu %8llu\n",
+ make_dev_hdr(scratch, 12, dip),
+ (unsigned long long)dip->avgs.q2c.n,
+ (unsigned long long)dip->n_ds,
+ ratio,
+ (unsigned long long)dip->avgs.blks.min,
+ (unsigned long long)blks_avg,
+ (unsigned long long)dip->avgs.blks.max,
+ (unsigned long long)dip->avgs.blks.total);
+
+ }
+}
+
+void output_dip_merge_ratio(FILE *ofp)
+{
+ struct d_info *dip;
+
+ fprintf(ofp, "%10s | %8s %8s %7s | %8s %8s %8s %8s\n", "DEV", "#Q", "#D", "Ratio", "BLKmin", "BLKavg", "BLKmax", "Total");
+ fprintf(ofp, "---------- | -------- -------- ------- | -------- -------- -------- --------\n");
+ if (devices == NULL) {
+ struct list_head *p;
+
+ __list_for_each(p, &all_devs) {
+ dip = list_entry(p, struct d_info, head);
+ __output_dip_merge_ratio(ofp, dip);
+ }
+ }
+ else {
+ int i;
+ unsigned int mjr, mnr;
+ char *p = devices;
+
+ while (p && ((i = sscanf(p, "%u,%u", &mjr, &mnr)) == 2)) {
+ dip = __dip_find((__u32)((mjr << MINORBITS) | mnr));
+ __output_dip_merge_ratio(ofp, dip);
+
+ p = strchr(p, ';');
+ if (p) p++;
+ }
+ }
+
+ fprintf(ofp, "\n");
+}
+
+#define AVG(a,b) (100.0 * ((double)(a) / (double)(b)))
+#define CALC_AVG(ap) (ap)->avg = ((ap)->n == 0 ? 0.0 : \
+ (BIT_TIME((ap)->total) / \
+ (double)(ap)->n))
+char *q2i_v_q2C(struct d_info *dip, char *s)
+{
+ double q2c;
+
+ if (dip->avgs.q2i.n == 0) return " ";
+
+ q2c = dip->avgs.q2i.avg + dip->avgs.i2d.avg + dip->avgs.d2c.avg;
+ sprintf(s, "%5.1lf%%", AVG(dip->avgs.q2i.avg, q2c));
+
+ return s;
+}
+
+char *i2d_v_q2C(struct d_info *dip, char *s)
+{
+ double q2c;
+
+ if (dip->avgs.i2d.n == 0) return " ";
+
+ q2c = dip->avgs.q2i.avg + dip->avgs.i2d.avg + dip->avgs.d2c.avg;
+ sprintf(s, "%5.1lf%%", AVG(dip->avgs.i2d.avg, q2c));
+
+ return s;
+}
+
+char *d2c_v_q2C(struct d_info *dip, char *s)
+{
+ double q2c;
+
+ if (dip->avgs.i2d.n == 0) return " ";
+
+ q2c = dip->avgs.q2i.avg + dip->avgs.i2d.avg + dip->avgs.d2c.avg;
+ sprintf(s, "%5.1lf%%", AVG(dip->avgs.d2c.avg, q2c));
+
+ return s;
+}
+
+void __output_dip_prep_ohead(FILE *ofp, struct d_info *dip)
+{
+ char dev_info[12];
+ char s1[16], s2[16], s3[16];
+
+ if ((dip->avgs.q2i.n > 0 && dip->avgs.i2d.n > 0 &&
+ dip->avgs.d2c.n > 0)) {
+ CALC_AVG(&dip->avgs.q2i);
+ CALC_AVG(&dip->avgs.i2d);
+ CALC_AVG(&dip->avgs.d2c);
+
+ fprintf(ofp, "%10s | %6s %6s %6s\n",
+ make_dev_hdr(dev_info, 12, dip),
+ q2i_v_q2C(dip, s1), i2d_v_q2C(dip, s2),
+ d2c_v_q2C(dip, s3));
+ }
+}
+
+void output_dip_prep_ohead(FILE *ofp)
+{
+ struct d_info *dip;
+
+ fprintf(ofp, "%10s | %6s %6s %6s\n", "DEV", "Q2I", "I2D", "D2C");
+ fprintf(ofp, "---------- | ------ ------ ------\n");
+
+ if (devices == NULL) {
+ struct list_head *p;
+
+ __list_for_each(p, &all_devs) {
+ dip = list_entry(p, struct d_info, head);
+ __output_dip_prep_ohead(ofp, dip);
+ }
+ }
+ else {
+ int i;
+ unsigned int mjr, mnr;
+ char *p = devices;
+
+ while (p && ((i = sscanf(p, "%u,%u", &mjr, &mnr)) == 2)) {
+ dip = __dip_find((__u32)((mjr << MINORBITS) | mnr));
+ __output_dip_prep_ohead(ofp, dip);
+
+ p = strchr(p, ';');
+ if (p) p++;
+ }
+ }
+
+ fprintf(ofp, "\n");
+}
+
+void __output_pip_avg(FILE *ofp, struct p_info *pip, struct avg_info *ap)
+{
+ if (ap->n > 0) {
+ char proc_name[12];
+ snprintf(proc_name, 12, pip->name);
+
+ ap->avg = BIT_TIME(ap->total) / (double)ap->n;
+ __output_avg(ofp, proc_name, ap);
+ }
+}
+
+void output_pip_avg(FILE *ofp, char *hdr, ai_pip_t (*func)(struct p_info *))
+{
+ struct p_info *pip;
+
+ output_hdr(ofp, hdr);
+ if (exes == NULL) {
+ struct list_head *p;
+
+ __list_for_each(p, &all_procs) {
+ pip = list_entry(p, struct p_info, head);
+ __output_pip_avg(ofp, pip, func(pip));
+ }
+ }
+ else {
+ char *exe, *p, *next, *exes_save = strdup(exes);
+
+ p = exes_save;
+ while (exes_save != NULL) {
+ exe = exes_save;
+ if ((next = strchr(exes_save, ',')) != NULL) {
+ *next = '\0';
+ exes_save = next+1;
+ }
+ else
+ exes_save = NULL;
+
+ pip = find_process((__u32)-1, exe);
+ if (pip)
+ __output_pip_avg(ofp, pip, func(pip));
+ }
+ }
+
+ fprintf(ofp, "\n");
+}
+
+void output_dip_avgs(FILE *ofp)
+{
+ char dev_info[12];
+ struct d_info *dip;
+
+ output_hdr2(ofp,"Dev");
+ if (devices == NULL) {
+ struct list_head *p;
+
+ __list_for_each(p, &all_devs) {
+ dip = list_entry(p, struct d_info, head);
+ __output_avg2(ofp, make_dev_hdr(dev_info, 12, dip),
+ &dip->avgs);
+ }
+ }
+ else {
+ int i;
+ unsigned int mjr, mnr;
+ char *p = devices;
+
+ while (p && ((i = sscanf(p, "%u,%u", &mjr, &mnr)) == 2)) {
+ dip = __dip_find((__u32)((mjr << MINORBITS) | mnr));
+ __output_avg2(ofp, make_dev_hdr(dev_info, 12, dip),
+ &dip->avgs);
+
+ p = strchr(p, ';');
+ if (p) p++;
+ }
+ }
+
+ fprintf(ofp, "\n");
+}
+
+void output_pip_avgs(FILE *ofp)
+{
+ char exe[16];
+ struct p_info *pip;
+
+ output_hdr2(ofp,"Exe");
+ if (exes == NULL) {
+ struct list_head *p;
+
+ __list_for_each(p, &all_procs) {
+ pip = list_entry(p, struct p_info, head);
+ snprintf(exe, 12, pip->name);
+ __output_avg2(ofp, exe, &pip->avgs);
+ }
+ }
+ else {
+ char *exe, *p, *next, *exes_save = strdup(exes);
+
+ p = exes_save;
+ while (exes_save != NULL && *exes_save != '\0') {
+ exe = exes_save;
+ if ((next = strchr(exes_save, ',')) != NULL) {
+ *next = '\0';
+ exes_save = next+1;
+ }
+ else
+ exes_save = NULL;
+
+ pip = find_process((__u32)-1, exe);
+ if (pip) {
+ snprintf(exe, 12, pip->name);
+ __output_avg2(ofp, exe, &pip->avgs);
+ }
+ }
+ }
+
+ fprintf(ofp, "\n");
+}
+
+int output_avgs(FILE *ofp)
+{
+ if (exes == NULL || *exes != '\0') {
+ output_section_hdr(ofp, "Per Process");
+ output_pip_avg(ofp, "Q2Q", pip_q2q_avg);
+ output_pip_avg(ofp, "Q2A", pip_q2a_avg);
+ output_pip_avg(ofp, "Q2I", pip_q2i_avg);
+ output_pip_avg(ofp, "I2D", pip_i2d_avg);
+ output_pip_avg(ofp, "D2C", pip_d2c_avg);
+ output_pip_avg(ofp, "Q2C", pip_q2c_avg);
+ }
+
+ output_section_hdr(ofp, "Per Device");
+ output_dip_avg(ofp, "Q2Q", dip_q2q_avg);
+ output_dip_avg(ofp, "Q2A", dip_q2a_avg);
+ output_dip_avg(ofp, "Q2I", dip_q2i_avg);
+ output_dip_avg(ofp, "I2D", dip_i2d_avg);
+ output_dip_avg(ofp, "D2C", dip_d2c_avg);
+ output_dip_avg(ofp, "Q2C", dip_q2c_avg);
+
+ output_section_hdr(ofp, "All Devices");
+ output_hdr(ofp, "ALL");
+ __output_avg(ofp, "Q2Q", &all_avgs.q2q);
+ __output_avg(ofp, "Q2A", &all_avgs.q2a);
+ __output_avg(ofp, "Q2I", &all_avgs.q2i);
+ __output_avg(ofp, "I2D", &all_avgs.i2d);
+ __output_avg(ofp, "D2C", &all_avgs.d2c);
+ __output_avg(ofp, "Q2C", &all_avgs.q2c);
+
+ if (exes == NULL || *exes != '\0') {
+ output_section_hdr(ofp, "Per Process (avgs)");
+ output_pip_avgs(ofp);
+ }
+
+ output_section_hdr(ofp, "Per Device (avgs)");
+ output_dip_avgs(ofp);
+
+ output_section_hdr(ofp, "Device Merge Information");
+ output_dip_merge_ratio(ofp);
+
+ output_section_hdr(ofp, "Device Overhead");
+ output_dip_prep_ohead(ofp);
+
+ return 0;
+}
+
+void __output_ranges(FILE *ofp, struct list_head *head_p, float base)
+{
+ struct range_info *rip;
+ struct list_head *p;
+ float limit = base + 0.4;
+
+ __list_for_each(p, head_p) {
+ rip = list_entry(p, struct range_info, head);
+ fprintf(ofp, "%13.9lf %5.1f\n", BIT_TIME(rip->start), base);
+ fprintf(ofp, "%13.9lf %5.1f\n", BIT_TIME(rip->start), limit);
+ fprintf(ofp, "%13.9lf %5.1f\n", BIT_TIME(rip->end), limit);
+ fprintf(ofp, "%13.9lf %5.1f\n", BIT_TIME(rip->end), base);
+ }
+}
+
+int output_regions(FILE *ofp, char *header, struct region_info *reg,
+ float base)
+{
+ if (reg->qr_cur != NULL)
+ list_add_tail(®->qr_cur->head, ®->qranges);
+ if (reg->cr_cur != NULL)
+ list_add_tail(®->cr_cur->head, ®->cranges);
+
+ if (list_len(®->qranges) == 0 && list_len(®->cranges) == 0)
+ return 0;
+
+ fprintf(ofp, "# %16s : q activity\n", header);
+ __output_ranges(ofp, ®->qranges, base);
+ fprintf(ofp, "\n");
+
+ fprintf(ofp, "# %16s : c activity\n", header);
+ __output_ranges(ofp, ®->cranges, base + 0.5);
+ fprintf(ofp, "\n");
+
+ return 1;
+}
+
+float __output_dev(FILE *ofp, struct d_info *dip, float base)
+{
+ char header[128];
+ sprintf(header, "%d,%d", MAJOR(dip->device), MINOR(dip->device));
+ if (output_regions(ofp, header, &dip->regions, base))
+ base += 1.0;
+
+ return base;
+}
+
+float output_devs(FILE *ofp, float base)
+{
+ struct d_info *dip;
+
+ fprintf(ofp, "# Per device\n" );
+ if (devices == NULL) {
+ struct list_head *p;
+ __list_for_each(p, &all_devs) {
+ dip = list_entry(p, struct d_info, head);
+ base = __output_dev(ofp, dip, base);
+ }
+ }
+ else {
+ int i;
+ unsigned int mjr, mnr;
+ char *p = devices;
+
+ while (p && ((i = sscanf(p, "%u,%u", &mjr, &mnr)) == 2)) {
+ dip = __dip_find((__u32)((mjr << MINORBITS) | mnr));
+ ASSERT(dip);
+
+ base = __output_dev(ofp, dip, base);
+
+ p = strchr(p, ';');
+ if (p) p++;
+ }
+ }
+
+ return base;
+}
+
+static inline int exe_match(char *exe, char *name)
+{
+ return (exe == NULL) || (strstr(name, exe) != NULL);
+}
+
+float __output_procs(FILE *ofp, float base, char *match)
+{
+ struct p_info *pip;
+ struct list_head *p;
+
+ __list_for_each(p, &all_procs) {
+ pip = list_entry(p, struct p_info, head);
+
+ if (exe_match(match, pip->name) &&
+ output_regions(ofp, pip->name,
+ &pip->regions, base))
+ base += 1.0;
+ }
+
+ return base;
+}
+
+float output_procs(FILE *ofp, float base)
+{
+ fprintf(ofp, "# Per process\n" );
+ if (exes == NULL)
+ base = __output_procs(ofp, base, NULL);
+ else {
+ char *exe, *next, *p, *exes_save = strdup(exes);
+
+ p = exes_save;
+ while (exes_save != NULL) {
+ exe = exes_save;
+ if ((next = strchr(exes_save, ',')) != NULL) {
+ *next = '\0';
+ exes_save = next+1;
+ }
+ else
+ exes_save = NULL;
+
+ base = __output_procs(ofp, base, exe);
+ }
+ free(p);
+ }
+
+ return base;
+}
+
+int output_ranges(FILE *ofp)
+{
+ float base = 0.0;
+
+ fprintf(ofp, "# %s\n", "Total System");
+ if (output_regions(ofp, "Total System", &all_regions, base))
+ base += 1.0;
+
+ if (n_devs > 1)
+ base = output_devs(ofp, base);
+
+ base = output_procs(ofp, base);
+
+ return 0;
+}
--- /dev/null
+/*
+ * blktrace output analysis: generate a timeline & gather statistics
+ *
+ * Copyright (C) 2006 Alan D. Brunelle <Alan.Brunelle@hp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#include <string.h>
+
+#include "globals.h"
+
+#define N_PID_HASH 128
+#define PID_HASH(pid) ((pid) & (N_PID_HASH-1))
+struct list_head *pid_heads = NULL;
+
+static void init_pid_heads(void)
+{
+ int i;
+
+ pid_heads = zmalloc(N_PID_HASH * sizeof(struct list_head));
+ for (i = 0; i < N_PID_HASH; i++)
+ INIT_LIST_HEAD(&pid_heads[i]);
+}
+
+static inline struct p_info *pid_to_proc(__u32 pid)
+{
+ struct p_pid *pp;
+ struct list_head *p, *head;
+
+ if (pid_heads == NULL) init_pid_heads();
+
+ head = &pid_heads[PID_HASH(pid)];
+ __list_for_each(p, head) {
+ pp = list_entry(p, struct p_pid, head);
+ if (pp->pid == pid)
+ return pp->pip;
+ }
+
+ return NULL;
+}
+
+void insert_proc_hash(struct p_info *pip, __u32 pid)
+{
+ struct p_pid *pp = zmalloc(sizeof(*pp));
+
+ if (pid_heads == NULL) init_pid_heads();
+
+ pp->pip = pip;
+ pp->pid = pid;
+
+ list_add_tail(&pp->head, &pid_heads[PID_HASH(pid)]);
+}
+
+int __find_process_pid(__u32 pid)
+{
+ return pid_to_proc(pid) != NULL;
+}
+
+struct p_info *find_process(__u32 pid, char *name)
+{
+ struct p_info *pip;
+ struct list_head *p;
+
+ if (pid != ((__u32)-1) && (pip = pid_to_proc(pid)))
+ return pip;
+
+ if (name) {
+ __list_for_each(p, &all_procs) {
+ pip = list_entry(p, struct p_info, head);
+ if (name && !strcmp(pip->name, name)) {
+ if (pid != ((__u32)-1))
+ insert_proc_hash(pip, pid);
+ return pip;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+void add_process(__u32 pid, char *name)
+{
+ struct p_info *pip = find_process(pid, name);
+
+ if (pip == NULL) {
+ pip = zmalloc(sizeof(*pip));
+
+ list_add_tail(&pip->head, &all_procs);
+ insert_proc_hash(pip, pid);
+ pip->last_q = (__u64)-1;
+ pip->name = strdup(name);
+ init_region(&pip->regions);
+ }
+}
+
+void pip_update_q(struct io *iop)
+{
+ if (iop->pip) {
+ update_lq(&iop->pip->last_q, &iop->pip->avgs.q2q, iop->t.time);
+ update_qregion(&iop->pip->regions, iop->t.time);
+ }
+}
--- /dev/null
+/*
+ * blktrace output analysis: generate a timeline & gather statistics
+ *
+ * Copyright (C) 2006 Alan D. Brunelle <Alan.Brunelle@hp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#include "globals.h"
+
+static inline void release_iop(struct io *iop)
+{
+ if (iop->pdu) free(iop->pdu);
+ IO_FREE(iop);
+}
+
+struct io *dip_find_exact(struct list_head *head, struct io *iop_in)
+{
+ struct io *iop;
+ struct list_head *p;
+
+ __list_for_each(p, head) {
+ iop = list_entry(p, struct io, dev_head);
+ if (is_bit(iop_in, iop))
+ return iop;
+ }
+ return NULL;
+}
+
+struct io *dip_find_in(struct list_head *head, struct io *iop_in)
+{
+ struct io *iop;
+ struct list_head *p;
+
+ __list_for_each(p, head) {
+ iop = list_entry(p, struct io, dev_head);
+ if (in_bit(iop, iop_in))
+ return iop;
+ }
+ return NULL;
+}
+
+struct io *dip_find_start(struct list_head *head, __u64 sector)
+{
+ struct io *iop;
+ struct list_head *p;
+
+ __list_for_each(p, head) {
+ iop = list_entry(p, struct io, dev_head);
+ if (BIT_START(iop) == sector)
+ return iop;
+ }
+ return NULL;
+}
+
+struct io *dip_find_end(struct list_head *head, __u64 sector)
+{
+ struct io *iop;
+ struct list_head *p;
+
+ __list_for_each(p, head) {
+ iop = list_entry(p, struct io, dev_head);
+ if (BIT_END(iop) == sector)
+ return iop;
+ }
+ return NULL;
+}
+
+struct io *dip_find_in_sec(struct list_head *head, __u64 sector)
+{
+ struct io *iop;
+ struct list_head *p;
+
+ __list_for_each(p, head) {
+ iop = list_entry(p, struct io, dev_head);
+ if (BIT_START(iop) <= sector && sector <= BIT_END(iop))
+ return iop;
+ }
+ return NULL;
+}
+
+struct io *dip_find_qa(struct list_head *head, struct blk_io_trace *t)
+{
+ struct io *iop;
+ struct list_head *p;
+
+ __list_for_each(p, head) {
+ iop = list_entry(p, struct io, dev_head);
+ if (iop->t.cpu == t->cpu && iop->t.sequence == (t->sequence-1))
+ return iop;
+ }
+ return NULL;
+}
+
+void dip_add_ms(struct list_head *head, struct io *d_iop)
+{
+ struct io *m_iop;
+ struct list_head *p;
+ struct io_list *iolp;
+
+ __list_for_each(p, head) {
+ m_iop = list_entry(p, struct io, dev_head);
+ if (in_bit(m_iop, d_iop)) {
+ iolp = malloc(sizeof(*iolp));
+ io_link(&iolp->iop, m_iop);
+ list_add_tail(&iolp->head, &d_iop->u.d.d_im_head);
+ }
+ }
+}
+
+void dip_add_qs(struct list_head *head, struct io *i_iop)
+{
+ struct io *q_iop;
+ struct list_head *p;
+ struct io_list *iolp;
+
+ __list_for_each(p, head) {
+ q_iop = list_entry(p, struct io, dev_head);
+ if (in_bit(q_iop, i_iop)) {
+ iolp = malloc(sizeof(*iolp));
+ io_link(&iolp->iop, q_iop);
+ list_add_tail(&iolp->head, &i_iop->u.i.i_qs_head);
+ }
+ }
+}
+
+void handle_queue(struct io *iop)
+{
+ struct io *tmp;
+
+ io_setup(iop, IOP_Q);
+
+ update_lq(&last_q, &all_avgs.q2q, iop->t.time);
+ update_qregion(&all_regions, iop->t.time);
+ dip_update_q(iop->dip, iop);
+ pip_update_q(iop);
+
+ tmp = dip_find_exact(dip_get_head(iop->dip, IOP_A), iop);
+ if (tmp) {
+ iop->u.q.qp_type = Q_A;
+ io_link(&iop->u.q.qp.q_a, tmp);
+ }
+ else
+ iop->u.q.qp_type = Q_NONE;
+
+#if defined(LVM_REMAP_WORKAROUND)
+ if (is_lvm) {
+ tmp = dip_find_qa(dip_get_head(iop->dip, IOP_A), &iop->t);
+ if (tmp) {
+ iop->u.q.qp_type = Q_A;
+ io_link(&iop->u.q.qp.q_a, tmp);
+ }
+ }
+#endif
+}
+
+void handle_merge(struct io *iop)
+{
+ struct io *q_iop;
+
+ io_setup(iop, IOP_M);
+
+ q_iop = dip_find_exact(dip_get_head(iop->dip, IOP_Q), iop);
+ if (q_iop)
+ io_link(&iop->u.m.m_q, q_iop);
+}
+
+void handle_insert(struct io *iop)
+{
+ struct io_list *iolp = malloc(sizeof(*iolp));
+
+ io_setup(iop, IOP_I);
+ INIT_LIST_HEAD(&iop->u.i.i_qs_head);
+ dip_add_qs(dip_get_head(iop->dip, IOP_Q), iop);
+}
+
+void handle_complete(struct io *iop)
+{
+ struct io *d_iop;
+
+ io_setup(iop, IOP_C);
+ update_blks(iop);
+ update_cregion(&all_regions, iop->t.time);
+ update_cregion(&iop->dip->regions, iop->t.time);
+ if (iop->pip)
+ update_cregion(&iop->pip->regions, iop->t.time);
+
+ d_iop = dip_find_exact(dip_get_head(iop->dip, IOP_D), iop);
+ if (d_iop) {
+ io_link(&iop->u.c.c_d, d_iop);
+
+ add_cy(iop);
+ }
+ else
+ io_free(iop);
+}
+
+void handle_issue(struct io *iop)
+{
+ struct io *i_iop;
+ struct io_list *iolp = malloc(sizeof(*iolp));
+
+ io_setup(iop, IOP_D);
+ iop->dip->n_ds++;
+
+ INIT_LIST_HEAD(&iop->u.d.d_im_head);
+ i_iop = dip_find_in(dip_get_head(iop->dip, IOP_I), iop);
+ if (i_iop) {
+ io_link(&iolp->iop, i_iop);
+ list_add_tail(&iolp->head, &iop->u.d.d_im_head);
+ }
+
+ dip_add_ms(dip_get_head(iop->dip, IOP_M), iop);
+}
+
+void handle_split(struct io *iop)
+{
+ struct io *q_iop;
+
+ pending_xs++;
+ io_setup(iop, IOP_X);
+
+ q_iop = dip_find_exact(dip_get_head(iop->dip, IOP_Q), iop);
+ if (q_iop)
+ io_link(&iop->u.x.x_q, q_iop);
+}
+
+#if 0
+void __x_add_c(struct io *y_iop, struct io *x_iop,
+ struct blk_io_trace_split_end *rp, int which)
+{
+ __u32 dev;
+ __u64 sector;
+ struct d_info *dip;
+ struct io **y_cp, *q_iop, *c_iop;
+
+ if (which == 1) {
+ y_cp = &y_iop->u.y.y_c1;
+ dev = be32_to_cpu(rp->dev1);
+ sector = be64_to_cpu(rp->sector1);
+ }
+ else {
+ y_cp = &y_iop->u.y.y_c2;
+ dev = be32_to_cpu(rp->dev2);
+ sector = be64_to_cpu(rp->sector2);
+ }
+
+ dip = __dip_find(dev);
+ ASSERT(dip != NULL);
+
+ q_iop = dip_find_end(dip_get_head(dip, IOP_Q), sector);
+ if (q_iop) {
+ q_iop->u.q.qp_type = Q_X;
+ io_link(&q_iop->u.q.qp.q_x, x_iop);
+ }
+
+ c_iop = dip_find_in_sec(dip_get_head(dip, IOP_C), sector);
+ if (c_iop)
+ io_link(y_cp, c_iop);
+}
+
+void handle_split_end(struct io *iop)
+{
+ struct io *x_iop;
+ struct blk_io_trace_split_end *rp = iop->pdu;
+
+ pending_xs--;
+ io_setup(iop, IOP_Y);
+
+ x_iop = dip_find_exact(dip_get_head(iop->dip, IOP_X), iop);
+ if (x_iop) {
+ __x_add_c(iop, x_iop, rp, 1);
+ __x_add_c(iop, x_iop, rp, 2);
+
+ rem_c(iop->u.y.y_c1);
+ rem_c(iop->u.y.y_c2);
+
+ add_cy(iop);
+ }
+ else
+ release_iop(iop);
+}
+#endif
+
+void handle_remap(struct io *iop)
+{
+ struct io *q_iop;
+ struct blk_io_trace_remap *rp = iop->pdu;
+ __u32 dev = be32_to_cpu(rp->device);
+ __u64 sector = be64_to_cpu(rp->sector);
+
+ q_iop = dip_find_in_sec(dip_get_head_dev(dev, IOP_Q), sector);
+ if (q_iop) {
+ io_setup(iop, IOP_A);
+
+#if defined(LVM_REMAP_WORKAROUND)
+ if (is_lvm) {
+ sector = iop->t.sector;
+ iop->t.sector = be64_to_cpu(rp->sector);
+ }
+#endif
+ io_link(&iop->u.a.a_q, q_iop);
+ }
+ else
+ release_iop(iop);
+}
+
+void extract_i(struct io *i_iop)
+{
+ struct io_list *iolp;
+ struct list_head *p, *q;
+
+ ASSERT(i_iop != NULL && i_iop->type == IOP_I);
+ list_for_each_safe(p, q, &i_iop->u.i.i_qs_head) {
+ iolp = list_entry(p, struct io_list, head);
+ LIST_DEL(&iolp->head);
+
+ ASSERT(iolp->iop->type == IOP_Q);
+ (void)__io_put(iolp->iop);
+
+ free(iolp);
+ }
+}
+
+/*
+ * Careful surgery
+ * (1) Need to remove D & its I & M's
+ * (2) Need to leave I's Q and M's Q's -- *no* io_put (__io_put instead)
+ */
+void handle_requeue(struct io *iop)
+{
+ struct io *d_iop;
+ struct io_list *iolp;
+ struct list_head *p, *q;
+
+ d_iop = dip_find_start(dip_get_head_dev(iop->t.device, IOP_D),
+ iop->t.sector);
+ if (d_iop) {
+ list_for_each_safe(p, q, &d_iop->u.d.d_im_head) {
+ iolp = list_entry(p, struct io_list, head);
+ LIST_DEL(&iolp->head);
+
+ if (iolp->iop->type == IOP_M)
+ (void)__io_put(iolp->iop->u.m.m_q);
+ else
+ extract_i(iolp->iop);
+
+ iolp->iop->users = 0;
+ io_free(iolp->iop);
+ free(iolp);
+ }
+
+ d_iop->users = 0;
+ io_free(d_iop);
+ }
+
+ release_iop(iop);
+
+}
+
+void __add_trace(struct io *iop)
+{
+ n_traces++;
+
+ if (verbose && (n_traces % 10000) == 0) {
+ printf("%10lu t, %10lu m %1lu f\r",
+ n_traces, n_io_allocs, n_io_frees);
+ fflush(stdout);
+ if ((n_traces % 1000000) == 0) printf("\n");
+ }
+
+ switch (iop->t.action & 0xffff) {
+ case __BLK_TA_QUEUE: handle_queue(iop); break;
+ case __BLK_TA_BACKMERGE: handle_merge(iop); break;
+ case __BLK_TA_FRONTMERGE: handle_merge(iop); break;
+ case __BLK_TA_ISSUE: handle_issue(iop); break;
+ case __BLK_TA_COMPLETE: handle_complete(iop); break;
+ case __BLK_TA_INSERT: handle_insert(iop); break;
+ case __BLK_TA_SPLIT: handle_split(iop); break;
+#if 0
+ case __BLK_TA_SPLIT_END: handle_split_end(iop); break;
+#endif
+ case __BLK_TA_REMAP: handle_remap(iop); break;
+ case __BLK_TA_REQUEUE: handle_requeue(iop); break;
+ }
+}
+
+void add_trace(struct io *iop)
+{
+ if (iop->t.action & BLK_TC_ACT(BLK_TC_NOTIFY)) {
+ char *slash = strchr(iop->pdu, '/');
+
+ if (slash)
+ *slash = '\0';
+ add_process(iop->t.pid, iop->pdu);
+ release_iop(iop);
+ }
+ else
+ __add_trace(iop);
+}
--- /dev/null
+/*
+ * blktrace output analysis: generate a timeline & gather statistics
+ *
+ * Copyright (C) 2006 Alan D. Brunelle <Alan.Brunelle@hp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#include "globals.h"
+
+typedef struct io *iop_t;
+typedef iop_t (*iop_func_t)(__u64 *, struct io*);
+
+struct io *iop_q_func(__u64 *timeline, struct io *iop);
+struct io *iop_x_func(__u64 *timeline, struct io *iop);
+struct io *iop_a_func(__u64 *timeline, struct io *iop);
+struct io *iop_m_func(__u64 *timeline, struct io *iop);
+struct io *iop_i_func(__u64 *timeline, struct io *iop);
+struct io *iop_d_func(__u64 *timeline, struct io *iop);
+struct io *iop_c_func(__u64 *timeline, struct io *iop);
+struct io *iop_y_func(__u64 *timeline, struct io *iop);
+
+iop_func_t traverse_func[] = {
+ iop_q_func,
+ iop_x_func,
+ iop_a_func,
+ iop_m_func,
+ iop_i_func,
+ iop_d_func,
+ iop_c_func,
+ iop_y_func,
+};
+
+void __traverse(__u64 *timeline, struct io *iop)
+{
+ while (iop != NULL && !iop->traversed) {
+ iop->traversed++;
+ iop = traverse_func[iop->type](timeline, iop);
+ }
+}
+
+void traverse(struct io *iop)
+{
+ int i;
+ __u64 timeline[N_IOP_TYPES];
+
+ for (i = 0; i < N_IOP_TYPES; i++)
+ timeline[i] = 0.0;
+
+ __traverse(timeline, iop);
+}
+
+void iop_q_update(__u64 *timeline, struct io *iop, __u64 q_time)
+{
+ update_q2c(iop, timeline[IOP_C] - q_time);
+
+ if (timeline[IOP_A] > 0.0) // IOP_X too
+ update_q2a(iop, timeline[IOP_A] - q_time);
+ else //IOP_M too
+ update_q2i(iop, timeline[IOP_I] - q_time);
+
+ update_i2d(iop, timeline[IOP_D] - timeline[IOP_I]);
+ update_d2c(iop, timeline[IOP_C] - timeline[IOP_D]);
+}
+
+struct io *iop_q_func(__u64 *timeline, struct io *iop)
+{
+ timeline[IOP_Q] = iop->t.time;
+ iop_q_update(timeline, iop, iop->t.time);
+
+ if (iop->u.q.qp_type == Q_A)
+ return iop->u.q.qp.q_a;
+ else if (iop->u.q.qp_type == Q_X)
+ return iop->u.q.qp.q_x;
+
+ /* Q_NONE */
+ return NULL;
+}
+
+struct io *iop_x_func(__u64 *timeline, struct io *iop)
+{
+ timeline[IOP_A] = iop->t.time; // Cover X & A in one slice
+ return iop->u.x.x_q;
+}
+
+struct io *iop_a_func(__u64 *timeline, struct io *iop)
+{
+ timeline[IOP_A] = iop->t.time;
+ return iop->u.a.a_q;
+}
+
+struct io *iop_m_func(__u64 *timeline, struct io *iop)
+{
+ timeline[IOP_I] = iop->t.time; // Cover M & I in one slice
+ return iop->u.m.m_q;
+}
+
+struct io *iop_i_func(__u64 *timeline, struct io *iop)
+{
+ struct list_head *p;
+ struct io_list *iolp;
+
+ timeline[IOP_I] = iop->t.time;
+ __list_for_each(p, &iop->u.i.i_qs_head) {
+ iolp = list_entry(p, struct io_list, head);
+ __traverse(timeline, iolp->iop);
+ }
+
+ return NULL;
+}
+
+struct io *iop_d_func(__u64 *timeline, struct io *iop)
+{
+ struct list_head *p;
+ struct io_list *iolp;
+
+ timeline[IOP_D] = iop->t.time;
+ __list_for_each(p, &iop->u.d.d_im_head) {
+ iolp = list_entry(p, struct io_list, head);
+ __traverse(timeline, iolp->iop);
+ }
+
+ return NULL;
+}
+
+struct io *iop_c_func(__u64 *timeline, struct io *iop)
+{
+ timeline[IOP_C] = iop->t.time;
+ return iop->u.c.c_d;
+}
+
+struct io *iop_y_func(__u64 *timeline, struct io *iop)
+{
+ timeline[IOP_Y] = iop->t.time;
+ __traverse(timeline, iop->u.y.y_c1);
+ __traverse(timeline, iop->u.y.y_c2);
+ return NULL;
+}