#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)
#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",
.flag = NULL,
.val = 'o'
},
+ {
+ .name = "seeks",
+ .has_arg = required_argument,
+ .flag = NULL,
+ .val = 's'
+ },
{
.name = "version",
.has_arg = no_argument,
"[ -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";
case 'o':
output_name = optarg;
break;
+ case 's':
+ seek_name = optarg;
+ break;
case 'v':
verbose = 1;
break;
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;
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)]);
#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 { \
__u32 device;
__u64 n_ds;
struct devmap *map;
+ void *seek_handle;
};
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;
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"
{
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));
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) {
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;
}
--- /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 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;
+}
}
dip_add_ms(dip_get_head(iop->dip, IOP_M), iop);
+ seeki_add(iop->dip->seek_handle, iop);
}
void handle_split(struct io *iop)