misc.o output.o proc.o seek.o trace.o trace_complete.o trace_im.o \
trace_issue.o trace_queue.o trace_remap.o trace_requeue.o \
../rbtree.o mmap.o trace_plug.o bno_dump.o unplug_hist.o q2d.o \
- aqd.o
+ aqd.o plat.o
all: depend $(PROGS)
}
+void aqd_exit(void *info)
+{
+ free(info);
+}
+
+void aqd_clean(void)
+{
+ clean_files(&aqd_files);
+}
+
void aqd_issue(void *info, double ts)
{
struct aqd_info *ap = info;
#define SETBUFFER_SIZE (64 * 1024)
-#define S_OPTS "aAB:d:D:e:hi:I:l:m:M:o:p:q:Q:s:S:t:T:u:VvX"
+#define S_OPTS "aAB:d:D:e:hi:I:l:L:m:M:o:p:q:Q:s:S:t:T:u:VvX"
static struct option l_opts[] = {
{
.name = "seek-absolute",
.flag = NULL,
.val = 'l'
},
+ {
+ .name = "periodic-latencies",
+ .has_arg = required_argument,
+ .flag = NULL,
+ .val = 'L'
+ },
{
.name = "seeks-per-second",
.has_arg = required_argument,
"[ -i <input name> | --input-file=<input name> ]\n" \
"[ -I <output name> | --iostat=<output name> ]\n" \
"[ -l <output name> | --d2c-latencies=<output name> ]\n" \
+ "[ -L <freq> | --periodic-latencies=<freq> ]\n" \
"[ -m <output name> | --seeks-per-second=<output name> ]\n" \
"[ -M <dev map> | --dev-maps=<dev map>\n" \
"[ -o <output name> | --output-file=<output name> ]\n" \
case 'l':
d2c_name = strdup(optarg);
break;
+ case 'L':
+ plat_freq = atof(optarg);
+ break;
case 'I':
iostat_name = strdup(optarg);
break;
LIST_HEAD(free_bilinks);
__u64 q_histo[N_HIST_BKTS], d_histo[N_HIST_BKTS];
+double plat_freq = 0.0;
double range_delta = 0.1;
__u64 last_q = (__u64)-1;
dip_exit();
seek_clean();
pip_exit();
+ aqd_clean();
io_free_all();
region_exit(&all_regions);
region_exit(&dip->regions);
seeki_exit(dip->seek_handle);
seeki_exit(dip->q2q_handle);
+ aqd_exit(dip->aqd_handle);
+ plat_exit(dip->q2c_plat_handle);
+ plat_exit(dip->d2c_plat_handle);
bno_dump_exit(dip->bno_dump_handle);
unplug_hist_exit(dip->unplug_hist_handle);
if (output_all_data)
dip->seek_handle = seeki_init(mkhandle(str, device, "_d2d"));
dip->q2q_handle = seeki_init(mkhandle(str, device, "_q2q"));
dip->aqd_handle = aqd_init(mkhandle(str, device, "_aqd"));
+ dip->q2c_plat_handle =
+ plat_init(mkhandle(str, device, "_q2c_plat"));
+ dip->d2c_plat_handle =
+ plat_init(mkhandle(str, device, "_d2c_plat"));
latency_init(dip);
list_add_tail(&dip->hash_head, &dev_heads[DEV_HASH(device)]);
list_add_tail(&dip->all_head, &all_devs);
[ -i <input name> | --input-file=<input name> ]
[ -I <output name> | --iostat=<output name> ]
[ -l <output name> | --d2c-latencies=<output name> ]
+[ -L <freq> | --periodic-latencies=<freq> ]
[ -m <output name> | --seeks-per-second=<output name> ]
[ -M <dev map> | --dev-maps=<dev map>
[ -o <output name> | --output-file=<output name> ]
This option instructs \texttt{btt} to generate the D2C latency file
discussed in section~\ref{sec:lat-d2c}.
+\subsection{\label{sec:o-L}\texttt{--periodic-latencies}/\texttt{-L}}
+
+ When given a value greater than 0, this option will create two data
+ files (q2c \& d2c) per device containing a periodic timestamp \&
+ average latency over that period.
+
\subsection{\label{sec:o-m}\texttt{--seeks-per-second}\texttt{-m}}
Tells \texttt{btt} to output seeks per second information. Each device
struct region_info regions;
struct devmap *map;
void *q2q_handle, *seek_handle, *bno_dump_handle, *unplug_hist_handle;
- void *q2d_priv, *aqd_handle;
+ void *q2d_priv, *aqd_handle, *q2c_plat_handle, *d2c_plat_handle;
FILE *d2c_ofp, *q2c_ofp;
struct avgs_info avgs;
struct stats stats, all_stats;
extern char bt_timeline_version[], *devices, *exes, *input_name, *output_name;
extern char *seek_name, *iostat_name, *d2c_name, *q2c_name, *per_io_name;
extern char *bno_dump_name, *unplug_hist_name, *sps_name, *aqd_name;
-extern double range_delta;
+extern double range_delta, plat_freq;
extern FILE *ranges_ofp, *avgs_ofp, *xavgs_ofp, *iostat_ofp, *per_io_ofp;
extern FILE *msgs_ofp;
extern int verbose, done, time_bounded, output_all_data, seek_absolute;
/* aqd.c */
void *aqd_init(char *str);
+void aqd_exit(void *info);
+void aqd_clean(void);
void aqd_issue(void *info, double ts);
void aqd_complete(void *info, double ts);
void bno_dump_add(void *handle, struct io *iop);
void bno_dump_clean(void);
+/* plat.c */
+void *plat_init(char *str);
+void plat_exit(void *info);
+void plat_clean(void);
+void plat_x2c(void *info, __u64 ts, __u64 latency);
+
/* q2d.c */
void q2d_histo_add(void *priv, __u64 q2d);
void *q2d_init(void);
void latency_d2c(struct d_info *dip, __u64 tstamp, __u64 latency)
{
+ plat_x2c(dip->d2c_plat_handle, tstamp, latency);
latency_out(dip->d2c_ofp, tstamp, latency);
}
void latency_q2c(struct d_info *dip, __u64 tstamp, __u64 latency)
{
+ plat_x2c(dip->q2c_plat_handle, tstamp, latency);
latency_out(dip->q2c_ofp, tstamp, latency);
}
--- /dev/null
+/*
+ * blktrace output analysis: generate a timeline & gather statistics
+ *
+ * (C) Copyright 2008 Hewlett-Packard Development Company, L.P.
+ * 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 plat_info {
+ long nl;
+ FILE *fp;
+ double first_ts, last_ts, tl;
+};
+
+static struct file_info *plat_files = NULL;
+
+void *plat_init(char *str)
+{
+ char *oname;
+ struct plat_info *pp;
+
+ if (plat_freq <= 0.0) return NULL;
+
+ pp = malloc(sizeof(*pp));
+ pp->nl = 0;
+ pp->first_ts = pp->last_ts = pp->tl = -1.0;
+
+ oname = malloc(strlen(str) + 32);
+ sprintf(oname, "%s.dat", str);
+ if ((pp->fp = fopen(oname, "w")) == NULL) {
+ perror(oname);
+ return NULL;
+ }
+ add_file(&plat_files, pp->fp, oname);
+
+ return pp;
+}
+
+void plat_exit(void *info)
+{
+ struct plat_info *pp = info;
+
+ if (pp == NULL) return;
+
+ if (pp->first_ts != -1.0) {
+ double delta = pp->last_ts - pp->first_ts;
+
+ fprintf(pp->fp, "%lf %lf\n",
+ pp->first_ts + (delta / 2), pp->tl / pp->nl);
+ }
+ free(info);
+}
+
+void plat_clean(void)
+{
+ clean_files(&plat_files);
+}
+
+void plat_x2c(void *info, __u64 ts, __u64 latency)
+{
+ double now = TO_SEC(ts);
+ double lat = TO_SEC(latency);
+ struct plat_info *pp = info;
+
+ if (pp == NULL) return;
+
+ if (pp->first_ts == -1.0) {
+ pp->first_ts = pp->last_ts = now;
+ pp->nl = 1;
+ pp->tl = lat;
+ }
+ else if ((now - pp->first_ts) >= plat_freq) {
+ double delta = pp->last_ts - pp->first_ts;
+
+ fprintf(pp->fp, "%lf %lf\n",
+ pp->first_ts + (delta / 2), pp->tl / pp->nl);
+
+ pp->first_ts = pp->last_ts = now;
+ pp->nl = 1;
+ pp->tl = lat;
+ }
+ else {
+ pp->last_ts = now;
+ pp->nl += 1;
+ pp->tl += lat;
+ }
+}
.br
[ \-l <\fIoutput name\fR> | \-\-d2c\-latencies=<\fIoutput name\fR> ]
.br
+[ \-L <\fIfreq\fR> | \-\-periodic\-latencies=<\fIfreq\fR> ]
+.br
[ \-M <\fIdev map\fR> | \-\-dev\-maps=<\fIdev map\fR>
.br
[ \-o <\fIoutput name\fR> | \-\-output\-file=<\fIoutput name\fR> ]
name for each device.
.RE
+.B \-L <\fIfreq\fR>
+.br
+.B \-\-periodic\-latencies=<\fIfreq\fR>
+.RS 4
+The \-L option allows one to output periodic latency information for both
+Q2C and D2C latencies. The frequency specified will regulate how often
+an average latency is output -- a floating point value expressing seconds.
+.RE
+
.B \-M <\fIdev map\fR>
.br
.B \-\-dev\-maps=<\fIdev map\fR>