[PATCH] Added iostat-style statistics to btt
[blktrace.git] / btt / iostat.c
CommitLineData
21e47d90
ADB
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 TO_SEC(nanosec) ((double)(nanosec) / 1.0e9)
26#define TO_MSEC(nanosec) (1000.0 * TO_SEC(nanosec))
27
28#define INC_STAT(dip, fld) \
29 do { \
30 (dip)->stats. fld ++; \
31 (dip)->all_stats. fld ++; \
32 } while (0)
33
34#define DEC_STAT(dip, fld) \
35 do { \
36 (dip)->stats. fld --; \
37 (dip)->all_stats. fld --; \
38 } while (0)
39
40#define ADD_STAT(dip, fld, val) \
41 do { \
42 __u64 __v = (val); \
43 (dip)->stats. fld += __v; \
44 (dip)->all_stats. fld += __v; \
45 } while (0)
46
47#define SUB_STAT(dip, fld, val) \
48 do { \
49 __u64 __v = (val); \
50 (dip)->stats. fld -= __v; \
51 (dip)->all_stats. fld -= __v; \
52 } while (0)
53
54__u64 last_start, iostat_last_stamp;
55__u64 iostat_interval = 1;
56char *iostat_name = NULL;
57FILE *iostat_ofp = NULL;
58
59void calc_waits(struct io *d_iop, __u64 c_time)
60{
61 __u64 waits = 0;
62 struct list_head *p;
63 struct io_list *iolp;
64
65 __list_for_each(p, &d_iop->u.d.d_im_head) {
66 iolp = list_entry(p, struct io_list, head);
67 waits += (c_time - iolp->iop->t.time);
68 }
69 ADD_STAT(d_iop->dip, wait, waits);
70}
71
72void dump_hdr(void)
73{
74 fprintf(iostat_ofp, "Device: rrqm/s wrqm/s r/s w/s "
75 "rsec/s wsec/s rkB/s wkB/s "
76 "avgrq-sz avgqu-sz await svctm %%util Stamp\n");
77}
78
79void iostat_init(void)
80{
81 last_start = (__u64)-1;
82 if (iostat_ofp)
83 dump_hdr();
84}
85
86void update_tot_qusz(struct d_info *dip, double now)
87{
88 dip->stats.tot_qusz += ((now - dip->stats.last_qu_change) *
89 dip->stats.cur_qusz);
90 dip->all_stats.tot_qusz += ((now - dip->all_stats.last_qu_change) *
91 dip->all_stats.cur_qusz);
92
93 dip->stats.last_qu_change = dip->all_stats.last_qu_change = now;
94}
95
96void update_idle_time(struct d_info *dip, double now)
97{
98 if (dip->stats.cur_dev == 0) {
99 dip->stats.idle_time += (now - dip->stats.last_dev_change);
100 dip->all_stats.idle_time +=
101 (now - dip->all_stats.last_dev_change);
102 }
103 dip->stats.last_dev_change = dip->all_stats.last_dev_change = now;
104}
105
106void __dump_stats(__u64 stamp, int all, struct d_info *dip)
107{
108 char hdr[16];
109 struct stats *sp;
110 double dt, nios, avgrq_sz, p_util, nrqm;
111 double now = TO_SEC(stamp);
112
113 if (all) {
114 dt = (double)stamp / 1.0e9;
115 sp = &dip->all_stats;
116 }
117 else {
118 dt = (double)(stamp-last_start) / 1.0e9;
119 sp = &dip->stats;
120 }
121
122 nios = (double)(sp->ios[0] + sp->ios[1]);
123 nrqm = (double)(sp->rqm[0] + sp->rqm[1]);
124 if (nios > 0.0) {
125 update_idle_time(dip, now);
126 update_tot_qusz(dip, now);
127 avgrq_sz = (double)(sp->sec[0] + sp->sec[1]) / nios;
128 p_util = 100.0 * (1.0 - (sp->idle_time / dt));
129
130 /*
131 * For AWAIT: nios should be the same as number of inserts
132 * and we add in nrqm (number of merges), which should give
133 * us the total number of IOs sent to the block IO layer.
134 */
135 fprintf(iostat_ofp, "%-11s ", make_dev_hdr(hdr, 11, dip));
136 fprintf(iostat_ofp, "%8.2lf ", (double)sp->rqm[1] / dt);
137 fprintf(iostat_ofp, "%8.2lf ", (double)sp->rqm[0] / dt);
138 fprintf(iostat_ofp, "%7.2lf ", (double)sp->ios[1] / dt);
139 fprintf(iostat_ofp, "%7.2lf ", (double)sp->ios[0] / dt);
140 fprintf(iostat_ofp, "%9.2lf ", (double)sp->sec[1] / dt);
141 fprintf(iostat_ofp, "%9.2lf ", (double)sp->sec[0] / dt);
142 fprintf(iostat_ofp, "%9.2lf ", (double)(sp->sec[1] / 2) / dt);
143 fprintf(iostat_ofp, "%9.2lf ", (double)(sp->sec[0] / 2) / dt);
144 fprintf(iostat_ofp, "%8.2lf ", avgrq_sz);
145 fprintf(iostat_ofp, "%8.2lf ", (double)sp->tot_qusz / dt);
146 fprintf(iostat_ofp, "%7.2lf ", TO_MSEC(sp->wait) / (nios+nrqm));
147 fprintf(iostat_ofp, "%7.2lf ", TO_MSEC(sp->svctm) / nios);
148 fprintf(iostat_ofp, "%6.2lf", p_util);
149 if (all)
150 fprintf(iostat_ofp, "%8s\n", "TOTAL");
151 else {
152 fprintf(iostat_ofp, "%8.2lf\n", TO_SEC(stamp));
153 sp->rqm[0] = sp->rqm[1] = 0;
154 sp->ios[0] = sp->ios[1] = 0;
155 sp->sec[0] = sp->sec[1] = 0;
156 sp->wait = sp->svctm = 0;
157
158 sp->tot_qusz = sp->idle_time = 0.0;
159 }
160 }
161}
162
163void iostat_dump_stats(__u64 stamp, int all)
164{
165 struct d_info *dip;
166
167 if (all)
168 dump_hdr();
169 if (devices == NULL) {
170 struct list_head *p;
171
172 __list_for_each(p, &all_devs) {
173 dip = list_entry(p, struct d_info, head);
174 __dump_stats(stamp, all, dip);
175 }
176 }
177 else {
178 int i;
179 unsigned int mjr, mnr;
180 char *p = devices;
181
182 while (p && ((i = sscanf(p, "%u,%u", &mjr, &mnr)) == 2)) {
183 dip = __dip_find((__u32)((mjr << MINORBITS) | mnr));
184 __dump_stats(stamp, all, dip);
185
186 p = strchr(p, ';');
187 if (p) p++;
188 }
189 }
190}
191
192void iostat_check_time(__u64 stamp)
193{
194 if (iostat_ofp) {
195 if (last_start == (__u64)-1)
196 last_start = stamp;
197 else if ((stamp - last_start) >= iostat_interval) {
198 iostat_dump_stats(stamp, 0);
199 last_start = stamp;
200 }
201
202 iostat_last_stamp = stamp;
203 }
204}
205
206void iostat_insert(struct io *iop)
207{
208 update_tot_qusz(iop->dip, TO_SEC(iop->t.time));
209 INC_STAT(iop->dip, cur_qusz);
210}
211
212void iostat_merge(struct io *iop)
213{
214 INC_STAT(iop->dip, rqm[IOP_RW(iop)]);
215}
216
217void iostat_issue(struct io *iop)
218{
219 int rw = IOP_RW(iop);
220 struct d_info *dip = iop->dip;
221 double now = TO_SEC(iop->t.time);
222
223 INC_STAT(dip, ios[rw]);
224 ADD_STAT(dip, sec[rw], iop->t.bytes >> 9);
225
226 update_idle_time(dip, now);
227 INC_STAT(dip, cur_dev);
228}
229
230void iostat_complete(struct io *iop)
231{
232 struct io *d_iop;
233 struct d_info *dip = iop->dip;
234
235 if ((d_iop = iop->u.c.c_d) != NULL) {
236 double now = TO_SEC(iop->t.time);
237 calc_waits(d_iop, iop->t.time);
238
239 update_tot_qusz(dip, now);
240 DEC_STAT(dip, cur_qusz);
241
242 update_idle_time(dip, now);
243 DEC_STAT(dip, cur_dev);
244
245 ADD_STAT(dip, svctm, iop->t.time - d_iop->t.time);
246 }
247}