[PATCH] btt: Fix invalid q2a
[blktrace.git] / btt / output.c
CommitLineData
63eba147
JA
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
24typedef struct avg_info *ai_dip_t;
25ai_dip_t dip_q2q_avg(struct d_info *dip) { return &dip->avgs.q2q; }
26ai_dip_t dip_q2c_avg(struct d_info *dip) { return &dip->avgs.q2c; }
27ai_dip_t dip_q2a_avg(struct d_info *dip) { return &dip->avgs.q2a; }
28ai_dip_t dip_q2i_avg(struct d_info *dip) { return &dip->avgs.q2i; }
29ai_dip_t dip_i2d_avg(struct d_info *dip) { return &dip->avgs.i2d; }
30ai_dip_t dip_d2c_avg(struct d_info *dip) { return &dip->avgs.d2c; }
31
32typedef struct avg_info *ai_pip_t;
33ai_pip_t pip_q2q_avg(struct p_info *pip) { return &pip->avgs.q2q; }
34ai_pip_t pip_q2c_avg(struct p_info *pip) { return &pip->avgs.q2c; }
35ai_pip_t pip_q2a_avg(struct p_info *pip) { return &pip->avgs.q2a; }
36ai_pip_t pip_q2i_avg(struct p_info *pip) { return &pip->avgs.q2i; }
37ai_pip_t pip_i2d_avg(struct p_info *pip) { return &pip->avgs.i2d; }
38ai_pip_t pip_d2c_avg(struct p_info *pip) { return &pip->avgs.d2c; }
39
40void output_section_hdr(FILE *ofp, char *hdr)
41{
42 fprintf(ofp, "==================== ");
43 fprintf(ofp, hdr);
44 fprintf(ofp, " ====================\n\n");
45}
46
47void 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
54void __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
63void 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
69static 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
78void __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
91char *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
105void __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
114void 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
144void __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;
9f11c1a7 152 blks_avg = (double)dip->avgs.blks.total / d2c_n;
63eba147
JA
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
166void 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))
201char *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
213char *i2d_v_q2C(struct d_info *dip, char *s)
214{
215 double q2c;
216
5225e788 217 if (dip->avgs.d2c.n == 0) return " ";
63eba147
JA
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
225char *d2c_v_q2C(struct d_info *dip, char *s)
226{
227 double q2c;
228
aa994ec5 229 if (dip->avgs.d2c.n == 0) return " ";
63eba147
JA
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
237void __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
255void 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
5225e788
AB
287void __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
308void 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
63eba147
JA
343void __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
354void 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
389void 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
422void 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
461int 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
5225e788
AB
504 output_section_hdr(ofp, "Device Seek Information");
505 output_dip_seek_info(ofp);
506
63eba147
JA
507 return 0;
508}
509
510void __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
525int 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
547float __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
557float 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
588static inline int exe_match(char *exe, char *name)
589{
590 return (exe == NULL) || (strstr(name, exe) != NULL);
591}
592
593float __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
610float 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
636int 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}