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