[PATCH] btt: seek additions
[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         mean = seeki_mean(dip->seek_handle);
297         median = seeki_median(dip->seek_handle);
298         nmodes = seeki_mode(dip->seek_handle, &modes, &most_seeks);
299
300         fprintf(ofp, "%10s | %15lld %15.1lf %15lld | %lld(%d)",
301                 make_dev_hdr(dev_info, 12, dip), nseeks, mean, median, 
302                 nmodes > 0 ? modes[0] : 0, most_seeks);
303         for (i = 1; i < nmodes; i++)
304                 fprintf(ofp, " %lld", modes[i]);
305         fprintf(ofp, "\n");
306 }
307
308 void output_dip_seek_info(FILE *ofp)
309 {
310         struct d_info *dip;
311
312         fprintf(ofp, "%10s | %15s %15s %15s | %-15s\n", "DEV", "NSEEKS", 
313                         "MEAN", "MEDIAN", "MODE");
314         fprintf(ofp, "---------- "
315                         "| --------------- --------------- --------------- "
316                         "| ---------------\n");
317
318         if (devices == NULL) {
319                 struct list_head *p;
320
321                 __list_for_each(p, &all_devs) {
322                         dip = list_entry(p, struct d_info, head);
323                         __output_dip_seek_info(ofp, dip);
324                 }
325         }
326         else {
327                 int i;
328                 unsigned int mjr, mnr;
329                 char *p = devices;
330
331                 while (p && ((i = sscanf(p, "%u,%u", &mjr, &mnr)) == 2)) {
332                         dip = __dip_find((__u32)((mjr << MINORBITS) | mnr));
333                         __output_dip_seek_info(ofp, dip);
334
335                         p = strchr(p, ';');
336                         if (p) p++;
337                 }
338         }
339
340         fprintf(ofp, "\n");
341 }
342
343 void __output_pip_avg(FILE *ofp, struct p_info *pip, struct avg_info *ap)
344 {
345         if (ap->n > 0) {
346                 char proc_name[12];
347                 snprintf(proc_name, 12, pip->name);
348
349                 ap->avg = BIT_TIME(ap->total) / (double)ap->n;
350                 __output_avg(ofp, proc_name, ap);
351         }
352 }
353
354 void output_pip_avg(FILE *ofp, char *hdr, ai_pip_t (*func)(struct p_info *))
355 {
356         struct p_info *pip;
357
358         output_hdr(ofp, hdr);
359         if (exes == NULL) {
360                 struct list_head *p;
361
362                 __list_for_each(p, &all_procs) {
363                         pip = list_entry(p, struct p_info, head);
364                         __output_pip_avg(ofp, pip, func(pip));
365                 }
366         }
367         else {
368                 char *exe, *p, *next, *exes_save = strdup(exes);
369
370                 p = exes_save;
371                 while (exes_save != NULL) {
372                         exe = exes_save;
373                         if ((next = strchr(exes_save, ',')) != NULL) {
374                                 *next = '\0';
375                                 exes_save = next+1;
376                         }
377                         else
378                                 exes_save = NULL;
379
380                         pip = find_process((__u32)-1, exe);
381                         if (pip)
382                                 __output_pip_avg(ofp, pip, func(pip));
383                 }
384         }
385
386         fprintf(ofp, "\n");
387 }
388
389 void output_dip_avgs(FILE *ofp)
390 {
391         char dev_info[12];
392         struct d_info *dip;
393
394         output_hdr2(ofp,"Dev");
395         if (devices == NULL) {
396                 struct list_head *p;
397
398                 __list_for_each(p, &all_devs) {
399                         dip = list_entry(p, struct d_info, head);
400                         __output_avg2(ofp, make_dev_hdr(dev_info, 12, dip),
401                                         &dip->avgs);
402                 }
403         }
404         else {
405                 int i;
406                 unsigned int mjr, mnr;
407                 char *p = devices;
408
409                 while (p && ((i = sscanf(p, "%u,%u", &mjr, &mnr)) == 2)) {
410                         dip = __dip_find((__u32)((mjr << MINORBITS) | mnr));
411                         __output_avg2(ofp, make_dev_hdr(dev_info, 12, dip),
412                                         &dip->avgs);
413
414                         p = strchr(p, ';');
415                         if (p) p++;
416                 }
417         }
418
419         fprintf(ofp, "\n");
420 }
421
422 void output_pip_avgs(FILE *ofp)
423 {
424         char exe[16];
425         struct p_info *pip;
426
427         output_hdr2(ofp,"Exe");
428         if (exes == NULL) {
429                 struct list_head *p;
430
431                 __list_for_each(p, &all_procs) {
432                         pip = list_entry(p, struct p_info, head);
433                         snprintf(exe, 12, pip->name);
434                         __output_avg2(ofp, exe, &pip->avgs);
435                 }
436         }
437         else {
438                 char *exe, *p, *next, *exes_save = strdup(exes);
439
440                 p = exes_save;
441                 while (exes_save != NULL && *exes_save != '\0') {
442                         exe = exes_save;
443                         if ((next = strchr(exes_save, ',')) != NULL) {
444                                 *next = '\0';
445                                 exes_save = next+1;
446                         }
447                         else
448                                 exes_save = NULL;
449
450                         pip = find_process((__u32)-1, exe);
451                         if (pip) {
452                                 snprintf(exe, 12, pip->name);
453                                 __output_avg2(ofp, exe, &pip->avgs);
454                         }
455                 }
456         }
457
458         fprintf(ofp, "\n");
459 }
460
461 int output_avgs(FILE *ofp)
462 {
463         if (exes == NULL || *exes != '\0') {
464                 output_section_hdr(ofp, "Per Process");
465                 output_pip_avg(ofp, "Q2Q", pip_q2q_avg);
466                 output_pip_avg(ofp, "Q2A", pip_q2a_avg);
467                 output_pip_avg(ofp, "Q2I", pip_q2i_avg);
468                 output_pip_avg(ofp, "I2D", pip_i2d_avg);
469                 output_pip_avg(ofp, "D2C", pip_d2c_avg);
470                 output_pip_avg(ofp, "Q2C", pip_q2c_avg);
471         }
472
473         output_section_hdr(ofp, "Per Device");
474         output_dip_avg(ofp, "Q2Q", dip_q2q_avg);
475         output_dip_avg(ofp, "Q2A", dip_q2a_avg);
476         output_dip_avg(ofp, "Q2I", dip_q2i_avg);
477         output_dip_avg(ofp, "I2D", dip_i2d_avg);
478         output_dip_avg(ofp, "D2C", dip_d2c_avg);
479         output_dip_avg(ofp, "Q2C", dip_q2c_avg);
480
481         output_section_hdr(ofp, "All Devices");
482         output_hdr(ofp, "ALL");
483         __output_avg(ofp, "Q2Q", &all_avgs.q2q);
484         __output_avg(ofp, "Q2A", &all_avgs.q2a);
485         __output_avg(ofp, "Q2I", &all_avgs.q2i);
486         __output_avg(ofp, "I2D", &all_avgs.i2d);
487         __output_avg(ofp, "D2C", &all_avgs.d2c);
488         __output_avg(ofp, "Q2C", &all_avgs.q2c);
489
490         if (exes == NULL || *exes != '\0') {
491                 output_section_hdr(ofp, "Per Process (avgs)");
492                 output_pip_avgs(ofp);
493         }
494
495         output_section_hdr(ofp, "Per Device (avgs)");
496         output_dip_avgs(ofp);
497
498         output_section_hdr(ofp, "Device Merge Information");
499         output_dip_merge_ratio(ofp);
500
501         output_section_hdr(ofp, "Device Overhead");
502         output_dip_prep_ohead(ofp);
503
504         output_section_hdr(ofp, "Device Seek Information");
505         output_dip_seek_info(ofp);
506
507         return 0;
508 }
509
510 void __output_ranges(FILE *ofp, struct list_head *head_p, float base)
511 {
512         struct range_info *rip;
513         struct list_head *p;
514         float limit = base + 0.4;
515
516         __list_for_each(p, head_p) {
517                 rip = list_entry(p, struct range_info, head);
518                 fprintf(ofp, "%13.9lf %5.1f\n", BIT_TIME(rip->start), base);
519                 fprintf(ofp, "%13.9lf %5.1f\n", BIT_TIME(rip->start), limit);
520                 fprintf(ofp, "%13.9lf %5.1f\n", BIT_TIME(rip->end), limit);
521                 fprintf(ofp, "%13.9lf %5.1f\n", BIT_TIME(rip->end), base);
522         }
523 }
524
525 int output_regions(FILE *ofp, char *header, struct region_info *reg, 
526                           float base)
527 {
528         if (reg->qr_cur != NULL)
529                 list_add_tail(&reg->qr_cur->head, &reg->qranges);
530         if (reg->cr_cur != NULL)
531                 list_add_tail(&reg->cr_cur->head, &reg->cranges);
532
533         if (list_len(&reg->qranges) == 0 && list_len(&reg->cranges) == 0)
534                 return 0;
535
536         fprintf(ofp, "# %16s : q activity\n", header);
537         __output_ranges(ofp, &reg->qranges, base);
538         fprintf(ofp, "\n");
539
540         fprintf(ofp, "# %16s : c activity\n", header);
541         __output_ranges(ofp, &reg->cranges, base + 0.5);
542         fprintf(ofp, "\n");
543
544         return 1;
545 }
546
547 float __output_dev(FILE *ofp, struct d_info *dip, float base)
548 {
549         char header[128];
550         sprintf(header, "%d,%d", MAJOR(dip->device), MINOR(dip->device));
551         if (output_regions(ofp, header, &dip->regions, base))
552                 base += 1.0;
553
554         return base;
555 }
556
557 float output_devs(FILE *ofp, float base)
558 {
559         struct d_info *dip;
560
561         fprintf(ofp, "# Per device\n" );
562         if (devices == NULL) {
563                 struct list_head *p;
564                 __list_for_each(p, &all_devs) {
565                         dip = list_entry(p, struct d_info, head);
566                         base = __output_dev(ofp, dip, base);
567                 }
568         }
569         else {
570                 int i;
571                 unsigned int mjr, mnr;
572                 char *p = devices;
573
574                 while (p && ((i = sscanf(p, "%u,%u", &mjr, &mnr)) == 2)) {
575                         dip = __dip_find((__u32)((mjr << MINORBITS) | mnr));
576                         ASSERT(dip);
577
578                         base = __output_dev(ofp, dip, base);
579
580                         p = strchr(p, ';');
581                         if (p) p++;
582                 }
583         }
584
585         return base;
586 }
587
588 static inline int exe_match(char *exe, char *name)
589 {
590         return (exe == NULL) || (strstr(name, exe) != NULL);
591 }
592
593 float __output_procs(FILE *ofp, float base, char *match)
594 {
595         struct p_info *pip;
596         struct list_head *p;
597
598         __list_for_each(p, &all_procs) {
599                 pip = list_entry(p, struct p_info, head);
600
601                 if (exe_match(match, pip->name) &&
602                                 output_regions(ofp, pip->name,
603                                                &pip->regions, base))
604                         base += 1.0;
605         }
606
607         return base;
608 }
609
610 float output_procs(FILE *ofp, float base)
611 {
612         fprintf(ofp, "# Per process\n" );
613         if (exes == NULL)
614                 base = __output_procs(ofp, base, NULL);
615         else {
616                 char *exe, *next, *p, *exes_save = strdup(exes);
617
618                 p = exes_save;
619                 while (exes_save != NULL) {
620                         exe = exes_save;
621                         if ((next = strchr(exes_save, ',')) != NULL) {
622                                 *next = '\0';
623                                 exes_save = next+1;
624                         }
625                         else
626                                 exes_save = NULL;
627
628                         base = __output_procs(ofp, base, exe);
629                 }
630                 free(p);
631         }
632
633         return base;
634 }
635
636 int output_ranges(FILE *ofp)
637 {
638         float base = 0.0;
639
640         fprintf(ofp, "# %s\n", "Total System");
641         if (output_regions(ofp, "Total System", &all_regions, base))
642                 base += 1.0;
643
644         if (n_devs > 1)
645                 base = output_devs(ofp, base);
646
647         base = output_procs(ofp, base);
648
649         return 0;
650 }