[PATCH] BTT patch: (2/3) per-IO stream output
[blktrace.git] / btt / iostat.c
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 = 1;
53 char *iostat_name = NULL;
54 FILE *iostat_ofp = NULL;
55
56 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 void im2d2c_func(struct io *c_iop, struct io *im_iop)
64 {
65         ADD_STAT(c_iop->dip, wait, tdelta(im_iop, c_iop));
66 }
67
68 void iostat_init(void)
69 {
70         last_start = (__u64)-1;
71         if (iostat_ofp)
72                 dump_hdr();
73 }
74
75 void update_tot_qusz(struct d_info *dip, double now)
76 {
77         dip->stats.tot_qusz += ((now - dip->stats.last_qu_change) *
78                                                 dip->stats.cur_qusz);
79         dip->all_stats.tot_qusz += ((now - dip->all_stats.last_qu_change) *
80                                                 dip->all_stats.cur_qusz);
81
82         dip->stats.last_qu_change = dip->all_stats.last_qu_change = now;
83 }
84
85 void update_idle_time(struct d_info *dip, double now, int force)
86 {
87         if (dip->stats.cur_dev == 0 || force) {
88                 dip->stats.idle_time += (now - dip->stats.last_dev_change);
89                 dip->all_stats.idle_time += 
90                                        (now - dip->all_stats.last_dev_change);
91         }
92         dip->stats.last_dev_change = dip->all_stats.last_dev_change = now;
93 }
94
95 void __dump_stats(__u64 stamp, int all, struct d_info *dip, struct stats_t *asp)
96 {
97         char hdr[16];
98         struct stats *sp;
99         double dt, nios, avgrq_sz, p_util, nrqm, await, svctm;
100         double now = TO_SEC(stamp);
101
102         if (all) {
103                 dt = (double)stamp / 1.0e9;
104                 sp = &dip->all_stats;
105         }
106         else {
107                 dt = (double)(stamp-last_start) / 1.0e9;
108                 sp = &dip->stats;
109         }
110
111         nios = (double)(sp->ios[0] + sp->ios[1]);
112         nrqm = (double)(sp->rqm[0] + sp->rqm[1]);
113         update_idle_time(dip, now, 1);
114         update_tot_qusz(dip, now);
115
116         if (nios > 0.0) {
117                 avgrq_sz = (double)(sp->sec[0] + sp->sec[1]) / nios;
118                 svctm = TO_MSEC(sp->svctm) / nios;
119         }
120         else
121                 avgrq_sz = svctm = 0.0;
122
123         await = ((nios + nrqm) > 0.0) ? TO_MSEC(sp->wait) / (nios+nrqm) : 0.0;
124         p_util = (sp->idle_time <= dt) ? 100.0 * (1.0 - (sp->idle_time / dt)) :
125                                          0.0;
126
127         /*
128          * For AWAIT: nios should be the same as number of inserts
129          * and we add in nrqm (number of merges), which should give
130          * us the total number of IOs sent to the block IO layer.
131          */
132         fprintf(iostat_ofp, "%-11s ", make_dev_hdr(hdr, 11, dip));
133         fprintf(iostat_ofp, "%8.2lf ", (double)sp->rqm[1] / dt);
134         fprintf(iostat_ofp, "%8.2lf ", (double)sp->rqm[0] / dt);
135         fprintf(iostat_ofp, "%7.2lf ", (double)sp->ios[1] / dt);
136         fprintf(iostat_ofp, "%7.2lf ", (double)sp->ios[0] / dt);
137         fprintf(iostat_ofp, "%9.2lf ", (double)sp->sec[1] / dt);
138         fprintf(iostat_ofp, "%9.2lf ", (double)sp->sec[0] / dt);
139         fprintf(iostat_ofp, "%9.2lf ", (double)(sp->sec[1] / 2) / dt);
140         fprintf(iostat_ofp, "%9.2lf ", (double)(sp->sec[0] / 2) / dt);
141         fprintf(iostat_ofp, "%8.2lf ", avgrq_sz);
142         fprintf(iostat_ofp, "%8.2lf ", (double)sp->tot_qusz / dt);
143         fprintf(iostat_ofp, "%7.2lf ", await);
144         fprintf(iostat_ofp, "%7.2lf ", svctm);
145         fprintf(iostat_ofp, "%6.2lf", p_util);
146         if (all)
147                 fprintf(iostat_ofp, "%8s\n", "TOTAL");
148         else {
149                 fprintf(iostat_ofp, "%8.2lf\n", TO_SEC(stamp));
150                 sp->rqm[0] = sp->rqm[1] = 0;
151                 sp->ios[0] = sp->ios[1] = 0;
152                 sp->sec[0] = sp->sec[1] = 0;
153                 sp->wait = sp->svctm = 0;
154
155                 sp->tot_qusz = sp->idle_time = 0.0;
156         }
157
158         if (asp) {
159                 int i;
160
161                 asp->n += 1.0;
162                 for (i = 0; i < 2; i++) {
163                         asp->rqm_s[i] += ((double)sp->rqm[i] / dt);
164                         asp->ios_s[i] += ((double)sp->ios[i] / dt);
165                         asp->sec_s[i] += ((double)sp->sec[i] / dt);
166                 }
167                 asp->avgrq_sz += avgrq_sz;
168                 asp->avgqu_sz += (double)sp->tot_qusz / dt;
169                 asp->await += await;
170                 asp->svctm += svctm;
171                 asp->p_util += p_util;
172         }
173 }
174
175 void __dump_stats_t(__u64 stamp, struct stats_t *asp, int all)
176 {
177         if (asp->n < 2.0) return;       // What's the point?
178
179         fprintf(iostat_ofp, "%-11s ", "TOTAL");
180         fprintf(iostat_ofp, "%8.2lf ", asp->rqm_s[0]);
181         fprintf(iostat_ofp, "%8.2lf ", asp->rqm_s[1]);
182         fprintf(iostat_ofp, "%7.2lf ", asp->ios_s[0]);
183         fprintf(iostat_ofp, "%7.2lf ", asp->ios_s[1]);
184         fprintf(iostat_ofp, "%9.2lf ", asp->sec_s[0]);
185         fprintf(iostat_ofp, "%9.2lf ", asp->sec_s[1]);
186         fprintf(iostat_ofp, "%9.2lf ", asp->sec_s[0] / 2.0);
187         fprintf(iostat_ofp, "%9.2lf ", asp->sec_s[1] / 2.0);
188         fprintf(iostat_ofp, "%8.2lf ", asp->avgrq_sz / asp->n);
189         fprintf(iostat_ofp, "%8.2lf ", asp->avgqu_sz / asp->n);
190         fprintf(iostat_ofp, "%7.2lf ", asp->await / asp->n);
191         fprintf(iostat_ofp, "%7.2lf ", asp->svctm / asp->n);
192         fprintf(iostat_ofp, "%6.2lf", asp->p_util / asp->n);
193         if (all)
194                 fprintf(iostat_ofp, "%8s\n", "TOTAL");
195         else 
196                 fprintf(iostat_ofp, "%8.2lf\n", TO_SEC(stamp));
197 }
198
199 void iostat_dump_stats(__u64 stamp, int all)
200 {
201         struct d_info *dip;
202         struct stats_t as;
203
204         memset(&as, 0, sizeof(struct stats_t));
205         if (all)
206                 dump_hdr();
207
208         if (devices == NULL) {
209                 struct list_head *p;
210
211                 __list_for_each(p, &all_devs) {
212                         dip = list_entry(p, struct d_info, all_head);
213                         __dump_stats(stamp, all, dip, &as);
214                 }
215         }
216         else {
217                 int i;
218                 unsigned int mjr, mnr;
219                 char *p = devices;
220
221                 while (p && ((i = sscanf(p, "%u,%u", &mjr, &mnr)) == 2)) {
222                         dip = __dip_find((__u32)((mjr << MINORBITS) | mnr));
223                         __dump_stats(stamp, all, dip, &as);
224
225                         p = strchr(p, ';');
226                         if (p) p++;
227                 }
228         }
229
230         __dump_stats_t(stamp, &as, all);
231
232         if (!all && iostat_ofp)
233                 fprintf(iostat_ofp, "\n");
234 }
235
236 void iostat_check_time(__u64 stamp)
237 {
238         if (iostat_ofp) {
239                 if (last_start == (__u64)-1)
240                         last_start = stamp;
241                 else if ((stamp - last_start) >= iostat_interval) {
242                         iostat_dump_stats(stamp, 0);
243                         last_start = stamp;
244                 }
245
246                 iostat_last_stamp = stamp;
247         }
248 }
249
250 void iostat_insert(struct io *iop)
251 {
252         update_tot_qusz(iop->dip, TO_SEC(iop->t.time));
253         INC_STAT(iop->dip, cur_qusz);
254 }
255
256 void iostat_merge(struct io *iop)
257 {
258         INC_STAT(iop->dip, rqm[IOP_RW(iop)]);
259 }
260
261 void iostat_issue(struct io *iop)
262 {
263         int rw = IOP_RW(iop);
264         struct d_info *dip = iop->dip;
265         double now = TO_SEC(iop->t.time);
266
267         INC_STAT(dip, ios[rw]);
268         ADD_STAT(dip, sec[rw], iop->t.bytes >> 9);
269
270         update_idle_time(dip, now, 0);
271         INC_STAT(dip, cur_dev);
272 }
273
274 void iostat_unissue(struct io *iop)
275 {
276         int rw = IOP_RW(iop);
277         struct d_info *dip = iop->dip;
278
279         DEC_STAT(dip, ios[rw]);
280         SUB_STAT(dip, sec[rw], iop->t.bytes >> 9);
281         DEC_STAT(dip, cur_dev);
282 }
283
284 void iostat_complete(struct io *d_iop, struct io *c_iop)
285 {
286         double now = TO_SEC(c_iop->t.time);
287         struct d_info *dip = d_iop->dip;
288
289         dip_foreach(d_iop, IOP_I, im2d2c_func, 0);
290         dip_foreach(d_iop, IOP_M, im2d2c_func, 0);
291
292         update_tot_qusz(dip, now);
293         DEC_STAT(dip, cur_qusz);
294
295         update_idle_time(dip, now, 0);
296         DEC_STAT(dip, cur_dev);
297
298         ADD_STAT(dip, svctm, tdelta(d_iop, c_iop));
299 }