Home | History | Annotate | Download | only in btt
      1 /*
      2  * blktrace output analysis: generate a timeline & gather statistics
      3  *
      4  * Copyright (C) 2006 Alan D. Brunelle <Alan.Brunelle (at) hp.com>
      5  *
      6  *  This program is free software; you can redistribute it and/or modify
      7  *  it under the terms of the GNU General Public License as published by
      8  *  the Free Software Foundation; either version 2 of the License, or
      9  *  (at your option) any later version.
     10  *
     11  *  This program is distributed in the hope that it will be useful,
     12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
     13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     14  *  GNU General Public License for more details.
     15  *
     16  *  You should have received a copy of the GNU General Public License
     17  *  along with this program; if not, write to the Free Software
     18  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
     19  *
     20  */
     21 #include <stdio.h>
     22 #include <unistd.h>
     23 #include "globals.h"
     24 
     25 #define INC_STAT(dip, fld)						\
     26 	do {								\
     27 		(dip)->stats. fld ++;					\
     28 		(dip)->all_stats. fld ++;				\
     29 	} while (0)
     30 
     31 #define DEC_STAT(dip, fld)						\
     32 	do {								\
     33 		(dip)->stats. fld --;					\
     34 		(dip)->all_stats. fld --;				\
     35 	} while (0)
     36 
     37 #define ADD_STAT(dip, fld, val)						\
     38 	do {								\
     39 		__u64 __v = (val);					\
     40 		(dip)->stats. fld += __v;				\
     41 		(dip)->all_stats. fld += __v;				\
     42 	} while (0)
     43 
     44 #define SUB_STAT(dip, fld, val)						\
     45 	do {								\
     46 		__u64 __v = (val);					\
     47 		(dip)->stats. fld -= __v;				\
     48 		(dip)->all_stats. fld -= __v;				\
     49 	} while (0)
     50 
     51 __u64 last_start, iostat_last_stamp;
     52 __u64 iostat_interval = 1000000000;
     53 char *iostat_name = NULL;
     54 FILE *iostat_ofp = NULL;
     55 
     56 static void dump_hdr(void)
     57 {
     58 	fprintf(iostat_ofp, "Device:       rrqm/s   wrqm/s     r/s     w/s    "
     59 			    "rsec/s    wsec/s     rkB/s     wkB/s "
     60 			    "avgrq-sz avgqu-sz   await   svctm  %%util   Stamp\n");
     61 }
     62 
     63 static void update_tot_qusz(struct d_info *dip, double now)
     64 {
     65 	dip->stats.tot_qusz += ((now - dip->stats.last_qu_change) *
     66 						dip->stats.cur_qusz);
     67 	dip->all_stats.tot_qusz += ((now - dip->all_stats.last_qu_change) *
     68 						dip->all_stats.cur_qusz);
     69 
     70 	dip->stats.last_qu_change = dip->all_stats.last_qu_change = now;
     71 }
     72 
     73 static void update_idle_time(struct d_info *dip, double now, int force)
     74 {
     75 	if (dip->stats.cur_dev == 0 || force) {
     76 		dip->stats.idle_time += (now - dip->stats.last_dev_change);
     77 		dip->all_stats.idle_time +=
     78 				       (now - dip->all_stats.last_dev_change);
     79 	}
     80 	dip->stats.last_dev_change = dip->all_stats.last_dev_change = now;
     81 }
     82 
     83 void __dump_stats(__u64 stamp, int all, struct d_info *dip, struct stats_t *asp)
     84 {
     85 	char hdr[16];
     86 	struct stats *sp;
     87 	double dt, nios, avgrq_sz, p_util, nrqm, await, svctm;
     88 	double now = TO_SEC(stamp);
     89 
     90 	if (all) {
     91 		dt = (double)stamp / 1.0e9;
     92 		sp = &dip->all_stats;
     93 	} else {
     94 		dt = (double)(stamp-last_start) / 1.0e9;
     95 		sp = &dip->stats;
     96 	}
     97 
     98 	nios = (double)(sp->ios[0] + sp->ios[1]);
     99 	nrqm = (double)(sp->rqm[0] + sp->rqm[1]);
    100 	update_idle_time(dip, now, 1);
    101 	update_tot_qusz(dip, now);
    102 
    103 	if (nios > 0.0) {
    104 		avgrq_sz = (double)(sp->sec[0] + sp->sec[1]) / nios;
    105 		svctm = TO_MSEC(sp->svctm) / nios;
    106 	} else
    107 		avgrq_sz = svctm = 0.0;
    108 
    109 	await = ((nios + nrqm) > 0.0) ? TO_MSEC(sp->wait) / (nios+nrqm) : 0.0;
    110 	p_util = (sp->idle_time <= dt) ? 100.0 * (1.0 - (sp->idle_time / dt)) :
    111 	                                 0.0;
    112 
    113 	/*
    114 	 * For AWAIT: nios should be the same as number of inserts
    115 	 * and we add in nrqm (number of merges), which should give
    116 	 * us the total number of IOs sent to the block IO layer.
    117 	 */
    118 	fprintf(iostat_ofp, "%-11s ", make_dev_hdr(hdr, 11, dip, 1));
    119 	fprintf(iostat_ofp, "%8.2lf ", (double)sp->rqm[1] / dt);
    120 	fprintf(iostat_ofp, "%8.2lf ", (double)sp->rqm[0] / dt);
    121 	fprintf(iostat_ofp, "%7.2lf ", (double)sp->ios[1] / dt);
    122 	fprintf(iostat_ofp, "%7.2lf ", (double)sp->ios[0] / dt);
    123 	fprintf(iostat_ofp, "%9.2lf ", (double)sp->sec[1] / dt);
    124 	fprintf(iostat_ofp, "%9.2lf ", (double)sp->sec[0] / dt);
    125 	fprintf(iostat_ofp, "%9.2lf ", (double)(sp->sec[1] / 2) / dt);
    126 	fprintf(iostat_ofp, "%9.2lf ", (double)(sp->sec[0] / 2) / dt);
    127 	fprintf(iostat_ofp, "%8.2lf ", avgrq_sz);
    128 	fprintf(iostat_ofp, "%8.2lf ", (double)sp->tot_qusz / dt);
    129 	fprintf(iostat_ofp, "%7.2lf ", await);
    130 	fprintf(iostat_ofp, "%7.2lf ", svctm);
    131 	fprintf(iostat_ofp, "%6.2lf", p_util);
    132 	if (all)
    133 		fprintf(iostat_ofp, "%8s\n", "TOTAL");
    134 	else {
    135 		fprintf(iostat_ofp, "%8.2lf\n", TO_SEC(stamp));
    136 		sp->rqm[0] = sp->rqm[1] = 0;
    137 		sp->ios[0] = sp->ios[1] = 0;
    138 		sp->sec[0] = sp->sec[1] = 0;
    139 		sp->wait = sp->svctm = 0;
    140 
    141 		sp->tot_qusz = sp->idle_time = 0.0;
    142 	}
    143 
    144 	if (asp) {
    145 		int i;
    146 
    147 		asp->n += 1.0;
    148 		for (i = 0; i < 2; i++) {
    149 			asp->rqm_s[i] += ((double)sp->rqm[i] / dt);
    150 			asp->ios_s[i] += ((double)sp->ios[i] / dt);
    151 			asp->sec_s[i] += ((double)sp->sec[i] / dt);
    152 		}
    153 		asp->avgrq_sz += avgrq_sz;
    154 		asp->avgqu_sz += (double)sp->tot_qusz / dt;
    155 		asp->await += await;
    156 		asp->svctm += svctm;
    157 		asp->p_util += p_util;
    158 	}
    159 }
    160 
    161 static void __dump_stats_t(__u64 stamp, struct stats_t *asp, int all)
    162 {
    163 	if (asp->n < 2.0) return;	// What's the point?
    164 
    165 	fprintf(iostat_ofp, "%-11s ", "TOTAL");
    166 	fprintf(iostat_ofp, "%8.2lf ", asp->rqm_s[0]);
    167 	fprintf(iostat_ofp, "%8.2lf ", asp->rqm_s[1]);
    168 	fprintf(iostat_ofp, "%7.2lf ", asp->ios_s[0]);
    169 	fprintf(iostat_ofp, "%7.2lf ", asp->ios_s[1]);
    170 	fprintf(iostat_ofp, "%9.2lf ", asp->sec_s[0]);
    171 	fprintf(iostat_ofp, "%9.2lf ", asp->sec_s[1]);
    172 	fprintf(iostat_ofp, "%9.2lf ", asp->sec_s[0] / 2.0);
    173 	fprintf(iostat_ofp, "%9.2lf ", asp->sec_s[1] / 2.0);
    174 	fprintf(iostat_ofp, "%8.2lf ", asp->avgrq_sz / asp->n);
    175 	fprintf(iostat_ofp, "%8.2lf ", asp->avgqu_sz / asp->n);
    176 	fprintf(iostat_ofp, "%7.2lf ", asp->await / asp->n);
    177 	fprintf(iostat_ofp, "%7.2lf ", asp->svctm / asp->n);
    178 	fprintf(iostat_ofp, "%6.2lf", asp->p_util / asp->n);
    179 	if (all)
    180 		fprintf(iostat_ofp, "%8s\n", "TOTAL");
    181 	else
    182 		fprintf(iostat_ofp, "%8.2lf\n", TO_SEC(stamp));
    183 }
    184 
    185 void iostat_init(void)
    186 {
    187 	last_start = (__u64)-1;
    188 	if (iostat_ofp)
    189 		dump_hdr();
    190 }
    191 
    192 void iostat_dump_stats(__u64 stamp, int all)
    193 {
    194 	struct d_info *dip;
    195 	struct stats_t as;
    196 
    197 	memset(&as, 0, sizeof(struct stats_t));
    198 	if (all)
    199 		dump_hdr();
    200 
    201 	if (devices == NULL) {
    202 		struct list_head *p;
    203 
    204 		__list_for_each(p, &all_devs) {
    205 			dip = list_entry(p, struct d_info, all_head);
    206 			__dump_stats(stamp, all, dip, &as);
    207 		}
    208 	} else {
    209 		int i;
    210 		unsigned int mjr, mnr;
    211 		char *p = devices;
    212 
    213 		while (p && ((i = sscanf(p, "%u,%u", &mjr, &mnr)) == 2)) {
    214 			dip = __dip_find((__u32)((mjr << MINORBITS) | mnr));
    215 			__dump_stats(stamp, all, dip, &as);
    216 
    217 			p = strchr(p, ';');
    218 			if (p) p++;
    219 		}
    220 	}
    221 
    222 	__dump_stats_t(stamp, &as, all);
    223 
    224 	if (!all && iostat_ofp)
    225 		fprintf(iostat_ofp, "\n");
    226 }
    227 
    228 void iostat_check_time(__u64 stamp)
    229 {
    230 	if (iostat_ofp) {
    231 		if (last_start == (__u64)-1)
    232 			last_start = stamp;
    233 		else if ((stamp - last_start) >= iostat_interval) {
    234 			iostat_dump_stats(stamp, 0);
    235 			last_start = stamp;
    236 		}
    237 
    238 		iostat_last_stamp = stamp;
    239 	}
    240 }
    241 
    242 void iostat_getrq(struct io *iop)
    243 {
    244 	update_tot_qusz(iop->dip, TO_SEC(iop->t.time));
    245 	INC_STAT(iop->dip, cur_qusz);
    246 }
    247 
    248 void iostat_merge(struct io *iop)
    249 {
    250 	INC_STAT(iop->dip, rqm[IOP_RW(iop)]);
    251 }
    252 
    253 void iostat_issue(struct io *iop)
    254 {
    255 	int rw = IOP_RW(iop);
    256 	struct d_info *dip = iop->dip;
    257 	double now = TO_SEC(iop->t.time);
    258 
    259 	INC_STAT(dip, ios[rw]);
    260 	ADD_STAT(dip, sec[rw], iop->t.bytes >> 9);
    261 
    262 	update_idle_time(dip, now, 0);
    263 	INC_STAT(dip, cur_dev);
    264 }
    265 
    266 void iostat_complete(struct io *q_iop, struct io *c_iop)
    267 {
    268 	double now = TO_SEC(c_iop->t.time);
    269 	struct d_info *dip = q_iop->dip;
    270 
    271 	if (q_iop->i_time != (__u64)-1)
    272 		ADD_STAT(c_iop->dip, wait, tdelta(q_iop->i_time,c_iop->t.time));
    273 	else if (q_iop->m_time != (__u64)-1)
    274 		ADD_STAT(c_iop->dip, wait, tdelta(q_iop->m_time,c_iop->t.time));
    275 
    276 	update_tot_qusz(dip, now);
    277 	DEC_STAT(dip, cur_qusz);
    278 
    279 	update_idle_time(dip, now, 0);
    280 	DEC_STAT(dip, cur_dev);
    281 
    282 	ADD_STAT(dip, svctm, tdelta(q_iop->t.time, c_iop->t.time));
    283 }
    284