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>
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)
[ -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 ]
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.
#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",
.flag = NULL,
.val = 'I'
},
+ {
+ .name = "d2c-latencies",
+ .has_arg = required_argument,
+ .flag = NULL,
+ .val = 'l'
+ },
{
.name = "dev-maps",
.has_arg = required_argument,
.flag = NULL,
.val = 'o'
},
+ {
+ .name = "q2c-latencies",
+ .has_arg = required_argument,
+ .flag = NULL,
+ .val = 'q'
+ },
{
.name = "seeks",
.has_arg = required_argument,
"[ -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" \
case 'i':
input_name = optarg;
break;
+ case 'l':
+ d2c_name = optarg;
+ break;
case 'I':
iostat_name = optarg;
break;
case 'o':
output_name = optarg;
break;
+ case 'q':
+ q2c_name = optarg;
+ break;
case 's':
seek_name = optarg;
break;
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;
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);
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));
#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)) { \
};
#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;
};
__u64 n_ds;
struct devmap *map;
void *seek_handle;
+ FILE *d2c_ofp, *q2c_ofp;
struct stats stats, all_stats;
};
};
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;
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);
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"
--- /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 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);
+}
*/
#include <stdio.h>
#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
#include <unistd.h>
#define INLINE_DECLARE
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);
+ }
+}
*/
#include "globals.h"
+static struct file_info *all_files = NULL;
+
struct seek_bkt {
long long sectors;
int nseeks;
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;
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);
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)
{
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)