#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))
+#define IOP_RW(iop) (IOP_READ(iop) ? 1 : 0)
#if defined(DEBUG)
#define ASSERT(truth) do { \
char devno[32];
};
+struct stats {
+ __u64 rqm[2];
+ __u64 ios[2];
+ __u64 sec[2];
+ __u64 wait;
+ __u64 svctm;
+
+ int cur_qusz, cur_dev;
+ double last_qu_change, last_dev_change, tot_qusz, idle_time;
+};
+
struct d_info {
struct list_head head, hash_head;
struct list_head iop_heads[N_IOP_TYPES];
__u64 n_ds;
struct devmap *map;
void *seek_handle;
+ struct stats stats, all_stats;
};
struct io {
};
extern char bt_timeline_version[], *devices, *exes, *input_name, *output_name;
-extern char *seek_name;
+extern char *seek_name, *iostat_name;
extern double range_delta;
-extern FILE *ranges_ofp, *avgs_ofp;
+extern FILE *ranges_ofp, *avgs_ofp, *iostat_ofp;
extern int is_lvm, verbose, ifd;
extern unsigned int n_devs;
extern unsigned long n_traces, n_io_allocs, n_io_frees;
extern struct my_mem *free_ios, *free_bits;
extern char iop_map[];
extern unsigned int pending_xs;
+extern __u64 iostat_interval, iostat_last_stamp;
void add_trace(struct io *iop);
int in_devices(struct blk_io_trace *t);
+char *make_dev_hdr(char *pad, size_t len, struct d_info *dip);
int output_avgs(FILE *ofp);
int output_ranges(FILE *ofp);
unsigned int do_read(int ifd, void *buf, int len);
long long seeki_median(void *handle);
int seeki_mode(void *handle, long long **modes_p, int *nseeks_p);
+void iostat_init(void);
+void iostat_insert(struct io *iop);
+void iostat_merge(struct io *iop);
+void iostat_issue(struct io *iop);
+void iostat_complete(struct io *iop);
+void iostat_check_time(__u64 stamp);
+void iostat_dump_stats(__u64 stamp, int all);
+
#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 <stdio.h>
+#include <unistd.h>
+#include "globals.h"
+
+#define TO_SEC(nanosec) ((double)(nanosec) / 1.0e9)
+#define TO_MSEC(nanosec) (1000.0 * TO_SEC(nanosec))
+
+#define INC_STAT(dip, fld) \
+ do { \
+ (dip)->stats. fld ++; \
+ (dip)->all_stats. fld ++; \
+ } while (0)
+
+#define DEC_STAT(dip, fld) \
+ do { \
+ (dip)->stats. fld --; \
+ (dip)->all_stats. fld --; \
+ } while (0)
+
+#define ADD_STAT(dip, fld, val) \
+ do { \
+ __u64 __v = (val); \
+ (dip)->stats. fld += __v; \
+ (dip)->all_stats. fld += __v; \
+ } while (0)
+
+#define SUB_STAT(dip, fld, val) \
+ do { \
+ __u64 __v = (val); \
+ (dip)->stats. fld -= __v; \
+ (dip)->all_stats. fld -= __v; \
+ } while (0)
+
+__u64 last_start, iostat_last_stamp;
+__u64 iostat_interval = 1;
+char *iostat_name = NULL;
+FILE *iostat_ofp = NULL;
+
+void calc_waits(struct io *d_iop, __u64 c_time)
+{
+ __u64 waits = 0;
+ struct list_head *p;
+ struct io_list *iolp;
+
+ __list_for_each(p, &d_iop->u.d.d_im_head) {
+ iolp = list_entry(p, struct io_list, head);
+ waits += (c_time - iolp->iop->t.time);
+ }
+ ADD_STAT(d_iop->dip, wait, waits);
+}
+
+void dump_hdr(void)
+{
+ fprintf(iostat_ofp, "Device: rrqm/s wrqm/s r/s w/s "
+ "rsec/s wsec/s rkB/s wkB/s "
+ "avgrq-sz avgqu-sz await svctm %%util Stamp\n");
+}
+
+void iostat_init(void)
+{
+ last_start = (__u64)-1;
+ if (iostat_ofp)
+ dump_hdr();
+}
+
+void update_tot_qusz(struct d_info *dip, double now)
+{
+ dip->stats.tot_qusz += ((now - dip->stats.last_qu_change) *
+ dip->stats.cur_qusz);
+ dip->all_stats.tot_qusz += ((now - dip->all_stats.last_qu_change) *
+ dip->all_stats.cur_qusz);
+
+ dip->stats.last_qu_change = dip->all_stats.last_qu_change = now;
+}
+
+void update_idle_time(struct d_info *dip, double now)
+{
+ if (dip->stats.cur_dev == 0) {
+ dip->stats.idle_time += (now - dip->stats.last_dev_change);
+ dip->all_stats.idle_time +=
+ (now - dip->all_stats.last_dev_change);
+ }
+ dip->stats.last_dev_change = dip->all_stats.last_dev_change = now;
+}
+
+void __dump_stats(__u64 stamp, int all, struct d_info *dip)
+{
+ char hdr[16];
+ struct stats *sp;
+ double dt, nios, avgrq_sz, p_util, nrqm;
+ double now = TO_SEC(stamp);
+
+ if (all) {
+ dt = (double)stamp / 1.0e9;
+ sp = &dip->all_stats;
+ }
+ else {
+ dt = (double)(stamp-last_start) / 1.0e9;
+ sp = &dip->stats;
+ }
+
+ nios = (double)(sp->ios[0] + sp->ios[1]);
+ nrqm = (double)(sp->rqm[0] + sp->rqm[1]);
+ if (nios > 0.0) {
+ update_idle_time(dip, now);
+ update_tot_qusz(dip, now);
+ avgrq_sz = (double)(sp->sec[0] + sp->sec[1]) / nios;
+ p_util = 100.0 * (1.0 - (sp->idle_time / dt));
+
+ /*
+ * For AWAIT: nios should be the same as number of inserts
+ * and we add in nrqm (number of merges), which should give
+ * us the total number of IOs sent to the block IO layer.
+ */
+ fprintf(iostat_ofp, "%-11s ", make_dev_hdr(hdr, 11, dip));
+ fprintf(iostat_ofp, "%8.2lf ", (double)sp->rqm[1] / dt);
+ fprintf(iostat_ofp, "%8.2lf ", (double)sp->rqm[0] / dt);
+ fprintf(iostat_ofp, "%7.2lf ", (double)sp->ios[1] / dt);
+ fprintf(iostat_ofp, "%7.2lf ", (double)sp->ios[0] / dt);
+ fprintf(iostat_ofp, "%9.2lf ", (double)sp->sec[1] / dt);
+ fprintf(iostat_ofp, "%9.2lf ", (double)sp->sec[0] / dt);
+ fprintf(iostat_ofp, "%9.2lf ", (double)(sp->sec[1] / 2) / dt);
+ fprintf(iostat_ofp, "%9.2lf ", (double)(sp->sec[0] / 2) / dt);
+ fprintf(iostat_ofp, "%8.2lf ", avgrq_sz);
+ fprintf(iostat_ofp, "%8.2lf ", (double)sp->tot_qusz / dt);
+ fprintf(iostat_ofp, "%7.2lf ", TO_MSEC(sp->wait) / (nios+nrqm));
+ fprintf(iostat_ofp, "%7.2lf ", TO_MSEC(sp->svctm) / nios);
+ fprintf(iostat_ofp, "%6.2lf", p_util);
+ if (all)
+ fprintf(iostat_ofp, "%8s\n", "TOTAL");
+ else {
+ fprintf(iostat_ofp, "%8.2lf\n", TO_SEC(stamp));
+ sp->rqm[0] = sp->rqm[1] = 0;
+ sp->ios[0] = sp->ios[1] = 0;
+ sp->sec[0] = sp->sec[1] = 0;
+ sp->wait = sp->svctm = 0;
+
+ sp->tot_qusz = sp->idle_time = 0.0;
+ }
+ }
+}
+
+void iostat_dump_stats(__u64 stamp, int all)
+{
+ struct d_info *dip;
+
+ if (all)
+ dump_hdr();
+ if (devices == NULL) {
+ struct list_head *p;
+
+ __list_for_each(p, &all_devs) {
+ dip = list_entry(p, struct d_info, head);
+ __dump_stats(stamp, all, 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));
+ __dump_stats(stamp, all, dip);
+
+ p = strchr(p, ';');
+ if (p) p++;
+ }
+ }
+}
+
+void iostat_check_time(__u64 stamp)
+{
+ if (iostat_ofp) {
+ if (last_start == (__u64)-1)
+ last_start = stamp;
+ else if ((stamp - last_start) >= iostat_interval) {
+ iostat_dump_stats(stamp, 0);
+ last_start = stamp;
+ }
+
+ iostat_last_stamp = stamp;
+ }
+}
+
+void iostat_insert(struct io *iop)
+{
+ update_tot_qusz(iop->dip, TO_SEC(iop->t.time));
+ INC_STAT(iop->dip, cur_qusz);
+}
+
+void iostat_merge(struct io *iop)
+{
+ INC_STAT(iop->dip, rqm[IOP_RW(iop)]);
+}
+
+void iostat_issue(struct io *iop)
+{
+ int rw = IOP_RW(iop);
+ struct d_info *dip = iop->dip;
+ double now = TO_SEC(iop->t.time);
+
+ INC_STAT(dip, ios[rw]);
+ ADD_STAT(dip, sec[rw], iop->t.bytes >> 9);
+
+ update_idle_time(dip, now);
+ INC_STAT(dip, cur_dev);
+}
+
+void iostat_complete(struct io *iop)
+{
+ struct io *d_iop;
+ struct d_info *dip = iop->dip;
+
+ if ((d_iop = iop->u.c.c_d) != NULL) {
+ double now = TO_SEC(iop->t.time);
+ calc_waits(d_iop, iop->t.time);
+
+ update_tot_qusz(dip, now);
+ DEC_STAT(dip, cur_qusz);
+
+ update_idle_time(dip, now);
+ DEC_STAT(dip, cur_dev);
+
+ ADD_STAT(dip, svctm, iop->t.time - d_iop->t.time);
+ }
+}