misc: sprintf be gone
[vpp.git] / src / tools / g2 / events.c
1 /*
2  *------------------------------------------------------------------
3  * Copyright (c) 2005-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 <stdlib.h>
18 #include <unistd.h>
19 #include <sys/stat.h>
20 #include <sys/fcntl.h>
21 #include <sys/mman.h>
22 #include <arpa/inet.h>
23 #include <stdio.h>
24 #include <gtk/gtk.h>
25 #include "g2.h"
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <fcntl.h>
29 #include <string.h>
30
31 /*
32  * globals
33  */
34 boolean g_little_endian;
35 event_t *g_events;
36 ulong g_nevents;
37 pid_sort_t *g_pids;
38 pid_sort_t *g_original_pids;
39 int g_npids;
40 pid_data_t *g_pid_data_list;
41
42 /*
43  * locals
44  */
45 pid_data_t **s_pidhash;
46
47 /*
48  * config parameters
49  */
50
51 double ticks_per_ns=1000.0;
52 boolean ticks_per_ns_set;
53
54 /****************************************************************************
55 * event_init
56 ****************************************************************************/
57
58 void event_init(void)
59 {
60     ulong endian;
61     char *ep;
62     char *askstr;
63     int tmp;
64
65     ep = (char *)&endian;
66     endian = 0x12345678;
67     if (*ep != 0x12)
68         g_little_endian = TRUE;
69     else
70         g_little_endian = FALSE;
71
72     askstr = getprop("dont_ask_ticks_per_ns_initially");
73
74     if (askstr && (*askstr == 't' || *askstr == 'T')) {
75         tmp = atol(getprop_default("ticks_per_ns", 0));
76         if (tmp > 0) {
77             ticks_per_ns = tmp;
78             ticks_per_ns_set = TRUE;
79         }
80     }
81 }
82
83 /****************************************************************************
84 * find_or_add_pid
85 ****************************************************************************/
86
87 pid_data_t *find_or_add_pid (ulong pid)
88 {
89     pid_data_t *pp;
90     ulong bucket;
91
92     bucket = pid % PIDHASH_NBUCKETS;
93
94     pp = s_pidhash[bucket];
95
96     if (pp == 0) {
97         pp = g_malloc0(sizeof(pid_data_t));
98         pp->pid_value = pid;
99         s_pidhash[bucket] = pp;
100         g_npids++;
101         return(pp);
102     }
103     while (pp) {
104         if (pp->pid_value == pid)
105             return(pp);
106         pp = pp->next;
107     }
108
109     pp = g_malloc0(sizeof(pid_data_t));
110     pp->pid_value = pid;
111     pp->next = s_pidhash[bucket];
112     s_pidhash[bucket] = pp;
113     g_npids++;
114     return(pp);
115 }
116
117 /****************************************************************************
118 * pid_cmp
119 ****************************************************************************/
120
121 int pid_cmp(const void *a1, const void *a2)
122 {
123     pid_sort_t *p1 = (pid_sort_t *)a1;
124     pid_sort_t *p2 = (pid_sort_t *)a2;
125
126     if (p1->pid_value < p2->pid_value)
127         return(-1);
128     else if (p1->pid_value == p2->pid_value)
129         return(0);
130     else
131         return(1);
132 }
133
134 /****************************************************************************
135 * make_sorted_pid_vector
136 ****************************************************************************/
137
138 static void make_sorted_pid_vector(void)
139 {
140     pid_data_t *pp;
141     pid_data_t **p_previous;
142     pid_sort_t *psp;
143     int i;
144
145     psp = g_pids = g_malloc0(sizeof(pid_sort_t)*g_npids);
146
147     for (i = 0; i < PIDHASH_NBUCKETS; i++) {
148         pp = s_pidhash[i];
149         while(pp) {
150             psp->pid = pp;
151             psp->pid_value = pp->pid_value;
152             psp++;
153             pp = pp->next;
154         }
155     }
156
157     qsort(&g_pids[0], g_npids, sizeof(pid_sort_t), pid_cmp);
158
159     /* put the sort order into the pid objects */
160     psp = g_pids;
161
162     /*
163      * This is rather gross.
164      *
165      * We happen to know that whenever this function is called, the hash table
166      * structure itself is immediately torn down. So the "next" pointers in the
167      * pid_data_t elements are about to become useless.
168      *
169      * So we re-use them, to link all the pid_data_t elements together into a
170      * single unified linked list, with g_pid_data_list pointing to the head.
171      * This means we can walk all the pid_data_t objects if we really want to.
172      * Reading snapshots from disk is one example.
173      *
174      * Alternatively we could just leave the hash table in place; this is
175      * far nicer, but as it happens, trading O(n) lookups for O(1) lookups
176      * isn't actually a problem for the restricted post-tear-down usage. So for
177      * now we take the memory savings and swap our hash table for a list.
178      */
179     p_previous = &g_pid_data_list;
180     for (i = 0; i < g_npids; i++) {
181         pp = psp->pid;
182         pp->pid_index = i;
183         *p_previous = pp;
184         p_previous = &pp->next;
185         psp++;
186     }
187     *p_previous = NULL;
188
189     /*
190      * Squirrel away original (sorted) vector, so we can
191      * toggle between "chase" mode, snapshots, and the original
192      * display method on short notice
193      */
194     g_original_pids = g_malloc0(sizeof(pid_sort_t)*g_npids);
195     memcpy (g_original_pids, g_pids, sizeof(pid_sort_t)*g_npids);
196 }
197
198 /****************************************************************************
199 * read_events
200 ****************************************************************************/
201
202 void read_events(char *filename)
203 {
204     ulong *ulp;
205     ulong size;
206     event_t *ep;
207     raw_event_t *rep;
208     ulonglong start_time=0ULL;
209     ulonglong low_time;
210     boolean once=TRUE;
211     int i;
212     char tmpbuf [128];
213
214     ulp = (ulong *)mapfile(filename, &size);
215
216     if (ulp == NULL) {
217         snprintf(tmpbuf, sizeof(tmpbuf), "Couldn't open %s\n", filename);
218         infobox("Read Event Log Failure", tmpbuf);
219         return;
220     }
221
222     g_nevents = ntohl(*ulp);
223
224     if (size != (g_nevents*sizeof(raw_event_t) + sizeof(g_nevents))) {
225         snprintf(tmpbuf, sizeof(tmpbuf),
226                  "%s was damaged, or isn't an event log.\n", filename);
227         infobox("Bad Input File", tmpbuf);
228         g_nevents = 0;
229         unmapfile((char *)ulp, size);
230         return;
231     }
232
233     rep = (raw_event_t *)(ulp+1);
234
235     if (g_events)
236         g_free(g_events);
237
238     g_events = (event_t *)g_malloc(g_nevents * sizeof(event_t));
239     ep = g_events;
240
241     while (g_npids > 0) {
242         g_free((g_pids + g_npids-1)->pid);
243         g_npids--;
244     }
245     if (g_pids) {
246         g_free(g_pids);
247         g_free(g_original_pids);
248         g_pids = 0;
249         g_original_pids = 0;
250     }
251
252     s_pidhash = (pid_data_t **)g_malloc0(
253         PIDHASH_NBUCKETS*sizeof(pid_data_t *));
254
255     /* $$$ add a SEGV handler... */
256     for (i = 0; i < g_nevents; i++) {
257         if (once) {
258             once = FALSE;
259             start_time = ((ulonglong)ntohl(rep->time[0]));
260             start_time <<= 32;
261             low_time = ntohl(rep->time[1]);
262             low_time &= 0xFFFFFFFF;
263             start_time |= low_time;
264             ep->time = 0LL;
265         } else {
266             ep->time = ((ulonglong)ntohl(rep->time[0]));
267             ep->time <<= 32;
268             low_time = ntohl(rep->time[1]);
269             low_time &= 0xFFFFFFFF;
270             ep->time |= low_time;
271             ep->time -= start_time;
272             ep->time /= ticks_per_ns;
273         }
274         ep->code = ntohl(rep->code);
275         ep->pid = find_or_add_pid(ntohl(rep->pid));
276         ep->datum = ntohl(rep->datum);
277         ep->flags = 0;
278         ep++;
279         rep++;
280     }
281
282     unmapfile((char *)ulp, size);
283
284     make_sorted_pid_vector();
285     g_free(s_pidhash);
286     s_pidhash = 0;
287
288     /* Give the view-1 world a chance to reset a few things... */
289     view1_read_events_callback();
290 }
291
292 static event_t *add_ep;
293
294 /****************************************************************************
295 * cpel_event_init
296 ****************************************************************************/
297 void cpel_event_init (ulong nevents)
298 {
299     g_nevents = nevents;
300     if (g_events)
301         g_free(g_events);
302     add_ep = g_events = (event_t *)g_malloc(g_nevents * sizeof(event_t));
303     while (g_npids > 0) {
304         g_free((g_pids + g_npids-1)->pid);
305         g_npids--;
306     }
307     if (g_pids) {
308         g_free(g_pids);
309         g_free(g_original_pids);
310         g_pids = 0;
311         g_original_pids = 0;
312     }
313     s_pidhash = (pid_data_t **)g_malloc0(
314         PIDHASH_NBUCKETS*sizeof(pid_data_t *));
315 }
316
317 /****************************************************************************
318 * add_cpel_event
319 ****************************************************************************/
320
321 void add_cpel_event(ulonglong delta, ulong track, ulong event, ulong datum)
322 {
323     event_t *ep;
324
325     ep = add_ep++;
326     ep->time = delta;
327     ep->pid = find_or_add_pid(track);
328     ep->code = event;
329     ep->datum = datum;
330     ep->flags = 0;
331 }
332
333 /****************************************************************************
334 * add_clib_event
335 ****************************************************************************/
336
337 void add_clib_event(double delta, unsigned short track,
338                     unsigned short event, unsigned int index)
339 {
340     event_t *ep;
341
342     ep = add_ep++;
343     ep->time = (ulonglong) (delta * 1e9); /* time in integer nanoseconds */
344     ep->pid = find_or_add_pid(track);
345     ep->code = event;
346     ep->datum = index;
347     ep->flags = EVENT_FLAG_CLIB;
348 }
349
350 /****************************************************************************
351 * cpel_event_finalize
352 ****************************************************************************/
353
354 void cpel_event_finalize(void)
355 {
356     make_sorted_pid_vector();
357     g_free(s_pidhash);
358     s_pidhash = 0;
359
360     /* Give the view-1 world a chance to reset a few things... */
361     view1_read_events_callback();
362 }
363
364 /****************************************************************************
365 * mapfile
366 ****************************************************************************/
367
368 char *mapfile (char *file, ulong *sizep)
369 {
370     struct stat statb;
371     char *rv;
372     int maphfile;
373     size_t mapfsize;
374
375     maphfile = open (file, O_RDONLY);
376
377     if (maphfile < 0)
378         return (NULL);
379
380     if (fstat (maphfile, &statb) < 0) {
381         return (NULL);
382     }
383
384     /* Don't try to mmap directories, FIFOs, semaphores, etc. */
385     if (! (statb.st_mode & S_IFREG)) {
386         return (NULL);
387     }
388
389     mapfsize = statb.st_size;
390
391     if (mapfsize < 3) {
392         close (maphfile);
393         return (NULL);
394     }
395
396     rv = mmap (0, mapfsize, PROT_READ, MAP_SHARED, maphfile, 0);
397
398     if (rv == 0) {
399         g_error ("%s mapping problem, I quit...\n", file);
400     }
401
402     close (maphfile);
403
404     if (madvise (rv, mapfsize, MADV_SEQUENTIAL) < 0) {
405         return (rv);
406     }
407
408     if (sizep) {
409         *sizep = mapfsize;
410     }
411     return (rv);
412 }
413
414 /****************************************************************************
415 * unmapfile
416 ****************************************************************************/
417
418 boolean unmapfile (char *addr, ulong size)
419 {
420     if (munmap (addr, size) < 0) {
421         g_warning("Unmap error, addr 0x%lx size 0x%x\n",
422                   (unsigned long) addr, (unsigned int)size);
423         return(FALSE);
424     }
425     return(TRUE);
426 }
427
428 /****************************************************************************
429 * find_event_index
430 * Binary search for first event whose time is >= t
431 ****************************************************************************/
432
433 int find_event_index (ulonglong t)
434 {
435     int index, bottom, top;
436     event_t *ep;
437
438     bottom = g_nevents-1;
439     top = 0;
440
441     while (1) {
442         index = (bottom + top) / 2;
443
444         ep = (g_events + index);
445
446         if (ep->time == t)
447             return(index);
448
449         if (top >= bottom) {
450             while (index > 0 && ep->time > t) {
451                 ep--;
452                 index--;
453             }
454             while (index < g_nevents && ep->time < t) {
455                 ep++;
456                 index++;
457             }
458             return(index);
459         }
460
461         if (ep->time < t)
462             top = index + 1;
463         else
464             bottom = index - 1;
465     }
466 }
467
468 /****************************************************************************
469 * events_about
470 ****************************************************************************/
471
472 void events_about (char *tmpbuf)
473 {
474     snprintf(tmpbuf+strlen(tmpbuf), 1024 - strlen(tmpbuf),
475              "%d total events, %.3f ticks per us\n",
476              (int)g_nevents, ticks_per_ns);
477 }