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