1/*
2 * blktrace output analysis: generate a timeline & gather statistics
3 *
4 * Copyright (C) 2006 Alan D. Brunelle <Alan.Brunelle@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;
53char *iostat_name = NULL;
54FILE *iostat_ofp = NULL;
55
56static 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
63static 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
73static 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
83void __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
161static 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
185void iostat_init(void)
186{
187	last_start = (__u64)-1;
188	if (iostat_ofp)
189		dump_hdr();
190}
191
192void 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
228void 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
242void 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
248void iostat_merge(struct io *iop)
249{
250	INC_STAT(iop->dip, rqm[IOP_RW(iop)]);
251}
252
253void 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
266void 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