Clean up event log merge code
[vpp.git] / src / 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   t->type_index_plus_one = i + 1;
81   hash_set_mem (em->event_type_by_format, t->format, i);
82 }
83
84 static uword
85 find_or_create_type (elog_main_t * em, elog_event_type_t * t)
86 {
87   uword *p = hash_get_mem (em->event_type_by_format, t->format);
88   uword i;
89
90   if (p)
91     i = p[0];
92   else
93     {
94       i = vec_len (em->event_types);
95       vec_add1 (em->event_types, t[0]);
96       new_event_type (em, i);
97     }
98
99   return i;
100 }
101
102 /* External function to register types. */
103 word
104 elog_event_type_register (elog_main_t * em, elog_event_type_t * t)
105 {
106   elog_event_type_t *static_type = t;
107   word l;
108
109   elog_lock (em);
110
111   l = vec_len (em->event_types);
112
113   t->type_index_plus_one = 1 + l;
114
115   ASSERT (t->format);
116
117   /* If format args are not specified try to be smart about providing defaults
118      so most of the time user does not have to specify them. */
119   if (!t->format_args)
120     {
121       uword i, l;
122       char *this_arg;
123
124       l = strlen (t->format);
125       for (i = 0; i < l; i++)
126         {
127           if (t->format[i] != '%')
128             continue;
129           if (i + 1 >= l)
130             continue;
131           if (t->format[i + 1] == '%')  /* %% */
132             continue;
133
134           switch (t->format[i + 1])
135             {
136             default:
137             case 'd':
138             case 'x':
139             case 'u':
140               this_arg = "i4";  /* size of u32 */
141               break;
142             case 'f':
143               this_arg = "f8";  /* defaults to f64 */
144               break;
145             case 's':
146               this_arg = "s0";  /* defaults to null terminated string. */
147               break;
148             }
149
150           t->format_args =
151             (char *) format ((u8 *) t->format_args, "%s", this_arg);
152         }
153
154       /* Null terminate. */
155       vec_add1 (t->format_args, 0);
156     }
157
158   vec_add1 (em->event_types, t[0]);
159
160   t = em->event_types + l;
161
162   /* Make copies of strings for hashing etc. */
163   if (t->function)
164     t->format = (char *) format (0, "%s %s%c", t->function, t->format, 0);
165   else
166     t->format = (char *) format (0, "%s%c", t->format, 0);
167
168   t->format_args = (char *) format (0, "%s%c", t->format_args, 0);
169
170   /* Construct string table. */
171   {
172     uword i;
173     t->n_enum_strings = static_type->n_enum_strings;
174     for (i = 0; i < t->n_enum_strings; i++)
175       {
176         if (!static_type->enum_strings[i])
177           static_type->enum_strings[i] = "MISSING";
178         vec_add1 (t->enum_strings_vector,
179                   (char *) format (0, "%s%c", static_type->enum_strings[i],
180                                    0));
181       }
182   }
183
184   new_event_type (em, l);
185   elog_unlock (em);
186
187   return l;
188 }
189
190 word
191 elog_track_register (elog_main_t * em, elog_track_t * t)
192 {
193   word l;
194
195   elog_lock (em);
196
197   l = vec_len (em->tracks);
198
199   t->track_index_plus_one = 1 + l;
200
201   ASSERT (t->name);
202
203   vec_add1 (em->tracks, t[0]);
204
205   t = em->tracks + l;
206
207   t->name = (char *) format (0, "%s%c", t->name, 0);
208
209   elog_unlock (em);
210
211   return l;
212 }
213
214 static uword
215 parse_2digit_decimal (char *p, uword * number)
216 {
217   uword i = 0;
218   u8 digits[2];
219
220   digits[0] = digits[1] = 0;
221   while (p[i] >= '0' && p[i] <= '9')
222     {
223       if (i >= 2)
224         break;
225       digits[i] = p[i] - '0';
226       i++;
227     }
228
229   if (i >= 1 && i <= 2)
230     {
231       if (i == 1)
232         *number = digits[0];
233       else
234         *number = 10 * digits[0] + digits[1];
235       return i;
236     }
237   else
238     return 0;
239 }
240
241 static u8 *
242 fixed_format (u8 * s, char *fmt, char *result, uword * result_len)
243 {
244   char *f = fmt;
245   char *percent;
246   uword l = 0;
247
248   while (1)
249     {
250       if (f[0] == 0)
251         break;
252       if (f[0] == '%' && f[1] != '%')
253         break;
254       f++;
255     }
256   if (f > fmt)
257     vec_add (s, fmt, f - fmt);
258
259   if (f[0] != '%')
260     goto done;
261
262   /* Skip percent. */
263   percent = f++;
264
265   /* Skip possible +-= justification. */
266   f += f[0] == '+' || f[0] == '-' || f[0] == '=';
267
268   /* Skip possible X.Y width. */
269   while ((f[0] >= '0' && f[0] <= '9') || f[0] == '.')
270     f++;
271
272   /* Skip wlL as in e.g. %Ld. */
273   f += f[0] == 'w' || f[0] == 'l' || f[0] == 'L';
274
275   /* Finally skip format letter. */
276   f += f[0] != 0;
277
278   ASSERT (*result_len > f - percent);
279   l = clib_min (f - percent, *result_len - 1);
280   clib_memcpy (result, percent, l);
281   result[l] = 0;
282
283 done:
284   *result_len = f - fmt;
285   return s;
286 }
287
288 u8 *
289 format_elog_event (u8 * s, va_list * va)
290 {
291   elog_main_t *em = va_arg (*va, elog_main_t *);
292   elog_event_t *e = va_arg (*va, elog_event_t *);
293   elog_event_type_t *t;
294   char *a, *f;
295   void *d = (u8 *) e->data;
296   char arg_format[64];
297
298   t = vec_elt_at_index (em->event_types, e->type);
299
300   f = t->format;
301   a = t->format_args;
302   while (1)
303     {
304       uword n_bytes = 0, n_digits, f_bytes = 0;
305
306       f_bytes = sizeof (arg_format);
307       s = fixed_format (s, f, arg_format, &f_bytes);
308       f += f_bytes;
309
310       if (a == 0 || a[0] == 0)
311         {
312           /* Format must also be at end. */
313           ASSERT (f[0] == 0);
314           break;
315         }
316
317       /* Don't go past end of event data. */
318       ASSERT (d < (void *) (e->data + sizeof (e->data)));
319
320       n_digits = parse_2digit_decimal (a + 1, &n_bytes);
321       switch (a[0])
322         {
323         case 'i':
324         case 't':
325         case 'T':
326           {
327             u32 i = 0;
328             u64 l = 0;
329
330             if (n_bytes == 1)
331               i = ((u8 *) d)[0];
332             else if (n_bytes == 2)
333               i = clib_mem_unaligned (d, u16);
334             else if (n_bytes == 4)
335               i = clib_mem_unaligned (d, u32);
336             else if (n_bytes == 8)
337               l = clib_mem_unaligned (d, u64);
338             else
339               ASSERT (0);
340             if (a[0] == 't')
341               {
342                 char *e =
343                   vec_elt (t->enum_strings_vector, n_bytes == 8 ? l : i);
344                 s = format (s, arg_format, e);
345               }
346             else if (a[0] == 'T')
347               {
348                 char *e =
349                   vec_elt_at_index (em->string_table, n_bytes == 8 ? l : i);
350                 s = format (s, arg_format, e);
351               }
352             else if (n_bytes == 8)
353               s = format (s, arg_format, l);
354             else
355               s = format (s, arg_format, i);
356           }
357           break;
358
359         case 'f':
360           {
361             f64 x = 0;
362             if (n_bytes == 4)
363               x = clib_mem_unaligned (d, f32);
364             else if (n_bytes == 8)
365               x = clib_mem_unaligned (d, f64);
366             else
367               ASSERT (0);
368             s = format (s, arg_format, x);
369           }
370           break;
371
372         case 's':
373           s = format (s, arg_format, d);
374           if (n_bytes == 0)
375             n_bytes = strlen (d) + 1;
376           break;
377
378         default:
379           ASSERT (0);
380           break;
381         }
382
383       ASSERT (n_digits > 0 && n_digits <= 2);
384       a += 1 + n_digits;
385       d += n_bytes;
386     }
387
388   return s;
389 }
390
391 u8 *
392 format_elog_track (u8 * s, va_list * va)
393 {
394   elog_main_t *em = va_arg (*va, elog_main_t *);
395   elog_event_t *e = va_arg (*va, elog_event_t *);
396   elog_track_t *t = vec_elt_at_index (em->tracks, e->track);
397   return format (s, "%s", t->name);
398 }
399
400 void
401 elog_time_now (elog_time_stamp_t * et)
402 {
403   u64 cpu_time_now, os_time_now_nsec;
404   struct timespec ts;
405
406 #ifdef CLIB_UNIX
407   {
408 #include <sys/syscall.h>
409     syscall (SYS_clock_gettime, CLOCK_REALTIME, &ts);
410     cpu_time_now = clib_cpu_time_now ();
411     /* Subtract 3/30/2017's worth of seconds to retain precision */
412     os_time_now_nsec = 1e9 * (ts.tv_sec - 1490885108) + ts.tv_nsec;
413   }
414 #else
415   cpu_time_now = clib_cpu_time_now ();
416   os_time_now_nsec = 0;
417 #endif
418
419   et->cpu = cpu_time_now;
420   et->os_nsec = os_time_now_nsec;
421 }
422
423 always_inline i64
424 elog_time_stamp_diff_os_nsec (elog_time_stamp_t * t1, elog_time_stamp_t * t2)
425 {
426   return (i64) t1->os_nsec - (i64) t2->os_nsec;
427 }
428
429 always_inline i64
430 elog_time_stamp_diff_cpu (elog_time_stamp_t * t1, elog_time_stamp_t * t2)
431 {
432   return (i64) t1->cpu - (i64) t2->cpu;
433 }
434
435 always_inline f64
436 elog_nsec_per_clock (elog_main_t * em)
437 {
438   return ((f64) elog_time_stamp_diff_os_nsec (&em->serialize_time,
439                                               &em->init_time)
440           / (f64) elog_time_stamp_diff_cpu (&em->serialize_time,
441                                             &em->init_time));
442 }
443
444 void
445 elog_alloc (elog_main_t * em, u32 n_events)
446 {
447   if (em->event_ring)
448     vec_free (em->event_ring);
449
450   /* Ring size must be a power of 2. */
451   em->event_ring_size = n_events = max_pow2 (n_events);
452
453   /* Leave an empty ievent at end so we can always speculatively write
454      and event there (possibly a long form event). */
455   vec_resize_aligned (em->event_ring, n_events, CLIB_CACHE_LINE_BYTES);
456 }
457
458 void
459 elog_init (elog_main_t * em, u32 n_events)
460 {
461   memset (em, 0, sizeof (em[0]));
462
463   em->lock = 0;
464
465   if (n_events > 0)
466     elog_alloc (em, n_events);
467
468   clib_time_init (&em->cpu_timer);
469
470   em->n_total_events_disable_limit = ~0;
471
472   /* Make track 0. */
473   em->default_track.name = "default";
474   elog_track_register (em, &em->default_track);
475
476   elog_time_now (&em->init_time);
477 }
478
479 /* Returns number of events in ring and start index. */
480 static uword
481 elog_event_range (elog_main_t * em, uword * lo)
482 {
483   uword l = em->event_ring_size;
484   u64 i = em->n_total_events;
485
486   /* Ring never wrapped? */
487   if (i <= (u64) l)
488     {
489       if (lo)
490         *lo = 0;
491       return i;
492     }
493   else
494     {
495       if (lo)
496         *lo = i & (l - 1);
497       return l;
498     }
499 }
500
501 elog_event_t *
502 elog_peek_events (elog_main_t * em)
503 {
504   elog_event_t *e, *f, *es = 0;
505   uword i, j, n;
506
507   n = elog_event_range (em, &j);
508   for (i = 0; i < n; i++)
509     {
510       vec_add2 (es, e, 1);
511       f = vec_elt_at_index (em->event_ring, j);
512       e[0] = f[0];
513
514       /* Convert absolute time from cycles to seconds from start. */
515       e->time =
516         (e->time_cycles -
517          em->init_time.cpu) * em->cpu_timer.seconds_per_clock;
518
519       j = (j + 1) & (em->event_ring_size - 1);
520     }
521
522   return es;
523 }
524
525 /* Add a formatted string to the string table. */
526 u32
527 elog_string (elog_main_t * em, char *fmt, ...)
528 {
529   u32 offset;
530   va_list va;
531
532   va_start (va, fmt);
533   offset = vec_len (em->string_table);
534   em->string_table = (char *) va_format ((u8 *) em->string_table, fmt, &va);
535   va_end (va);
536
537   /* Null terminate string if it is not already. */
538   if (vec_end (em->string_table)[-1] != 0)
539     vec_add1 (em->string_table, 0);
540
541   return offset;
542 }
543
544 elog_event_t *
545 elog_get_events (elog_main_t * em)
546 {
547   if (!em->events)
548     em->events = elog_peek_events (em);
549   return em->events;
550 }
551
552 static void
553 maybe_fix_string_table_offset (elog_event_t * e,
554                                elog_event_type_t * t, u32 offset)
555 {
556   void *d = (u8 *) e->data;
557   char *a;
558
559   if (offset == 0)
560     return;
561
562   a = t->format_args;
563
564   while (1)
565     {
566       uword n_bytes = 0, n_digits;
567
568       if (a[0] == 0)
569         break;
570
571       /* Don't go past end of event data. */
572       ASSERT (d < (void *) (e->data + sizeof (e->data)));
573
574       n_digits = parse_2digit_decimal (a + 1, &n_bytes);
575       switch (a[0])
576         {
577         case 'T':
578           ASSERT (n_bytes == 4);
579           clib_mem_unaligned (d, u32) += offset;
580           break;
581
582         case 'i':
583         case 't':
584         case 'f':
585         case 's':
586           break;
587
588         default:
589           ASSERT (0);
590           break;
591         }
592
593       ASSERT (n_digits > 0 && n_digits <= 2);
594       a += 1 + n_digits;
595       d += n_bytes;
596     }
597 }
598
599 static int
600 elog_cmp (void *a1, void *a2)
601 {
602   elog_event_t *e1 = a1;
603   elog_event_t *e2 = a2;
604
605   if (e1->time < e2->time)
606     return -1;
607
608   if (e1->time > e2->time)
609     return 1;
610
611   return 0;
612 }
613
614 /*
615  * merge two event logs. Complicated and cranky.
616  */
617 void
618 elog_merge (elog_main_t * dst, u8 * dst_tag, elog_main_t * src, u8 * src_tag,
619             f64 align_tweak)
620 {
621   elog_event_t *e;
622   uword l;
623   u32 string_table_offset_for_src_events;
624   u32 track_offset_for_src_tracks;
625   elog_track_t newt;
626   int i;
627
628   memset (&newt, 0, sizeof (newt));
629
630   /* Acquire src and dst events */
631   elog_get_events (src);
632   elog_get_events (dst);
633
634   string_table_offset_for_src_events = vec_len (dst->string_table);
635   vec_append (dst->string_table, src->string_table);
636
637   l = vec_len (dst->events);
638   vec_append (dst->events, src->events);
639
640   /* Prepend the supplied tag (if any) to all dst track names */
641   if (dst_tag)
642     {
643       for (i = 0; i < vec_len (dst->tracks); i++)
644         {
645           elog_track_t *t = vec_elt_at_index (dst->tracks, i);
646           char *new_name;
647
648           new_name = (char *) format (0, "%s:%s%c", dst_tag, t->name, 0);
649           vec_free (t->name);
650           t->name = new_name;
651         }
652     }
653
654   /*
655    * Remember where we started allocating new tracks while merging
656    */
657   track_offset_for_src_tracks = vec_len (dst->tracks);
658
659   /* Copy / tag source tracks */
660   for (i = 0; i < vec_len (src->tracks); i++)
661     {
662       elog_track_t *t = vec_elt_at_index (src->tracks, i);
663       if (src_tag)
664         newt.name = (char *) format (0, "%s:%s%c", src_tag, t->name, 0);
665       else
666         newt.name = (char *) format (0, "%s%c", t->name, 0);
667       (void) elog_track_register (dst, &newt);
668       vec_free (newt.name);
669     }
670
671   /* Across all (copied) src events... */
672   for (e = dst->events + l; e < vec_end (dst->events); e++)
673     {
674       elog_event_type_t *t = vec_elt_at_index (src->event_types, e->type);
675
676       /* Remap type from src -> dst. */
677       e->type = find_or_create_type (dst, t);
678
679       /* Remap string table offsets for 'T' format args */
680       maybe_fix_string_table_offset (e, t,
681                                      string_table_offset_for_src_events);
682
683       /* Remap track */
684       e->track += track_offset_for_src_tracks;
685     }
686
687   /* Adjust event times for relative starting times of event streams. */
688   {
689     f64 dt_event, dt_os_nsec, dt_clock_nsec;
690
691     /* Set clock parameters if dst was not generated by unserialize. */
692     if (dst->serialize_time.cpu == 0)
693       {
694         dst->init_time = src->init_time;
695         dst->serialize_time = src->serialize_time;
696         dst->nsec_per_cpu_clock = src->nsec_per_cpu_clock;
697       }
698
699     dt_os_nsec =
700       elog_time_stamp_diff_os_nsec (&src->init_time, &dst->init_time);
701
702     dt_event = dt_os_nsec;
703     dt_clock_nsec =
704       (elog_time_stamp_diff_cpu (&src->init_time, &dst->init_time) * .5 *
705        (dst->nsec_per_cpu_clock + src->nsec_per_cpu_clock));
706
707     /*
708      * Heuristic to see if src/dst came from same time source.
709      * If frequencies are "the same" and os clock and cpu clock agree
710      * to within 100e-9 secs about time difference between src/dst
711      * init_time, then we use cpu clock.  Otherwise we use OS clock.
712      *
713      * When merging event logs from different systems, time paradoxes
714      * at the O(1ms) level are to be expected. Hence, the "align_tweak"
715      * parameter. If two events logged on different processors are known
716      * to occur in a specific order - and with a reasonably-estimated
717      * interval - supply a non-zero "align_tweak" parameter
718      */
719     if (fabs (src->nsec_per_cpu_clock - dst->nsec_per_cpu_clock) < 1e-2
720         && fabs (dt_os_nsec - dt_clock_nsec) < 100)
721       dt_event = dt_clock_nsec;
722
723     /* Convert to seconds. */
724     dt_event *= 1e-9;
725
726     /*
727      * Move the earlier set of events later, to avoid creating
728      * events which preceed the Big Bang (aka have negative timestamps).
729      *
730      * Not to any scale, we have something like the following picture:
731      *
732      * DST capture start point
733      *       ^
734      *       +--- dt_event --+
735      *                       v
736      *                 SRC capture start point
737      *
738      * In this case dt_event is positive, src started after dst,
739      * to put src events onto a common timebase we have to move them
740      * forward in time. Naturally, the opposite case is
741      * possible, too: dt_event will be negative, and so we have to
742      * move dst events forward in time by the |dt_event|.
743      * In both cases, we add align_tweak.
744      */
745     if (dt_event > 0)
746       {
747         /* Src started after dst. */
748         for (e = dst->events + l; e < vec_end (dst->events); e++)
749           e->time += dt_event + align_tweak;
750       }
751     else
752       {
753         /* Dst started after src. */
754         dt_event = -dt_event;
755         for (e = dst->events + 0; e < dst->events + l; e++)
756           e->time += dt_event + align_tweak;
757       }
758   }
759
760   /* Sort events by increasing time. */
761   vec_sort_with_function (dst->events, elog_cmp);
762
763   dst->n_total_events = vec_len (dst->events);
764
765   /* Recreate the event ring or the results won't serialize */
766   {
767     int i;
768
769     ASSERT (dst->cpu_timer.seconds_per_clock);
770
771     elog_alloc (dst, vec_len (dst->events));
772     for (i = 0; i < vec_len (dst->events); i++)
773       {
774         elog_event_t *es, *ed;
775
776         es = dst->events + i;
777         ed = dst->event_ring + i;
778
779         ed[0] = es[0];
780       }
781   }
782 }
783
784 static void
785 serialize_elog_event (serialize_main_t * m, va_list * va)
786 {
787   elog_main_t *em = va_arg (*va, elog_main_t *);
788   elog_event_t *e = va_arg (*va, elog_event_t *);
789   elog_event_type_t *t = vec_elt_at_index (em->event_types, e->type);
790   u8 *d = e->data;
791   u8 *p = (u8 *) t->format_args;
792
793   serialize_integer (m, e->type, sizeof (e->type));
794   serialize_integer (m, e->track, sizeof (e->track));
795   serialize (m, serialize_f64, e->time);
796
797   while (*p)
798     {
799       uword n_digits, n_bytes = 0;
800
801       n_digits = parse_2digit_decimal ((char *) p + 1, &n_bytes);
802
803       switch (p[0])
804         {
805         case 'i':
806         case 't':
807         case 'T':
808           if (n_bytes == 1)
809             serialize_integer (m, d[0], sizeof (u8));
810           else if (n_bytes == 2)
811             serialize_integer (m, clib_mem_unaligned (d, u16), sizeof (u16));
812           else if (n_bytes == 4)
813             serialize_integer (m, clib_mem_unaligned (d, u32), sizeof (u32));
814           else if (n_bytes == 8)
815             serialize (m, serialize_64, clib_mem_unaligned (d, u64));
816           else
817             ASSERT (0);
818           break;
819
820         case 's':
821           serialize_cstring (m, (char *) d);
822           if (n_bytes == 0)
823             n_bytes = strlen ((char *) d) + 1;
824           break;
825
826         case 'f':
827           if (n_bytes == 4)
828             serialize (m, serialize_f32, clib_mem_unaligned (d, f32));
829           else if (n_bytes == 8)
830             serialize (m, serialize_f64, clib_mem_unaligned (d, f64));
831           else
832             ASSERT (0);
833           break;
834
835         default:
836           ASSERT (0);
837           break;
838         }
839
840       p += 1 + n_digits;
841       d += n_bytes;
842     }
843 }
844
845 static void
846 unserialize_elog_event (serialize_main_t * m, va_list * va)
847 {
848   elog_main_t *em = va_arg (*va, elog_main_t *);
849   elog_event_t *e = va_arg (*va, elog_event_t *);
850   elog_event_type_t *t;
851   u8 *p, *d;
852
853   {
854     u16 tmp[2];
855
856     unserialize_integer (m, &tmp[0], sizeof (e->type));
857     unserialize_integer (m, &tmp[1], sizeof (e->track));
858
859     e->type = tmp[0];
860     e->track = tmp[1];
861
862     /* Make sure it fits. */
863     ASSERT (e->type == tmp[0]);
864     ASSERT (e->track == tmp[1]);
865   }
866
867   t = vec_elt_at_index (em->event_types, e->type);
868
869   unserialize (m, unserialize_f64, &e->time);
870
871   d = e->data;
872   p = (u8 *) t->format_args;
873
874   while (p && *p)
875     {
876       uword n_digits, n_bytes = 0;
877       u32 tmp;
878
879       n_digits = parse_2digit_decimal ((char *) p + 1, &n_bytes);
880
881       switch (p[0])
882         {
883         case 'i':
884         case 't':
885         case 'T':
886           if (n_bytes == 1)
887             {
888               unserialize_integer (m, &tmp, sizeof (u8));
889               d[0] = tmp;
890             }
891           else if (n_bytes == 2)
892             {
893               unserialize_integer (m, &tmp, sizeof (u16));
894               clib_mem_unaligned (d, u16) = tmp;
895             }
896           else if (n_bytes == 4)
897             {
898               unserialize_integer (m, &tmp, sizeof (u32));
899               clib_mem_unaligned (d, u32) = tmp;
900             }
901           else if (n_bytes == 8)
902             {
903               u64 x;
904               unserialize (m, unserialize_64, &x);
905               clib_mem_unaligned (d, u64) = x;
906             }
907           else
908             ASSERT (0);
909           break;
910
911         case 's':
912           {
913             char *t;
914             unserialize_cstring (m, &t);
915             if (n_bytes == 0)
916               n_bytes = strlen (t) + 1;
917             clib_memcpy (d, t, clib_min (n_bytes, vec_len (t)));
918             vec_free (t);
919             break;
920           }
921
922         case 'f':
923           if (n_bytes == 4)
924             {
925               f32 x;
926               unserialize (m, unserialize_f32, &x);
927               clib_mem_unaligned (d, f32) = x;
928             }
929           else if (n_bytes == 8)
930             {
931               f64 x;
932               unserialize (m, unserialize_f64, &x);
933               clib_mem_unaligned (d, f64) = x;
934             }
935           else
936             ASSERT (0);
937           break;
938
939         default:
940           ASSERT (0);
941           break;
942         }
943
944       p += 1 + n_digits;
945       d += n_bytes;
946     }
947 }
948
949 static void
950 serialize_elog_event_type (serialize_main_t * m, va_list * va)
951 {
952   elog_event_type_t *t = va_arg (*va, elog_event_type_t *);
953   int n = va_arg (*va, int);
954   int i, j;
955   for (i = 0; i < n; i++)
956     {
957       serialize_cstring (m, t[i].format);
958       serialize_cstring (m, t[i].format_args);
959       serialize_integer (m, t[i].type_index_plus_one,
960                          sizeof (t->type_index_plus_one));
961       serialize_integer (m, t[i].n_enum_strings,
962                          sizeof (t[i].n_enum_strings));
963       for (j = 0; j < t[i].n_enum_strings; j++)
964         serialize_cstring (m, t[i].enum_strings_vector[j]);
965     }
966 }
967
968 static void
969 unserialize_elog_event_type (serialize_main_t * m, va_list * va)
970 {
971   elog_event_type_t *t = va_arg (*va, elog_event_type_t *);
972   int n = va_arg (*va, int);
973   int i, j;
974   for (i = 0; i < n; i++)
975     {
976       unserialize_cstring (m, &t[i].format);
977       unserialize_cstring (m, &t[i].format_args);
978       unserialize_integer (m, &t[i].type_index_plus_one,
979                            sizeof (t->type_index_plus_one));
980       unserialize_integer (m, &t[i].n_enum_strings,
981                            sizeof (t[i].n_enum_strings));
982       vec_resize (t[i].enum_strings_vector, t[i].n_enum_strings);
983       for (j = 0; j < t[i].n_enum_strings; j++)
984         unserialize_cstring (m, &t[i].enum_strings_vector[j]);
985     }
986 }
987
988 static void
989 serialize_elog_track (serialize_main_t * m, va_list * va)
990 {
991   elog_track_t *t = va_arg (*va, elog_track_t *);
992   int n = va_arg (*va, int);
993   int i;
994   for (i = 0; i < n; i++)
995     {
996       serialize_cstring (m, t[i].name);
997     }
998 }
999
1000 static void
1001 unserialize_elog_track (serialize_main_t * m, va_list * va)
1002 {
1003   elog_track_t *t = va_arg (*va, elog_track_t *);
1004   int n = va_arg (*va, int);
1005   int i;
1006   for (i = 0; i < n; i++)
1007     {
1008       unserialize_cstring (m, &t[i].name);
1009     }
1010 }
1011
1012 static void
1013 serialize_elog_time_stamp (serialize_main_t * m, va_list * va)
1014 {
1015   elog_time_stamp_t *st = va_arg (*va, elog_time_stamp_t *);
1016   serialize (m, serialize_64, st->os_nsec);
1017   serialize (m, serialize_64, st->cpu);
1018 }
1019
1020 static void
1021 unserialize_elog_time_stamp (serialize_main_t * m, va_list * va)
1022 {
1023   elog_time_stamp_t *st = va_arg (*va, elog_time_stamp_t *);
1024   unserialize (m, unserialize_64, &st->os_nsec);
1025   unserialize (m, unserialize_64, &st->cpu);
1026 }
1027
1028 static char *elog_serialize_magic = "elog v0";
1029
1030 void
1031 serialize_elog_main (serialize_main_t * m, va_list * va)
1032 {
1033   elog_main_t *em = va_arg (*va, elog_main_t *);
1034   int flush_ring = va_arg (*va, int);
1035   elog_event_t *e;
1036
1037   serialize_magic (m, elog_serialize_magic, strlen (elog_serialize_magic));
1038
1039   serialize_integer (m, em->event_ring_size, sizeof (u32));
1040
1041   elog_time_now (&em->serialize_time);
1042   serialize (m, serialize_elog_time_stamp, &em->serialize_time);
1043   serialize (m, serialize_elog_time_stamp, &em->init_time);
1044
1045   vec_serialize (m, em->event_types, serialize_elog_event_type);
1046   vec_serialize (m, em->tracks, serialize_elog_track);
1047   vec_serialize (m, em->string_table, serialize_vec_8);
1048
1049   /* Free old events (cached) in case they have changed. */
1050   if (flush_ring)
1051     {
1052       vec_free (em->events);
1053       elog_get_events (em);
1054     }
1055
1056   serialize_integer (m, vec_len (em->events), sizeof (u32));
1057
1058   /* SMP logs can easily have local time paradoxes... */
1059   vec_sort_with_function (em->events, elog_cmp);
1060
1061   vec_foreach (e, em->events) serialize (m, serialize_elog_event, em, e);
1062 }
1063
1064 void
1065 unserialize_elog_main (serialize_main_t * m, va_list * va)
1066 {
1067   elog_main_t *em = va_arg (*va, elog_main_t *);
1068   uword i;
1069   u32 rs;
1070
1071   unserialize_check_magic (m, elog_serialize_magic,
1072                            strlen (elog_serialize_magic));
1073
1074   unserialize_integer (m, &rs, sizeof (u32));
1075   em->event_ring_size = rs;
1076   elog_init (em, em->event_ring_size);
1077
1078   unserialize (m, unserialize_elog_time_stamp, &em->serialize_time);
1079   unserialize (m, unserialize_elog_time_stamp, &em->init_time);
1080   em->nsec_per_cpu_clock = elog_nsec_per_clock (em);
1081
1082   vec_unserialize (m, &em->event_types, unserialize_elog_event_type);
1083   for (i = 0; i < vec_len (em->event_types); i++)
1084     new_event_type (em, i);
1085
1086   vec_unserialize (m, &em->tracks, unserialize_elog_track);
1087   vec_unserialize (m, &em->string_table, unserialize_vec_8);
1088
1089   {
1090     u32 ne;
1091     elog_event_t *e;
1092
1093     unserialize_integer (m, &ne, sizeof (u32));
1094     vec_resize (em->events, ne);
1095     vec_foreach (e, em->events)
1096       unserialize (m, unserialize_elog_event, em, e);
1097   }
1098 }
1099
1100 /*
1101  * fd.io coding-style-patch-verification: ON
1102  *
1103  * Local Variables:
1104  * eval: (c-set-style "gnu")
1105  * End:
1106  */