Reorganize source tree to use single autotools instance
[vpp.git] / src / tools / perftool / cpeldump.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
36 char *time_format = "%.03d:%.02d:%.02d:%.03d:%.03d ";
37 static char version[] = "cpeldump 2.0";
38
39 #define USEC_PER_MS 1000LL
40 #define USEC_PER_SECOND (1000*USEC_PER_MS)
41 #define USEC_PER_MINUTE (60*USEC_PER_SECOND)
42 #define USEC_PER_HOUR (60*USEC_PER_MINUTE)
43
44 uword *the_strtab_hash; /* (name, base-VA) hash of all string tables */
45 uword *the_evtdef_hash; /* (event-id, event-definition) hash */
46 uword *the_trackdef_hash; /* (track-id, track-definition) hash */
47
48 int widest_name_format=5;
49 int widest_track_format=5;
50
51 typedef struct bound_event_ {
52     u32 event_code;
53     u8  *event_str;
54     u8  *datum_str;
55     u32  is_strtab_ref;
56 } bound_event_t;
57
58 bound_event_t *bound_events;
59
60 typedef struct bound_track_ {
61     u32 track;
62     u8  *track_str;
63 } bound_track_t;
64
65 bound_track_t *bound_tracks;
66
67 void fatal(char *s)
68 {
69     fprintf(stderr, "%s", s);
70     exit(1);
71 }
72
73 typedef enum {
74     PASS1=1,
75     PASS2=2,
76 } pass_t;
77
78 typedef struct {
79     int (*pass1)(cpel_section_header_t *, int, FILE *);
80     int (*pass2)(cpel_section_header_t *, int, FILE *);
81 } section_processor_t;
82
83 int bad_section(cpel_section_header_t *sh, int verbose, FILE *ofp)
84 {
85     fprintf(ofp, "Bad (type 0) section, skipped...\n");
86     return(0);
87 }
88
89 int noop_pass(cpel_section_header_t *sh, int verbose, FILE *ofp)
90 {
91     return(0);
92 }
93
94 int strtab_pass1(cpel_section_header_t *sh, int verbose, FILE *ofp)
95 {
96     uword *p;
97     u8 *strtab_data_area = (u8 *)(sh+1);
98     
99     /* Multiple string tables with the same name are Bad... */
100     p = hash_get_mem(the_strtab_hash, strtab_data_area);
101     if (p) {
102         fprintf(ofp, "Duplicate string table name %s", strtab_data_area);
103     }
104     /*
105      * Looks funny, but we really do want key = first string in the
106      * table, value = address(first string in the table) 
107      */
108     hash_set_mem(the_strtab_hash, strtab_data_area, strtab_data_area);
109     if (verbose) {
110         fprintf(stderr, "String Table %s\n", strtab_data_area);
111     }
112     return(0);
113 }
114
115 int evtdef_pass1(cpel_section_header_t *sh, int verbose, FILE *ofp)
116 {
117     int i, nevents;
118     event_definition_section_header_t *edh;
119     event_definition_t *ep;
120     u8 *this_strtab;
121     u32 event_code;
122     uword *p;
123     bound_event_t *bp;
124     int thislen;
125
126     edh = (event_definition_section_header_t *)(sh+1);
127     nevents = ntohl(edh->number_of_event_definitions);
128     
129     if (verbose) {
130         fprintf(stderr, "Event Definition Section: %d definitions\n",
131                 nevents);
132     }
133
134     p = hash_get_mem(the_strtab_hash, edh->string_table_name);
135     if (!p) {
136         fprintf(ofp, "Fatal: couldn't find string table\n");
137         return(1);
138     }
139     this_strtab = (u8 *)p[0];
140
141     ep = (event_definition_t *)(edh+1);
142     
143     for (i = 0; i < nevents; i++) {
144         event_code = ntohl(ep->event);
145         p = hash_get(the_evtdef_hash, event_code);
146         if (p) {
147             fprintf(ofp, "Event %d redefined, retain first definition\n",
148                     event_code);
149             continue;
150         }
151         vec_add2(bound_events, bp, 1);
152         bp->event_code = event_code;
153         bp->event_str = this_strtab + ntohl(ep->event_format);
154         bp->datum_str = this_strtab + ntohl(ep->datum_format);
155         bp->is_strtab_ref = 0;
156         /* Decide if the datum format is a %s format => strtab reference */
157         {
158             int j;
159             int seen_percent=0;
160
161             for (j = 0; j < strlen((char *)bp->datum_str); j++) {
162                 if (bp->datum_str[j] == '%'){
163                     seen_percent=1;
164                     continue;
165                 }
166                 if (seen_percent && bp->datum_str[j] == 's') {
167                     bp->is_strtab_ref = 1;
168                 }
169             }
170         }
171         
172         hash_set(the_evtdef_hash, event_code, bp - bound_events);
173
174         thislen = strlen((char *)bp->event_str);
175         if (thislen > widest_name_format)
176             widest_name_format = thislen;
177
178         ep++;
179     }
180     return (0);
181 }
182
183 int trackdef_pass1(cpel_section_header_t *sh, int verbose, FILE *ofp)
184 {
185     int i, nevents;
186     track_definition_section_header_t *tdh;
187     track_definition_t *tp;
188     u8 *this_strtab;
189     u32 track_code;
190     uword *p;
191     bound_track_t *btp;
192     int thislen;
193
194     tdh = (track_definition_section_header_t *)(sh+1);
195     nevents = ntohl(tdh->number_of_track_definitions);
196     
197     if (verbose) {
198         fprintf(stderr, "Track Definition Section: %d definitions\n",
199                 nevents);
200     }
201
202     p = hash_get_mem(the_strtab_hash, tdh->string_table_name);
203     if (!p) {
204         fprintf(ofp, "Fatal: couldn't find string table\n");
205         return(1);
206     }
207     this_strtab = (u8 *)p[0];
208
209     tp = (track_definition_t *)(tdh+1);
210     
211     for (i = 0; i < nevents; i++) {
212         track_code = ntohl(tp->track);
213         p = hash_get(the_trackdef_hash, track_code);
214         if (p) {
215             fprintf(ofp, "track %d redefined, retain first definition\n",
216                     track_code);
217             continue;
218         }
219         vec_add2(bound_tracks, btp, 1);
220         btp->track = track_code;
221         btp->track_str = this_strtab + ntohl(tp->track_format);
222         hash_set(the_trackdef_hash, track_code, btp - bound_tracks);
223
224         thislen = strlen((char *)btp->track_str);
225         if (thislen > widest_track_format)
226             widest_track_format = thislen;
227         tp++;
228     }
229     return (0);
230 }
231
232 int unsupported_pass (cpel_section_header_t *sh, int verbose, FILE *ofp)
233 {
234     if (verbose) {
235         fprintf(stderr, "Unsupported type %d section\n",
236                 ntohl(sh->section_type));
237     }
238     return(0);
239 }
240
241 int event_pass2(cpel_section_header_t *sh, int verbose, FILE *ofp)
242 {
243     event_section_header_t *eh;
244     f64 ticks_per_us;
245     u32 event_code, track_code;
246     u64 starttime = 0xFFFFFFFFFFFFFFFFULL;
247     int nevents;
248     int i;
249     uword *p;
250     event_entry_t *ep;
251     u64 now;
252     u64 delta;
253     u32 hours, minutes, seconds, msec, usec;
254     u32 time0, time1;
255     double d;
256     bound_event_t *bp;
257     bound_event_t generic_event;
258     bound_track_t *tp=0;
259     bound_track_t generic_track;
260     u32 last_track_code;
261     u8 *s, *evtpad, *trackpad;
262     u8 *this_strtab;
263
264     generic_event.event_str = (u8 *)"%d";
265     generic_event.datum_str = (u8 *)"0x%08x";
266     generic_event.is_strtab_ref = 0;
267
268     generic_track.track_str = (u8 *)"%d";
269     last_track_code = 0xdeadbeef;
270
271     eh = (event_section_header_t *)(sh+1);
272     nevents = ntohl(eh->number_of_events);
273     ticks_per_us = ((double)ntohl(eh->clock_ticks_per_second)) / 1e6;
274
275     if (verbose) {
276         fprintf(stderr, "Event section: %d events, %.3f ticks_per_us\n", 
277                 nevents, ticks_per_us);
278     }
279
280     ep = (event_entry_t *)(eh+1);
281
282     p = hash_get_mem(the_strtab_hash, eh->string_table_name);
283     if (!p) {
284         fprintf(ofp, "Fatal: couldn't find string table\n");
285         return(1);
286     }
287     this_strtab = (u8 *)p[0];
288
289     evtpad = format(0, "%%-%ds ", widest_name_format);
290     vec_add1(evtpad, 0);
291     trackpad = format(0, "%%-%ds ", widest_track_format);
292     vec_add1(trackpad, 0);
293
294     for (i = 0; i < nevents; i++) {
295         time0 = ntohl (ep->time[0]);
296         time1 = ntohl (ep->time[1]);
297
298         now = (((u64) time0)<<32) | time1;
299         
300         /* Convert from bus ticks to usec */
301         d = now;
302         d /= ticks_per_us;
303
304         now = d;
305
306         if (starttime == 0xFFFFFFFFFFFFFFFFULL)
307             starttime = now;
308         
309         delta = now - starttime;
310
311         /* Delta = time since first event, in usec */
312
313         hours = delta / USEC_PER_HOUR;
314         if (hours) 
315             delta -= ((u64) hours * USEC_PER_HOUR);
316         minutes = delta / USEC_PER_MINUTE;
317         if (minutes)
318             delta -= ((u64) minutes * USEC_PER_MINUTE);
319         seconds = delta / USEC_PER_SECOND;
320         if (seconds)
321             delta -= ((u64) seconds * USEC_PER_SECOND);
322         msec = delta / USEC_PER_MS;
323         if (msec)
324             delta -= ((u64) msec * USEC_PER_MS);
325
326         usec = delta;
327
328         /* Output the timestamp */
329         fprintf(ofp, time_format, hours, minutes, seconds, msec, usec);
330
331         /* output the track */
332         track_code = ntohl(ep->track);
333
334         if (track_code != last_track_code) {
335             p = hash_get(the_trackdef_hash, track_code);
336             if (p) {
337                 tp = &bound_tracks[p[0]];
338             } else {
339                 tp = &generic_track;
340             }
341         }
342         s = format(0, (char *)tp->track_str, track_code);
343         vec_add1(s, 0);
344         fprintf(ofp, (char *)trackpad, s);
345         vec_free(s);
346
347         /* output the event and datum */
348         if (0 && verbose) {
349             fprintf(stderr, "raw event code %d, raw event datum 0x%x\n",
350                     ntohl(ep->event_code), ntohl(ep->event_datum));
351         }
352
353         event_code = ntohl(ep->event_code);
354         p = hash_get(the_evtdef_hash, event_code);
355         if (p) {
356             bp = &bound_events[p[0]];
357         } else {
358             bp = &generic_event;
359         }
360         s = format(0, (char *)bp->event_str, ntohl(ep->event_code));
361         vec_add1(s, 0);
362         fprintf(ofp, (char *)evtpad, s);
363         vec_free(s);
364         if (bp->is_strtab_ref) {
365             fprintf(ofp, (char *) bp->datum_str, 
366                     &this_strtab[ntohl(ep->event_datum)]);
367         } else {
368             fprintf(ofp, (char *) bp->datum_str, ntohl(ep->event_datum));
369         }
370         fputs("\n", ofp);
371         ep++;
372     }
373     vec_free(evtpad);
374     vec_free(trackpad);
375     return(0);
376 }
377
378 /* 
379  * Note: If necessary, add passes / columns to this table to 
380  * handle section order dependencies.
381  */
382
383 section_processor_t processors[CPEL_NUM_SECTION_TYPES+1] =
384 {
385     {bad_section,       noop_pass},             /* type 0 -- f**ked */
386     {strtab_pass1,      noop_pass},             /* type 1 -- STRTAB */
387     {unsupported_pass,  noop_pass},             /* type 2 -- SYMTAB */
388     {evtdef_pass1,      noop_pass},             /* type 3 -- EVTDEF */
389     {trackdef_pass1,    noop_pass},             /* type 4 -- TRACKDEF */
390     {noop_pass,         event_pass2},           /* type 5 -- EVENTS */
391 };
392
393
394 int process_section(cpel_section_header_t *sh, int verbose, FILE *ofp,
395                     pass_t pass)
396 {
397     u32 type;
398     type = ntohl(sh->section_type);
399     int rv;
400     int (*fp)(cpel_section_header_t *, int, FILE *);
401
402     if (type > CPEL_NUM_SECTION_TYPES) {
403         fprintf(stderr, "Unknown section type %d\n", type);
404         return(1);
405     }
406     switch(pass) {
407     case PASS1:
408         fp = processors[type].pass1;
409         break;
410
411     case PASS2:
412         fp = processors[type].pass2;
413         break;
414         
415     default:
416         fprintf(stderr, "Unknown pass %d\n", pass);
417         return(1);
418     }
419
420     rv = (*fp)(sh, verbose, ofp);
421
422     return(rv);
423 }
424
425 int cpel_dump_file_header(cpel_file_header_t *fh, int verbose, FILE *ofp)
426 {
427     time_t file_time;
428
429     if (verbose) {
430         fprintf(stderr, "CPEL file: %s-endian, version %d\n",
431                 ((fh->endian_version & CPEL_FILE_LITTLE_ENDIAN) ? 
432                  "little" : "big"), 
433                 fh->endian_version & CPEL_FILE_VERSION_MASK);
434         
435         file_time = ntohl(fh->file_date);
436         
437         fprintf(stderr, "File created %s", ctime(&file_time));
438         fprintf(stderr, "File has %d sections\n", 
439                 ntohs(fh->nsections));
440     }
441
442     return(0);
443 }
444
445
446 int cpel_dump(u8 *cpel, int verbose, FILE *ofp)
447 {
448     cpel_file_header_t *fh;
449     cpel_section_header_t *sh;
450     u16 nsections;
451     u32 section_size;
452     int i;
453
454     /* First, the file header */
455     fh = (cpel_file_header_t *)cpel;
456     if (fh->endian_version != CPEL_FILE_VERSION) {
457         if (fh->endian_version & CPEL_FILE_LITTLE_ENDIAN) {
458             fprintf(stderr, "Little endian data format not supported\n");
459             return(1);
460         }
461         fprintf(stderr, "Unsupported file version 0x%x\n", 
462                 fh->endian_version);
463         return(1);
464     }
465     cpel_dump_file_header(fh, verbose, ofp);
466     nsections = ntohs(fh->nsections);
467
468     /*
469      * Take two passes through the file. PASS1 builds
470      * data structures, PASS2 actually dumps the file.
471      * Just in case the sections are in an unobvious order.
472      */
473     sh = (cpel_section_header_t *)(fh+1);
474     for (i = 0; i < nsections; i++) {
475         section_size = ntohl(sh->data_length);
476
477         if(verbose) {
478             fprintf(stderr, 
479                     "Section type %d, size %d\n", ntohl(sh->section_type),
480                     section_size);
481         }
482
483         if(process_section(sh, verbose, ofp, PASS1))
484             return(1);
485
486         sh++;
487         sh = (cpel_section_header_t *)(((u8 *)sh)+section_size);
488     }
489
490     sh = (cpel_section_header_t *)(fh+1);
491     for (i = 0; i < nsections; i++) {
492         if(process_section(sh, verbose, ofp, PASS2))
493             return(1);
494         section_size = ntohl(sh->data_length);
495         sh++;
496         sh = (cpel_section_header_t *)(((u8 *)sh)+section_size);
497     }
498     return(0);
499 }
500
501
502 char *mapfile (char *file)
503 {
504     struct stat statb;
505     char *rv;
506     int maphfile;
507     size_t mapfsize;
508     
509     maphfile = open (file, O_RDONLY);
510
511     if (maphfile < 0)
512     {
513         fprintf (stderr, "Couldn't read %s, skipping it...\n", file);
514         return (NULL);
515     }
516
517     if (fstat (maphfile, &statb) < 0)
518     {
519         fprintf (stderr, "Couldn't get size of %s, skipping it...\n", file);
520         return (NULL);
521     }
522
523     /* Don't try to mmap directories, FIFOs, semaphores, etc. */
524     if (! (statb.st_mode & S_IFREG)) {
525         fprintf (stderr, "%s is not a regular file, skipping it...\n", file);
526         return (NULL);
527     }
528
529     mapfsize = statb.st_size;
530
531     if (mapfsize < 3)
532     {
533         fprintf (stderr, "%s zero-length, skipping it...\n", file);
534         close (maphfile);
535         return (NULL);
536     }
537
538     rv = mmap (0, mapfsize, PROT_READ, MAP_SHARED, maphfile, 0);
539
540     if (rv == 0)
541     {
542         fprintf (stderr, "%s problem mapping, I quit...\n", file);
543         exit (-1);
544     }
545     close (maphfile);
546     return (rv);
547 }
548
549 /*
550  * main 
551  */
552 int main (int argc, char **argv)
553 {
554     char *cpel_file = 0;
555     char *outputfile = 0;
556     FILE *ofp;
557     char *cpel;
558     int verbose=0;
559     int curarg=1;
560
561     while (curarg < argc) {
562         if (!strncmp(argv[curarg], "--input-file", 3)) {
563             curarg++;
564             if (curarg < argc) {
565                 cpel_file = argv[curarg];
566                 curarg++;
567                 continue;
568             }
569             fatal("Missing filename after --input-file\n");
570         }
571         if (!strncmp(argv[curarg], "--output-file", 3)) {
572             curarg ++;
573             if (curarg < argc) {
574                 outputfile = argv[curarg];
575                 curarg ++;
576                 continue;
577             }
578             fatal("Missing filename after --output-file\n");
579         }
580         if (!strncmp(argv[curarg], "--verbose", 3)) {
581             curarg++;
582             verbose = 1;
583             continue;
584         }
585
586     usage:
587         fprintf(stderr, 
588           "cpeldump --input-file <filename> [--output-file <filename>]\n");
589         fprintf(stderr, "%s\n", version);
590         exit(1);
591     }
592
593     if (cpel_file == 0)
594         goto usage;
595
596     cpel = mapfile(cpel_file);
597     if (cpel == 0) {
598         fprintf(stderr, "Couldn't map %s...\n", cpel_file);
599         exit(1);
600     }
601
602     if (!outputfile) {
603         ofp = fdopen(1, "w");
604         if (ofp == NULL) {
605             fprintf(stderr, "Couldn't fdopen(1)?\n");
606             exit(1);
607         }
608     } else {
609         ofp = fopen(outputfile, "w");
610         if (ofp == NULL) {
611             fprintf(stderr, "Couldn't create %s...\n", outputfile);
612             exit(1);
613         }
614     }
615
616     the_strtab_hash = hash_create_string (0, sizeof (uword));
617     the_evtdef_hash = hash_create (0, sizeof (uword));
618     the_trackdef_hash = hash_create (0, sizeof (uword));
619
620 #ifdef TEST_TRACK_INFO
621     {
622         bound_track_t *btp;
623         vec_add2(bound_tracks, btp, 1);
624         btp->track = 0;
625         btp->track_str = "cpu %d";
626         hash_set(the_trackdef_hash, 0, btp - bound_tracks);
627         hash_set(the_trackdef_hash, 1, btp - bound_tracks);
628     }
629 #endif
630
631     if (cpel_dump((u8 *)cpel, verbose, ofp)) {
632         if (outputfile)
633             unlink(outputfile);
634     }
635
636     fclose(ofp);
637     return(0);
638 }