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:
7 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 Copyright (c) 2005,2009 Eliot Dresselhaus
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:
26 The above copyright notice and this permission notice shall be
27 included in all copies or substantial portions of the Software.
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.
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>
46 elog_lock (elog_main_t * em)
48 if (PREDICT_FALSE (em->lock != 0))
49 while (__sync_lock_test_and_set (em->lock, 1))
54 elog_unlock (elog_main_t * em)
56 if (PREDICT_FALSE (em->lock != 0))
58 CLIB_MEMORY_BARRIER ();
63 /* Non-inline version. */
65 elog_event_data (elog_main_t * em,
66 elog_event_type_t * type, elog_track_t * track, u64 cpu_time)
68 return elog_event_data_inline (em, type, track, cpu_time);
72 new_event_type (elog_main_t * em, uword i)
74 elog_event_type_t *t = vec_elt_at_index (em->event_types, i);
76 if (!em->event_type_by_format)
77 em->event_type_by_format =
78 hash_create_vec ( /* size */ 0, sizeof (u8), sizeof (uword));
80 t->type_index_plus_one = i + 1;
81 hash_set_mem (em->event_type_by_format, t->format, i);
85 find_or_create_type (elog_main_t * em, elog_event_type_t * t)
87 uword *p = hash_get_mem (em->event_type_by_format, t->format);
94 i = vec_len (em->event_types);
95 vec_add1 (em->event_types, t[0]);
96 new_event_type (em, i);
102 /* External function to register types. */
104 elog_event_type_register (elog_main_t * em, elog_event_type_t * t)
106 elog_event_type_t *static_type = t;
111 /* Multiple simultaneous registration attempts, */
112 if (t->type_index_plus_one > 0)
115 return t->type_index_plus_one - 1;
118 l = vec_len (em->event_types);
120 t->type_index_plus_one = 1 + l;
124 /* If format args are not specified try to be smart about providing defaults
125 so most of the time user does not have to specify them. */
131 l = strlen (t->format);
132 for (i = 0; i < l; i++)
134 if (t->format[i] != '%')
138 if (t->format[i + 1] == '%') /* %% */
141 switch (t->format[i + 1])
147 this_arg = "i4"; /* size of u32 */
150 this_arg = "f8"; /* defaults to f64 */
153 this_arg = "s0"; /* defaults to null terminated string. */
158 (char *) format ((u8 *) t->format_args, "%s", this_arg);
161 /* Null terminate. */
162 vec_add1 (t->format_args, 0);
165 vec_add1 (em->event_types, t[0]);
167 t = em->event_types + l;
169 /* Make copies of strings for hashing etc. */
171 t->format = (char *) format (0, "%s %s%c", t->function, t->format, 0);
173 t->format = (char *) format (0, "%s%c", t->format, 0);
175 t->format_args = (char *) format (0, "%s%c", t->format_args, 0);
177 /* Construct string table. */
180 t->n_enum_strings = static_type->n_enum_strings;
181 for (i = 0; i < t->n_enum_strings; i++)
183 if (!static_type->enum_strings[i])
184 static_type->enum_strings[i] = "MISSING";
185 vec_add1 (t->enum_strings_vector,
186 (char *) format (0, "%s%c", static_type->enum_strings[i],
191 new_event_type (em, l);
198 elog_track_register (elog_main_t * em, elog_track_t * t)
204 l = vec_len (em->tracks);
206 t->track_index_plus_one = 1 + l;
210 vec_add1 (em->tracks, t[0]);
214 t->name = (char *) format (0, "%s%c", t->name, 0);
222 parse_2digit_decimal (char *p, uword * number)
227 digits[0] = digits[1] = 0;
228 while (p[i] >= '0' && p[i] <= '9')
232 digits[i] = p[i] - '0';
236 if (i >= 1 && i <= 2)
241 *number = 10 * digits[0] + digits[1];
249 fixed_format (u8 * s, char *fmt, char *result, uword * result_len)
259 if (f[0] == '%' && f[1] != '%')
264 vec_add (s, fmt, f - fmt);
272 /* Skip possible +-= justification. */
273 f += f[0] == '+' || f[0] == '-' || f[0] == '=';
275 /* Skip possible X.Y width. */
276 while ((f[0] >= '0' && f[0] <= '9') || f[0] == '.')
279 /* Skip wlL as in e.g. %Ld. */
280 f += f[0] == 'w' || f[0] == 'l' || f[0] == 'L';
282 /* Finally skip format letter. */
285 ASSERT (*result_len > f - percent);
286 l = clib_min (f - percent, *result_len - 1);
287 clib_memcpy (result, percent, l);
291 *result_len = f - fmt;
296 format_elog_event (u8 * s, va_list * va)
298 elog_main_t *em = va_arg (*va, elog_main_t *);
299 elog_event_t *e = va_arg (*va, elog_event_t *);
300 elog_event_type_t *t;
302 void *d = (u8 *) e->data;
305 t = vec_elt_at_index (em->event_types, e->type);
311 uword n_bytes = 0, n_digits, f_bytes = 0;
313 f_bytes = sizeof (arg_format);
314 s = fixed_format (s, f, arg_format, &f_bytes);
317 if (a == 0 || a[0] == 0)
319 /* Format must also be at end. */
324 /* Don't go past end of event data. */
325 ASSERT (d < (void *) (e->data + sizeof (e->data)));
327 n_digits = parse_2digit_decimal (a + 1, &n_bytes);
339 else if (n_bytes == 2)
340 i = clib_mem_unaligned (d, u16);
341 else if (n_bytes == 4)
342 i = clib_mem_unaligned (d, u32);
343 else if (n_bytes == 8)
344 l = clib_mem_unaligned (d, u64);
350 vec_elt (t->enum_strings_vector, n_bytes == 8 ? l : i);
351 s = format (s, arg_format, e);
353 else if (a[0] == 'T')
356 vec_elt_at_index (em->string_table, n_bytes == 8 ? l : i);
357 s = format (s, arg_format, e);
359 else if (n_bytes == 8)
360 s = format (s, arg_format, l);
362 s = format (s, arg_format, i);
370 x = clib_mem_unaligned (d, f32);
371 else if (n_bytes == 8)
372 x = clib_mem_unaligned (d, f64);
375 s = format (s, arg_format, x);
380 s = format (s, arg_format, d);
382 n_bytes = strlen (d) + 1;
390 ASSERT (n_digits > 0 && n_digits <= 2);
399 format_elog_track (u8 * s, va_list * va)
401 elog_main_t *em = va_arg (*va, elog_main_t *);
402 elog_event_t *e = va_arg (*va, elog_event_t *);
403 elog_track_t *t = vec_elt_at_index (em->tracks, e->track);
404 return format (s, "%s", t->name);
408 elog_time_now (elog_time_stamp_t * et)
410 u64 cpu_time_now, os_time_now_nsec;
415 #include <sys/syscall.h>
416 syscall (SYS_clock_gettime, CLOCK_REALTIME, &ts);
417 cpu_time_now = clib_cpu_time_now ();
418 /* Subtract 3/30/2017's worth of seconds to retain precision */
419 os_time_now_nsec = 1e9 * (ts.tv_sec - 1490885108) + ts.tv_nsec;
422 cpu_time_now = clib_cpu_time_now ();
423 os_time_now_nsec = 0;
426 et->cpu = cpu_time_now;
427 et->os_nsec = os_time_now_nsec;
431 elog_time_stamp_diff_os_nsec (elog_time_stamp_t * t1, elog_time_stamp_t * t2)
433 return (i64) t1->os_nsec - (i64) t2->os_nsec;
437 elog_time_stamp_diff_cpu (elog_time_stamp_t * t1, elog_time_stamp_t * t2)
439 return (i64) t1->cpu - (i64) t2->cpu;
443 elog_nsec_per_clock (elog_main_t * em)
445 return ((f64) elog_time_stamp_diff_os_nsec (&em->serialize_time,
447 / (f64) elog_time_stamp_diff_cpu (&em->serialize_time,
452 elog_alloc (elog_main_t * em, u32 n_events)
455 vec_free (em->event_ring);
457 /* Ring size must be a power of 2. */
458 em->event_ring_size = n_events = max_pow2 (n_events);
460 /* Leave an empty ievent at end so we can always speculatively write
461 and event there (possibly a long form event). */
462 vec_resize_aligned (em->event_ring, n_events, CLIB_CACHE_LINE_BYTES);
466 elog_init (elog_main_t * em, u32 n_events)
468 memset (em, 0, sizeof (em[0]));
473 elog_alloc (em, n_events);
475 clib_time_init (&em->cpu_timer);
477 em->n_total_events_disable_limit = ~0;
480 em->default_track.name = "default";
481 elog_track_register (em, &em->default_track);
483 elog_time_now (&em->init_time);
486 /* Returns number of events in ring and start index. */
488 elog_event_range (elog_main_t * em, uword * lo)
490 uword l = em->event_ring_size;
491 u64 i = em->n_total_events;
493 /* Ring never wrapped? */
509 elog_peek_events (elog_main_t * em)
511 elog_event_t *e, *f, *es = 0;
514 n = elog_event_range (em, &j);
515 for (i = 0; i < n; i++)
518 f = vec_elt_at_index (em->event_ring, j);
521 /* Convert absolute time from cycles to seconds from start. */
524 em->init_time.cpu) * em->cpu_timer.seconds_per_clock;
526 j = (j + 1) & (em->event_ring_size - 1);
532 /* Add a formatted string to the string table. */
534 elog_string (elog_main_t * em, char *fmt, ...)
540 offset = vec_len (em->string_table);
541 em->string_table = (char *) va_format ((u8 *) em->string_table, fmt, &va);
544 /* Null terminate string if it is not already. */
545 if (vec_end (em->string_table)[-1] != 0)
546 vec_add1 (em->string_table, 0);
552 elog_get_events (elog_main_t * em)
555 em->events = elog_peek_events (em);
560 maybe_fix_string_table_offset (elog_event_t * e,
561 elog_event_type_t * t, u32 offset)
563 void *d = (u8 *) e->data;
573 uword n_bytes = 0, n_digits;
578 /* Don't go past end of event data. */
579 ASSERT (d < (void *) (e->data + sizeof (e->data)));
581 n_digits = parse_2digit_decimal (a + 1, &n_bytes);
585 ASSERT (n_bytes == 4);
586 clib_mem_unaligned (d, u32) += offset;
600 ASSERT (n_digits > 0 && n_digits <= 2);
607 elog_cmp (void *a1, void *a2)
609 elog_event_t *e1 = a1;
610 elog_event_t *e2 = a2;
612 if (e1->time < e2->time)
615 if (e1->time > e2->time)
622 * merge two event logs. Complicated and cranky.
625 elog_merge (elog_main_t * dst, u8 * dst_tag, elog_main_t * src, u8 * src_tag,
630 u32 string_table_offset_for_src_events;
631 u32 track_offset_for_src_tracks;
635 memset (&newt, 0, sizeof (newt));
637 /* Acquire src and dst events */
638 elog_get_events (src);
639 elog_get_events (dst);
641 string_table_offset_for_src_events = vec_len (dst->string_table);
642 vec_append (dst->string_table, src->string_table);
644 l = vec_len (dst->events);
645 vec_append (dst->events, src->events);
647 /* Prepend the supplied tag (if any) to all dst track names */
650 for (i = 0; i < vec_len (dst->tracks); i++)
652 elog_track_t *t = vec_elt_at_index (dst->tracks, i);
655 new_name = (char *) format (0, "%s:%s%c", dst_tag, t->name, 0);
662 * Remember where we started allocating new tracks while merging
664 track_offset_for_src_tracks = vec_len (dst->tracks);
666 /* Copy / tag source tracks */
667 for (i = 0; i < vec_len (src->tracks); i++)
669 elog_track_t *t = vec_elt_at_index (src->tracks, i);
671 newt.name = (char *) format (0, "%s:%s%c", src_tag, t->name, 0);
673 newt.name = (char *) format (0, "%s%c", t->name, 0);
674 (void) elog_track_register (dst, &newt);
675 vec_free (newt.name);
678 /* Across all (copied) src events... */
679 for (e = dst->events + l; e < vec_end (dst->events); e++)
681 elog_event_type_t *t = vec_elt_at_index (src->event_types, e->type);
683 /* Remap type from src -> dst. */
684 e->type = find_or_create_type (dst, t);
686 /* Remap string table offsets for 'T' format args */
687 maybe_fix_string_table_offset (e, t,
688 string_table_offset_for_src_events);
691 e->track += track_offset_for_src_tracks;
694 /* Adjust event times for relative starting times of event streams. */
696 f64 dt_event, dt_os_nsec, dt_clock_nsec;
698 /* Set clock parameters if dst was not generated by unserialize. */
699 if (dst->serialize_time.cpu == 0)
701 dst->init_time = src->init_time;
702 dst->serialize_time = src->serialize_time;
703 dst->nsec_per_cpu_clock = src->nsec_per_cpu_clock;
707 elog_time_stamp_diff_os_nsec (&src->init_time, &dst->init_time);
709 dt_event = dt_os_nsec;
711 (elog_time_stamp_diff_cpu (&src->init_time, &dst->init_time) * .5 *
712 (dst->nsec_per_cpu_clock + src->nsec_per_cpu_clock));
715 * Heuristic to see if src/dst came from same time source.
716 * If frequencies are "the same" and os clock and cpu clock agree
717 * to within 100e-9 secs about time difference between src/dst
718 * init_time, then we use cpu clock. Otherwise we use OS clock.
720 * When merging event logs from different systems, time paradoxes
721 * at the O(1ms) level are to be expected. Hence, the "align_tweak"
722 * parameter. If two events logged on different processors are known
723 * to occur in a specific order - and with a reasonably-estimated
724 * interval - supply a non-zero "align_tweak" parameter
726 if (fabs (src->nsec_per_cpu_clock - dst->nsec_per_cpu_clock) < 1e-2
727 && fabs (dt_os_nsec - dt_clock_nsec) < 100)
728 dt_event = dt_clock_nsec;
730 /* Convert to seconds. */
734 * Move the earlier set of events later, to avoid creating
735 * events which preceed the Big Bang (aka have negative timestamps).
737 * Not to any scale, we have something like the following picture:
739 * DST capture start point
743 * SRC capture start point
745 * In this case dt_event is positive, src started after dst,
746 * to put src events onto a common timebase we have to move them
747 * forward in time. Naturally, the opposite case is
748 * possible, too: dt_event will be negative, and so we have to
749 * move dst events forward in time by the |dt_event|.
750 * In both cases, we add align_tweak.
754 /* Src started after dst. */
755 for (e = dst->events + l; e < vec_end (dst->events); e++)
756 e->time += dt_event + align_tweak;
760 /* Dst started after src. */
761 dt_event = -dt_event;
762 for (e = dst->events + 0; e < dst->events + l; e++)
763 e->time += dt_event + align_tweak;
767 /* Sort events by increasing time. */
768 vec_sort_with_function (dst->events, elog_cmp);
770 dst->n_total_events = vec_len (dst->events);
772 /* Recreate the event ring or the results won't serialize */
776 ASSERT (dst->cpu_timer.seconds_per_clock);
778 elog_alloc (dst, vec_len (dst->events));
779 for (i = 0; i < vec_len (dst->events); i++)
781 elog_event_t *es, *ed;
783 es = dst->events + i;
784 ed = dst->event_ring + i;
792 serialize_elog_event (serialize_main_t * m, va_list * va)
794 elog_main_t *em = va_arg (*va, elog_main_t *);
795 elog_event_t *e = va_arg (*va, elog_event_t *);
796 elog_event_type_t *t = vec_elt_at_index (em->event_types, e->type);
798 u8 *p = (u8 *) t->format_args;
800 serialize_integer (m, e->type, sizeof (e->type));
801 serialize_integer (m, e->track, sizeof (e->track));
802 serialize (m, serialize_f64, e->time);
806 uword n_digits, n_bytes = 0;
808 n_digits = parse_2digit_decimal ((char *) p + 1, &n_bytes);
816 serialize_integer (m, d[0], sizeof (u8));
817 else if (n_bytes == 2)
818 serialize_integer (m, clib_mem_unaligned (d, u16), sizeof (u16));
819 else if (n_bytes == 4)
820 serialize_integer (m, clib_mem_unaligned (d, u32), sizeof (u32));
821 else if (n_bytes == 8)
822 serialize (m, serialize_64, clib_mem_unaligned (d, u64));
828 serialize_cstring (m, (char *) d);
830 n_bytes = strlen ((char *) d) + 1;
835 serialize (m, serialize_f32, clib_mem_unaligned (d, f32));
836 else if (n_bytes == 8)
837 serialize (m, serialize_f64, clib_mem_unaligned (d, f64));
853 unserialize_elog_event (serialize_main_t * m, va_list * va)
855 elog_main_t *em = va_arg (*va, elog_main_t *);
856 elog_event_t *e = va_arg (*va, elog_event_t *);
857 elog_event_type_t *t;
863 unserialize_integer (m, &tmp[0], sizeof (e->type));
864 unserialize_integer (m, &tmp[1], sizeof (e->track));
869 /* Make sure it fits. */
870 ASSERT (e->type == tmp[0]);
871 ASSERT (e->track == tmp[1]);
874 t = vec_elt_at_index (em->event_types, e->type);
876 unserialize (m, unserialize_f64, &e->time);
879 p = (u8 *) t->format_args;
883 uword n_digits, n_bytes = 0;
886 n_digits = parse_2digit_decimal ((char *) p + 1, &n_bytes);
895 unserialize_integer (m, &tmp, sizeof (u8));
898 else if (n_bytes == 2)
900 unserialize_integer (m, &tmp, sizeof (u16));
901 clib_mem_unaligned (d, u16) = tmp;
903 else if (n_bytes == 4)
905 unserialize_integer (m, &tmp, sizeof (u32));
906 clib_mem_unaligned (d, u32) = tmp;
908 else if (n_bytes == 8)
911 unserialize (m, unserialize_64, &x);
912 clib_mem_unaligned (d, u64) = x;
921 unserialize_cstring (m, &t);
923 n_bytes = strlen (t) + 1;
924 clib_memcpy (d, t, clib_min (n_bytes, vec_len (t)));
933 unserialize (m, unserialize_f32, &x);
934 clib_mem_unaligned (d, f32) = x;
936 else if (n_bytes == 8)
939 unserialize (m, unserialize_f64, &x);
940 clib_mem_unaligned (d, f64) = x;
957 serialize_elog_event_type (serialize_main_t * m, va_list * va)
959 elog_event_type_t *t = va_arg (*va, elog_event_type_t *);
960 int n = va_arg (*va, int);
962 for (i = 0; i < n; i++)
964 serialize_cstring (m, t[i].format);
965 serialize_cstring (m, t[i].format_args);
966 serialize_integer (m, t[i].type_index_plus_one,
967 sizeof (t->type_index_plus_one));
968 serialize_integer (m, t[i].n_enum_strings,
969 sizeof (t[i].n_enum_strings));
970 for (j = 0; j < t[i].n_enum_strings; j++)
971 serialize_cstring (m, t[i].enum_strings_vector[j]);
976 unserialize_elog_event_type (serialize_main_t * m, va_list * va)
978 elog_event_type_t *t = va_arg (*va, elog_event_type_t *);
979 int n = va_arg (*va, int);
981 for (i = 0; i < n; i++)
983 unserialize_cstring (m, &t[i].format);
984 unserialize_cstring (m, &t[i].format_args);
985 unserialize_integer (m, &t[i].type_index_plus_one,
986 sizeof (t->type_index_plus_one));
987 unserialize_integer (m, &t[i].n_enum_strings,
988 sizeof (t[i].n_enum_strings));
989 vec_resize (t[i].enum_strings_vector, t[i].n_enum_strings);
990 for (j = 0; j < t[i].n_enum_strings; j++)
991 unserialize_cstring (m, &t[i].enum_strings_vector[j]);
996 serialize_elog_track (serialize_main_t * m, va_list * va)
998 elog_track_t *t = va_arg (*va, elog_track_t *);
999 int n = va_arg (*va, int);
1001 for (i = 0; i < n; i++)
1003 serialize_cstring (m, t[i].name);
1008 unserialize_elog_track (serialize_main_t * m, va_list * va)
1010 elog_track_t *t = va_arg (*va, elog_track_t *);
1011 int n = va_arg (*va, int);
1013 for (i = 0; i < n; i++)
1015 unserialize_cstring (m, &t[i].name);
1020 serialize_elog_time_stamp (serialize_main_t * m, va_list * va)
1022 elog_time_stamp_t *st = va_arg (*va, elog_time_stamp_t *);
1023 serialize (m, serialize_64, st->os_nsec);
1024 serialize (m, serialize_64, st->cpu);
1028 unserialize_elog_time_stamp (serialize_main_t * m, va_list * va)
1030 elog_time_stamp_t *st = va_arg (*va, elog_time_stamp_t *);
1031 unserialize (m, unserialize_64, &st->os_nsec);
1032 unserialize (m, unserialize_64, &st->cpu);
1035 static char *elog_serialize_magic = "elog v0";
1038 serialize_elog_main (serialize_main_t * m, va_list * va)
1040 elog_main_t *em = va_arg (*va, elog_main_t *);
1041 int flush_ring = va_arg (*va, int);
1044 serialize_magic (m, elog_serialize_magic, strlen (elog_serialize_magic));
1046 serialize_integer (m, em->event_ring_size, sizeof (u32));
1048 elog_time_now (&em->serialize_time);
1049 serialize (m, serialize_elog_time_stamp, &em->serialize_time);
1050 serialize (m, serialize_elog_time_stamp, &em->init_time);
1052 vec_serialize (m, em->event_types, serialize_elog_event_type);
1053 vec_serialize (m, em->tracks, serialize_elog_track);
1054 vec_serialize (m, em->string_table, serialize_vec_8);
1056 /* Free old events (cached) in case they have changed. */
1059 vec_free (em->events);
1060 elog_get_events (em);
1063 serialize_integer (m, vec_len (em->events), sizeof (u32));
1065 /* SMP logs can easily have local time paradoxes... */
1066 vec_sort_with_function (em->events, elog_cmp);
1068 vec_foreach (e, em->events) serialize (m, serialize_elog_event, em, e);
1072 unserialize_elog_main (serialize_main_t * m, va_list * va)
1074 elog_main_t *em = va_arg (*va, elog_main_t *);
1078 unserialize_check_magic (m, elog_serialize_magic,
1079 strlen (elog_serialize_magic));
1081 unserialize_integer (m, &rs, sizeof (u32));
1082 em->event_ring_size = rs;
1083 elog_init (em, em->event_ring_size);
1085 unserialize (m, unserialize_elog_time_stamp, &em->serialize_time);
1086 unserialize (m, unserialize_elog_time_stamp, &em->init_time);
1087 em->nsec_per_cpu_clock = elog_nsec_per_clock (em);
1089 vec_unserialize (m, &em->event_types, unserialize_elog_event_type);
1090 for (i = 0; i < vec_len (em->event_types); i++)
1091 new_event_type (em, i);
1093 vec_unserialize (m, &em->tracks, unserialize_elog_track);
1094 vec_unserialize (m, &em->string_table, unserialize_vec_8);
1100 unserialize_integer (m, &ne, sizeof (u32));
1101 vec_resize (em->events, ne);
1102 vec_foreach (e, em->events)
1103 unserialize (m, unserialize_elog_event, em, e);
1108 * fd.io coding-style-patch-verification: ON
1111 * eval: (c-set-style "gnu")