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