[PATCH] Added in Q2C and D2C latency output option.
authorAlan David Brunelle <Alan.Brunelle@hp.com>
Thu, 21 Sep 2006 07:17:43 +0000 (09:17 +0200)
committerJens Axboe <axboe@kernel.dk>
Thu, 21 Sep 2006 07:17:43 +0000 (09:17 +0200)
Also: cleaned up empty seek and latency files on exit.

Signed-off-by: Alan D. Brunelle <Alan.Brunelle@hp.com>
Signed-off-by: Jens Axboe <axboe@kernel.dk>
btt/Makefile
btt/README
btt/args.c
btt/bt_timeline.c
btt/devs.c
btt/globals.h
btt/latency.c [new file with mode: 0644]
btt/misc.c
btt/seek.c
btt/traverse.c

index b86e8282221eb4ddfb8934b66e8c48ed5d7f1dfb..2d60eedef7d933e135bb8868983746aac07961de 100644 (file)
@@ -1,14 +1,14 @@
 CC     = gcc
 MYFLAGS = -DLVM_REMAP_WORKAROUND
-CFLAGS = -Wall -O2 -W -g
-#CFLAGS        = -Wall -g -W -UDO_INLINE -DDEBUG
+#CFLAGS        = -Wall -O2 -W -g
+CFLAGS = -Wall -g -W -UDO_INLINE -DDEBUG
 ALL_CFLAGS = $(CFLAGS) -I.. -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 seek.o iostat.o
+         traverse.o iofree.o devmap.o cylist.o seek.o iostat.o latency.o
 
 all: depend $(PROGS)
 
index 41ecdc40f1ae590880ac387a14e5ceead0d0374a..938f9cece3d39ce04c6281b72afbb462a3169c77 100644 (file)
@@ -13,8 +13,10 @@ Usage: btt
        [ -h               | --help ]
        [ -i <input name>  | --input-file=<input name> ]
        [ -I <output name> | --iostat=<output name> ]
+       [ -l <output name> | --d2c-latencies=<output name> ]
        [ -M <dev map>     | --dev-maps=<dev map>
        [ -o <output name> | --output-file=<output name> ]
+       [ -q <output name> | --q2c-latencies=<output name> ]
        [ -s <output name> | --seeks=<output name> ]
        [ -S <interval>    | --iostat-interval=<interval> ]
        [ -V               | --version ]
@@ -41,6 +43,10 @@ file.  Refer to the iostat (sysstat) documentation for details on the
 data columns. The -S option specifies the interval to use between data
 output, it defaults to once per second.
 
+The -l and -q options allow one to output per-IO D2C and Q2C latencies
+respectively. The supplied argument provides the basis for the output
+name for each device.
+
 The -M option takes in a file generated by the provided script
 (gen_disk_info.py), and allows for better output of device names.
 
index 7b4d0ee474472c792a9ea9cb48e80e08e5ec2f54..9dcb694cd7f248310a467e6a3668a1ceff0dcc62 100644 (file)
@@ -28,7 +28,7 @@
 
 #include "globals.h"
 
-#define S_OPTS "d:D:e:hi:I:M:o:s:S:Vv"
+#define S_OPTS "d:D:e:hi:I:l:M:o:q:s:S:Vv"
 static struct option l_opts[] = {
        {
                .name = "range-delta",
@@ -66,6 +66,12 @@ static struct option l_opts[] = {
                .flag = NULL,
                .val = 'I'
        },
+       {
+               .name = "d2c-latencies",
+               .has_arg = required_argument,
+               .flag = NULL,
+               .val = 'l'
+       },
        {
                .name = "dev-maps",
                .has_arg = required_argument,
@@ -78,6 +84,12 @@ static struct option l_opts[] = {
                .flag = NULL,
                .val = 'o'
        },
+       {
+               .name = "q2c-latencies",
+               .has_arg = required_argument,
+               .flag = NULL,
+               .val = 'q'
+       },
        {
                .name = "seeks",
                .has_arg = required_argument,
@@ -114,8 +126,10 @@ static char usage_str[] = \
        "[ -h               | --help ]\n" \
        "[ -i <input name>  | --input-file=<input name> ]\n" \
        "[ -I <output name> | --iostat=<output name> ]\n" \
+       "[ -l <output name> | --d2c-latencies=<output name> ]\n" \
        "[ -M <dev map>     | --dev-maps=<dev map>\n" \
        "[ -o <output name> | --output-file=<output name> ]\n" \
+       "[ -q <output name> | --q2c-latencies=<output name> ]\n" \
        "[ -s <output name> | --seeks=<output name> ]\n" \
        "[ -S <interval>    | --iostat-interval=<interval> ]\n" \
        "[ -V               | --version ]\n" \
@@ -149,6 +163,9 @@ void handle_args(int argc, char *argv[])
                case 'i':
                        input_name = optarg;
                        break;
+               case 'l':
+                       d2c_name = optarg;
+                       break;
                case 'I':
                        iostat_name = optarg;
                        break;
@@ -158,6 +175,9 @@ void handle_args(int argc, char *argv[])
                case 'o':
                        output_name = optarg;
                        break;
+               case 'q':
+                       q2c_name = optarg;
+                       break;
                case 's':
                        seek_name = optarg;
                        break;
index 9e69fe51a609df6c94d70b4e2046083b7ea0f0e2..9d07382f17f78ffbdb10a48f878cf4b84f43d6a6 100644 (file)
@@ -31,6 +31,8 @@ char *exes = NULL;
 char *input_name = NULL;
 char *output_name = NULL;
 char *seek_name = NULL;
+char *d2c_name = NULL;
+char *q2c_name = NULL;
 double range_delta = 0.1;
 FILE *ranges_ofp, *avgs_ofp;
 int verbose = 0;
@@ -97,6 +99,9 @@ int process(void)
                iostat_dump_stats(iostat_last_stamp, 1);
        }
 
+       seek_clean();
+       latency_clean();
+
        if (verbose)
                printf("\n%10lu traces, %10lu mallocs %1lu frees\n",
                                       n_traces, n_io_allocs, n_io_frees);
index 2801804f550aaeed03651ee9e00c670c83c53fec..1d41ee9594df0b49118e88e953fc1e60ca101637 100644 (file)
@@ -69,6 +69,7 @@ struct d_info *dip_add(__u32 device, struct io *iop)
                init_region(&dip->regions);
                dip->map = dev_map_find(device);
                dip->seek_handle = seeki_init(device);
+               latency_init(dip);
                memset(&dip->stats, 0, sizeof(dip->stats));
                memset(&dip->all_stats, 0, sizeof(dip->all_stats));
 
index de67e0433af4530163c332ad70cc9155bc7ec051..4a5314050b27073b0a0f064f40b0b3a22dcb5c36 100644 (file)
@@ -34,6 +34,9 @@
 #define IOP_READ(iop)  ((iop)->t.action & BLK_TC_ACT(BLK_TC_READ))
 #define IOP_RW(iop)    (IOP_READ(iop) ? 1 : 0)
 
+#define TO_SEC(nanosec)        ((double)(nanosec) / 1.0e9)
+#define TO_MSEC(nanosec) (1000.0 * TO_SEC(nanosec))
+
 #if defined(DEBUG)
 #define ASSERT(truth)   do {                                           \
                                if (!(truth)) {                         \
@@ -72,6 +75,12 @@ enum iop_type {
 };
 #define N_IOP_TYPES    (IOP_Y + 1)
 
+struct file_info {
+       struct file_info *next;
+       FILE *ofp;
+       char *oname;
+};
+
 struct my_mem {
        struct my_mem *next;
 };
@@ -157,6 +166,7 @@ struct d_info {
        __u64 n_ds;
        struct devmap *map;
        void *seek_handle;
+       FILE *d2c_ofp, *q2c_ofp;
        struct stats stats, all_stats;
 };
 
@@ -204,7 +214,7 @@ struct io {
 };
 
 extern char bt_timeline_version[], *devices, *exes, *input_name, *output_name;
-extern char *seek_name, *iostat_name;
+extern char *seek_name, *iostat_name, *d2c_name, *q2c_name;
 extern double range_delta;
 extern FILE *ranges_ofp, *avgs_ofp, *iostat_ofp;
 extern int verbose, ifd;
@@ -240,11 +250,14 @@ struct d_info *dip_add(__u32 device, struct io *iop);
 void traverse(struct io *iop);
 void io_free_resources(struct io *iop);
 void *seeki_init(__u32 device);
+void seek_clean(void);
 void seeki_add(void *handle, struct io *iop);
 double seeki_mean(void *handle);
 long long seeki_nseeks(void *handle);
 long long seeki_median(void *handle);
 int seeki_mode(void *handle, long long **modes_p, int *nseeks_p);
+void add_file(struct file_info **fipp, FILE *fp, char *oname);
+void clean_files(struct file_info **fipp);
 
 void iostat_init(void);
 void iostat_insert(struct io *iop);
@@ -254,4 +267,9 @@ void iostat_complete(struct io *iop);
 void iostat_check_time(__u64 stamp);
 void iostat_dump_stats(__u64 stamp, int all);
 
+void latency_init(struct d_info *dip);
+void latency_clean(void);
+void latency_d2c(struct d_info *dip, __u64 tstamp, __u64 latency);
+void latency_q2c(struct d_info *dip, __u64 tstamp, __u64 latency);
+
 #include "inlines.h"
diff --git a/btt/latency.c b/btt/latency.c
new file mode 100644 (file)
index 0000000..916f566
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * 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 struct file_info *all_files = NULL;
+
+static inline void latency_out(FILE *ofp, __u64 tstamp, __u64 latency)
+{
+       if (ofp)
+               fprintf(ofp, "%lf %lf\n", TO_SEC(tstamp), TO_SEC(latency));
+}
+
+FILE *latency_open(__u32 device, char *name, char *post)
+{
+       FILE *fp;
+       char *oname;
+       int mjr, mnr;
+
+       if (name == NULL) return NULL;
+
+       mjr = device >> MINORBITS;
+       mnr = device & ((1 << MINORBITS) - 1);
+
+       oname = malloc(strlen(name)+32);
+       sprintf(oname, "%s_%03d,%03d_%s.dat", name, mjr, mnr, post);
+       if ((fp = fopen(oname, "w")) == NULL)
+               perror(oname);
+       else
+               add_file(&all_files, fp, oname);
+
+       return fp;
+}
+
+void latency_init(struct d_info *dip)
+{
+       dip->d2c_ofp = latency_open(dip->device, d2c_name, "d2c");
+       dip->q2c_ofp = latency_open(dip->device, q2c_name, "q2c");
+}
+
+void latency_clean(void)
+{
+       clean_files(&all_files);
+}
+
+void latency_d2c(struct d_info *dip, __u64 tstamp, __u64 latency)
+{
+       latency_out(dip->d2c_ofp, tstamp, latency);
+}
+
+void latency_q2c(struct d_info *dip, __u64 tstamp, __u64 latency)
+{
+       latency_out(dip->q2c_ofp, tstamp, latency);
+}
index 8659db7981cf058c863aa0ff3de9a3477643152b..e0e396c12292a439479035857a2ac6bd92206c0f 100644 (file)
@@ -20,6 +20,8 @@
  */
 #include <stdio.h>
 #include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
 #include <unistd.h>
 
 #define INLINE_DECLARE
@@ -82,3 +84,29 @@ unsigned int do_read(int ifd, void *buf, int len)
 
        return 0;
 }
+
+void add_file(struct file_info **fipp, FILE *fp, char *oname)
+{
+       struct file_info *fip = malloc(sizeof(struct file_info));
+
+       fip->ofp = fp;
+       fip->oname = oname;
+       fip->next = *fipp;
+       *fipp = fip;
+}
+
+void clean_files(struct file_info **fipp)
+{
+       struct stat buf;
+       struct file_info *fip;
+
+       while ((fip = *fipp) != NULL) {
+               *fipp = fip->next;
+
+               fclose(fip->ofp);
+               if (!stat(fip->oname, &buf) && (buf.st_size == 0))
+                       unlink(fip->oname);
+               free(fip->oname);
+               free(fip);
+       }
+}
index 9ee8eecb8ace47c16d4f4d5f60facb1859005978..3003611a8e8d595b2c80ebc5d080e3ef992e5665 100644 (file)
@@ -20,6 +20,8 @@
  */
 #include "globals.h"
 
+static struct file_info *all_files = NULL;
+
 struct seek_bkt {
        long long sectors;
        int nseeks;
@@ -45,14 +47,21 @@ FILE *seek_open(__u32 device, char rw)
        mjr = device >> MINORBITS;
        mnr = device & ((1 << MINORBITS) - 1);
 
-       oname = malloc(strlen(seek_name + 32));
+       oname = malloc(strlen(seek_name)+32);
        sprintf(oname, "%s_%03d,%03d_%c.dat", seek_name, mjr, mnr, rw);
        if ((fp = fopen(oname, "w")) == NULL)
                perror(oname);
+       else
+               add_file(&all_files, fp, oname);
 
        return fp;
 }
 
+void seek_clean(void)
+{
+       clean_files(&all_files);
+}
+
 long long seek_dist(struct seeki *sip, struct io *iop)
 {
        long long dist;
index fd4a847819051544413280fc4a09e88dc49674b9..d48755a3860b70f1d7e12c12df0925d036e73b31 100644 (file)
@@ -74,7 +74,10 @@ void traverse(struct io *iop)
 
 void iop_q_update(__u64 *timeline, struct io *iop, __u64 q_time)
 {
-       update_q2c(iop, timeline[IOP_C] - q_time);
+       __u64 q2c = timeline[IOP_C] - q_time;
+
+       update_q2c(iop, q2c);
+       latency_q2c(iop->dip, iop->t.time, q2c);
 
        if (timeline[IOP_A] > 0.0)      // IOP_X too
                update_q2a(iop, timeline[IOP_A] - q_time);
@@ -82,7 +85,6 @@ void iop_q_update(__u64 *timeline, struct io *iop, __u64 q_time)
                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]);
 }
 
 void iop_q_func(__u64 *timeline, struct io *iop)
@@ -127,11 +129,15 @@ void iop_d_func(__u64 *timeline, struct io *iop)
 {
        struct list_head *p;
        struct io_list *iolp;
+       __u64 d2c = timeline[IOP_C] - 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);
        }
+
+       update_d2c(iop, d2c);
+       latency_d2c(iop->dip, iop->t.time, d2c);
 }
 
 void iop_c_func(__u64 *timeline, struct io *iop)