From: Alan D. Brunelle Date: Thu, 14 Sep 2006 23:53:19 +0000 (+0200) Subject: [PATCH] btt: seek additions X-Git-Tag: blktrace-0.99.2~25 X-Git-Url: https://git.kernel.dk/?a=commitdiff_plain;h=5225e7882fcf560afe419c49e03d6fd1ead60eb1;p=blktrace.git [PATCH] btt: seek additions 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 | --seeks= ] Will create files with the name: seek__[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 --- diff --git a/btt/Makefile b/btt/Makefile index 31f63a6..86d70f0 100644 --- a/btt/Makefile +++ b/btt/Makefile @@ -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) diff --git a/btt/args.c b/btt/args.c index d7ecb48..bed39c8 100644 --- a/btt/args.c +++ b/btt/args.c @@ -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-file= ]\n" \ "(-l | -m) | (--lvm | -md)\n" \ "[ -o | --output-file= ]\n" \ + "[ -s | --seeks= ]\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; diff --git a/btt/bt_timeline.c b/btt/bt_timeline.c index 87255fc..1c3216d 100644 --- a/btt/bt_timeline.c +++ b/btt/bt_timeline.c @@ -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; diff --git a/btt/devs.c b/btt/devs.c index fe87942..90d44fa 100644 --- a/btt/devs.c +++ b/btt/devs.c @@ -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)]); diff --git a/btt/globals.h b/btt/globals.h index 137d276..463b135 100644 --- a/btt/globals.h +++ b/btt/globals.h @@ -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" diff --git a/btt/output.c b/btt/output.c index 8c84bc9..6973031 100644 --- a/btt/output.c +++ b/btt/output.c @@ -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 index 0000000..9ee8eec --- /dev/null +++ b/btt/seek.c @@ -0,0 +1,184 @@ +/* + * blktrace output analysis: generate a timeline & gather statistics + * + * Copyright (C) 2006 Alan D. Brunelle + * + * 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; +} diff --git a/btt/trace.c b/btt/trace.c index 0008ffa..5425123 100644 --- a/btt/trace.c +++ b/btt/trace.c @@ -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)