ebfd3d835f4556927f1332f64a86f1a49b917a08
[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, "%15s %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, "%-15s %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, "%15s %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, "%-15s %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 void __pip_output_avg2(struct p_info *pip, void *arg)
92 {
93         __output_avg2((FILE *)arg, pip->name, &pip->avgs);
94 }
95
96 void __dip_output_avg2(struct d_info *dip, void *arg)
97 {
98         char dev_info[15];
99         __output_avg2((FILE *)arg, make_dev_hdr(dev_info, 15, dip), &dip->avgs);
100 }
101
102 char *make_dev_hdr(char *pad, size_t len, struct d_info *dip)
103 {
104         if (dip->map == NULL)
105                 snprintf(pad, len, "(%3d,%3d)", 
106                          MAJOR(dip->device), MINOR(dip->device));
107         else
108                 snprintf(pad, len, "%s", dip->map->device);
109
110         return pad;
111 }
112
113 struct __oda {
114         FILE *ofp;
115         ai_dip_t (*func)(struct d_info *);
116 };
117 void __output_dip_avg(struct d_info *dip, void *arg)
118 {
119         struct __oda *odap = arg;
120         ai_dip_t ap = odap->func(dip);
121         if (ap->n > 0) {
122                 char dev_info[15];
123                 ap->avg = BIT_TIME(ap->total) / (double)ap->n;
124                 __output_avg(odap->ofp, make_dev_hdr(dev_info, 15, dip), ap);
125         }
126 }
127
128 void output_dip_avg(FILE *ofp, char *hdr, ai_dip_t (*func)(struct d_info *))
129 {
130         struct __oda oda = { .ofp = ofp, .func = func};
131         output_hdr(ofp, hdr);
132         dip_foreach_out(__output_dip_avg, &oda);
133         fprintf(ofp, "\n");
134 }
135
136 int n_merges = 0;
137 struct {
138         unsigned long long nq, nd, blkmin, blkmax, total;
139 } merge_data;
140 void __output_dip_merge_ratio(struct d_info *dip, void *arg)
141 {
142         double blks_avg;
143         char scratch[15];
144         double ratio, q2c_n, d2c_n;
145
146         if (dip->n_qs == 0 || dip->n_ds == 0)
147                 return;
148         else if (dip->n_qs < dip->n_ds)
149                 dip->n_qs = dip->n_ds;
150
151         q2c_n = dip->n_qs;
152         d2c_n = dip->n_ds;
153         if (q2c_n > 0.0 && d2c_n > 0.0) {
154                 if (q2c_n < d2c_n)
155                         ratio = 1.0;
156                 else
157                         ratio = q2c_n / d2c_n;
158                 blks_avg = (double)dip->avgs.blks.total / d2c_n;
159                 fprintf((FILE *)arg, 
160                         "%10s | %8llu %8llu %7.1lf | %8llu %8llu %8llu %8llu\n",
161                         make_dev_hdr(scratch, 15, dip),
162                         (unsigned long long)dip->n_qs,
163                         (unsigned long long)dip->n_ds,
164                         ratio,
165                         (unsigned long long)dip->avgs.blks.min,
166                         (unsigned long long)blks_avg,
167                         (unsigned long long)dip->avgs.blks.max,
168                         (unsigned long long)dip->avgs.blks.total);
169
170                 if (n_merges++ == 0) {
171                         merge_data.blkmin = dip->avgs.blks.min;
172                         merge_data.blkmax = dip->avgs.blks.max;
173                 }
174
175                 merge_data.nq += dip->n_qs;
176                 merge_data.nd += dip->n_ds;
177                 merge_data.total += dip->avgs.blks.total;
178                 if (dip->avgs.blks.min < merge_data.blkmin)
179                         merge_data.blkmin = dip->avgs.blks.min;
180                 if (dip->avgs.blks.max > merge_data.blkmax)
181                         merge_data.blkmax = dip->avgs.blks.max;
182         }
183 }
184
185 void output_dip_merge_ratio(FILE *ofp)
186 {
187         fprintf(ofp, "%10s | %8s %8s %7s | %8s %8s %8s %8s\n", "DEV", "#Q", "#D", "Ratio", "BLKmin", "BLKavg", "BLKmax", "Total");
188         fprintf(ofp, "---------- | -------- -------- ------- | -------- -------- -------- --------\n");
189         dip_foreach_out(__output_dip_merge_ratio, ofp);
190         if (n_merges > 1) {
191                 fprintf(ofp, "---------- | -------- -------- ------- | -------- -------- -------- --------\n");
192                 fprintf(ofp, "%10s | %8s %8s %7s | %8s %8s %8s %8s\n", "DEV", "#Q", "#D", "Ratio", "BLKmin", "BLKavg", "BLKmax", "Total");
193                 fprintf((FILE *)ofp, 
194                         "%10s | %8llu %8llu %7.1lf | %8llu %8llu %8llu %8llu\n",
195                         "TOTAL", merge_data.nq, merge_data.nd, 
196                         (float)merge_data.nq / (float)merge_data.nd,
197                         merge_data.blkmin, 
198                         merge_data.total / merge_data.nd,
199                         merge_data.blkmax, merge_data.total);
200         }
201         fprintf(ofp, "\n");
202 }
203
204 #define AVG(a,b) (100.0 * ((double)(a) / (double)(b)))
205 #define CALC_AVG(ap) (ap)->avg = ((ap)->n == 0 ? 0.0 :                    \
206                                                  (BIT_TIME((ap)->total) / \
207                                                         (double)(ap)->n))
208 char *q2i_v_q2C(struct d_info *dip, char *s)
209 {
210         double q2c;
211
212         if (dip->avgs.q2i.n == 0) return " ";
213
214         q2c = dip->avgs.q2i.avg + dip->avgs.i2d.avg + dip->avgs.d2c.avg;
215         sprintf(s, "%5.1lf%%", AVG(dip->avgs.q2i.avg, q2c));
216
217         return s;
218 }
219
220 char *i2d_v_q2C(struct d_info *dip, char *s)
221 {
222         double q2c;
223
224         if (dip->avgs.d2c.n == 0) return " ";
225
226         q2c = dip->avgs.q2i.avg + dip->avgs.i2d.avg + dip->avgs.d2c.avg;
227         sprintf(s, "%5.1lf%%", AVG(dip->avgs.i2d.avg, q2c));
228
229         return s;
230 }
231
232 char *d2c_v_q2C(struct d_info *dip, char *s)
233 {
234         double q2c;
235
236         if (dip->avgs.d2c.n == 0) return " ";
237
238         q2c = dip->avgs.q2i.avg + dip->avgs.i2d.avg + dip->avgs.d2c.avg;
239         sprintf(s, "%5.1lf%%", AVG(dip->avgs.d2c.avg, q2c));
240
241         return s;
242 }
243
244 void __output_dip_prep_ohead(struct d_info *dip, void *arg)
245 {
246         char dev_info[15];
247         char s1[16], s2[16], s3[16];
248
249         if ((dip->avgs.q2i.n > 0 && dip->avgs.i2d.n > 0 &&
250                                                 dip->avgs.d2c.n > 0)) {
251                 CALC_AVG(&dip->avgs.q2i);
252                 CALC_AVG(&dip->avgs.i2d);
253                 CALC_AVG(&dip->avgs.d2c);
254
255                 fprintf((FILE *)arg, "%10s | %6s %6s %6s\n",
256                         make_dev_hdr(dev_info, 15, dip),
257                         q2i_v_q2C(dip, s1), i2d_v_q2C(dip, s2),
258                         d2c_v_q2C(dip, s3));
259         }
260 }
261
262 void output_dip_prep_ohead(FILE *ofp)
263 {
264         fprintf(ofp, "%10s | %6s %6s %6s\n", "DEV", "Q2I", "I2D", "D2C");
265         fprintf(ofp, "---------- | ------ ------ ------\n");
266         dip_foreach_out(__output_dip_prep_ohead, ofp);
267         fprintf(ofp, "\n");
268 }
269
270 struct seek_mode_info {
271         struct seek_mode_info *next;
272         long long mode;
273         int nseeks;
274 };
275 struct o_seek_info {
276         long long nseeks, median;
277         double mean;
278         struct seek_mode_info *head;
279 } seek_info;
280 int n_seeks;
281
282 void output_seek_mode_info(FILE *ofp, struct o_seek_info *sip)
283 {
284         struct seek_mode_info *p, *this, *new_list = NULL;
285
286         ASSERT(sip->head != NULL);
287         while ((this = sip->head) != NULL) {
288                 sip->head = this->next;
289                 this->next = NULL;
290
291                 if (new_list == NULL || this->nseeks > new_list->nseeks)
292                         new_list = this;
293                 else if (this->nseeks == new_list->nseeks) {
294                         assert(this->nseeks == new_list->nseeks);
295                         for (p = new_list; p != NULL; p = p->next)
296                                 if (p->mode == this->mode)
297                                         break;
298
299                         if (p)
300                                 this->nseeks += p->nseeks;
301                         else
302                                 this->next = new_list;
303                         new_list = this;
304                 }
305         }
306
307         fprintf(ofp, "%10s | %15lld %15.1lf %15lld | %lld(%d)",
308                 "Average", sip->nseeks, sip->mean / sip->nseeks, 
309                 sip->median / sip->nseeks, new_list->mode, new_list->nseeks);
310
311         for (p = new_list->next; p != NULL; p = p->next)
312                 fprintf(ofp, " %lld(%d)", p->mode, p->nseeks);
313 }
314
315 void add_seek_mode_info(struct o_seek_info *sip, struct mode *mp)
316 {
317         int i;
318         long long *lp = mp->modes;
319         struct seek_mode_info *smip;
320
321         n_seeks++;
322         for (i = 0; i < mp->nmds; i++, lp++) {
323                 for (smip = sip->head; smip; smip = smip->next) {
324                         if (smip->mode == *lp) {
325                                 smip->nseeks += mp->most_seeks;
326                                 break;
327                         }
328                 }
329                 if (!smip) {
330                         struct seek_mode_info *new = malloc(sizeof(*new));
331
332                         new->next = sip->head; 
333                         sip->head = new;
334                         new->mode = *lp;
335                         new->nseeks = mp->most_seeks;
336
337                         add_buf(new);
338                 }
339         }
340 }
341
342 static void do_output_dip_seek_info(struct d_info *dip, FILE *ofp, int is_q2q)
343 {
344         double mean;
345         int i, nmodes;
346         long long nseeks;
347         char dev_info[15];
348         long long median;
349         struct mode m;
350         void *handle = is_q2q ? dip->q2q_handle : dip->seek_handle;
351
352         nseeks = seeki_nseeks(handle);
353         if (nseeks > 0) {
354                 mean = seeki_mean(handle);
355                 median = seeki_median(handle);
356                 nmodes = seeki_mode(handle, &m);
357
358                 fprintf(ofp, "%10s | %15lld %15.1lf %15lld | %lld(%d)",
359                         make_dev_hdr(dev_info, 15, dip), nseeks, mean, median, 
360                         nmodes > 0 ? m.modes[0] : 0, m.most_seeks);
361                 for (i = 1; i < nmodes; i++)
362                         fprintf(ofp, " %lld", m.modes[i]);
363                 fprintf(ofp, "\n");
364
365                 seek_info.nseeks += nseeks;
366                 seek_info.mean += (nseeks * mean);
367                 seek_info.median += (nseeks * median);
368                 add_seek_mode_info(&seek_info, &m);
369                 free(m.modes);
370         }
371 }
372
373 void __output_dip_seek_info(struct d_info *dip, void *arg)
374 {
375         do_output_dip_seek_info(dip, (FILE *)arg, 0);
376 }
377
378 void __output_dip_q2q_seek_info(struct d_info *dip, void *arg)
379 {
380         do_output_dip_seek_info(dip, (FILE *)arg, 1);
381 }
382
383 void output_dip_seek_info(FILE *ofp)
384 {
385         n_seeks = 1;
386         memset(&seek_info, 0, sizeof(seek_info));
387
388         fprintf(ofp, "%10s | %15s %15s %15s | %-15s\n", "DEV", "NSEEKS", 
389                         "MEAN", "MEDIAN", "MODE");
390         fprintf(ofp, "---------- | --------------- --------------- --------------- | ---------------\n");
391         dip_foreach_out(__output_dip_seek_info, ofp);
392         if (n_seeks > 1) {
393                 fprintf(ofp, "---------- | --------------- --------------- --------------- | ---------------\n");
394                 fprintf(ofp, "%10s | %15s %15s %15s | %-15s\n", 
395                         "Overall", "NSEEKS", "MEAN", "MEDIAN", "MODE");
396                 output_seek_mode_info(ofp, &seek_info);
397                 fprintf(ofp, "\n");
398         }
399         fprintf(ofp, "\n");
400 }
401
402 void output_dip_q2q_seek_info(FILE *ofp)
403 {
404         n_seeks = 1;
405         memset(&seek_info, 0, sizeof(seek_info));
406
407         fprintf(ofp, "%10s | %15s %15s %15s | %-15s\n", "DEV", "NSEEKS", 
408                         "MEAN", "MEDIAN", "MODE");
409         fprintf(ofp, "---------- | --------------- --------------- --------------- | ---------------\n");
410         dip_foreach_out(__output_dip_q2q_seek_info, ofp);
411         if (n_seeks > 1) {
412                 fprintf(ofp, "---------- | --------------- --------------- --------------- | ---------------\n");
413                 fprintf(ofp, "%10s | %15s %15s %15s | %-15s\n", 
414                         "Overall", "NSEEKS", "MEAN", "MEDIAN", "MODE");
415                 output_seek_mode_info(ofp, &seek_info);
416                 fprintf(ofp, "\n");
417         }
418         fprintf(ofp, "\n");
419 }
420
421 struct __opa {
422         FILE *ofp;
423         ai_pip_t (*func)(struct p_info *);
424 };
425
426 void __output_pip_avg(struct p_info *pip, void *arg)
427 {
428         struct __opa *opap = arg;
429         ai_pip_t ap = opap->func(pip);
430
431         if (ap->n > 0) {
432                 char proc_name[15];
433                 snprintf(proc_name, 15, pip->name);
434
435                 ap->avg = BIT_TIME(ap->total) / (double)ap->n;
436                 __output_avg(opap->ofp, proc_name, ap);
437         }
438 }
439
440 void output_pip_avg(FILE *ofp, char *hdr, ai_pip_t (*func)(struct p_info *))
441 {
442         struct __opa opa = { .ofp = ofp, .func = func };
443
444         output_hdr(ofp, hdr);
445         pip_foreach_out(__output_pip_avg, &opa);
446         fprintf(ofp, "\n");
447 }
448
449 void output_dip_avgs(FILE *ofp)
450 {
451         output_hdr2(ofp,"Dev");
452         dip_foreach_out(__dip_output_avg2, ofp);
453         fprintf(ofp, "\n");
454 }
455
456 void output_pip_avgs(FILE *ofp)
457 {
458         output_hdr2(ofp,"Exe");
459         pip_foreach_out(__pip_output_avg2, ofp);
460         fprintf(ofp, "\n");
461 }
462
463 int n_plugs;
464 struct plug_info {
465         long n_plugs, n_timer_unplugs;
466         double t_percent;
467 } plug_info;
468
469 void __dip_output_plug(struct d_info *dip, void *arg)
470 {
471         char dev_info[15];
472         FILE *ofp = arg;
473         double delta, pct;
474
475         if (dip->nplugs > 0) {
476                 if (dip->is_plugged) dip_unplug(dip->device, dip->end_time, 0);
477                 delta = dip->end_time - dip->start_time;
478                 pct = 100.0 * ((dip->plugged_time / delta) / delta);
479
480                 fprintf(ofp, "%10s | %10d(%10d) | %13.9lf%%\n", 
481                         make_dev_hdr(dev_info, 15, dip), 
482                         dip->nplugs, dip->n_timer_unplugs, pct);
483
484                 n_plugs++;
485                 plug_info.n_plugs += dip->nplugs;
486                 plug_info.n_timer_unplugs += dip->n_timer_unplugs;
487                 plug_info.t_percent += pct;
488         }
489 }
490
491 void __dip_output_plug_all(FILE *ofp, struct plug_info *p)
492 {
493         fprintf(ofp, "---------- | ---------- ----------  | ----------------\n");
494         fprintf(ofp, "%10s | %10s %10s  | %s\n", 
495                 "Overall", "# Plugs", "# Timer Us", "% Time Q Plugged");
496         fprintf(ofp, "%10s | %10ld(%10ld) | %13.9lf%%\n", "Average",
497                 p->n_plugs / n_plugs, p->n_timer_unplugs / n_plugs, 
498                 p->t_percent / n_plugs);
499
500 }
501
502 void output_plug_info(FILE *ofp)
503 {
504         fprintf(ofp, "%10s | %10s %10s  | %s\n", 
505                 "DEV", "# Plugs", "# Timer Us", "% Time Q Plugged");
506         fprintf(ofp, "---------- | ---------- ----------  | ----------------\n");
507         dip_foreach_out(__dip_output_plug, ofp);
508         if (n_plugs > 1)
509                 __dip_output_plug_all(ofp, &plug_info);
510         fprintf(ofp, "\n");
511 }
512
513 int n_actQs;
514 struct actQ_info {
515         __u64 t_qs;
516         __u64 t_act_qs;
517 } actQ_info;
518
519 void __dip_output_actQ(struct d_info *dip, void *arg)
520 {
521         if (dip->n_qs > 0 && !remapper_dev(dip->device)) {
522                 char dev_info[15];
523                 double a_actQs = (double)dip->t_act_q / (double)dip->n_qs;
524
525                 fprintf((FILE *)arg, "%10s | %13.1lf\n", 
526                         make_dev_hdr(dev_info, 15, dip), a_actQs);
527                         
528                 n_actQs++;
529                 actQ_info.t_qs += dip->n_qs;
530                 actQ_info.t_act_qs += dip->t_act_q;
531         }
532 }
533
534 void __dip_output_actQ_all(FILE *ofp, struct actQ_info *p)
535 {
536         fprintf(ofp, "---------- | -------------\n");
537         fprintf(ofp, "%10s | %13s\n", "Overall", "Avgs Reqs @ Q");
538         fprintf(ofp, "%10s | %13.1lf\n", "Average", 
539                 (double)p->t_act_qs / (double)p->t_qs);
540 }
541
542 void output_actQ_info(FILE *ofp)
543 {
544         fprintf(ofp, "%10s | %13s\n", "DEV", "Avg Reqs @ Q");
545         fprintf(ofp, "---------- | -------------\n");
546         dip_foreach_out(__dip_output_actQ, ofp);
547         if (n_actQs > 1)
548                 __dip_output_actQ_all(ofp, &actQ_info);
549         fprintf(ofp, "\n");
550 }
551
552 void output_histos(void)
553 {
554         int i;
555         FILE *ofp;
556         char fname[256];
557
558         if (output_name == NULL) return;
559
560         sprintf(fname, "%s_qhist.dat", output_name);
561         ofp = fopen(fname, "w");
562         if (!ofp) {
563                 perror(fname);
564                 return;
565         }
566
567         fprintf(ofp, "# BTT histogram data\n");
568         fprintf(ofp, "# Q buckets\n");
569         for (i = 0; i < (N_HIST_BKTS-1); i++) 
570                 fprintf(ofp, "%4d %lld\n", (i+1), (long long)q_histo[i]);
571         fprintf(ofp, "\n# Q bucket for > %d\n%4d %lld\n", (int)N_HIST_BKTS-1,
572                 N_HIST_BKTS-1, (long long)q_histo[N_HIST_BKTS-1]);
573         fclose(ofp);
574
575         sprintf(fname, "%s_dhist.dat", output_name);
576         ofp = fopen(fname, "w");
577         if (!ofp) {
578                 perror(fname);
579                 return;
580         }
581         fprintf(ofp, "# D buckets\n");
582         for (i = 0; i < (N_HIST_BKTS-1); i++)
583                 fprintf(ofp, "%4d %lld\n", (i+1), (long long)d_histo[i]);
584         fprintf(ofp, "\n# D bucket for > %d\n%4d %lld\n", (int)N_HIST_BKTS-1,
585                 N_HIST_BKTS-1, (long long)d_histo[N_HIST_BKTS-1]);
586         fclose(ofp);
587 }
588
589 int output_avgs(FILE *ofp)
590 {
591         if (output_all_data) {
592                 if (exes == NULL || *exes != '\0') {
593                         output_section_hdr(ofp, "Per Process");
594                         output_pip_avg(ofp, "Q2Q", pip_q2q_avg);
595                         output_pip_avg(ofp, "Q2A", pip_q2a_avg);
596                         output_pip_avg(ofp, "Q2I", pip_q2i_avg);
597                         output_pip_avg(ofp, "I2D", pip_i2d_avg);
598                         output_pip_avg(ofp, "D2C", pip_d2c_avg);
599                         output_pip_avg(ofp, "Q2C", pip_q2c_avg);
600                 }
601
602                 output_section_hdr(ofp, "Per Device");
603                 output_dip_avg(ofp, "Q2Q", dip_q2q_avg);
604                 output_dip_avg(ofp, "Q2A", dip_q2a_avg);
605                 output_dip_avg(ofp, "Q2I", dip_q2i_avg);
606                 output_dip_avg(ofp, "I2D", dip_i2d_avg);
607                 output_dip_avg(ofp, "D2C", dip_d2c_avg);
608                 output_dip_avg(ofp, "Q2C", dip_q2c_avg);
609         }
610
611         output_section_hdr(ofp, "All Devices");
612         output_hdr(ofp, "ALL");
613         __output_avg(ofp, "Q2Q", &all_avgs.q2q);
614         __output_avg(ofp, "Q2A", &all_avgs.q2a);
615         __output_avg(ofp, "Q2I", &all_avgs.q2i);
616         __output_avg(ofp, "I2D", &all_avgs.i2d);
617         __output_avg(ofp, "D2C", &all_avgs.d2c);
618         __output_avg(ofp, "Q2C", &all_avgs.q2c);
619         fprintf(ofp, "\n");
620
621         output_section_hdr(ofp, "Device Overhead");
622         output_dip_prep_ohead(ofp);
623
624         if (output_all_data) {
625                 if (exes == NULL || *exes != '\0') {
626                         output_section_hdr(ofp, "Per Process (avgs)");
627                         output_pip_avgs(ofp);
628                 }
629
630                 output_section_hdr(ofp, "Per Device (avgs)");
631                 output_dip_avgs(ofp);
632         }
633
634         output_section_hdr(ofp, "Device Merge Information");
635         output_dip_merge_ratio(ofp);
636
637         output_section_hdr(ofp, "Device Q2Q Seek Information");
638         output_dip_q2q_seek_info(ofp);
639
640         output_section_hdr(ofp, "Device D2D Seek Information");
641         output_dip_seek_info(ofp);
642
643         output_section_hdr(ofp, "Plug Information");
644         output_plug_info(ofp);
645
646         output_section_hdr(ofp, "Active Requests At Q Information");
647         output_actQ_info(ofp);
648
649         output_histos();
650
651         return 0;
652 }
653
654 void __output_ranges(FILE *ofp, struct list_head *head_p, float base)
655 {
656         struct range_info *rip;
657         struct list_head *p;
658         float limit = base + 0.4;
659
660         __list_for_each(p, head_p) {
661                 rip = list_entry(p, struct range_info, head);
662                 fprintf(ofp, "%13.9lf %5.1f\n", BIT_TIME(rip->start), base);
663                 fprintf(ofp, "%13.9lf %5.1f\n", BIT_TIME(rip->start), limit);
664                 fprintf(ofp, "%13.9lf %5.1f\n", BIT_TIME(rip->end), limit);
665                 fprintf(ofp, "%13.9lf %5.1f\n", BIT_TIME(rip->end), base);
666         }
667 }
668
669 int output_regions(FILE *ofp, char *header, struct region_info *reg, 
670                           float base)
671 {
672         if (list_len(&reg->qranges) == 0 && list_len(&reg->cranges) == 0)
673                 return 0;
674
675         fprintf(ofp, "# %16s : q activity\n", header);
676         __output_ranges(ofp, &reg->qranges, base);
677         fprintf(ofp, "\n");
678
679         fprintf(ofp, "# %16s : c activity\n", header);
680         __output_ranges(ofp, &reg->cranges, base + 0.5);
681         fprintf(ofp, "\n");
682
683         return 1;
684 }
685
686 struct __od {
687         FILE *ofp;
688         float base;
689 };
690 void __output_dev(struct d_info *dip, void *arg)
691 {
692         char header[128];
693         struct __od *odp = arg;
694
695         sprintf(header, "%d,%d", MAJOR(dip->device), MINOR(dip->device));
696         if (output_regions(odp->ofp, header, &dip->regions, odp->base))
697                 odp->base += 1.0;
698 }
699
700 float output_devs(FILE *ofp, float base)
701 {
702         struct __od od = { .ofp = ofp, .base = base };
703
704         fprintf(ofp, "# Per device\n" );
705         dip_foreach_out(__output_dev, &od);
706         return od.base;
707 }
708
709 static inline int exe_match(char *exe, char *name)
710 {
711         return (exe == NULL) || (strstr(name, exe) != NULL);
712 }
713
714 struct __op {
715         FILE *ofp;
716         float base;
717 };
718 void __output_procs(struct p_info *pip, void *arg)
719 {
720         struct __op *opp = arg;
721         output_regions(opp->ofp, pip->name, &pip->regions, opp->base);
722         opp->base += 1.0;
723 }
724
725 float output_procs(FILE *ofp, float base)
726 {
727         struct __op op = { .ofp = ofp, .base = base };
728
729         fprintf(ofp, "# Per process\n" );
730         pip_foreach_out(__output_procs, &op);
731         return op.base;
732 }
733
734 int output_ranges(FILE *ofp)
735 {
736         float base = 0.0;
737
738         fprintf(ofp, "# %s\n", "Total System");
739         if (output_regions(ofp, "Total System", &all_regions, base))
740                 base += 1.0;
741
742         if (n_devs > 1)
743                 base = output_devs(ofp, base);
744
745         base = output_procs(ofp, base);
746
747         return 0;
748 }