f40a1fb1c84ffcab0938b7d52a1f208da6c69f38
[vpp.git] / perftool / cpelatency.c
1 /* 
2  *------------------------------------------------------------------
3  * Copyright (c) 2006-2016 Cisco and/or its affiliates.
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at:
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <netinet/in.h>
20 #include <string.h>
21 #include <sys/types.h>
22 #include <sys/stat.h>
23 #include <fcntl.h>
24 #include <sys/fcntl.h>
25 #include <sys/mman.h>
26 #include <unistd.h>
27 #include <ctype.h>
28 #include <vppinfra/clib.h>
29 #include <vppinfra/vec.h>
30 #include <vppinfra/hash.h>
31 #include <pwd.h>
32 #include <stdarg.h>
33 #include <time.h>
34 #include "cpel.h"
35 #include <math.h>
36
37 char *time_format = "%.03d:%.02d:%.02d:%.03d:%.03d ";
38 static char version[] = "cpelatency 2.0";
39
40 #define USEC_PER_MS 1000LL
41 #define USEC_PER_SECOND (1000*USEC_PER_MS)
42 #define USEC_PER_MINUTE (60*USEC_PER_SECOND)
43 #define USEC_PER_HOUR (60*USEC_PER_MINUTE)
44
45 uword *the_strtab_hash; /* (name, base-VA) hash of all string tables */
46 uword *the_evtdef_hash; /* (event-id, event-definition) hash */
47 uword *the_trackdef_hash;      /* (track-id, track-definition) hash */
48 uword *the_pidtid_hash;         /* ("pid:xxx tid:yy", track-definition) hash */
49
50 f64 ticks_per_us;
51 u32 start_event_code = 2;       /* default: XR thread ready event */
52 u32 end_event_code = 1;         /* default: XR thread running event */
53 int exclude_kernel_from_summary_stats=1;
54 int summary_stats_only;
55 int scatterplot;
56 u8 *name_filter;
57 int have_trackdefs;
58
59 typedef enum {
60     SORT_MAX_TIME=1,
61     SORT_MAX_OCCURRENCES,
62     SORT_NAME,
63 } sort_t;
64
65 sort_t sort_type = SORT_MAX_TIME;
66
67 int widest_name_format=5;
68 int widest_track_format=20;
69
70 typedef struct bound_event_ {
71     u32 event_code;
72     u8  *event_str;
73     u8  *datum_str;
74     u32  is_strtab_ref;
75 } bound_event_t;
76
77 bound_event_t *bound_events;
78
79 typedef struct bound_track_ {
80     u32 track;
81     u8  *track_str;
82     u64 state_start_ticks;
83     u64 *ticks_in_state; /* vector of state occurrences */
84     f64  mean_ticks_in_state;
85     f64  variance_ticks_in_state;
86     f64  total_ticks_in_state;
87 } bound_track_t;
88
89 bound_track_t *bound_tracks;
90
91 void fatal(char *s)
92 {
93     fprintf(stderr, "%s", s);
94     exit(1);
95 }
96
97 typedef enum {
98     PASS1=1,
99     PASS2=2,
100 } pass_t;
101
102 typedef struct {
103     int (*pass1)(cpel_section_header_t *, int, FILE *);
104     int (*pass2)(cpel_section_header_t *, int, FILE *);
105 } section_processor_t;
106
107 int bad_section(cpel_section_header_t *sh, int verbose, FILE *ofp)
108 {
109     fprintf(ofp, "Bad (type 0) section, skipped...\n");
110     return(0);
111 }
112
113 int noop_pass(cpel_section_header_t *sh, int verbose, FILE *ofp)
114 {
115     return(0);
116 }
117
118 int strtab_pass1(cpel_section_header_t *sh, int verbose, FILE *ofp)
119 {
120     uword *p;
121     u8 *strtab_data_area = (u8 *)(sh+1);
122     
123     /* Multiple string tables with the same name are Bad... */
124     p = hash_get_mem(the_strtab_hash, strtab_data_area);
125     if (p) {
126         fprintf(ofp, "Duplicate string table name %s", strtab_data_area);
127     }
128     /*
129      * Looks funny, but we really do want key = first string in the
130      * table, value = address(first string in the table) 
131      */
132     hash_set_mem(the_strtab_hash, strtab_data_area, strtab_data_area);
133     if (verbose) {
134         fprintf(ofp, "String Table %s\n", strtab_data_area);
135     }
136     return(0);
137 }
138
139 int evtdef_pass1(cpel_section_header_t *sh, int verbose, FILE *ofp)
140 {
141     int i, nevents;
142     event_definition_section_header_t *edh;
143     event_definition_t *ep;
144     u8 *this_strtab;
145     u32 event_code;
146     uword *p;
147     bound_event_t *bp;
148     int thislen;
149
150     edh = (event_definition_section_header_t *)(sh+1);
151     nevents = ntohl(edh->number_of_event_definitions);
152     
153     if (verbose) {
154         fprintf(ofp, "Event Definition Section: %d definitions\n",
155                 nevents);
156     }
157
158     p = hash_get_mem(the_strtab_hash, edh->string_table_name);
159     if (!p) {
160         fprintf(ofp, "Fatal: couldn't find string table\n");
161         return(1);
162     }
163     this_strtab = (u8 *)p[0];
164
165     ep = (event_definition_t *)(edh+1);
166     
167     for (i = 0; i < nevents; i++) {
168         event_code = ntohl(ep->event);
169         p = hash_get(the_evtdef_hash, event_code);
170         if (p) {
171             fprintf(ofp, "Event %d redefined, retain first definition\n",
172                     event_code);
173             continue;
174         }
175         vec_add2(bound_events, bp, 1);
176         bp->event_code = event_code;
177         bp->event_str = this_strtab + ntohl(ep->event_format);
178         bp->datum_str = this_strtab + ntohl(ep->datum_format);
179         bp->is_strtab_ref = 0;
180         /* Decide if the datum format is a %s format => strtab reference */
181         {
182             int j;
183             int seen_percent=0;
184
185             for (j = 0; j < strlen((char *) bp->datum_str); j++) {
186                 if (bp->datum_str[j] == '%'){
187                     seen_percent=1;
188                     continue;
189                 }
190                 if (seen_percent && bp->datum_str[j] == 's') {
191                     bp->is_strtab_ref = 1;
192                 }
193             }
194         }
195         
196         hash_set(the_evtdef_hash, event_code, bp - bound_events);
197
198         thislen = strlen((char *) bp->event_str);
199         if (thislen > widest_name_format)
200             widest_name_format = thislen;
201
202         ep++;
203     }
204     return (0);
205 }
206
207 int trackdef_pass1(cpel_section_header_t *sh, int verbose, FILE *ofp)
208 {
209     int i, nevents;
210     track_definition_section_header_t *tdh;
211     track_definition_t *tp;
212     u8 *this_strtab;
213     u32 track_code;
214     uword *p;
215     bound_track_t *btp;
216     int thislen;
217     u8 *pidstr;
218     u8 *pidtid_str;
219     u8 *cp;
220     int tid, pid;
221
222     tdh = (track_definition_section_header_t *)(sh+1);
223     nevents = ntohl(tdh->number_of_track_definitions);
224     
225     if (verbose) {
226         fprintf(ofp, "Track Definition Section: %d definitions\n",
227                 nevents);
228     }
229
230     p = hash_get_mem(the_strtab_hash, tdh->string_table_name);
231     if (!p) {
232         fprintf(ofp, "Fatal: couldn't find string table\n");
233         return(1);
234     }
235     this_strtab = (u8 *)p[0];
236
237     tp = (track_definition_t *)(tdh+1);
238     
239     for (i = 0; i < nevents; i++) {
240         track_code = ntohl(tp->track);
241         p = hash_get(the_trackdef_hash, track_code);
242         if (p) {
243             fprintf(stderr, "track %d redefined, retain first definition\n",
244                     track_code);
245             continue;
246         }
247         vec_add2(bound_tracks, btp, 1);
248         btp->track = track_code;
249         btp->track_str = this_strtab + ntohl(tp->track_format);
250         hash_set(the_trackdef_hash, track_code, btp - bound_tracks);
251
252         if (verbose) {
253             fprintf(stderr, "adding track '%s'\n", btp->track_str);
254         }
255
256         thislen = strlen((char *) btp->track_str);
257         if (thislen > widest_track_format)
258             widest_track_format = thislen;
259
260         /* convert track_str "eth_server t11(20498)" to "pid:20498 tid:11" */
261         cp = btp->track_str;
262         while (*cp && *cp != '(')
263             cp++;
264         if (!*cp) {
265             fprintf(stderr, "error canonicalizing '%s'\n", btp->track_str);
266             goto out;
267         }
268         pidstr = cp+1;          /* remember location of PID */
269
270         while (cp > btp->track_str && *cp != 't')
271             cp--;
272
273         if (cp == btp->track_str) {
274             fprintf(stderr, "error canonicalizing '%s'\n", btp->track_str);
275             goto out;
276         }
277         tid = atol((char *)(cp+1));
278         pid = atol((char *) pidstr);
279         pidtid_str = format(0, "pid:%d tid:%d", pid, tid);
280         vec_add1(pidtid_str, 0);
281
282         /* 
283          * Note: duplicates are possible due to thread create / 
284          * thread destroy operations.
285          */
286         p = hash_get_mem(the_pidtid_hash, pidtid_str);
287         if (p) {
288             vec_free(pidtid_str);
289             goto out;
290         }
291         hash_set_mem(the_pidtid_hash, pidtid_str, btp - bound_tracks);
292
293     out:
294         tp++;
295     }
296     have_trackdefs = 1;
297     return (0);
298 }
299
300 int unsupported_pass (cpel_section_header_t *sh, int verbose, FILE *ofp)
301 {
302     if (verbose) {
303         fprintf(ofp, "Unsupported type %d section\n",
304                 ntohl(sh->section_type));
305     }
306     return(0);
307 }
308
309 int event_pass2(cpel_section_header_t *sh, int verbose, FILE *ofp)
310 {
311     event_section_header_t *eh;
312     int nevents;
313     int i;
314     uword *p;
315     event_entry_t *ep;
316     u64 now;
317     u32 time0, time1;
318     u32 track_code;
319     u8 *this_strtab;
320     u64 ticks_in_state;
321     bound_track_t *btp;
322     bound_track_t *state_track=0;
323     u8 *pidtid_str;
324     u8 *pidtid_dup;
325     u8 *ecp;
326     u32 event_code;
327
328     eh = (event_section_header_t *)(sh+1);
329     nevents = ntohl(eh->number_of_events);
330     ticks_per_us = ((double)ntohl(eh->clock_ticks_per_second)) / 1e6;
331
332     if (verbose) {
333         fprintf(ofp, "%.3f ticks_per_us\n", ticks_per_us);
334     }
335
336     ep = (event_entry_t *)(eh+1);
337
338     p = hash_get_mem(the_strtab_hash, eh->string_table_name);
339     if (!p) {
340         fprintf(ofp, "Fatal: couldn't find string table\n");
341         return(1);
342     }
343     this_strtab = (u8 *)p[0];
344
345     /*
346      * Some logger implementation that doesn't produce
347      * trackdef sections, synthesize the bound_tracks vector
348      */
349     if (!have_trackdefs) {
350         for (i = 0; i < nevents; i++) {
351             track_code = ntohl(ep->track);
352             pidtid_dup = format(0, "%d", track_code);
353             vec_add1(pidtid_dup, 0);
354             p = hash_get_mem(the_pidtid_hash, pidtid_dup);
355             if (!p) {
356                 vec_add2(bound_tracks, btp, 1);
357                 btp->track = track_code;
358                 btp->track_str = pidtid_dup;
359                 hash_set(the_trackdef_hash, track_code, btp - bound_tracks);
360                 hash_set_mem(the_pidtid_hash, pidtid_dup, btp - bound_tracks);
361             } else {
362                 vec_free(pidtid_dup);
363             }
364             ep++;
365         }
366     }
367
368     ep = (event_entry_t *)(eh+1);
369
370     for (i = 0; i < nevents; i++) {
371         time0 = ntohl (ep->time[0]);
372         time1 = ntohl (ep->time[1]);
373
374         now = (((u64) time0)<<32) | time1;
375         
376         event_code = ntohl(ep->event_code);
377
378         /* Find the corresponding track via the pidtid hash table */
379         if (event_code == start_event_code || event_code == end_event_code) {
380             if (have_trackdefs) {
381                 pidtid_str = this_strtab + ntohl(ep->event_datum);
382                 pidtid_dup = format(0, (char *) pidtid_str);
383                 vec_add1(pidtid_dup, 0);
384                 ecp = &pidtid_dup[vec_len(pidtid_dup)-1];
385                 while (*--ecp == ' ')
386                     *ecp = 0;
387             } else {
388                 pidtid_dup = format(0, "%d", ntohl(ep->track));
389                 vec_add1(pidtid_dup, 0);
390             }
391
392             p = hash_get_mem(the_pidtid_hash, pidtid_dup);
393             if (!p) {
394                 fprintf(stderr, "warning: couldn't find '%s'\n",
395                         pidtid_dup);
396                 vec_free(pidtid_dup);
397                 ep++;
398                 continue;
399             }
400             state_track = &bound_tracks[p[0]];
401         }
402         /* Found the start-event code ? */
403         if (event_code == start_event_code) {
404             state_track->state_start_ticks = now;
405         } else if (event_code == end_event_code) {
406             /*
407              * Add a ticks-in-state record, unless
408              * e.g. the log started with the exit event
409              */
410             if (state_track->state_start_ticks) {
411                 ticks_in_state = now - state_track->state_start_ticks;
412                 vec_add1(state_track->ticks_in_state, ticks_in_state);
413                 state_track->state_start_ticks = 0;
414             }
415             /* Otherwise, nothing */
416         }
417         ep++;
418     }
419     return(0);
420 }
421
422 /* 
423  * Note: If necessary, add passes / columns to this table to 
424  * handle section order dependencies.
425  */
426
427 section_processor_t processors[CPEL_NUM_SECTION_TYPES+1] =
428 {
429     {bad_section,       noop_pass},             /* type 0 -- f**ked */
430     {strtab_pass1,      noop_pass},             /* type 1 -- STRTAB */
431     {unsupported_pass,  noop_pass},             /* type 2 -- SYMTAB */
432     {evtdef_pass1,      noop_pass},             /* type 3 -- EVTDEF */
433     {trackdef_pass1,    noop_pass},             /* type 4 -- TRACKDEF */
434     {noop_pass,         event_pass2},           /* type 5 -- EVENTS */
435 };
436
437
438 int process_section(cpel_section_header_t *sh, int verbose, FILE *ofp,
439                     pass_t pass)
440 {
441     u32 type;
442     type = ntohl(sh->section_type);
443     int rv;
444     int (*fp)(cpel_section_header_t *, int, FILE *);
445
446     if (type > CPEL_NUM_SECTION_TYPES) {
447         fprintf(stderr, "Unknown section type %d\n", type);
448         return(1);
449     }
450     switch(pass) {
451     case PASS1:
452         fp = processors[type].pass1;
453         break;
454
455     case PASS2:
456         fp = processors[type].pass2;
457         break;
458         
459     default:
460         fprintf(stderr, "Unknown pass %d\n", pass);
461         return(1);
462     }
463
464     rv = (*fp)(sh, verbose, ofp);
465
466     return(rv);
467 }
468
469 int cpel_dump_file_header(cpel_file_header_t *fh, int verbose, FILE *ofp)
470 {
471     time_t file_time;
472
473     if (verbose) {
474         fprintf(ofp, "CPEL file: %s-endian, version %d\n",
475                 ((fh->endian_version & CPEL_FILE_LITTLE_ENDIAN) ? 
476                  "little" : "big"), 
477                 fh->endian_version & CPEL_FILE_VERSION_MASK);
478
479         file_time = ntohl(fh->file_date);
480         
481         fprintf(ofp, "File created %s", ctime(&file_time));
482         fprintf(ofp, "File has %d sections\n", 
483                 ntohs(fh->nsections));
484     }
485
486     return(0);
487 }
488
489
490 int cpel_dump(u8 *cpel, int verbose, FILE *ofp)
491 {
492     cpel_file_header_t *fh;
493     cpel_section_header_t *sh;
494     u16 nsections;
495     u32 section_size;
496     int i;
497
498     /* First, the file header */
499     fh = (cpel_file_header_t *)cpel;
500     if (fh->endian_version != CPEL_FILE_VERSION) {
501         if (fh->endian_version & CPEL_FILE_LITTLE_ENDIAN) {
502             fprintf(stderr, "Little endian data format not supported\n");
503             return(1);
504         }
505         fprintf(stderr, "Unsupported file version 0x%x\n", 
506                 fh->endian_version);
507         return(1);
508     }
509     cpel_dump_file_header(fh, verbose, ofp);
510     nsections = ntohs(fh->nsections);
511
512     /*
513      * Take two passes through the file. PASS1 builds
514      * data structures, PASS2 actually dumps the file.
515      * Just in case the sections are in an unobvious order.
516      */
517     sh = (cpel_section_header_t *)(fh+1);
518     for (i = 0; i < nsections; i++) {
519         section_size = ntohl(sh->data_length);
520
521         if(verbose) {
522             fprintf(ofp, "Section type %d, size %d\n", ntohl(sh->section_type),
523                     section_size);
524         }
525
526         if(process_section(sh, verbose, ofp, PASS1))
527             return(1);
528
529         sh++;
530         sh = (cpel_section_header_t *)(((u8 *)sh)+section_size);
531     }
532
533     sh = (cpel_section_header_t *)(fh+1);
534     for (i = 0; i < nsections; i++) {
535         if(process_section(sh, verbose, ofp, PASS2))
536             return(1);
537         section_size = ntohl(sh->data_length);
538         sh++;
539         sh = (cpel_section_header_t *)(((u8 *)sh)+section_size);
540     }
541     return(0);
542 }
543
544 void compute_state_statistics(int verbose, FILE *ofp)
545 {
546     int i, j;
547     bound_track_t *bp;
548     f64 fticks;
549
550     /* Across the bound tracks */
551     for (i = 0; i < vec_len(bound_tracks); i++) {
552         bp = &bound_tracks[i];
553         bp->mean_ticks_in_state = 0.0;
554         bp->variance_ticks_in_state = 0.0;
555         bp->total_ticks_in_state = 0.0;
556         for (j = 0; j < vec_len(bp->ticks_in_state); j++) {
557             bp->total_ticks_in_state += (f64) bp->ticks_in_state[j];
558         }
559         /* Compute mean */
560         if (vec_len(bp->ticks_in_state)) {
561             bp->mean_ticks_in_state = bp->total_ticks_in_state / 
562                 ((f64) vec_len(bp->ticks_in_state));
563         }
564         /* Accumulate sum: (Xi-Xbar)**2 */
565         for (j = 0; j < vec_len(bp->ticks_in_state); j++) {
566             fticks = bp->ticks_in_state[j];
567             bp->variance_ticks_in_state += 
568                 (fticks - bp->mean_ticks_in_state)*
569                 (fticks - bp->mean_ticks_in_state);
570         }
571         /* Compute s**2, the unbiased estimator of sigma**2 */
572         if (vec_len(bp->ticks_in_state) > 1) {
573             bp->variance_ticks_in_state /= (f64) 
574                 (vec_len(bp->ticks_in_state)-1);
575         }
576     }
577 }
578
579 int track_compare_max (const void *arg1, const void *arg2)
580 {
581     bound_track_t *a1 = (bound_track_t *)arg1;
582     bound_track_t *a2 = (bound_track_t *)arg2;
583     f64 v1, v2;
584
585     v1 = a1->total_ticks_in_state;
586     v2 = a2->total_ticks_in_state;
587     
588     if (v1 < v2)
589         return (1);
590     else if (v1 == v2)
591         return (0);
592     else return (-1);
593 }
594
595 int track_compare_occurrences (const void *arg1, const void *arg2)
596 {
597     bound_track_t *a1 = (bound_track_t *)arg1;
598     bound_track_t *a2 = (bound_track_t *)arg2;
599     f64 v1, v2;
600
601     v1 = (f64) vec_len(a1->ticks_in_state);
602     v2 = (f64) vec_len(a2->ticks_in_state);
603     
604     if (v1 < v2)
605         return (1);
606     else if (v1 == v2)
607         return (0);
608     else return (-1);
609 }
610
611 int track_compare_name (const void *arg1, const void *arg2)
612 {
613     bound_track_t *a1 = (bound_track_t *)arg1;
614     bound_track_t *a2 = (bound_track_t *)arg2;
615
616     return (strcmp((char *)(a1->track_str), (char *)(a2->track_str)));
617 }
618
619 void sort_state_statistics(sort_t type, FILE *ofp)
620 {
621     int (*compare)(const void *, const void *);
622
623     if (summary_stats_only)
624         return;
625
626     switch(type) {
627     case SORT_MAX_TIME:
628         fprintf(ofp, "Results sorted by max time in state.\n\n");
629         compare = track_compare_max;
630         break;
631
632     case SORT_MAX_OCCURRENCES:
633         fprintf(ofp, "Results sorted by max occurrences of state.\n\n");
634         compare = track_compare_occurrences;
635         break;
636
637     case SORT_NAME:
638         compare = track_compare_name;
639         fprintf(ofp, "Results sorted by process name, thread ID, PID\n\n");
640         break;
641
642     default:
643         fatal("sort type not set?");
644     }
645     
646     qsort (bound_tracks, vec_len(bound_tracks), 
647            sizeof (bound_track_t), compare);    
648 }
649
650 void print_state_statistics(int verbose, FILE *ofp)
651 {
652     int i,j;
653     u8 *trackpad;
654     bound_track_t *bp;
655     f64 total_time = 0.0;
656     f64 total_switches = 0.0;
657
658     trackpad = format(0, "%%-%ds ", widest_track_format);
659     vec_add1(trackpad, 0);
660
661     if (!summary_stats_only) {
662         fprintf(ofp, (char *)trackpad, "ProcName Thread(PID)");
663         fprintf(ofp, "  Mean(us)     Stdev(us)   Total(us)      N\n");
664     }
665         
666     for (i = 0; i < vec_len(bound_tracks); i++) {
667         bp = &bound_tracks[i];
668         if (bp->mean_ticks_in_state == 0.0)
669             continue;
670
671         if (name_filter &&
672             strncmp((char *)bp->track_str, (char *)name_filter, 
673                     strlen((char *)name_filter)))
674             continue;
675
676         /*
677          * Exclude kernel threads (e.g. idle thread) from
678          * state statistics 
679          */
680         if (exclude_kernel_from_summary_stats && 
681             !strncmp((char *) bp->track_str, "kernel ", 7))
682             continue;
683
684         total_switches += (f64) vec_len(bp->ticks_in_state);
685         
686         if (!summary_stats_only) {
687             fprintf(ofp, (char *) trackpad, bp->track_str);
688             fprintf(ofp, "%10.3f +- %10.3f", 
689                     bp->mean_ticks_in_state / ticks_per_us,
690                     sqrt(bp->variance_ticks_in_state) 
691                     / ticks_per_us);
692             fprintf(ofp, "%12.3f", 
693                     bp->total_ticks_in_state / ticks_per_us);
694             fprintf(ofp, "%8d\n", vec_len(bp->ticks_in_state));
695         }
696
697         if (scatterplot) {
698             for (j = 0; j < vec_len(bp->ticks_in_state); j++) {
699                 fprintf(ofp, "%.3f\n", 
700                         (f64)bp->ticks_in_state[j] / ticks_per_us);
701             }
702         }
703
704         total_time += bp->total_ticks_in_state;
705     }
706     
707     if (!summary_stats_only)
708         fprintf(ofp, "\n");
709     fprintf(ofp, "Note: the following statistics %s kernel-thread activity.\n",
710             exclude_kernel_from_summary_stats ? "exclude" : "include");
711     if (name_filter)
712         fprintf(ofp, 
713                 "Note: only pid/proc/threads matching '%s' are included.\n",
714                 name_filter);
715
716     fprintf(ofp, 
717       "Total time in state: %10.3f (us), Total state occurrences: %.0f\n", 
718             total_time / ticks_per_us, total_switches);
719     fprintf(ofp, "Average time in state: %10.3f (us)\n",
720             (total_time / total_switches) / ticks_per_us);
721     fprintf(ofp, "State start event: %d, state end event: %d\n",
722             start_event_code, end_event_code);
723 }
724
725 char *mapfile (char *file)
726 {
727     struct stat statb;
728     char *rv;
729     int maphfile;
730     size_t mapfsize;
731     
732     maphfile = open (file, O_RDONLY);
733
734     if (maphfile < 0)
735     {
736         fprintf (stderr, "Couldn't read %s, skipping it...\n", file);
737         return (NULL);
738     }
739
740     if (fstat (maphfile, &statb) < 0)
741     {
742         fprintf (stderr, "Couldn't get size of %s, skipping it...\n", file);
743         return (NULL);
744     }
745
746     /* Don't try to mmap directories, FIFOs, semaphores, etc. */
747     if (! (statb.st_mode & S_IFREG)) {
748         fprintf (stderr, "%s is not a regular file, skipping it...\n", file);
749         return (NULL);
750     }
751
752     mapfsize = statb.st_size;
753
754     if (mapfsize < 3)
755     {
756         fprintf (stderr, "%s zero-length, skipping it...\n", file);
757         close (maphfile);
758         return (NULL);
759     }
760
761     rv = mmap (0, mapfsize, PROT_READ, MAP_SHARED, maphfile, 0);
762
763     if (rv == 0)
764     {
765         fprintf (stderr, "%s problem mapping, I quit...\n", file);
766         exit (-1);
767     }
768     close (maphfile);
769     return (rv);
770 }
771
772 /*
773  * main 
774  */
775 int main (int argc, char **argv)
776 {
777     char *cpel_file = 0;
778     char *outputfile = 0;
779     FILE *ofp;
780     char *cpel;
781     int verbose=0;
782     int curarg=1;
783
784     while (curarg < argc) {
785         if (!strncmp(argv[curarg], "--input-file", 3)) {
786             curarg++;
787             if (curarg < argc) {
788                 cpel_file = argv[curarg];
789                 curarg++;
790                 continue;
791             }
792             fatal("Missing filename after --input-file\n");
793         }
794         if (!strncmp(argv[curarg], "--output-file", 3)) {
795             curarg ++;
796             if (curarg < argc) {
797                 outputfile = argv[curarg];
798                 curarg ++;
799                 continue;
800             }
801             fatal("Missing filename after --output-file\n");
802         }
803         if (!strncmp(argv[curarg], "--verbose", 3)) {
804             curarg++;
805             verbose++;
806             continue;
807         }
808         if (!strncmp(argv[curarg], "--scatterplot", 4)) {
809             curarg++;
810             scatterplot=1;
811             continue;
812         }
813
814         if (!strncmp(argv[curarg], "--start-event", 4)) {
815             curarg++;
816             if (curarg < argc) {
817                 start_event_code = atol(argv[curarg]);
818                 curarg ++;
819                 continue;
820             }
821             fatal("Missing integer after --start-event\n");
822         }
823         if (!strncmp(argv[curarg], "--end-event", 4)) {
824             curarg++;
825             if (curarg < argc) {
826                 end_event_code = atol(argv[curarg]);
827                 curarg ++;
828                 continue;
829             }
830             fatal("Missing integer after --end-event\n");
831         }
832         if (!strncmp(argv[curarg], "--max-time-sort", 7)) {
833             sort_type = SORT_MAX_TIME;
834             curarg++;
835             continue;
836         }
837         if (!strncmp(argv[curarg], "--max-occurrence-sort", 7)) {
838             sort_type = SORT_MAX_OCCURRENCES;
839             curarg++;
840             continue;
841         }
842         if (!strncmp(argv[curarg], "--name-sort", 3)) {
843             sort_type = SORT_NAME;
844             curarg++;
845             continue;
846         }
847         if (!strncmp(argv[curarg], "--kernel-included", 3)) {
848             exclude_kernel_from_summary_stats = 0;
849             curarg++;
850             continue;
851         }
852         if (!strncmp(argv[curarg], "--summary", 3)) {
853             summary_stats_only=1;
854             curarg++;
855             continue;
856         }
857         if (!strncmp(argv[curarg], "--filter", 3)) {
858             curarg ++;
859             if (curarg < argc) {
860                 name_filter = (u8 *) argv[curarg];
861                 curarg ++;
862                 continue;
863             }
864             fatal("Missing filter string after --filter\n");
865         }
866         
867
868     usage:
869         fprintf(stderr, 
870           "cpelatency --input-file <filename> [--output-file <filename>]\n");
871         fprintf(stderr, 
872           "          [--start-event <decimal>] [--verbose]\n");
873         fprintf(stderr, 
874           "          [--end-event <decimal>]\n");
875         fprintf(stderr, 
876           "          [--max-time-sort(default) | --max-occurrence-sort |\n");
877
878         fprintf(stderr, 
879           "           --name-sort-sort] [--kernel-included]\n");
880
881         fprintf(stderr, 
882           "          [--summary-stats-only] [--scatterplot]\n");
883
884         fprintf(stderr, "%s\n", version);
885         exit(1);
886     }
887
888     if (cpel_file == 0)
889         goto usage;
890
891     cpel = mapfile(cpel_file);
892     if (cpel == 0) {
893         fprintf(stderr, "Couldn't map %s...\n", cpel_file);
894         exit(1);
895     }
896
897     if (!outputfile) {
898         ofp = fdopen(1, "w");
899         if (ofp == NULL) {
900             fprintf(stderr, "Couldn't fdopen(1)?\n");
901             exit(1);
902         }
903     } else {
904         ofp = fopen(outputfile, "w");
905         if (ofp == NULL) {
906             fprintf(stderr, "Couldn't create %s...\n", outputfile);
907             exit(1);
908         }
909     }
910
911     the_strtab_hash = hash_create_string (0, sizeof (uword));
912     the_evtdef_hash = hash_create (0, sizeof (uword));
913     the_trackdef_hash = hash_create (0, sizeof (uword));
914     the_pidtid_hash = hash_create_string (0, sizeof(uword));
915
916     if (cpel_dump((u8 *)cpel, verbose, ofp)) {
917         if (outputfile)
918             unlink(outputfile);
919     }
920
921     compute_state_statistics(verbose, ofp);
922     sort_state_statistics(sort_type, ofp);
923     print_state_statistics(verbose, ofp);
924
925     fclose(ofp);
926     return(0);
927 }