7ae4ea1d2e27e9fca3bea5a71dc529621626ddad
[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   memset(&newt, 0, sizeof (newt));
586
587   elog_get_events (src);
588   elog_get_events (dst);
589
590   string_table_offset_for_src_events = vec_len (dst->string_table);
591   vec_append (dst->string_table, src->string_table);
592
593   l = vec_len (dst->events);
594   vec_add (dst->events, src->events, vec_len (src->events));
595
596   /* Prepend the supplied tag (if any) to all dst track names */
597   if (dst_tag)
598     {
599       for (i = 0; i < vec_len(dst->tracks); i++)
600         {
601           elog_track_t * t = vec_elt_at_index (dst->tracks, i);
602           char * new_name;
603
604           new_name = (char *) format (0, "%s:%s%c", dst_tag, t->name, 0);
605           vec_free (t->name);
606           t->name = new_name;
607         }
608     }
609   
610   track_offset_for_src_tracks = vec_len (dst->tracks);
611   
612   /* Copy / tag source tracks */
613   for (i = 0; i < vec_len (src->tracks); i++)
614     {
615       elog_track_t * t = vec_elt_at_index (src->tracks, i);
616       if (src_tag)
617         newt.name = (char *) format (0, "%s:%s%c", src_tag, t->name, 0);
618       else
619         newt.name = (char *) format (0, "%s%c", t->name, 0);
620       (void) elog_track_register (dst, &newt);
621       vec_free (newt.name);
622     }
623   
624   /* Across all (copied) src events... */
625   for (e = dst->events + l; e < vec_end (dst->events); e++)
626     {
627       elog_event_type_t * t = vec_elt_at_index (src->event_types, e->type);
628       
629       /* Remap type from src -> dst. */
630       e->type = find_or_create_type (dst, t);
631
632       /* Remap string table offsets for 'T' format args */
633       maybe_fix_string_table_offset (e, t, string_table_offset_for_src_events);
634       
635       /* Remap track */
636       e->track += track_offset_for_src_tracks;
637     }
638
639   /* Adjust event times for relative starting times of event streams. */
640   {
641     f64 dt_event, dt_os_nsec, dt_clock_nsec;
642
643     /* Set clock parameters if dst was not generated by unserialize. */
644     if (dst->serialize_time.cpu == 0)
645       {
646         dst->init_time = src->init_time;
647         dst->serialize_time = src->serialize_time;
648         dst->nsec_per_cpu_clock = src->nsec_per_cpu_clock;
649       }
650
651     dt_os_nsec = elog_time_stamp_diff_os_nsec (&src->init_time, &dst->init_time);
652
653     dt_event = dt_os_nsec;
654     dt_clock_nsec = (elog_time_stamp_diff_cpu (&src->init_time, &dst->init_time)
655                      * .5*(dst->nsec_per_cpu_clock + src->nsec_per_cpu_clock));
656
657     /* Heuristic to see if src/dst came from same time source.
658        If frequencies are "the same" and os clock and cpu clock agree
659        to within 100e-9 secs about time difference between src/dst
660        init_time, then we use cpu clock.  Otherwise we use OS clock. */
661     if (fabs (src->nsec_per_cpu_clock - dst->nsec_per_cpu_clock) < 1e-2
662         && fabs (dt_os_nsec - dt_clock_nsec) < 100)
663       dt_event = dt_clock_nsec;
664
665     /* Convert to seconds. */
666     dt_event *= 1e-9;
667
668     if (dt_event > 0)
669       {
670         /* Src started after dst. */
671         for (e = dst->events + l; e < vec_end (dst->events); e++)
672           e->time += dt_event;
673       }
674     else
675       {
676         /* Dst started after src. */
677         for (e = dst->events + 0; e < dst->events + l; e++)
678           e->time += dt_event;
679       }
680   }
681
682   /* Sort events by increasing time. */
683   vec_sort_with_function (dst->events, elog_cmp);
684
685   /* Recreate the event ring or the results won't serialize */
686   {
687     int i;
688
689     ASSERT (dst->cpu_timer.seconds_per_clock);
690
691     elog_alloc (dst, vec_len (dst->events));
692     for (i = 0; i < vec_len(dst->events); i++)
693       {
694         elog_event_t *es, *ed;
695         
696         es = dst->events + i;
697         ed = dst->event_ring + i;
698         
699         ed[0] = es[0];
700         
701         /* Invert elog_peek_events calculation */
702         ed->time_cycles = 
703           (es->time/dst->cpu_timer.seconds_per_clock) + dst->init_time.cpu;
704       }
705     dst->n_total_events = vec_len (dst->events);
706   }
707 }
708
709 static void
710 serialize_elog_event (serialize_main_t * m, va_list * va)
711 {
712   elog_main_t * em = va_arg (*va, elog_main_t *);
713   elog_event_t * e = va_arg (*va, elog_event_t *);
714   elog_event_type_t * t = vec_elt_at_index (em->event_types, e->type);
715   u8 * d = e->data;
716   u8 * p = (u8 *) t->format_args;
717
718   serialize_integer (m, e->type, sizeof (e->type));
719   serialize_integer (m, e->track, sizeof (e->track));
720   serialize (m, serialize_f64, e->time);
721
722   while (*p)
723     {
724       uword n_digits, n_bytes = 0;
725
726       n_digits = parse_2digit_decimal ((char *) p + 1, &n_bytes);
727
728       switch (p[0])
729         {
730         case 'i':
731         case 't':
732         case 'T':
733           if (n_bytes == 1)
734             serialize_integer (m, d[0], sizeof (u8));
735           else if (n_bytes == 2)
736             serialize_integer (m, clib_mem_unaligned (d, u16), sizeof (u16));
737           else if (n_bytes == 4)
738             serialize_integer (m, clib_mem_unaligned (d, u32), sizeof (u32));
739           else if (n_bytes == 8)
740             serialize (m, serialize_64, clib_mem_unaligned (d, u64));
741           else
742             ASSERT (0);
743           break;
744
745         case 's':
746           serialize_cstring (m, (char *) d);
747           if (n_bytes == 0)
748             n_bytes = strlen ((char *) d) + 1;
749           break;
750
751         case 'f':
752           if (n_bytes == 4)
753             serialize (m, serialize_f32, clib_mem_unaligned (d, f32));
754           else if (n_bytes == 8)
755             serialize (m, serialize_f64, clib_mem_unaligned (d, f64));
756           else
757             ASSERT (0);
758           break;
759
760         default:
761           ASSERT (0);
762           break;
763         }
764
765       p += 1 + n_digits;
766       d += n_bytes;
767     }
768 }
769
770 static void
771 unserialize_elog_event (serialize_main_t * m, va_list * va)
772 {
773   elog_main_t * em = va_arg (*va, elog_main_t *);
774   elog_event_t * e = va_arg (*va, elog_event_t *);
775   elog_event_type_t * t;
776   u8 * p, * d;
777
778   {
779     u16 tmp[2];
780
781     unserialize_integer (m, &tmp[0], sizeof (e->type));
782     unserialize_integer (m, &tmp[1], sizeof (e->track));
783
784     e->type = tmp[0];
785     e->track = tmp[1];
786
787     /* Make sure it fits. */
788     ASSERT (e->type == tmp[0]);
789     ASSERT (e->track == tmp[1]);
790   }
791
792   t = vec_elt_at_index (em->event_types, e->type);
793
794   unserialize (m, unserialize_f64, &e->time);
795
796   d = e->data;
797   p = (u8 *) t->format_args;
798
799   while (p && *p)
800     {
801       uword n_digits, n_bytes = 0;
802       u32 tmp;
803
804       n_digits = parse_2digit_decimal ((char *) p + 1, &n_bytes);
805
806       switch (p[0])
807         {
808         case 'i':
809         case 't':
810         case 'T':
811           if (n_bytes == 1)
812             {
813               unserialize_integer (m, &tmp, sizeof (u8));
814               d[0] = tmp;
815             }
816           else if (n_bytes == 2)
817             {
818               unserialize_integer (m, &tmp, sizeof (u16));
819               clib_mem_unaligned (d, u16) = tmp;
820             }
821           else if (n_bytes == 4)
822             {
823               unserialize_integer (m, &tmp, sizeof (u32));
824               clib_mem_unaligned (d, u32) = tmp;
825             }
826           else if (n_bytes == 8)
827             {
828               u64 x;
829               unserialize (m, unserialize_64, &x);
830               clib_mem_unaligned (d, u64) = x;
831             }
832           else
833             ASSERT (0);
834           break;
835
836         case 's': {
837           char * t;
838           unserialize_cstring (m, &t);
839           if (n_bytes == 0)
840             n_bytes = strlen (t) + 1;
841           clib_memcpy (d, t, clib_min (n_bytes, vec_len (t)));
842           vec_free (t);
843           break;
844         }
845
846         case 'f':
847           if (n_bytes == 4)
848             {
849               f32 x;
850               unserialize (m, unserialize_f32, &x);
851               clib_mem_unaligned (d, f32) = x;
852             }
853           else if (n_bytes == 8)
854             {
855               f64 x;
856               unserialize (m, unserialize_f64, &x);
857               clib_mem_unaligned (d, f64) = x;
858             }
859           else
860             ASSERT (0);
861           break;
862
863         default:
864           ASSERT (0);
865           break;
866         }
867
868       p += 1 + n_digits;
869       d += n_bytes;
870     }
871 }
872
873 static void
874 serialize_elog_event_type (serialize_main_t * m, va_list * va)
875 {
876   elog_event_type_t * t = va_arg (*va, elog_event_type_t *);
877   int n = va_arg (*va, int);
878   int i, j;
879   for (i = 0; i < n; i++)
880     {
881       serialize_cstring (m, t[i].format);
882       serialize_cstring (m, t[i].format_args);
883       serialize_integer (m, t[i].type_index_plus_one, sizeof (t->type_index_plus_one));
884       serialize_integer (m, t[i].n_enum_strings, sizeof (t[i].n_enum_strings));
885       for (j = 0; j < t[i].n_enum_strings; j++)
886         serialize_cstring (m, t[i].enum_strings_vector[j]);
887     }
888 }
889
890 static void
891 unserialize_elog_event_type (serialize_main_t * m, va_list * va)
892 {
893   elog_event_type_t * t = va_arg (*va, elog_event_type_t *);
894   int n = va_arg (*va, int);
895   int i, j;
896   for (i = 0; i < n; i++)
897     {
898       unserialize_cstring (m, &t[i].format);
899       unserialize_cstring (m, &t[i].format_args);
900       unserialize_integer (m, &t[i].type_index_plus_one, sizeof (t->type_index_plus_one));
901       unserialize_integer (m, &t[i].n_enum_strings, sizeof (t[i].n_enum_strings));
902       vec_resize (t[i].enum_strings_vector, t[i].n_enum_strings);
903       for (j = 0; j < t[i].n_enum_strings; j++)
904         unserialize_cstring (m, &t[i].enum_strings_vector[j]);
905     }
906 }
907
908 static void
909 serialize_elog_track (serialize_main_t * m, va_list * va)
910 {
911   elog_track_t * t = va_arg (*va, elog_track_t *);
912   int n = va_arg (*va, int);
913   int i;
914   for (i = 0; i < n; i++)
915     {
916       serialize_cstring (m, t[i].name);
917     }
918 }
919
920 static void
921 unserialize_elog_track (serialize_main_t * m, va_list * va)
922 {
923   elog_track_t * t = va_arg (*va, elog_track_t *);
924   int n = va_arg (*va, int);
925   int i;
926   for (i = 0; i < n; i++)
927     {
928       unserialize_cstring (m, &t[i].name);
929     }
930 }
931
932 static void
933 serialize_elog_time_stamp (serialize_main_t * m, va_list * va)
934 {
935   elog_time_stamp_t * st = va_arg (*va, elog_time_stamp_t *);
936   serialize (m, serialize_64, st->os_nsec);
937   serialize (m, serialize_64, st->cpu);
938 }
939
940 static void
941 unserialize_elog_time_stamp (serialize_main_t * m, va_list * va)
942 {
943   elog_time_stamp_t * st = va_arg (*va, elog_time_stamp_t *);
944   unserialize (m, unserialize_64, &st->os_nsec);
945   unserialize (m, unserialize_64, &st->cpu);
946 }
947
948 static char * elog_serialize_magic = "elog v0";
949
950 void
951 serialize_elog_main (serialize_main_t * m, va_list * va)
952 {
953   elog_main_t * em = va_arg (*va, elog_main_t *);
954   elog_event_t * e;
955
956   serialize_magic (m, elog_serialize_magic, strlen (elog_serialize_magic));
957
958   serialize_integer (m, em->event_ring_size, sizeof (u32));
959
960   elog_time_now (&em->serialize_time);
961   serialize (m, serialize_elog_time_stamp, &em->serialize_time);
962   serialize (m, serialize_elog_time_stamp, &em->init_time);
963
964   vec_serialize (m, em->event_types, serialize_elog_event_type);
965   vec_serialize (m, em->tracks, serialize_elog_track);
966   vec_serialize (m, em->string_table, serialize_vec_8);
967
968   /* Free old events (cached) in case they have changed. */
969   vec_free (em->events);
970   elog_get_events (em);
971
972   serialize_integer (m, vec_len (em->events), sizeof (u32));
973
974   /* SMP logs can easily have local time paradoxes... */
975   vec_sort_with_function (em->events, elog_cmp);
976
977   vec_foreach (e, em->events)
978     serialize (m, serialize_elog_event, em, e);
979 }
980
981 void
982 unserialize_elog_main (serialize_main_t * m, va_list * va)
983 {
984   elog_main_t * em = va_arg (*va, elog_main_t *);
985   uword i;
986   u32 rs;
987
988   unserialize_check_magic (m, elog_serialize_magic,
989                            strlen (elog_serialize_magic));
990
991   unserialize_integer (m, &rs, sizeof (u32));
992   em->event_ring_size = rs;
993   elog_init (em, em->event_ring_size);
994
995   unserialize (m, unserialize_elog_time_stamp, &em->serialize_time);
996   unserialize (m, unserialize_elog_time_stamp, &em->init_time);
997   em->nsec_per_cpu_clock = elog_nsec_per_clock (em);
998
999   vec_unserialize (m, &em->event_types, unserialize_elog_event_type);
1000   for (i = 0; i < vec_len (em->event_types); i++)
1001     new_event_type (em, i);
1002
1003   vec_unserialize (m, &em->tracks, unserialize_elog_track);
1004   vec_unserialize (m, &em->string_table, unserialize_vec_8);
1005
1006   {
1007     u32 ne;
1008     elog_event_t * e;
1009
1010     unserialize_integer (m, &ne, sizeof (u32));
1011     vec_resize (em->events, ne);
1012     vec_foreach (e, em->events)
1013       unserialize (m, unserialize_elog_event, em, e);
1014   }
1015 }