[PATCH] btt: seek additions
authorAlan D. Brunelle <Alan.Brunelle@hp.com>
Thu, 14 Sep 2006 23:53:19 +0000 (01:53 +0200)
committerJens Axboe <axboe@kernel.dk>
Thu, 14 Sep 2006 23:53:19 +0000 (01:53 +0200)
Added seek analysis to btt:

==================== Device Seek Information ====================

       DEV |          NSEEKS            MEAN          MEDIAN | MODE
---------- | --------------- --------------- --------------- | ---------------
 (  8, 16) |           15573      10297802.6               0 | 0(10710)

Shows the:
- Device (major, minor)

- Number of seeks (IOs issued to driver)

- Average (mean) of sector differences IO-to-IO

- Median seek (sector difference)

- Mode(s) - showing the mode and the number of times it occurred, thus in
  this example we see 68.8% of the "seeks" were contiguous (or overlapping).

Also added in the ability to dump all read and write seek values:

[ -s <output name> | --seeks=<output name> ]

Will create files with the name:

seek_<mjr,mnr>_[rw].dat

like:

seek_008,016_r.dat
seek_008,016_w.dat

Which contain relative sector offsets for reads and writes respectively.

Signed-off-by: Jens Axboe <axboe@kernel.dk>
btt/Makefile
btt/args.c
btt/bt_timeline.c
btt/devs.c
btt/globals.h
btt/output.c
btt/seek.c [new file with mode: 0644]
btt/trace.c

index 31f63a67a420a3f3cc40bdf3838c6b6b81c5e1b7..86d70f060aaf58795f8cfe5d960336842ec2a066 100644 (file)
@@ -8,7 +8,7 @@ PROGS   = btt
 #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
+         traverse.o iofree.o devmap.o cylist.o seek.o
 
 all: depend $(PROGS)
 
index d7ecb489937869e8f1b48300411199d58f855ef4..bed39c8c7ba7bc5dfb5c80ebd0e144fee8b6226e 100644 (file)
@@ -28,7 +28,7 @@
 
 #include "globals.h"
 
-#define S_OPTS "d:D:e:hlmM:i:o:Vv"
+#define S_OPTS "d:D:e:hlmM:i:o:s:Vv"
 static struct option l_opts[] = {
        {
                .name = "range-delta",
@@ -84,6 +84,12 @@ static struct option l_opts[] = {
                .flag = NULL,
                .val = 'o'
        },
+       {
+               .name = "seeks",
+               .has_arg = required_argument,
+               .flag = NULL,
+               .val = 's'
+       },
        {
                .name = "version",
                .has_arg = no_argument,
@@ -108,6 +114,7 @@ static char usage_str[] = \
        "[ -i <input name>  | --input-file=<input name> ]\n" \
        "(-l | -m)          | (--lvm | -md)\n" \
        "[ -o <output name> | --output-file=<output name> ]\n" \
+       "[ -s <output name> | --seeks=<output name> ]\n" \
        "[ -V               | --version ]\n" \
        "[ -v               | --verbose ]\n\n";
 
@@ -151,6 +158,9 @@ void handle_args(int argc, char *argv[])
                case 'o':
                        output_name = optarg;
                        break;
+               case 's':
+                       seek_name = optarg;
+                       break;
                case 'v':
                        verbose = 1;
                        break;
index 87255fccd59f09d0f86d6d8d59ce79ec68c22fab..1c3216db66101e50f3dc37bc968f5153a5280c0e 100644 (file)
@@ -30,6 +30,7 @@ char *devices = NULL;
 char *exes = NULL;
 char *input_name = NULL;
 char *output_name = NULL;
+char *seek_name = NULL;
 double range_delta = 0.1;
 FILE *ranges_ofp, *avgs_ofp;
 int is_lvm = -1;
index fe879425972a5bab41e47a77ea677c4480d71bb8..90d44fa988772d6542d591be004e01e8d0a9298e 100644 (file)
@@ -68,6 +68,7 @@ struct d_info *dip_add(__u32 device, struct io *iop)
                        INIT_LIST_HEAD(&dip->iop_heads[i]);
                init_region(&dip->regions);
                dip->map = dev_map_find(device);
+               dip->seek_handle = seeki_init(device);
 
                if (dev_heads == NULL) init_dev_heads();
                list_add_tail(&dip->hash_head, &dev_heads[DEV_HASH(device)]);
index 137d2760b91aa4c53c204110292520b72935bf2b..463b135e9908b98ab7c1fe0fda6398a13abc3642 100644 (file)
@@ -31,6 +31,7 @@
 
 #define BIT_START(iop) ((iop)->t.sector)
 #define BIT_END(iop)   ((iop)->t.sector + ((iop)->t.bytes >> 9))
+#define IOP_READ(iop)  ((iop)->t.action & BLK_TC_ACT(BLK_TC_READ))
 
 #if defined(DEBUG)
 #define ASSERT(truth)   do {                                           \
@@ -143,6 +144,7 @@ struct d_info {
        __u32 device;
        __u64 n_ds;
        struct devmap *map;
+       void *seek_handle;
 };
 
 struct io {
@@ -179,6 +181,7 @@ struct io {
 };
 
 extern char bt_timeline_version[], *devices, *exes, *input_name, *output_name;
+extern char *seek_name;
 extern double range_delta;
 extern FILE *ranges_ofp, *avgs_ofp;
 extern int is_lvm, verbose, ifd;
@@ -211,5 +214,11 @@ 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);
+void *seeki_init(__u32 device);
+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);
 
 #include "inlines.h"
index 8c84bc9f4f3048573bdbbbced0eee8dab76e4520..6973031696b58632fb4730ac5dd50d3894bd9dc3 100644 (file)
@@ -214,7 +214,7 @@ char *i2d_v_q2C(struct d_info *dip, char *s)
 {
        double q2c;
 
-       if (dip->avgs.i2d.n == 0) return " ";
+       if (dip->avgs.d2c.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));
@@ -284,6 +284,62 @@ void output_dip_prep_ohead(FILE *ofp)
        fprintf(ofp, "\n");
 }
 
+void __output_dip_seek_info(FILE *ofp, struct d_info *dip)
+{
+       double mean;
+       int i, nmodes, most_seeks;
+       long long nseeks;
+       char dev_info[12];
+       long long median, *modes;
+
+       nseeks = seeki_nseeks(dip->seek_handle);
+       mean = seeki_mean(dip->seek_handle);
+       median = seeki_median(dip->seek_handle);
+       nmodes = seeki_mode(dip->seek_handle, &modes, &most_seeks);
+
+       fprintf(ofp, "%10s | %15lld %15.1lf %15lld | %lld(%d)",
+               make_dev_hdr(dev_info, 12, dip), nseeks, mean, median, 
+               nmodes > 0 ? modes[0] : 0, most_seeks);
+       for (i = 1; i < nmodes; i++)
+               fprintf(ofp, " %lld", modes[i]);
+       fprintf(ofp, "\n");
+}
+
+void output_dip_seek_info(FILE *ofp)
+{
+       struct d_info *dip;
+
+       fprintf(ofp, "%10s | %15s %15s %15s | %-15s\n", "DEV", "NSEEKS", 
+                       "MEAN", "MEDIAN", "MODE");
+       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_seek_info(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_seek_info(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) {
@@ -445,6 +501,9 @@ int output_avgs(FILE *ofp)
        output_section_hdr(ofp, "Device Overhead");
        output_dip_prep_ohead(ofp);
 
+       output_section_hdr(ofp, "Device Seek Information");
+       output_dip_seek_info(ofp);
+
        return 0;
 }
 
diff --git a/btt/seek.c b/btt/seek.c
new file mode 100644 (file)
index 0000000..9ee8eec
--- /dev/null
@@ -0,0 +1,184 @@
+/*
+ * 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 seek_bkt {
+       long long sectors;
+       int nseeks;
+};
+
+struct seeki {
+       FILE *rfp, *wfp;
+       struct seek_bkt *seek_bkts;
+       int nseek_bkts;
+       long long total_seeks;
+       double total_sectors;
+       long long last_start, last_end;
+};
+
+FILE *seek_open(__u32 device, char rw)
+{
+       FILE *fp;
+       char *oname;
+       int mjr, mnr;
+
+       if (seek_name == NULL) return NULL;
+
+       mjr = device >> MINORBITS;
+       mnr = device & ((1 << MINORBITS) - 1);
+
+       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);
+
+       return fp;
+}
+
+long long seek_dist(struct seeki *sip, struct io *iop)
+{
+       long long dist;
+       long long start = BIT_START(iop), end = BIT_END(iop);
+
+       /* Some overlap means no seek */
+       if (((sip->last_start <= start) && (start <= sip->last_end)) ||
+           ((sip->last_start <= end) && (end <= sip->last_end)))
+               dist = 0;
+       else if (start > sip->last_end)
+               dist = start - sip->last_end;
+       else
+               dist = start - sip->last_start;
+
+       sip->last_start = start;
+       sip->last_end = end;
+       return dist;
+}
+
+void *seeki_init(__u32 device)
+{
+       struct seeki *sip = malloc(sizeof(struct seeki));
+
+       sip->rfp = seek_open(device, 'r');
+       sip->wfp = seek_open(device, 'w');
+       sip->seek_bkts = NULL;
+       sip->nseek_bkts = 0;
+       sip->total_seeks = 0;
+       sip->total_sectors = 0.0;
+       sip->last_start = sip->last_end = 0;
+
+       return sip;
+}
+
+void seeki_add(void *handle, struct io *iop)
+{
+       int left, mid, right;
+       struct seek_bkt *bkt;
+       struct seeki *sip = handle;
+       long long dist = seek_dist(sip, iop);
+       FILE *fp = IOP_READ(iop) ? sip->rfp : sip->wfp;
+
+       if (fp)
+               fprintf(fp, "%15.9lf %13lld\n", BIT_TIME(iop->t.time), dist);
+
+       dist = llabs(dist);
+       sip->total_seeks++;
+       sip->total_sectors += dist;
+
+       left = 0;
+       right = sip->nseek_bkts-1;
+       while (left <= right) {
+               mid = (left+right)/2;
+               bkt = &sip->seek_bkts[mid];
+               if (dist == bkt->sectors) {
+                       bkt->nseeks++;
+                       return;
+               }
+
+               if (dist > bkt->sectors)
+                       left = mid + 1;
+               else
+                       right = mid - 1;
+       }
+
+       sip->seek_bkts = realloc(sip->seek_bkts, 
+                                (sip->nseek_bkts+1) * sizeof(struct seek_bkt));
+       if (sip->nseek_bkts > left)
+               memmove(&sip->seek_bkts[left+1], &sip->seek_bkts[left], 
+                       (sip->nseek_bkts - left) * sizeof(struct seek_bkt));
+       (bkt = &sip->seek_bkts[left])->sectors = dist;
+       bkt->nseeks = 1;
+       sip->nseek_bkts++;
+}
+
+long long seeki_nseeks(void *handle)
+{
+       return ((struct seeki *)handle)->total_seeks;
+}
+
+double seeki_mean(void *handle)
+{
+       struct seeki *sip = handle;
+       return sip->total_sectors / sip->total_seeks;
+}
+
+long long seeki_median(void *handle)
+{
+       int i;
+       struct seek_bkt *p;
+       struct seeki *sip = handle;
+       long long sofar = 0, target = sip->total_seeks / 2;
+
+       if (sip->total_seeks == 0) return 0;
+       for (i = 0, p = sip->seek_bkts; i < sip->nseek_bkts; i++, p++)
+               if ((sofar + p->nseeks) < target)
+                       sofar += p->nseeks;
+               else
+                       break;
+
+       return p->sectors;
+}
+
+int seeki_mode(void *handle, long long **modes_p, int *nseeks_p)
+{
+       int i;
+       struct seek_bkt *p;
+       int most_seeks = 0;
+       struct seeki *sip = handle;
+       int nmodes = 0;
+       long long *modes = NULL;
+
+       for (i = 0, p = sip->seek_bkts; i < sip->nseek_bkts; i++, p++)
+               if ((modes == NULL) || (p->nseeks > most_seeks)) {
+                       most_seeks = p->nseeks;
+                       modes = realloc(modes, sizeof(long long));
+                       *modes = p->sectors;
+                       nmodes = 1;
+               }
+               else if (p->nseeks == most_seeks) {
+                       most_seeks = p->nseeks;
+                       modes = realloc(modes, (nmodes+1) * sizeof(long long));
+                       modes[nmodes++] = p->sectors;
+               }
+
+       *nseeks_p = most_seeks;
+       *modes_p = modes;
+       return nmodes;
+}
index 0008ffa563caeb3829495c22ce42ebba6835cc47..542512338c29af79d671b8a8417dba8ab272f7f6 100644 (file)
@@ -223,6 +223,7 @@ void handle_issue(struct io *iop)
        }
 
        dip_add_ms(dip_get_head(iop->dip, IOP_M), iop);
+       seeki_add(iop->dip->seek_handle, iop);
 }
 
 void handle_split(struct io *iop)