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 l = vec_len (em->event_types);
113 t->type_index_plus_one = 1 + l;
117 /* If format args are not specified try to be smart about providing defaults
118 so most of the time user does not have to specify them. */
124 l = strlen (t->format);
125 for (i = 0; i < l; i++)
127 if (t->format[i] != '%')
131 if (t->format[i + 1] == '%') /* %% */
134 switch (t->format[i + 1])
140 this_arg = "i4"; /* size of u32 */
143 this_arg = "f8"; /* defaults to f64 */
146 this_arg = "s0"; /* defaults to null terminated string. */
151 (char *) format ((u8 *) t->format_args, "%s", this_arg);
154 /* Null terminate. */
155 vec_add1 (t->format_args, 0);
158 vec_add1 (em->event_types, t[0]);
160 t = em->event_types + l;
162 /* Make copies of strings for hashing etc. */
164 t->format = (char *) format (0, "%s %s%c", t->function, t->format, 0);
166 t->format = (char *) format (0, "%s%c", t->format, 0);
168 t->format_args = (char *) format (0, "%s%c", t->format_args, 0);
170 /* Construct string table. */
173 t->n_enum_strings = static_type->n_enum_strings;
174 for (i = 0; i < t->n_enum_strings; i++)
176 if (!static_type->enum_strings[i])
177 static_type->enum_strings[i] = "MISSING";
178 vec_add1 (t->enum_strings_vector,
179 (char *) format (0, "%s%c", static_type->enum_strings[i],
184 new_event_type (em, l);
191 elog_track_register (elog_main_t * em, elog_track_t * t)
197 l = vec_len (em->tracks);
199 t->track_index_plus_one = 1 + l;
203 vec_add1 (em->tracks, t[0]);
207 t->name = (char *) format (0, "%s%c", t->name, 0);
215 parse_2digit_decimal (char *p, uword * number)
220 digits[0] = digits[1] = 0;
221 while (p[i] >= '0' && p[i] <= '9')
225 digits[i] = p[i] - '0';
229 if (i >= 1 && i <= 2)
234 *number = 10 * digits[0] + digits[1];
242 fixed_format (u8 * s, char *fmt, char *result, uword * result_len)
252 if (f[0] == '%' && f[1] != '%')
257 vec_add (s, fmt, f - fmt);
265 /* Skip possible +-= justification. */
266 f += f[0] == '+' || f[0] == '-' || f[0] == '=';
268 /* Skip possible X.Y width. */
269 while ((f[0] >= '0' && f[0] <= '9') || f[0] == '.')
272 /* Skip wlL as in e.g. %Ld. */
273 f += f[0] == 'w' || f[0] == 'l' || f[0] == 'L';
275 /* Finally skip format letter. */
278 ASSERT (*result_len > f - percent);
279 l = clib_min (f - percent, *result_len - 1);
280 clib_memcpy (result, percent, l);
284 *result_len = f - fmt;
289 format_elog_event (u8 * s, va_list * va)
291 elog_main_t *em = va_arg (*va, elog_main_t *);
292 elog_event_t *e = va_arg (*va, elog_event_t *);
293 elog_event_type_t *t;
295 void *d = (u8 *) e->data;
298 t = vec_elt_at_index (em->event_types, e->type);
304 uword n_bytes = 0, n_digits, f_bytes = 0;
306 f_bytes = sizeof (arg_format);
307 s = fixed_format (s, f, arg_format, &f_bytes);
310 if (a == 0 || a[0] == 0)
312 /* Format must also be at end. */
317 /* Don't go past end of event data. */
318 ASSERT (d < (void *) (e->data + sizeof (e->data)));
320 n_digits = parse_2digit_decimal (a + 1, &n_bytes);
332 else if (n_bytes == 2)
333 i = clib_mem_unaligned (d, u16);
334 else if (n_bytes == 4)
335 i = clib_mem_unaligned (d, u32);
336 else if (n_bytes == 8)
337 l = clib_mem_unaligned (d, u64);
343 vec_elt (t->enum_strings_vector, n_bytes == 8 ? l : i);
344 s = format (s, arg_format, e);
346 else if (a[0] == 'T')
349 vec_elt_at_index (em->string_table, n_bytes == 8 ? l : i);
350 s = format (s, arg_format, e);
352 else if (n_bytes == 8)
353 s = format (s, arg_format, l);
355 s = format (s, arg_format, i);
363 x = clib_mem_unaligned (d, f32);
364 else if (n_bytes == 8)
365 x = clib_mem_unaligned (d, f64);
368 s = format (s, arg_format, x);
373 s = format (s, arg_format, d);
375 n_bytes = strlen (d) + 1;
383 ASSERT (n_digits > 0 && n_digits <= 2);
392 format_elog_track (u8 * s, va_list * va)
394 elog_main_t *em = va_arg (*va, elog_main_t *);
395 elog_event_t *e = va_arg (*va, elog_event_t *);
396 elog_track_t *t = vec_elt_at_index (em->tracks, e->track);
397 return format (s, "%s", t->name);
401 elog_time_now (elog_time_stamp_t * et)
403 u64 cpu_time_now, os_time_now_nsec;
408 #include <sys/syscall.h>
409 syscall (SYS_clock_gettime, CLOCK_REALTIME, &ts);
410 cpu_time_now = clib_cpu_time_now ();
411 /* Subtract 3/30/2017's worth of seconds to retain precision */
412 os_time_now_nsec = 1e9 * (ts.tv_sec - 1490885108) + ts.tv_nsec;
415 cpu_time_now = clib_cpu_time_now ();
416 os_time_now_nsec = 0;
419 et->cpu = cpu_time_now;
420 et->os_nsec = os_time_now_nsec;
424 elog_time_stamp_diff_os_nsec (elog_time_stamp_t * t1, elog_time_stamp_t * t2)
426 return (i64) t1->os_nsec - (i64) t2->os_nsec;
430 elog_time_stamp_diff_cpu (elog_time_stamp_t * t1, elog_time_stamp_t * t2)
432 return (i64) t1->cpu - (i64) t2->cpu;
436 elog_nsec_per_clock (elog_main_t * em)
438 return ((f64) elog_time_stamp_diff_os_nsec (&em->serialize_time,
440 / (f64) elog_time_stamp_diff_cpu (&em->serialize_time,
445 elog_alloc (elog_main_t * em, u32 n_events)
448 vec_free (em->event_ring);
450 /* Ring size must be a power of 2. */
451 em->event_ring_size = n_events = max_pow2 (n_events);
453 /* Leave an empty ievent at end so we can always speculatively write
454 and event there (possibly a long form event). */
455 vec_resize_aligned (em->event_ring, n_events, CLIB_CACHE_LINE_BYTES);
459 elog_init (elog_main_t * em, u32 n_events)
461 memset (em, 0, sizeof (em[0]));
466 elog_alloc (em, n_events);
468 clib_time_init (&em->cpu_timer);
470 em->n_total_events_disable_limit = ~0;
473 em->default_track.name = "default";
474 elog_track_register (em, &em->default_track);
476 elog_time_now (&em->init_time);
479 /* Returns number of events in ring and start index. */
481 elog_event_range (elog_main_t * em, uword * lo)
483 uword l = em->event_ring_size;
484 u64 i = em->n_total_events;
486 /* Ring never wrapped? */
502 elog_peek_events (elog_main_t * em)
504 elog_event_t *e, *f, *es = 0;
507 n = elog_event_range (em, &j);
508 for (i = 0; i < n; i++)
511 f = vec_elt_at_index (em->event_ring, j);
514 /* Convert absolute time from cycles to seconds from start. */
517 em->init_time.cpu) * em->cpu_timer.seconds_per_clock;
519 j = (j + 1) & (em->event_ring_size - 1);
525 /* Add a formatted string to the string table. */
527 elog_string (elog_main_t * em, char *fmt, ...)
533 offset = vec_len (em->string_table);
534 em->string_table = (char *) va_format ((u8 *) em->string_table, fmt, &va);
537 /* Null terminate string if it is not already. */
538 if (vec_end (em->string_table)[-1] != 0)
539 vec_add1 (em->string_table, 0);
545 elog_get_events (elog_main_t * em)
548 em->events = elog_peek_events (em);
553 maybe_fix_string_table_offset (elog_event_t * e,
554 elog_event_type_t * t, u32 offset)
556 void *d = (u8 *) e->data;
566 uword n_bytes = 0, n_digits;
571 /* Don't go past end of event data. */
572 ASSERT (d < (void *) (e->data + sizeof (e->data)));
574 n_digits = parse_2digit_decimal (a + 1, &n_bytes);
578 ASSERT (n_bytes == 4);
579 clib_mem_unaligned (d, u32) += offset;
593 ASSERT (n_digits > 0 && n_digits <= 2);
600 elog_cmp (void *a1, void *a2)
602 elog_event_t *e1 = a1;
603 elog_event_t *e2 = a2;
605 if (e1->time < e2->time)
608 if (e1->time > e2->time)
615 * merge two event logs. Complicated and cranky.
618 elog_merge (elog_main_t * dst, u8 * dst_tag, elog_main_t * src, u8 * src_tag,
623 u32 string_table_offset_for_src_events;
624 u32 track_offset_for_src_tracks;
628 memset (&newt, 0, sizeof (newt));
630 /* Acquire src and dst events */
631 elog_get_events (src);
632 elog_get_events (dst);
634 string_table_offset_for_src_events = vec_len (dst->string_table);
635 vec_append (dst->string_table, src->string_table);
637 l = vec_len (dst->events);
638 vec_append (dst->events, src->events);
640 /* Prepend the supplied tag (if any) to all dst track names */
643 for (i = 0; i < vec_len (dst->tracks); i++)
645 elog_track_t *t = vec_elt_at_index (dst->tracks, i);
648 new_name = (char *) format (0, "%s:%s%c", dst_tag, t->name, 0);
655 * Remember where we started allocating new tracks while merging
657 track_offset_for_src_tracks = vec_len (dst->tracks);
659 /* Copy / tag source tracks */
660 for (i = 0; i < vec_len (src->tracks); i++)
662 elog_track_t *t = vec_elt_at_index (src->tracks, i);
664 newt.name = (char *) format (0, "%s:%s%c", src_tag, t->name, 0);
666 newt.name = (char *) format (0, "%s%c", t->name, 0);
667 (void) elog_track_register (dst, &newt);
668 vec_free (newt.name);
671 /* Across all (copied) src events... */
672 for (e = dst->events + l; e < vec_end (dst->events); e++)
674 elog_event_type_t *t = vec_elt_at_index (src->event_types, e->type);
676 /* Remap type from src -> dst. */
677 e->type = find_or_create_type (dst, t);
679 /* Remap string table offsets for 'T' format args */
680 maybe_fix_string_table_offset (e, t,
681 string_table_offset_for_src_events);
684 e->track += track_offset_for_src_tracks;
687 /* Adjust event times for relative starting times of event streams. */
689 f64 dt_event, dt_os_nsec, dt_clock_nsec;
691 /* Set clock parameters if dst was not generated by unserialize. */
692 if (dst->serialize_time.cpu == 0)
694 dst->init_time = src->init_time;
695 dst->serialize_time = src->serialize_time;
696 dst->nsec_per_cpu_clock = src->nsec_per_cpu_clock;
700 elog_time_stamp_diff_os_nsec (&src->init_time, &dst->init_time);
702 dt_event = dt_os_nsec;
704 (elog_time_stamp_diff_cpu (&src->init_time, &dst->init_time) * .5 *
705 (dst->nsec_per_cpu_clock + src->nsec_per_cpu_clock));
708 * Heuristic to see if src/dst came from same time source.
709 * If frequencies are "the same" and os clock and cpu clock agree
710 * to within 100e-9 secs about time difference between src/dst
711 * init_time, then we use cpu clock. Otherwise we use OS clock.
713 * When merging event logs from different systems, time paradoxes
714 * at the O(1ms) level are to be expected. Hence, the "align_tweak"
715 * parameter. If two events logged on different processors are known
716 * to occur in a specific order - and with a reasonably-estimated
717 * interval - supply a non-zero "align_tweak" parameter
719 if (fabs (src->nsec_per_cpu_clock - dst->nsec_per_cpu_clock) < 1e-2
720 && fabs (dt_os_nsec - dt_clock_nsec) < 100)
721 dt_event = dt_clock_nsec;
723 /* Convert to seconds. */
727 * Move the earlier set of events later, to avoid creating
728 * events which preceed the Big Bang (aka have negative timestamps).
730 * Not to any scale, we have something like the following picture:
732 * DST capture start point
736 * SRC capture start point
738 * In this case dt_event is positive, src started after dst,
739 * to put src events onto a common timebase we have to move them
740 * forward in time. Naturally, the opposite case is
741 * possible, too: dt_event will be negative, and so we have to
742 * move dst events forward in time by the |dt_event|.
743 * In both cases, we add align_tweak.
747 /* Src started after dst. */
748 for (e = dst->events + l; e < vec_end (dst->events); e++)
749 e->time += dt_event + align_tweak;
753 /* Dst started after src. */
754 dt_event = -dt_event;
755 for (e = dst->events + 0; e < dst->events + l; e++)
756 e->time += dt_event + align_tweak;
760 /* Sort events by increasing time. */
761 vec_sort_with_function (dst->events, elog_cmp);
763 dst->n_total_events = vec_len (dst->events);
765 /* Recreate the event ring or the results won't serialize */
769 ASSERT (dst->cpu_timer.seconds_per_clock);
771 elog_alloc (dst, vec_len (dst->events));
772 for (i = 0; i < vec_len (dst->events); i++)
774 elog_event_t *es, *ed;
776 es = dst->events + i;
777 ed = dst->event_ring + i;
785 serialize_elog_event (serialize_main_t * m, va_list * va)
787 elog_main_t *em = va_arg (*va, elog_main_t *);
788 elog_event_t *e = va_arg (*va, elog_event_t *);
789 elog_event_type_t *t = vec_elt_at_index (em->event_types, e->type);
791 u8 *p = (u8 *) t->format_args;
793 serialize_integer (m, e->type, sizeof (e->type));
794 serialize_integer (m, e->track, sizeof (e->track));
795 serialize (m, serialize_f64, e->time);
799 uword n_digits, n_bytes = 0;
801 n_digits = parse_2digit_decimal ((char *) p + 1, &n_bytes);
809 serialize_integer (m, d[0], sizeof (u8));
810 else if (n_bytes == 2)
811 serialize_integer (m, clib_mem_unaligned (d, u16), sizeof (u16));
812 else if (n_bytes == 4)
813 serialize_integer (m, clib_mem_unaligned (d, u32), sizeof (u32));
814 else if (n_bytes == 8)
815 serialize (m, serialize_64, clib_mem_unaligned (d, u64));
821 serialize_cstring (m, (char *) d);
823 n_bytes = strlen ((char *) d) + 1;
828 serialize (m, serialize_f32, clib_mem_unaligned (d, f32));
829 else if (n_bytes == 8)
830 serialize (m, serialize_f64, clib_mem_unaligned (d, f64));
846 unserialize_elog_event (serialize_main_t * m, va_list * va)
848 elog_main_t *em = va_arg (*va, elog_main_t *);
849 elog_event_t *e = va_arg (*va, elog_event_t *);
850 elog_event_type_t *t;
856 unserialize_integer (m, &tmp[0], sizeof (e->type));
857 unserialize_integer (m, &tmp[1], sizeof (e->track));
862 /* Make sure it fits. */
863 ASSERT (e->type == tmp[0]);
864 ASSERT (e->track == tmp[1]);
867 t = vec_elt_at_index (em->event_types, e->type);
869 unserialize (m, unserialize_f64, &e->time);
872 p = (u8 *) t->format_args;
876 uword n_digits, n_bytes = 0;
879 n_digits = parse_2digit_decimal ((char *) p + 1, &n_bytes);
888 unserialize_integer (m, &tmp, sizeof (u8));
891 else if (n_bytes == 2)
893 unserialize_integer (m, &tmp, sizeof (u16));
894 clib_mem_unaligned (d, u16) = tmp;
896 else if (n_bytes == 4)
898 unserialize_integer (m, &tmp, sizeof (u32));
899 clib_mem_unaligned (d, u32) = tmp;
901 else if (n_bytes == 8)
904 unserialize (m, unserialize_64, &x);
905 clib_mem_unaligned (d, u64) = x;
914 unserialize_cstring (m, &t);
916 n_bytes = strlen (t) + 1;
917 clib_memcpy (d, t, clib_min (n_bytes, vec_len (t)));
926 unserialize (m, unserialize_f32, &x);
927 clib_mem_unaligned (d, f32) = x;
929 else if (n_bytes == 8)
932 unserialize (m, unserialize_f64, &x);
933 clib_mem_unaligned (d, f64) = x;
950 serialize_elog_event_type (serialize_main_t * m, va_list * va)
952 elog_event_type_t *t = va_arg (*va, elog_event_type_t *);
953 int n = va_arg (*va, int);
955 for (i = 0; i < n; i++)
957 serialize_cstring (m, t[i].format);
958 serialize_cstring (m, t[i].format_args);
959 serialize_integer (m, t[i].type_index_plus_one,
960 sizeof (t->type_index_plus_one));
961 serialize_integer (m, t[i].n_enum_strings,
962 sizeof (t[i].n_enum_strings));
963 for (j = 0; j < t[i].n_enum_strings; j++)
964 serialize_cstring (m, t[i].enum_strings_vector[j]);
969 unserialize_elog_event_type (serialize_main_t * m, va_list * va)
971 elog_event_type_t *t = va_arg (*va, elog_event_type_t *);
972 int n = va_arg (*va, int);
974 for (i = 0; i < n; i++)
976 unserialize_cstring (m, &t[i].format);
977 unserialize_cstring (m, &t[i].format_args);
978 unserialize_integer (m, &t[i].type_index_plus_one,
979 sizeof (t->type_index_plus_one));
980 unserialize_integer (m, &t[i].n_enum_strings,
981 sizeof (t[i].n_enum_strings));
982 vec_resize (t[i].enum_strings_vector, t[i].n_enum_strings);
983 for (j = 0; j < t[i].n_enum_strings; j++)
984 unserialize_cstring (m, &t[i].enum_strings_vector[j]);
989 serialize_elog_track (serialize_main_t * m, va_list * va)
991 elog_track_t *t = va_arg (*va, elog_track_t *);
992 int n = va_arg (*va, int);
994 for (i = 0; i < n; i++)
996 serialize_cstring (m, t[i].name);
1001 unserialize_elog_track (serialize_main_t * m, va_list * va)
1003 elog_track_t *t = va_arg (*va, elog_track_t *);
1004 int n = va_arg (*va, int);
1006 for (i = 0; i < n; i++)
1008 unserialize_cstring (m, &t[i].name);
1013 serialize_elog_time_stamp (serialize_main_t * m, va_list * va)
1015 elog_time_stamp_t *st = va_arg (*va, elog_time_stamp_t *);
1016 serialize (m, serialize_64, st->os_nsec);
1017 serialize (m, serialize_64, st->cpu);
1021 unserialize_elog_time_stamp (serialize_main_t * m, va_list * va)
1023 elog_time_stamp_t *st = va_arg (*va, elog_time_stamp_t *);
1024 unserialize (m, unserialize_64, &st->os_nsec);
1025 unserialize (m, unserialize_64, &st->cpu);
1028 static char *elog_serialize_magic = "elog v0";
1031 serialize_elog_main (serialize_main_t * m, va_list * va)
1033 elog_main_t *em = va_arg (*va, elog_main_t *);
1034 int flush_ring = va_arg (*va, int);
1037 serialize_magic (m, elog_serialize_magic, strlen (elog_serialize_magic));
1039 serialize_integer (m, em->event_ring_size, sizeof (u32));
1041 elog_time_now (&em->serialize_time);
1042 serialize (m, serialize_elog_time_stamp, &em->serialize_time);
1043 serialize (m, serialize_elog_time_stamp, &em->init_time);
1045 vec_serialize (m, em->event_types, serialize_elog_event_type);
1046 vec_serialize (m, em->tracks, serialize_elog_track);
1047 vec_serialize (m, em->string_table, serialize_vec_8);
1049 /* Free old events (cached) in case they have changed. */
1052 vec_free (em->events);
1053 elog_get_events (em);
1056 serialize_integer (m, vec_len (em->events), sizeof (u32));
1058 /* SMP logs can easily have local time paradoxes... */
1059 vec_sort_with_function (em->events, elog_cmp);
1061 vec_foreach (e, em->events) serialize (m, serialize_elog_event, em, e);
1065 unserialize_elog_main (serialize_main_t * m, va_list * va)
1067 elog_main_t *em = va_arg (*va, elog_main_t *);
1071 unserialize_check_magic (m, elog_serialize_magic,
1072 strlen (elog_serialize_magic));
1074 unserialize_integer (m, &rs, sizeof (u32));
1075 em->event_ring_size = rs;
1076 elog_init (em, em->event_ring_size);
1078 unserialize (m, unserialize_elog_time_stamp, &em->serialize_time);
1079 unserialize (m, unserialize_elog_time_stamp, &em->init_time);
1080 em->nsec_per_cpu_clock = elog_nsec_per_clock (em);
1082 vec_unserialize (m, &em->event_types, unserialize_elog_event_type);
1083 for (i = 0; i < vec_len (em->event_types); i++)
1084 new_event_type (em, i);
1086 vec_unserialize (m, &em->tracks, unserialize_elog_track);
1087 vec_unserialize (m, &em->string_table, unserialize_vec_8);
1093 unserialize_integer (m, &ne, sizeof (u32));
1094 vec_resize (em->events, ne);
1095 vec_foreach (e, em->events)
1096 unserialize (m, unserialize_elog_event, em, e);
1101 * fd.io coding-style-patch-verification: ON
1104 * eval: (c-set-style "gnu")