dpdk: Add support for Mellanox ConnectX-4 devices
[vpp.git] / vppinfra / vppinfra / elog.c
1 /*
2  * Copyright (c) 2015 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 /*
16   Copyright (c) 2005,2009 Eliot Dresselhaus
17
18   Permission is hereby granted, free of charge, to any person obtaining
19   a copy of this software and associated documentation files (the
20   "Software"), to deal in the Software without restriction, including
21   without limitation the rights to use, copy, modify, merge, publish,
22   distribute, sublicense, and/or sell copies of the Software, and to
23   permit persons to whom the Software is furnished to do so, subject to
24   the following conditions:
25
26   The above copyright notice and this permission notice shall be
27   included in all copies or substantial portions of the Software.
28
29   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
30   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
31   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
32   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
33   LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
34   OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
35   WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
36 */
37
38 #include <vppinfra/elog.h>
39 #include <vppinfra/cache.h>
40 #include <vppinfra/error.h>
41 #include <vppinfra/format.h>
42 #include <vppinfra/hash.h>
43 #include <vppinfra/math.h>
44
45 static inline void
46 elog_lock (elog_main_t * em)
47 {
48   if (PREDICT_FALSE (em->lock != 0))
49     while (__sync_lock_test_and_set (em->lock, 1))
50       ;
51 }
52
53 static inline void
54 elog_unlock (elog_main_t * em)
55 {
56   if (PREDICT_FALSE (em->lock != 0))
57     {
58       CLIB_MEMORY_BARRIER ();
59       *em->lock = 0;
60     }
61 }
62
63 /* Non-inline version. */
64 void *
65 elog_event_data (elog_main_t * em,
66                  elog_event_type_t * type, elog_track_t * track, u64 cpu_time)
67 {
68   return elog_event_data_inline (em, type, track, cpu_time);
69 }
70
71 static void
72 new_event_type (elog_main_t * em, uword i)
73 {
74   elog_event_type_t *t = vec_elt_at_index (em->event_types, i);
75
76   if (!em->event_type_by_format)
77     em->event_type_by_format =
78       hash_create_vec ( /* size */ 0, sizeof (u8), sizeof (uword));
79
80   hash_set_mem (em->event_type_by_format, t->format, i);
81 }
82
83 static uword
84 find_or_create_type (elog_main_t * em, elog_event_type_t * t)
85 {
86   uword *p = hash_get_mem (em->event_type_by_format, t->format);
87   uword i;
88
89   if (p)
90     i = p[0];
91   else
92     {
93       i = vec_len (em->event_types);
94       vec_add1 (em->event_types, t[0]);
95       new_event_type (em, i);
96     }
97
98   return i;
99 }
100
101 /* External function to register types. */
102 word
103 elog_event_type_register (elog_main_t * em, elog_event_type_t * t)
104 {
105   elog_event_type_t *static_type = t;
106   word l;
107
108   elog_lock (em);
109
110   l = vec_len (em->event_types);
111
112   t->type_index_plus_one = 1 + l;
113
114   ASSERT (t->format);
115
116   /* If format args are not specified try to be smart about providing defaults
117      so most of the time user does not have to specify them. */
118   if (!t->format_args)
119     {
120       uword i, l;
121       char *this_arg;
122
123       l = strlen (t->format);
124       for (i = 0; i < l; i++)
125         {
126           if (t->format[i] != '%')
127             continue;
128           if (i + 1 >= l)
129             continue;
130           if (t->format[i + 1] == '%')  /* %% */
131             continue;
132
133           switch (t->format[i + 1])
134             {
135             default:
136             case 'd':
137             case 'x':
138             case 'u':
139               this_arg = "i4";  /* size of u32 */
140               break;
141             case 'f':
142               this_arg = "f8";  /* defaults to f64 */
143               break;
144             case 's':
145               this_arg = "s0";  /* defaults to null terminated string. */
146               break;
147             }
148
149           t->format_args =
150             (char *) format ((u8 *) t->format_args, "%s", this_arg);
151         }
152
153       /* Null terminate. */
154       vec_add1 (t->format_args, 0);
155     }
156
157   vec_add1 (em->event_types, t[0]);
158
159   t = em->event_types + l;
160
161   /* Make copies of strings for hashing etc. */
162   if (t->function)
163     t->format = (char *) format (0, "%s %s%c", t->function, t->format, 0);
164   else
165     t->format = (char *) format (0, "%s%c", t->format, 0);
166
167   t->format_args = (char *) format (0, "%s%c", t->format_args, 0);
168
169   /* Construct string table. */
170   {
171     uword i;
172     t->n_enum_strings = static_type->n_enum_strings;
173     for (i = 0; i < t->n_enum_strings; i++)
174       {
175         if (!static_type->enum_strings[i])
176           static_type->enum_strings[i] = "MISSING";
177         vec_add1 (t->enum_strings_vector,
178                   (char *) format (0, "%s%c", static_type->enum_strings[i],
179                                    0));
180       }
181   }
182
183   new_event_type (em, l);
184   elog_unlock (em);
185
186   return l;
187 }
188
189 word
190 elog_track_register (elog_main_t * em, elog_track_t * t)
191 {
192   word l;
193
194   elog_lock (em);
195
196   l = vec_len (em->tracks);
197
198   t->track_index_plus_one = 1 + l;
199
200   ASSERT (t->name);
201
202   vec_add1 (em->tracks, t[0]);
203
204   t = em->tracks + l;
205
206   t->name = (char *) format (0, "%s%c", t->name, 0);
207
208   elog_unlock (em);
209
210   return l;
211 }
212
213 static uword
214 parse_2digit_decimal (char *p, uword * number)
215 {
216   uword i = 0;
217   u8 digits[2];
218
219   digits[0] = digits[1] = 0;
220   while (p[i] >= '0' && p[i] <= '9')
221     {
222       if (i >= 2)
223         break;
224       digits[i] = p[i] - '0';
225       i++;
226     }
227
228   if (i >= 1 && i <= 2)
229     {
230       if (i == 1)
231         *number = digits[0];
232       else
233         *number = 10 * digits[0] + digits[1];
234       return i;
235     }
236   else
237     return 0;
238 }
239
240 static u8 *
241 fixed_format (u8 * s, char *fmt, char *result, uword * result_len)
242 {
243   char *f = fmt;
244   char *percent;
245   uword l = 0;
246
247   while (1)
248     {
249       if (f[0] == 0)
250         break;
251       if (f[0] == '%' && f[1] != '%')
252         break;
253       f++;
254     }
255   if (f > fmt)
256     vec_add (s, fmt, f - fmt);
257
258   if (f[0] != '%')
259     goto done;
260
261   /* Skip percent. */
262   percent = f++;
263
264   /* Skip possible +-= justification. */
265   f += f[0] == '+' || f[0] == '-' || f[0] == '=';
266
267   /* Skip possible X.Y width. */
268   while ((f[0] >= '0' && f[0] <= '9') || f[0] == '.')
269     f++;
270
271   /* Skip wlL as in e.g. %Ld. */
272   f += f[0] == 'w' || f[0] == 'l' || f[0] == 'L';
273
274   /* Finally skip format letter. */
275   f += f[0] != 0;
276
277   ASSERT (*result_len > f - percent);
278   l = clib_min (f - percent, *result_len - 1);
279   clib_memcpy (result, percent, l);
280   result[l] = 0;
281
282 done:
283   *result_len = f - fmt;
284   return s;
285 }
286
287 u8 *
288 format_elog_event (u8 * s, va_list * va)
289 {
290   elog_main_t *em = va_arg (*va, elog_main_t *);
291   elog_event_t *e = va_arg (*va, elog_event_t *);
292   elog_event_type_t *t;
293   char *a, *f;
294   void *d = (u8 *) e->data;
295   char arg_format[64];
296
297   t = vec_elt_at_index (em->event_types, e->type);
298
299   f = t->format;
300   a = t->format_args;
301   while (1)
302     {
303       uword n_bytes = 0, n_digits, f_bytes = 0;
304
305       f_bytes = sizeof (arg_format);
306       s = fixed_format (s, f, arg_format, &f_bytes);
307       f += f_bytes;
308
309       if (a == 0 || a[0] == 0)
310         {
311           /* Format must also be at end. */
312           ASSERT (f[0] == 0);
313           break;
314         }
315
316       /* Don't go past end of event data. */
317       ASSERT (d < (void *) (e->data + sizeof (e->data)));
318
319       n_digits = parse_2digit_decimal (a + 1, &n_bytes);
320       switch (a[0])
321         {
322         case 'i':
323         case 't':
324         case 'T':
325           {
326             u32 i = 0;
327             u64 l = 0;
328
329             if (n_bytes == 1)
330               i = ((u8 *) d)[0];
331             else if (n_bytes == 2)
332               i = clib_mem_unaligned (d, u16);
333             else if (n_bytes == 4)
334               i = clib_mem_unaligned (d, u32);
335             else if (n_bytes == 8)
336               l = clib_mem_unaligned (d, u64);
337             else
338               ASSERT (0);
339             if (a[0] == 't')
340               {
341                 char *e =
342                   vec_elt (t->enum_strings_vector, n_bytes == 8 ? l : i);
343                 s = format (s, arg_format, e);
344               }
345             else if (a[0] == 'T')
346               {
347                 char *e =
348                   vec_elt_at_index (em->string_table, n_bytes == 8 ? l : i);
349                 s = format (s, arg_format, e);
350               }
351             else if (n_bytes == 8)
352               s = format (s, arg_format, l);
353             else
354               s = format (s, arg_format, i);
355           }
356           break;
357
358         case 'f':
359           {
360             f64 x = 0;
361             if (n_bytes == 4)
362               x = clib_mem_unaligned (d, f32);
363             else if (n_bytes == 8)
364               x = clib_mem_unaligned (d, f64);
365             else
366               ASSERT (0);
367             s = format (s, arg_format, x);
368           }
369           break;
370
371         case 's':
372           s = format (s, arg_format, d);
373           if (n_bytes == 0)
374             n_bytes = strlen (d) + 1;
375           break;
376
377         default:
378           ASSERT (0);
379           break;
380         }
381
382       ASSERT (n_digits > 0 && n_digits <= 2);
383       a += 1 + n_digits;
384       d += n_bytes;
385     }
386
387   return s;
388 }
389
390 u8 *
391 format_elog_track (u8 * s, va_list * va)
392 {
393   elog_main_t *em = va_arg (*va, elog_main_t *);
394   elog_event_t *e = va_arg (*va, elog_event_t *);
395   elog_track_t *t = vec_elt_at_index (em->tracks, e->track);
396   return format (s, "%s", t->name);
397 }
398
399 void
400 elog_time_now (elog_time_stamp_t * et)
401 {
402   u64 cpu_time_now, os_time_now_nsec;
403
404 #ifdef CLIB_UNIX
405   {
406 #include <sys/syscall.h>
407     struct timespec ts;
408     syscall (SYS_clock_gettime, CLOCK_REALTIME, &ts);
409     cpu_time_now = clib_cpu_time_now ();
410     os_time_now_nsec = 1e9 * ts.tv_sec + ts.tv_nsec;
411   }
412 #else
413   cpu_time_now = clib_cpu_time_now ();
414   os_time_now_nsec = 0;
415 #endif
416
417   et->cpu = cpu_time_now;
418   et->os_nsec = os_time_now_nsec;
419 }
420
421 always_inline i64
422 elog_time_stamp_diff_os_nsec (elog_time_stamp_t * t1, elog_time_stamp_t * t2)
423 {
424   return (i64) t1->os_nsec - (i64) t2->os_nsec;
425 }
426
427 always_inline i64
428 elog_time_stamp_diff_cpu (elog_time_stamp_t * t1, elog_time_stamp_t * t2)
429 {
430   return (i64) t1->cpu - (i64) t2->cpu;
431 }
432
433 always_inline f64
434 elog_nsec_per_clock (elog_main_t * em)
435 {
436   return ((f64) elog_time_stamp_diff_os_nsec (&em->serialize_time,
437                                               &em->init_time)
438           / (f64) elog_time_stamp_diff_cpu (&em->serialize_time,
439                                             &em->init_time));
440 }
441
442 void
443 elog_alloc (elog_main_t * em, u32 n_events)
444 {
445   if (em->event_ring)
446     vec_free (em->event_ring);
447
448   /* Ring size must be a power of 2. */
449   em->event_ring_size = n_events = max_pow2 (n_events);
450
451   /* Leave an empty ievent at end so we can always speculatively write
452      and event there (possibly a long form event). */
453   vec_resize_aligned (em->event_ring, n_events, CLIB_CACHE_LINE_BYTES);
454 }
455
456 void
457 elog_init (elog_main_t * em, u32 n_events)
458 {
459   memset (em, 0, sizeof (em[0]));
460
461   em->lock = 0;
462
463   if (n_events > 0)
464     elog_alloc (em, n_events);
465
466   clib_time_init (&em->cpu_timer);
467
468   em->n_total_events_disable_limit = ~0;
469
470   /* Make track 0. */
471   em->default_track.name = "default";
472   elog_track_register (em, &em->default_track);
473
474   elog_time_now (&em->init_time);
475 }
476
477 /* Returns number of events in ring and start index. */
478 static uword
479 elog_event_range (elog_main_t * em, uword * lo)
480 {
481   uword l = em->event_ring_size;
482   u64 i = em->n_total_events;
483
484   /* Ring never wrapped? */
485   if (i <= (u64) l)
486     {
487       if (lo)
488         *lo = 0;
489       return i;
490     }
491   else
492     {
493       if (lo)
494         *lo = i & (l - 1);
495       return l;
496     }
497 }
498
499 elog_event_t *
500 elog_peek_events (elog_main_t * em)
501 {
502   elog_event_t *e, *f, *es = 0;
503   uword i, j, n;
504
505   n = elog_event_range (em, &j);
506   for (i = 0; i < n; i++)
507     {
508       vec_add2 (es, e, 1);
509       f = vec_elt_at_index (em->event_ring, j);
510       e[0] = f[0];
511
512       /* Convert absolute time from cycles to seconds from start. */
513       e->time =
514         (e->time_cycles -
515          em->init_time.cpu) * em->cpu_timer.seconds_per_clock;
516
517       j = (j + 1) & (em->event_ring_size - 1);
518     }
519
520   return es;
521 }
522
523 /* Add a formatted string to the string table. */
524 u32
525 elog_string (elog_main_t * em, char *fmt, ...)
526 {
527   u32 offset;
528   va_list va;
529
530   va_start (va, fmt);
531   offset = vec_len (em->string_table);
532   em->string_table = (char *) va_format ((u8 *) em->string_table, fmt, &va);
533   va_end (va);
534
535   /* Null terminate string if it is not already. */
536   if (vec_end (em->string_table)[-1] != 0)
537     vec_add1 (em->string_table, 0);
538
539   return offset;
540 }
541
542 elog_event_t *
543 elog_get_events (elog_main_t * em)
544 {
545   if (!em->events)
546     em->events = elog_peek_events (em);
547   return em->events;
548 }
549
550 static void
551 maybe_fix_string_table_offset (elog_event_t * e,
552                                elog_event_type_t * t, u32 offset)
553 {
554   void *d = (u8 *) e->data;
555   char *a;
556
557   if (offset == 0)
558     return;
559
560   a = t->format_args;
561
562   while (1)
563     {
564       uword n_bytes = 0, n_digits;
565
566       if (a[0] == 0)
567         break;
568
569       /* Don't go past end of event data. */
570       ASSERT (d < (void *) (e->data + sizeof (e->data)));
571
572       n_digits = parse_2digit_decimal (a + 1, &n_bytes);
573       switch (a[0])
574         {
575         case 'T':
576           ASSERT (n_bytes == 4);
577           clib_mem_unaligned (d, u32) += offset;
578           break;
579
580         case 'i':
581         case 't':
582         case 'f':
583         case 's':
584           break;
585
586         default:
587           ASSERT (0);
588           break;
589         }
590
591       ASSERT (n_digits > 0 && n_digits <= 2);
592       a += 1 + n_digits;
593       d += n_bytes;
594     }
595 }
596
597 static int
598 elog_cmp (void *a1, void *a2)
599 {
600   elog_event_t *e1 = a1;
601   elog_event_t *e2 = a2;
602
603   return e1->time - e2->time;
604 }
605
606 void
607 elog_merge (elog_main_t * dst, u8 * dst_tag, elog_main_t * src, u8 * src_tag)
608 {
609   elog_event_t *e;
610   uword l;
611   u32 string_table_offset_for_src_events;
612   u32 track_offset_for_src_tracks;
613   elog_track_t newt;
614   int i;
615
616   memset (&newt, 0, sizeof (newt));
617
618   elog_get_events (src);
619   elog_get_events (dst);
620
621   string_table_offset_for_src_events = vec_len (dst->string_table);
622   vec_append (dst->string_table, src->string_table);
623
624   l = vec_len (dst->events);
625   vec_add (dst->events, src->events, vec_len (src->events));
626
627   /* Prepend the supplied tag (if any) to all dst track names */
628   if (dst_tag)
629     {
630       for (i = 0; i < vec_len (dst->tracks); i++)
631         {
632           elog_track_t *t = vec_elt_at_index (dst->tracks, i);
633           char *new_name;
634
635           new_name = (char *) format (0, "%s:%s%c", dst_tag, t->name, 0);
636           vec_free (t->name);
637           t->name = new_name;
638         }
639     }
640
641   track_offset_for_src_tracks = vec_len (dst->tracks);
642
643   /* Copy / tag source tracks */
644   for (i = 0; i < vec_len (src->tracks); i++)
645     {
646       elog_track_t *t = vec_elt_at_index (src->tracks, i);
647       if (src_tag)
648         newt.name = (char *) format (0, "%s:%s%c", src_tag, t->name, 0);
649       else
650         newt.name = (char *) format (0, "%s%c", t->name, 0);
651       (void) elog_track_register (dst, &newt);
652       vec_free (newt.name);
653     }
654
655   /* Across all (copied) src events... */
656   for (e = dst->events + l; e < vec_end (dst->events); e++)
657     {
658       elog_event_type_t *t = vec_elt_at_index (src->event_types, e->type);
659
660       /* Remap type from src -> dst. */
661       e->type = find_or_create_type (dst, t);
662
663       /* Remap string table offsets for 'T' format args */
664       maybe_fix_string_table_offset (e, t,
665                                      string_table_offset_for_src_events);
666
667       /* Remap track */
668       e->track += track_offset_for_src_tracks;
669     }
670
671   /* Adjust event times for relative starting times of event streams. */
672   {
673     f64 dt_event, dt_os_nsec, dt_clock_nsec;
674
675     /* Set clock parameters if dst was not generated by unserialize. */
676     if (dst->serialize_time.cpu == 0)
677       {
678         dst->init_time = src->init_time;
679         dst->serialize_time = src->serialize_time;
680         dst->nsec_per_cpu_clock = src->nsec_per_cpu_clock;
681       }
682
683     dt_os_nsec =
684       elog_time_stamp_diff_os_nsec (&src->init_time, &dst->init_time);
685
686     dt_event = dt_os_nsec;
687     dt_clock_nsec =
688       (elog_time_stamp_diff_cpu (&src->init_time, &dst->init_time) * .5 *
689        (dst->nsec_per_cpu_clock + src->nsec_per_cpu_clock));
690
691     /* Heuristic to see if src/dst came from same time source.
692        If frequencies are "the same" and os clock and cpu clock agree
693        to within 100e-9 secs about time difference between src/dst
694        init_time, then we use cpu clock.  Otherwise we use OS clock. */
695     if (fabs (src->nsec_per_cpu_clock - dst->nsec_per_cpu_clock) < 1e-2
696         && fabs (dt_os_nsec - dt_clock_nsec) < 100)
697       dt_event = dt_clock_nsec;
698
699     /* Convert to seconds. */
700     dt_event *= 1e-9;
701
702     if (dt_event > 0)
703       {
704         /* Src started after dst. */
705         for (e = dst->events + l; e < vec_end (dst->events); e++)
706           e->time += dt_event;
707       }
708     else
709       {
710         /* Dst started after src. */
711         for (e = dst->events + 0; e < dst->events + l; e++)
712           e->time += dt_event;
713       }
714   }
715
716   /* Sort events by increasing time. */
717   vec_sort_with_function (dst->events, elog_cmp);
718
719   /* Recreate the event ring or the results won't serialize */
720   {
721     int i;
722
723     ASSERT (dst->cpu_timer.seconds_per_clock);
724
725     elog_alloc (dst, vec_len (dst->events));
726     for (i = 0; i < vec_len (dst->events); i++)
727       {
728         elog_event_t *es, *ed;
729
730         es = dst->events + i;
731         ed = dst->event_ring + i;
732
733         ed[0] = es[0];
734
735         /* Invert elog_peek_events calculation */
736         ed->time_cycles =
737           (es->time / dst->cpu_timer.seconds_per_clock) + dst->init_time.cpu;
738       }
739     dst->n_total_events = vec_len (dst->events);
740   }
741 }
742
743 static void
744 serialize_elog_event (serialize_main_t * m, va_list * va)
745 {
746   elog_main_t *em = va_arg (*va, elog_main_t *);
747   elog_event_t *e = va_arg (*va, elog_event_t *);
748   elog_event_type_t *t = vec_elt_at_index (em->event_types, e->type);
749   u8 *d = e->data;
750   u8 *p = (u8 *) t->format_args;
751
752   serialize_integer (m, e->type, sizeof (e->type));
753   serialize_integer (m, e->track, sizeof (e->track));
754   serialize (m, serialize_f64, e->time);
755
756   while (*p)
757     {
758       uword n_digits, n_bytes = 0;
759
760       n_digits = parse_2digit_decimal ((char *) p + 1, &n_bytes);
761
762       switch (p[0])
763         {
764         case 'i':
765         case 't':
766         case 'T':
767           if (n_bytes == 1)
768             serialize_integer (m, d[0], sizeof (u8));
769           else if (n_bytes == 2)
770             serialize_integer (m, clib_mem_unaligned (d, u16), sizeof (u16));
771           else if (n_bytes == 4)
772             serialize_integer (m, clib_mem_unaligned (d, u32), sizeof (u32));
773           else if (n_bytes == 8)
774             serialize (m, serialize_64, clib_mem_unaligned (d, u64));
775           else
776             ASSERT (0);
777           break;
778
779         case 's':
780           serialize_cstring (m, (char *) d);
781           if (n_bytes == 0)
782             n_bytes = strlen ((char *) d) + 1;
783           break;
784
785         case 'f':
786           if (n_bytes == 4)
787             serialize (m, serialize_f32, clib_mem_unaligned (d, f32));
788           else if (n_bytes == 8)
789             serialize (m, serialize_f64, clib_mem_unaligned (d, f64));
790           else
791             ASSERT (0);
792           break;
793
794         default:
795           ASSERT (0);
796           break;
797         }
798
799       p += 1 + n_digits;
800       d += n_bytes;
801     }
802 }
803
804 static void
805 unserialize_elog_event (serialize_main_t * m, va_list * va)
806 {
807   elog_main_t *em = va_arg (*va, elog_main_t *);
808   elog_event_t *e = va_arg (*va, elog_event_t *);
809   elog_event_type_t *t;
810   u8 *p, *d;
811
812   {
813     u16 tmp[2];
814
815     unserialize_integer (m, &tmp[0], sizeof (e->type));
816     unserialize_integer (m, &tmp[1], sizeof (e->track));
817
818     e->type = tmp[0];
819     e->track = tmp[1];
820
821     /* Make sure it fits. */
822     ASSERT (e->type == tmp[0]);
823     ASSERT (e->track == tmp[1]);
824   }
825
826   t = vec_elt_at_index (em->event_types, e->type);
827
828   unserialize (m, unserialize_f64, &e->time);
829
830   d = e->data;
831   p = (u8 *) t->format_args;
832
833   while (p && *p)
834     {
835       uword n_digits, n_bytes = 0;
836       u32 tmp;
837
838       n_digits = parse_2digit_decimal ((char *) p + 1, &n_bytes);
839
840       switch (p[0])
841         {
842         case 'i':
843         case 't':
844         case 'T':
845           if (n_bytes == 1)
846             {
847               unserialize_integer (m, &tmp, sizeof (u8));
848               d[0] = tmp;
849             }
850           else if (n_bytes == 2)
851             {
852               unserialize_integer (m, &tmp, sizeof (u16));
853               clib_mem_unaligned (d, u16) = tmp;
854             }
855           else if (n_bytes == 4)
856             {
857               unserialize_integer (m, &tmp, sizeof (u32));
858               clib_mem_unaligned (d, u32) = tmp;
859             }
860           else if (n_bytes == 8)
861             {
862               u64 x;
863               unserialize (m, unserialize_64, &x);
864               clib_mem_unaligned (d, u64) = x;
865             }
866           else
867             ASSERT (0);
868           break;
869
870         case 's':
871           {
872             char *t;
873             unserialize_cstring (m, &t);
874             if (n_bytes == 0)
875               n_bytes = strlen (t) + 1;
876             clib_memcpy (d, t, clib_min (n_bytes, vec_len (t)));
877             vec_free (t);
878             break;
879           }
880
881         case 'f':
882           if (n_bytes == 4)
883             {
884               f32 x;
885               unserialize (m, unserialize_f32, &x);
886               clib_mem_unaligned (d, f32) = x;
887             }
888           else if (n_bytes == 8)
889             {
890               f64 x;
891               unserialize (m, unserialize_f64, &x);
892               clib_mem_unaligned (d, f64) = x;
893             }
894           else
895             ASSERT (0);
896           break;
897
898         default:
899           ASSERT (0);
900           break;
901         }
902
903       p += 1 + n_digits;
904       d += n_bytes;
905     }
906 }
907
908 static void
909 serialize_elog_event_type (serialize_main_t * m, va_list * va)
910 {
911   elog_event_type_t *t = va_arg (*va, elog_event_type_t *);
912   int n = va_arg (*va, int);
913   int i, j;
914   for (i = 0; i < n; i++)
915     {
916       serialize_cstring (m, t[i].format);
917       serialize_cstring (m, t[i].format_args);
918       serialize_integer (m, t[i].type_index_plus_one,
919                          sizeof (t->type_index_plus_one));
920       serialize_integer (m, t[i].n_enum_strings,
921                          sizeof (t[i].n_enum_strings));
922       for (j = 0; j < t[i].n_enum_strings; j++)
923         serialize_cstring (m, t[i].enum_strings_vector[j]);
924     }
925 }
926
927 static void
928 unserialize_elog_event_type (serialize_main_t * m, va_list * va)
929 {
930   elog_event_type_t *t = va_arg (*va, elog_event_type_t *);
931   int n = va_arg (*va, int);
932   int i, j;
933   for (i = 0; i < n; i++)
934     {
935       unserialize_cstring (m, &t[i].format);
936       unserialize_cstring (m, &t[i].format_args);
937       unserialize_integer (m, &t[i].type_index_plus_one,
938                            sizeof (t->type_index_plus_one));
939       unserialize_integer (m, &t[i].n_enum_strings,
940                            sizeof (t[i].n_enum_strings));
941       vec_resize (t[i].enum_strings_vector, t[i].n_enum_strings);
942       for (j = 0; j < t[i].n_enum_strings; j++)
943         unserialize_cstring (m, &t[i].enum_strings_vector[j]);
944     }
945 }
946
947 static void
948 serialize_elog_track (serialize_main_t * m, va_list * va)
949 {
950   elog_track_t *t = va_arg (*va, elog_track_t *);
951   int n = va_arg (*va, int);
952   int i;
953   for (i = 0; i < n; i++)
954     {
955       serialize_cstring (m, t[i].name);
956     }
957 }
958
959 static void
960 unserialize_elog_track (serialize_main_t * m, va_list * va)
961 {
962   elog_track_t *t = va_arg (*va, elog_track_t *);
963   int n = va_arg (*va, int);
964   int i;
965   for (i = 0; i < n; i++)
966     {
967       unserialize_cstring (m, &t[i].name);
968     }
969 }
970
971 static void
972 serialize_elog_time_stamp (serialize_main_t * m, va_list * va)
973 {
974   elog_time_stamp_t *st = va_arg (*va, elog_time_stamp_t *);
975   serialize (m, serialize_64, st->os_nsec);
976   serialize (m, serialize_64, st->cpu);
977 }
978
979 static void
980 unserialize_elog_time_stamp (serialize_main_t * m, va_list * va)
981 {
982   elog_time_stamp_t *st = va_arg (*va, elog_time_stamp_t *);
983   unserialize (m, unserialize_64, &st->os_nsec);
984   unserialize (m, unserialize_64, &st->cpu);
985 }
986
987 static char *elog_serialize_magic = "elog v0";
988
989 void
990 serialize_elog_main (serialize_main_t * m, va_list * va)
991 {
992   elog_main_t *em = va_arg (*va, elog_main_t *);
993   elog_event_t *e;
994
995   serialize_magic (m, elog_serialize_magic, strlen (elog_serialize_magic));
996
997   serialize_integer (m, em->event_ring_size, sizeof (u32));
998
999   elog_time_now (&em->serialize_time);
1000   serialize (m, serialize_elog_time_stamp, &em->serialize_time);
1001   serialize (m, serialize_elog_time_stamp, &em->init_time);
1002
1003   vec_serialize (m, em->event_types, serialize_elog_event_type);
1004   vec_serialize (m, em->tracks, serialize_elog_track);
1005   vec_serialize (m, em->string_table, serialize_vec_8);
1006
1007   /* Free old events (cached) in case they have changed. */
1008   vec_free (em->events);
1009   elog_get_events (em);
1010
1011   serialize_integer (m, vec_len (em->events), sizeof (u32));
1012
1013   /* SMP logs can easily have local time paradoxes... */
1014   vec_sort_with_function (em->events, elog_cmp);
1015
1016   vec_foreach (e, em->events) serialize (m, serialize_elog_event, em, e);
1017 }
1018
1019 void
1020 unserialize_elog_main (serialize_main_t * m, va_list * va)
1021 {
1022   elog_main_t *em = va_arg (*va, elog_main_t *);
1023   uword i;
1024   u32 rs;
1025
1026   unserialize_check_magic (m, elog_serialize_magic,
1027                            strlen (elog_serialize_magic));
1028
1029   unserialize_integer (m, &rs, sizeof (u32));
1030   em->event_ring_size = rs;
1031   elog_init (em, em->event_ring_size);
1032
1033   unserialize (m, unserialize_elog_time_stamp, &em->serialize_time);
1034   unserialize (m, unserialize_elog_time_stamp, &em->init_time);
1035   em->nsec_per_cpu_clock = elog_nsec_per_clock (em);
1036
1037   vec_unserialize (m, &em->event_types, unserialize_elog_event_type);
1038   for (i = 0; i < vec_len (em->event_types); i++)
1039     new_event_type (em, i);
1040
1041   vec_unserialize (m, &em->tracks, unserialize_elog_track);
1042   vec_unserialize (m, &em->string_table, unserialize_vec_8);
1043
1044   {
1045     u32 ne;
1046     elog_event_t *e;
1047
1048     unserialize_integer (m, &ne, sizeof (u32));
1049     vec_resize (em->events, ne);
1050     vec_foreach (e, em->events)
1051       unserialize (m, unserialize_elog_event, em, e);
1052   }
1053 }
1054
1055 /*
1056  * fd.io coding-style-patch-verification: ON
1057  *
1058  * Local Variables:
1059  * eval: (c-set-style "gnu")
1060  * End:
1061  */