[PATCH] btt: seek statistics fix
[blktrace.git] / btt / output.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 "globals.h"
23
24 typedef struct avg_info *ai_dip_t;
25 ai_dip_t dip_q2q_avg(struct d_info *dip) { return &dip->avgs.q2q; }
26 ai_dip_t dip_q2c_avg(struct d_info *dip) { return &dip->avgs.q2c; }
27 ai_dip_t dip_q2a_avg(struct d_info *dip) { return &dip->avgs.q2a; }
28 ai_dip_t dip_q2i_avg(struct d_info *dip) { return &dip->avgs.q2i; }
29 ai_dip_t dip_i2d_avg(struct d_info *dip) { return &dip->avgs.i2d; }
30 ai_dip_t dip_d2c_avg(struct d_info *dip) { return &dip->avgs.d2c; }
31
32 typedef struct avg_info *ai_pip_t;
33 ai_pip_t pip_q2q_avg(struct p_info *pip) { return &pip->avgs.q2q; }
34 ai_pip_t pip_q2c_avg(struct p_info *pip) { return &pip->avgs.q2c; }
35 ai_pip_t pip_q2a_avg(struct p_info *pip) { return &pip->avgs.q2a; }
36 ai_pip_t pip_q2i_avg(struct p_info *pip) { return &pip->avgs.q2i; }
37 ai_pip_t pip_i2d_avg(struct p_info *pip) { return &pip->avgs.i2d; }
38 ai_pip_t pip_d2c_avg(struct p_info *pip) { return &pip->avgs.d2c; }
39
40 void output_section_hdr(FILE *ofp, char *hdr)
41 {
42         fprintf(ofp, "==================== ");
43         fprintf(ofp, hdr);
44         fprintf(ofp, " ====================\n\n");
45 }
46
47 void output_hdr(FILE *ofp, char *hdr)
48 {
49         fprintf(ofp, "%12s %13s %13s %13s %11s\n",
50                 hdr, "MIN", "AVG", "MAX", "N" );
51         fprintf(ofp, "------------ ------------- ------------- ------------- -----------\n");
52 }
53
54 void __output_avg(FILE *ofp, char *hdr, struct avg_info *ap)
55 {
56         if (ap->n > 0) {
57                 ap->avg = BIT_TIME(ap->total) / (double)ap->n;
58                 fprintf(ofp, "%-12s %13.9f %13.9f %13.9f %11d\n", hdr,
59                         BIT_TIME(ap->min), ap->avg, BIT_TIME(ap->max), ap->n);
60         }
61 }
62
63 void output_hdr2(FILE *ofp, char*hdr)
64 {
65         fprintf(ofp, "%12s %13s %13s %13s %13s %13s %13s\n", hdr, "Q2Q", "Q2A", "Q2I", "I2D", "D2C", "Q2C");
66         fprintf(ofp, "------------ ------------- ------------- ------------- ------------- ------------- -------------\n");
67 }
68
69 static inline char *avg2string(struct avg_info *ap, char *string)
70 {
71         if (ap->n > 0)
72                 sprintf(string, "%13.9f", ap->avg);
73         else
74                 sprintf(string, " ");
75         return string;
76 }
77
78 void __output_avg2(FILE *ofp, char *hdr, struct avgs_info *ap)
79 {
80         char c1[16], c2[16], c3[16], c4[16], c5[16], c6[16];
81
82         if (ap->q2q.n > 0 || ap->q2a.n > 0 || ap->q2i.n > 0 ||
83                         ap->i2d.n > 0 || ap->d2c.n > 0 || ap->q2c.n > 0) {
84                 fprintf(ofp, "%-12s %13s %13s %13s %13s %13s %13s\n", hdr,
85                         avg2string(&ap->q2q,c1), avg2string(&ap->q2a,c2),
86                         avg2string(&ap->q2i,c3), avg2string(&ap->i2d,c4),
87                         avg2string(&ap->d2c,c5), avg2string(&ap->q2c,c6));
88         }
89 }
90
91 char *make_dev_hdr(char *pad, size_t len, struct d_info *dip)
92 {
93         if (dip->map == NULL) {
94                 snprintf(pad, len, "(%3d,%3d)", 
95                          MAJOR(dip->device), MINOR(dip->device));
96         }
97         else {
98                 snprintf(pad, len, "[%3d,%3d]",
99                         dip->map->host, dip->map->target);
100         }
101
102         return pad;
103 }
104
105 void __output_dip_avg(FILE *ofp, struct d_info *dip, struct avg_info *ap)
106 {
107         if (ap->n > 0) {
108                 char dev_info[12];
109                 ap->avg = BIT_TIME(ap->total) / (double)ap->n;
110                 __output_avg(ofp, make_dev_hdr(dev_info, 12, dip), ap);
111         }
112 }
113
114 void output_dip_avg(FILE *ofp, char *hdr, ai_dip_t (*func)(struct d_info *))
115 {
116         struct d_info *dip;
117
118         output_hdr(ofp, hdr);
119         if (devices == NULL) {
120                 struct list_head *p;
121
122                 __list_for_each(p, &all_devs) {
123                         dip = list_entry(p, struct d_info, head);
124                         __output_dip_avg(ofp, dip, func(dip));
125                 }
126         }
127         else {
128                 int i;
129                 unsigned int mjr, mnr;
130                 char *p = devices;
131
132                 while (p && ((i = sscanf(p, "%u,%u", &mjr, &mnr)) == 2)) {
133                         dip = __dip_find((__u32)((mjr << MINORBITS) | mnr));
134                         __output_dip_avg(ofp, dip, func(dip));
135
136                         p = strchr(p, ';');
137                         if (p) p++;
138                 }
139         }
140
141         fprintf(ofp, "\n");
142 }
143
144 void __output_dip_merge_ratio(FILE *ofp, struct d_info *dip)
145 {
146         double blks_avg;
147         char scratch[12];
148         double ratio, q2c_n = dip->avgs.q2c.n, d2c_n = dip->n_ds;
149
150         if (q2c_n > 0.0 && d2c_n > 0.0) {
151                 ratio = q2c_n / d2c_n;
152                 blks_avg = (double)dip->avgs.blks.total / d2c_n;
153                 fprintf(ofp, "%10s | %8llu %8llu %7.1lf | %8llu %8llu %8llu %8llu\n",
154                         make_dev_hdr(scratch, 12, dip),
155                         (unsigned long long)dip->avgs.q2c.n,
156                         (unsigned long long)dip->n_ds,
157                         ratio,
158                         (unsigned long long)dip->avgs.blks.min,
159                         (unsigned long long)blks_avg,
160                         (unsigned long long)dip->avgs.blks.max,
161                         (unsigned long long)dip->avgs.blks.total);
162
163         }
164 }
165
166 void output_dip_merge_ratio(FILE *ofp)
167 {
168         struct d_info *dip;
169
170         fprintf(ofp, "%10s | %8s %8s %7s | %8s %8s %8s %8s\n", "DEV", "#Q", "#D", "Ratio", "BLKmin", "BLKavg", "BLKmax", "Total");
171         fprintf(ofp, "---------- | -------- -------- ------- | -------- -------- -------- --------\n");
172         if (devices == NULL) {
173                 struct list_head *p;
174
175                 __list_for_each(p, &all_devs) {
176                         dip = list_entry(p, struct d_info, head);
177                         __output_dip_merge_ratio(ofp, dip);
178                 }
179         }
180         else {
181                 int i;
182                 unsigned int mjr, mnr;
183                 char *p = devices;
184
185                 while (p && ((i = sscanf(p, "%u,%u", &mjr, &mnr)) == 2)) {
186                         dip = __dip_find((__u32)((mjr << MINORBITS) | mnr));
187                         __output_dip_merge_ratio(ofp, dip);
188
189                         p = strchr(p, ';');
190                         if (p) p++;
191                 }
192         }
193
194         fprintf(ofp, "\n");
195 }
196
197 #define AVG(a,b) (100.0 * ((double)(a) / (double)(b)))
198 #define CALC_AVG(ap) (ap)->avg = ((ap)->n == 0 ? 0.0 :                    \
199                                                  (BIT_TIME((ap)->total) / \
200                                                         (double)(ap)->n))
201 char *q2i_v_q2C(struct d_info *dip, char *s)
202 {
203         double q2c;
204
205         if (dip->avgs.q2i.n == 0) return " ";
206
207         q2c = dip->avgs.q2i.avg + dip->avgs.i2d.avg + dip->avgs.d2c.avg;
208         sprintf(s, "%5.1lf%%", AVG(dip->avgs.q2i.avg, q2c));
209
210         return s;
211 }
212
213 char *i2d_v_q2C(struct d_info *dip, char *s)
214 {
215         double q2c;
216
217         if (dip->avgs.d2c.n == 0) return " ";
218
219         q2c = dip->avgs.q2i.avg + dip->avgs.i2d.avg + dip->avgs.d2c.avg;
220         sprintf(s, "%5.1lf%%", AVG(dip->avgs.i2d.avg, q2c));
221
222         return s;
223 }
224
225 char *d2c_v_q2C(struct d_info *dip, char *s)
226 {
227         double q2c;
228
229         if (dip->avgs.d2c.n == 0) return " ";
230
231         q2c = dip->avgs.q2i.avg + dip->avgs.i2d.avg + dip->avgs.d2c.avg;
232         sprintf(s, "%5.1lf%%", AVG(dip->avgs.d2c.avg, q2c));
233
234         return s;
235 }
236
237 void __output_dip_prep_ohead(FILE *ofp, struct d_info *dip)
238 {
239         char dev_info[12];
240         char s1[16], s2[16], s3[16];
241
242         if ((dip->avgs.q2i.n > 0 && dip->avgs.i2d.n > 0 &&
243                                                 dip->avgs.d2c.n > 0)) {
244                 CALC_AVG(&dip->avgs.q2i);
245                 CALC_AVG(&dip->avgs.i2d);
246                 CALC_AVG(&dip->avgs.d2c);
247
248                 fprintf(ofp, "%10s | %6s %6s %6s\n",
249                         make_dev_hdr(dev_info, 12, dip),
250                         q2i_v_q2C(dip, s1), i2d_v_q2C(dip, s2),
251                         d2c_v_q2C(dip, s3));
252         }
253 }
254
255 void output_dip_prep_ohead(FILE *ofp)
256 {
257         struct d_info *dip;
258
259         fprintf(ofp, "%10s | %6s %6s %6s\n", "DEV", "Q2I", "I2D", "D2C");
260         fprintf(ofp, "---------- | ------ ------ ------\n");
261
262         if (devices == NULL) {
263                 struct list_head *p;
264
265                 __list_for_each(p, &all_devs) {
266                         dip = list_entry(p, struct d_info, head);
267                         __output_dip_prep_ohead(ofp, dip);
268                 }
269         }
270         else {
271                 int i;
272                 unsigned int mjr, mnr;
273                 char *p = devices;
274
275                 while (p && ((i = sscanf(p, "%u,%u", &mjr, &mnr)) == 2)) {
276                         dip = __dip_find((__u32)((mjr << MINORBITS) | mnr));
277                         __output_dip_prep_ohead(ofp, dip);
278
279                         p = strchr(p, ';');
280                         if (p) p++;
281                 }
282         }
283
284         fprintf(ofp, "\n");
285 }
286
287 void __output_dip_seek_info(FILE *ofp, struct d_info *dip)
288 {
289         double mean;
290         int i, nmodes, most_seeks;
291         long long nseeks;
292         char dev_info[12];
293         long long median, *modes;
294
295         nseeks = seeki_nseeks(dip->seek_handle);
296         if (nseeks > 0) {
297                 mean = seeki_mean(dip->seek_handle);
298                 median = seeki_median(dip->seek_handle);
299                 nmodes = seeki_mode(dip->seek_handle, &modes, &most_seeks);
300
301                 fprintf(ofp, "%10s | %15lld %15.1lf %15lld | %lld(%d)",
302                         make_dev_hdr(dev_info, 12, dip), nseeks, mean, median, 
303                         nmodes > 0 ? modes[0] : 0, most_seeks);
304                 for (i = 1; i < nmodes; i++)
305                         fprintf(ofp, " %lld", modes[i]);
306                 fprintf(ofp, "\n");
307         }
308 }
309
310 void output_dip_seek_info(FILE *ofp)
311 {
312         struct d_info *dip;
313
314         fprintf(ofp, "%10s | %15s %15s %15s | %-15s\n", "DEV", "NSEEKS", 
315                         "MEAN", "MEDIAN", "MODE");
316         fprintf(ofp, "---------- "
317                         "| --------------- --------------- --------------- "
318                         "| ---------------\n");
319
320         if (devices == NULL) {
321                 struct list_head *p;
322
323                 __list_for_each(p, &all_devs) {
324                         dip = list_entry(p, struct d_info, head);
325                         __output_dip_seek_info(ofp, dip);
326                 }
327         }
328         else {
329                 int i;
330                 unsigned int mjr, mnr;
331                 char *p = devices;
332
333                 while (p && ((i = sscanf(p, "%u,%u", &mjr, &mnr)) == 2)) {
334                         dip = __dip_find((__u32)((mjr << MINORBITS) | mnr));
335                         __output_dip_seek_info(ofp, dip);
336
337                         p = strchr(p, ';');
338                         if (p) p++;
339                 }
340         }
341
342         fprintf(ofp, "\n");
343 }
344
345 void __output_pip_avg(FILE *ofp, struct p_info *pip, struct avg_info *ap)
346 {
347         if (ap->n > 0) {
348                 char proc_name[12];
349                 snprintf(proc_name, 12, pip->name);
350
351                 ap->avg = BIT_TIME(ap->total) / (double)ap->n;
352                 __output_avg(ofp, proc_name, ap);
353         }
354 }
355
356 void output_pip_avg(FILE *ofp, char *hdr, ai_pip_t (*func)(struct p_info *))
357 {
358         struct p_info *pip;
359
360         output_hdr(ofp, hdr);
361         if (exes == NULL) {
362                 struct list_head *p;
363
364                 __list_for_each(p, &all_procs) {
365                         pip = list_entry(p, struct p_info, head);
366                         __output_pip_avg(ofp, pip, func(pip));
367                 }
368         }
369         else {
370                 char *exe, *p, *next, *exes_save = strdup(exes);
371
372                 p = exes_save;
373                 while (exes_save != NULL) {
374                         exe = exes_save;
375                         if ((next = strchr(exes_save, ',')) != NULL) {
376                                 *next = '\0';
377                                 exes_save = next+1;
378                         }
379                         else
380                                 exes_save = NULL;
381
382                         pip = find_process((__u32)-1, exe);
383                         if (pip)
384                                 __output_pip_avg(ofp, pip, func(pip));
385                 }
386         }
387
388         fprintf(ofp, "\n");
389 }
390
391 void output_dip_avgs(FILE *ofp)
392 {
393         char dev_info[12];
394         struct d_info *dip;
395
396         output_hdr2(ofp,"Dev");
397         if (devices == NULL) {
398                 struct list_head *p;
399
400                 __list_for_each(p, &all_devs) {
401                         dip = list_entry(p, struct d_info, head);
402                         __output_avg2(ofp, make_dev_hdr(dev_info, 12, dip),
403                                         &dip->avgs);
404                 }
405         }
406         else {
407                 int i;
408                 unsigned int mjr, mnr;
409                 char *p = devices;
410
411                 while (p && ((i = sscanf(p, "%u,%u", &mjr, &mnr)) == 2)) {
412                         dip = __dip_find((__u32)((mjr << MINORBITS) | mnr));
413                         __output_avg2(ofp, make_dev_hdr(dev_info, 12, dip),
414                                         &dip->avgs);
415
416                         p = strchr(p, ';');
417                         if (p) p++;
418                 }
419         }
420
421         fprintf(ofp, "\n");
422 }
423
424 void output_pip_avgs(FILE *ofp)
425 {
426         char exe[16];
427         struct p_info *pip;
428
429         output_hdr2(ofp,"Exe");
430         if (exes == NULL) {
431                 struct list_head *p;
432
433                 __list_for_each(p, &all_procs) {
434                         pip = list_entry(p, struct p_info, head);
435                         snprintf(exe, 12, pip->name);
436                         __output_avg2(ofp, exe, &pip->avgs);
437                 }
438         }
439         else {
440                 char *exe, *p, *next, *exes_save = strdup(exes);
441
442                 p = exes_save;
443                 while (exes_save != NULL && *exes_save != '\0') {
444                         exe = exes_save;
445                         if ((next = strchr(exes_save, ',')) != NULL) {
446                                 *next = '\0';
447                                 exes_save = next+1;
448                         }
449                         else
450                                 exes_save = NULL;
451
452                         pip = find_process((__u32)-1, exe);
453                         if (pip) {
454                                 snprintf(exe, 12, pip->name);
455                                 __output_avg2(ofp, exe, &pip->avgs);
456                         }
457                 }
458         }
459
460         fprintf(ofp, "\n");
461 }
462
463 int output_avgs(FILE *ofp)
464 {
465         if (exes == NULL || *exes != '\0') {
466                 output_section_hdr(ofp, "Per Process");
467                 output_pip_avg(ofp, "Q2Q", pip_q2q_avg);
468                 output_pip_avg(ofp, "Q2A", pip_q2a_avg);
469                 output_pip_avg(ofp, "Q2I", pip_q2i_avg);
470                 output_pip_avg(ofp, "I2D", pip_i2d_avg);
471                 output_pip_avg(ofp, "D2C", pip_d2c_avg);
472                 output_pip_avg(ofp, "Q2C", pip_q2c_avg);
473         }
474
475         output_section_hdr(ofp, "Per Device");
476         output_dip_avg(ofp, "Q2Q", dip_q2q_avg);
477         output_dip_avg(ofp, "Q2A", dip_q2a_avg);
478         output_dip_avg(ofp, "Q2I", dip_q2i_avg);
479         output_dip_avg(ofp, "I2D", dip_i2d_avg);
480         output_dip_avg(ofp, "D2C", dip_d2c_avg);
481         output_dip_avg(ofp, "Q2C", dip_q2c_avg);
482
483         output_section_hdr(ofp, "All Devices");
484         output_hdr(ofp, "ALL");
485         __output_avg(ofp, "Q2Q", &all_avgs.q2q);
486         __output_avg(ofp, "Q2A", &all_avgs.q2a);
487         __output_avg(ofp, "Q2I", &all_avgs.q2i);
488         __output_avg(ofp, "I2D", &all_avgs.i2d);
489         __output_avg(ofp, "D2C", &all_avgs.d2c);
490         __output_avg(ofp, "Q2C", &all_avgs.q2c);
491
492         if (exes == NULL || *exes != '\0') {
493                 output_section_hdr(ofp, "Per Process (avgs)");
494                 output_pip_avgs(ofp);
495         }
496
497         output_section_hdr(ofp, "Per Device (avgs)");
498         output_dip_avgs(ofp);
499
500         output_section_hdr(ofp, "Device Merge Information");
501         output_dip_merge_ratio(ofp);
502
503         output_section_hdr(ofp, "Device Overhead");
504         output_dip_prep_ohead(ofp);
505
506         output_section_hdr(ofp, "Device Seek Information");
507         output_dip_seek_info(ofp);
508
509         return 0;
510 }
511
512 void __output_ranges(FILE *ofp, struct list_head *head_p, float base)
513 {
514         struct range_info *rip;
515         struct list_head *p;
516         float limit = base + 0.4;
517
518         __list_for_each(p, head_p) {
519                 rip = list_entry(p, struct range_info, head);
520                 fprintf(ofp, "%13.9lf %5.1f\n", BIT_TIME(rip->start), base);
521                 fprintf(ofp, "%13.9lf %5.1f\n", BIT_TIME(rip->start), limit);
522                 fprintf(ofp, "%13.9lf %5.1f\n", BIT_TIME(rip->end), limit);
523                 fprintf(ofp, "%13.9lf %5.1f\n", BIT_TIME(rip->end), base);
524         }
525 }
526
527 int output_regions(FILE *ofp, char *header, struct region_info *reg, 
528                           float base)
529 {
530         if (reg->qr_cur != NULL)
531                 list_add_tail(&reg->qr_cur->head, &reg->qranges);
532         if (reg->cr_cur != NULL)
533                 list_add_tail(&reg->cr_cur->head, &reg->cranges);
534
535         if (list_len(&reg->qranges) == 0 && list_len(&reg->cranges) == 0)
536                 return 0;
537
538         fprintf(ofp, "# %16s : q activity\n", header);
539         __output_ranges(ofp, &reg->qranges, base);
540         fprintf(ofp, "\n");
541
542         fprintf(ofp, "# %16s : c activity\n", header);
543         __output_ranges(ofp, &reg->cranges, base + 0.5);
544         fprintf(ofp, "\n");
545
546         return 1;
547 }
548
549 float __output_dev(FILE *ofp, struct d_info *dip, float base)
550 {
551         char header[128];
552         sprintf(header, "%d,%d", MAJOR(dip->device), MINOR(dip->device));
553         if (output_regions(ofp, header, &dip->regions, base))
554                 base += 1.0;
555
556         return base;
557 }
558
559 float output_devs(FILE *ofp, float base)
560 {
561         struct d_info *dip;
562
563         fprintf(ofp, "# Per device\n" );
564         if (devices == NULL) {
565                 struct list_head *p;
566                 __list_for_each(p, &all_devs) {
567                         dip = list_entry(p, struct d_info, head);
568                         base = __output_dev(ofp, dip, base);
569                 }
570         }
571         else {
572                 int i;
573                 unsigned int mjr, mnr;
574                 char *p = devices;
575
576                 while (p && ((i = sscanf(p, "%u,%u", &mjr, &mnr)) == 2)) {
577                         dip = __dip_find((__u32)((mjr << MINORBITS) | mnr));
578                         ASSERT(dip);
579
580                         base = __output_dev(ofp, dip, base);
581
582                         p = strchr(p, ';');
583                         if (p) p++;
584                 }
585         }
586
587         return base;
588 }
589
590 static inline int exe_match(char *exe, char *name)
591 {
592         return (exe == NULL) || (strstr(name, exe) != NULL);
593 }
594
595 float __output_procs(FILE *ofp, float base, char *match)
596 {
597         struct p_info *pip;
598         struct list_head *p;
599
600         __list_for_each(p, &all_procs) {
601                 pip = list_entry(p, struct p_info, head);
602
603                 if (exe_match(match, pip->name) &&
604                                 output_regions(ofp, pip->name,
605                                                &pip->regions, base))
606                         base += 1.0;
607         }
608
609         return base;
610 }
611
612 float output_procs(FILE *ofp, float base)
613 {
614         fprintf(ofp, "# Per process\n" );
615         if (exes == NULL)
616                 base = __output_procs(ofp, base, NULL);
617         else {
618                 char *exe, *next, *p, *exes_save = strdup(exes);
619
620                 p = exes_save;
621                 while (exes_save != NULL) {
622                         exe = exes_save;
623                         if ((next = strchr(exes_save, ',')) != NULL) {
624                                 *next = '\0';
625                                 exes_save = next+1;
626                         }
627                         else
628                                 exes_save = NULL;
629
630                         base = __output_procs(ofp, base, exe);
631                 }
632                 free(p);
633         }
634
635         return base;
636 }
637
638 int output_ranges(FILE *ofp)
639 {
640         float base = 0.0;
641
642         fprintf(ofp, "# %s\n", "Total System");
643         if (output_regions(ofp, "Total System", &all_regions, base))
644                 base += 1.0;
645
646         if (n_devs > 1)
647                 base = output_devs(ofp, base);
648
649         base = output_procs(ofp, base);
650
651         return 0;
652 }