[PATCH] Convert to using on-the-fly RB trees, no post-traversal.
[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, c_iop->t.time - im_iop->t.time);
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)
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
159 void iostat_dump_stats(__u64 stamp, int all)
160 {
161         struct d_info *dip;
162
163         if (all)
164                 dump_hdr();
165         if (devices == NULL) {
166                 struct list_head *p;
167
168                 __list_for_each(p, &all_devs) {
169                         dip = list_entry(p, struct d_info, all_head);
170                         __dump_stats(stamp, all, dip);
171                 }
172         }
173         else {
174                 int i;
175                 unsigned int mjr, mnr;
176                 char *p = devices;
177
178                 while (p && ((i = sscanf(p, "%u,%u", &mjr, &mnr)) == 2)) {
179                         dip = __dip_find((__u32)((mjr << MINORBITS) | mnr));
180                         __dump_stats(stamp, all, dip);
181
182                         p = strchr(p, ';');
183                         if (p) p++;
184                 }
185         }
186         if (!all && iostat_ofp)
187                 fprintf(iostat_ofp, "\n");
188 }
189
190 void iostat_check_time(__u64 stamp)
191 {
192         if (iostat_ofp) {
193                 if (last_start == (__u64)-1)
194                         last_start = stamp;
195                 else if ((stamp - last_start) >= iostat_interval) {
196                         iostat_dump_stats(stamp, 0);
197                         last_start = stamp;
198                 }
199
200                 iostat_last_stamp = stamp;
201         }
202 }
203
204 void iostat_insert(struct io *iop)
205 {
206         update_tot_qusz(iop->dip, TO_SEC(iop->t.time));
207         INC_STAT(iop->dip, cur_qusz);
208 }
209
210 void iostat_merge(struct io *iop)
211 {
212         INC_STAT(iop->dip, rqm[IOP_RW(iop)]);
213 }
214
215 void iostat_issue(struct io *iop)
216 {
217         int rw = IOP_RW(iop);
218         struct d_info *dip = iop->dip;
219         double now = TO_SEC(iop->t.time);
220
221         INC_STAT(dip, ios[rw]);
222         ADD_STAT(dip, sec[rw], iop->t.bytes >> 9);
223
224         update_idle_time(dip, now, 0);
225         INC_STAT(dip, cur_dev);
226 }
227
228 void iostat_unissue(struct io *iop)
229 {
230         int rw = IOP_RW(iop);
231         struct d_info *dip = iop->dip;
232
233         DEC_STAT(dip, ios[rw]);
234         SUB_STAT(dip, sec[rw], iop->t.bytes >> 9);
235         DEC_STAT(dip, cur_dev);
236 }
237
238 void iostat_complete(struct io *d_iop, struct io *c_iop)
239 {
240         double now = TO_SEC(c_iop->t.time);
241         struct d_info *dip = d_iop->dip;
242
243         dip_foreach(d_iop, IOP_I, im2d2c_func, 0);
244         dip_foreach(d_iop, IOP_M, im2d2c_func, 0);
245
246         update_tot_qusz(dip, now);
247         DEC_STAT(dip, cur_qusz);
248
249         update_idle_time(dip, now, 0);
250         DEC_STAT(dip, cur_dev);
251
252         ADD_STAT(dip, svctm, c_iop->t.time - d_iop->t.time);
253 }