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>
45 static inline void elog_lock (elog_main_t * em)
47 if (PREDICT_FALSE(em->lock != 0))
48 while (__sync_lock_test_and_set (em->lock, 1))
52 static inline void elog_unlock (elog_main_t * em)
54 if (PREDICT_FALSE(em->lock != 0))
56 CLIB_MEMORY_BARRIER();
61 /* Non-inline version. */
63 elog_event_data (elog_main_t * em,
64 elog_event_type_t * type,
67 { return elog_event_data_inline (em, type, track, cpu_time); }
69 static void new_event_type (elog_main_t * em, uword i)
71 elog_event_type_t * t = vec_elt_at_index (em->event_types, i);
73 if (! em->event_type_by_format)
74 em->event_type_by_format = hash_create_vec (/* size */ 0, sizeof (u8), sizeof (uword));
76 hash_set_mem (em->event_type_by_format, t->format, i);
80 find_or_create_type (elog_main_t * em, elog_event_type_t * t)
82 uword * p = hash_get_mem (em->event_type_by_format, t->format);
89 i = vec_len (em->event_types);
90 vec_add1 (em->event_types, t[0]);
91 new_event_type (em, i);
97 /* External function to register types. */
98 word elog_event_type_register (elog_main_t * em, elog_event_type_t * t)
100 elog_event_type_t * static_type = t;
105 l = vec_len (em->event_types);
107 t->type_index_plus_one = 1 + l;
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)
118 l = strlen (t->format);
119 for (i = 0; i < l; i++)
121 if (t->format[i] != '%')
125 if (t->format[i+1] == '%') /* %% */
128 switch (t->format[i+1]) {
130 case 'd': case 'x': case 'u':
131 this_arg = "i4"; /* size of u32 */
134 this_arg = "f8"; /* defaults to f64 */
137 this_arg = "s0"; /* defaults to null terminated string. */
141 t->format_args = (char *) format ((u8 *) t->format_args, "%s", this_arg);
144 /* Null terminate. */
145 vec_add1 (t->format_args, 0);
148 vec_add1 (em->event_types, t[0]);
150 t = em->event_types + l;
152 /* Make copies of strings for hashing etc. */
154 t->format = (char *) format (0, "%s %s%c", t->function, t->format, 0);
156 t->format = (char *) format (0, "%s%c", t->format, 0);
158 t->format_args = (char *) format (0, "%s%c", t->format_args, 0);
160 /* Construct string table. */
163 t->n_enum_strings = static_type->n_enum_strings;
164 for (i = 0; i < t->n_enum_strings; i++)
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));
173 new_event_type (em, l);
179 word elog_track_register (elog_main_t * em, elog_track_t * t)
185 l = vec_len (em->tracks);
187 t->track_index_plus_one = 1 + l;
191 vec_add1 (em->tracks, t[0]);
195 t->name = (char *) format (0, "%s%c", t->name, 0);
202 static uword parse_2digit_decimal (char * p, uword * number)
207 digits[0] = digits[1] = 0;
208 while (p[i] >= '0' && p[i] <= '9')
212 digits[i] = p[i] - '0';
216 if (i >= 1 && i <= 2)
221 *number = 10 * digits[0] + digits[1];
228 static u8 * fixed_format (u8 * s, char * fmt, char * result, uword * result_len)
238 if (f[0] == '%' && f[1] != '%')
243 vec_add (s, fmt, f - fmt);
251 /* Skip possible +-= justification. */
252 f += f[0] == '+' || f[0] == '-' || f[0] == '=';
254 /* Skip possible X.Y width. */
255 while ((f[0] >= '0' && f[0] <= '9') || f[0] == '.')
258 /* Skip wlL as in e.g. %Ld. */
259 f += f[0] == 'w' || f[0] == 'l' || f[0] == 'L';
261 /* Finally skip format letter. */
264 ASSERT (*result_len > f - percent);
265 l = clib_min (f - percent, *result_len - 1);
266 clib_memcpy (result, percent, l);
270 *result_len = f - fmt;
274 u8 * format_elog_event (u8 * s, va_list * va)
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;
280 void * d = (u8 *) e->data;
283 t = vec_elt_at_index (em->event_types, e->type);
289 uword n_bytes = 0, n_digits, f_bytes = 0;
291 f_bytes = sizeof (arg_format);
292 s = fixed_format (s, f, arg_format, &f_bytes);
295 if (a == 0 || a[0] == 0)
297 /* Format must also be at end. */
302 /* Don't go past end of event data. */
303 ASSERT (d < (void *) (e->data + sizeof (e->data)));
305 n_digits = parse_2digit_decimal (a + 1, &n_bytes);
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);
327 char * e = vec_elt (t->enum_strings_vector, n_bytes == 8 ? l : i);
328 s = format (s, arg_format, e);
330 else if (a[0] == 'T')
332 char * e = vec_elt_at_index (em->string_table, n_bytes == 8 ? l : i);
333 s = format (s, arg_format, e);
335 else if (n_bytes == 8)
336 s = format (s, arg_format, l);
338 s = format (s, arg_format, i);
346 x = clib_mem_unaligned (d, f32);
347 else if (n_bytes == 8)
348 x = clib_mem_unaligned (d, f64);
351 s = format (s, arg_format, x);
356 s = format (s, arg_format, d);
358 n_bytes = strlen (d) + 1;
366 ASSERT (n_digits > 0 && n_digits <= 2);
374 u8 * format_elog_track (u8 * s, va_list * va)
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);
382 void elog_time_now (elog_time_stamp_t * et)
384 u64 cpu_time_now, os_time_now_nsec;
388 #include <sys/syscall.h>
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;
395 cpu_time_now = clib_cpu_time_now ();
396 os_time_now_nsec = 0;
399 et->cpu = cpu_time_now;
400 et->os_nsec = os_time_now_nsec;
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; }
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; }
414 elog_nsec_per_clock (elog_main_t * em)
416 return ((f64) elog_time_stamp_diff_os_nsec (&em->serialize_time,
418 / (f64) elog_time_stamp_diff_cpu (&em->serialize_time,
422 void elog_alloc (elog_main_t * em, u32 n_events)
425 vec_free (em->event_ring);
427 /* Ring size must be a power of 2. */
428 em->event_ring_size = n_events = max_pow2 (n_events);
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);
435 void elog_init (elog_main_t * em, u32 n_events)
437 memset (em, 0, sizeof (em[0]));
442 elog_alloc (em, n_events);
444 clib_time_init (&em->cpu_timer);
446 em->n_total_events_disable_limit = ~0;
449 em->default_track.name = "default";
450 elog_track_register (em, &em->default_track);
452 elog_time_now (&em->init_time);
455 /* Returns number of events in ring and start index. */
456 static uword elog_event_range (elog_main_t * em, uword * lo)
458 uword l = em->event_ring_size;
459 u64 i = em->n_total_events;
461 /* Ring never wrapped? */
469 if (lo) *lo = i & (l - 1);
474 elog_event_t * elog_peek_events (elog_main_t * em)
476 elog_event_t * e, * f, * es = 0;
479 n = elog_event_range (em, &j);
480 for (i = 0; i < n; i++)
483 f = vec_elt_at_index (em->event_ring, j);
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;
489 j = (j + 1) & (em->event_ring_size - 1);
495 /* Add a formatted string to the string table. */
496 u32 elog_string (elog_main_t * em, char * fmt, ...)
502 offset = vec_len (em->string_table);
503 em->string_table = (char *) va_format ((u8 *) em->string_table, fmt, &va);
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);
513 elog_event_t * elog_get_events (elog_main_t * em)
516 em->events = elog_peek_events (em);
520 static void maybe_fix_string_table_offset (elog_event_t * e,
521 elog_event_type_t * t,
524 void * d = (u8 *) e->data;
534 uword n_bytes = 0, n_digits;
539 /* Don't go past end of event data. */
540 ASSERT (d < (void *) (e->data + sizeof (e->data)));
542 n_digits = parse_2digit_decimal (a + 1, &n_bytes);
546 ASSERT (n_bytes == 4);
547 clib_mem_unaligned (d, u32) += offset;
561 ASSERT (n_digits > 0 && n_digits <= 2);
567 static int elog_cmp (void * a1, void * a2)
569 elog_event_t * e1 = a1;
570 elog_event_t * e2 = a2;
572 return e1->time - e2->time;
575 void elog_merge (elog_main_t * dst, u8 * dst_tag,
576 elog_main_t * src, u8 * src_tag)
580 u32 string_table_offset_for_src_events;
581 u32 track_offset_for_src_tracks;
585 memset(&newt, 0, sizeof (newt));
587 elog_get_events (src);
588 elog_get_events (dst);
590 string_table_offset_for_src_events = vec_len (dst->string_table);
591 vec_append (dst->string_table, src->string_table);
593 l = vec_len (dst->events);
594 vec_add (dst->events, src->events, vec_len (src->events));
596 /* Prepend the supplied tag (if any) to all dst track names */
599 for (i = 0; i < vec_len(dst->tracks); i++)
601 elog_track_t * t = vec_elt_at_index (dst->tracks, i);
604 new_name = (char *) format (0, "%s:%s%c", dst_tag, t->name, 0);
610 track_offset_for_src_tracks = vec_len (dst->tracks);
612 /* Copy / tag source tracks */
613 for (i = 0; i < vec_len (src->tracks); i++)
615 elog_track_t * t = vec_elt_at_index (src->tracks, i);
617 newt.name = (char *) format (0, "%s:%s%c", src_tag, t->name, 0);
619 newt.name = (char *) format (0, "%s%c", t->name, 0);
620 (void) elog_track_register (dst, &newt);
621 vec_free (newt.name);
624 /* Across all (copied) src events... */
625 for (e = dst->events + l; e < vec_end (dst->events); e++)
627 elog_event_type_t * t = vec_elt_at_index (src->event_types, e->type);
629 /* Remap type from src -> dst. */
630 e->type = find_or_create_type (dst, t);
632 /* Remap string table offsets for 'T' format args */
633 maybe_fix_string_table_offset (e, t, string_table_offset_for_src_events);
636 e->track += track_offset_for_src_tracks;
639 /* Adjust event times for relative starting times of event streams. */
641 f64 dt_event, dt_os_nsec, dt_clock_nsec;
643 /* Set clock parameters if dst was not generated by unserialize. */
644 if (dst->serialize_time.cpu == 0)
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;
651 dt_os_nsec = elog_time_stamp_diff_os_nsec (&src->init_time, &dst->init_time);
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));
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;
665 /* Convert to seconds. */
670 /* Src started after dst. */
671 for (e = dst->events + l; e < vec_end (dst->events); e++)
676 /* Dst started after src. */
677 for (e = dst->events + 0; e < dst->events + l; e++)
682 /* Sort events by increasing time. */
683 vec_sort_with_function (dst->events, elog_cmp);
685 /* Recreate the event ring or the results won't serialize */
689 ASSERT (dst->cpu_timer.seconds_per_clock);
691 elog_alloc (dst, vec_len (dst->events));
692 for (i = 0; i < vec_len(dst->events); i++)
694 elog_event_t *es, *ed;
696 es = dst->events + i;
697 ed = dst->event_ring + i;
701 /* Invert elog_peek_events calculation */
703 (es->time/dst->cpu_timer.seconds_per_clock) + dst->init_time.cpu;
705 dst->n_total_events = vec_len (dst->events);
710 serialize_elog_event (serialize_main_t * m, va_list * va)
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);
716 u8 * p = (u8 *) t->format_args;
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);
724 uword n_digits, n_bytes = 0;
726 n_digits = parse_2digit_decimal ((char *) p + 1, &n_bytes);
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));
746 serialize_cstring (m, (char *) d);
748 n_bytes = strlen ((char *) d) + 1;
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));
771 unserialize_elog_event (serialize_main_t * m, va_list * va)
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;
781 unserialize_integer (m, &tmp[0], sizeof (e->type));
782 unserialize_integer (m, &tmp[1], sizeof (e->track));
787 /* Make sure it fits. */
788 ASSERT (e->type == tmp[0]);
789 ASSERT (e->track == tmp[1]);
792 t = vec_elt_at_index (em->event_types, e->type);
794 unserialize (m, unserialize_f64, &e->time);
797 p = (u8 *) t->format_args;
801 uword n_digits, n_bytes = 0;
804 n_digits = parse_2digit_decimal ((char *) p + 1, &n_bytes);
813 unserialize_integer (m, &tmp, sizeof (u8));
816 else if (n_bytes == 2)
818 unserialize_integer (m, &tmp, sizeof (u16));
819 clib_mem_unaligned (d, u16) = tmp;
821 else if (n_bytes == 4)
823 unserialize_integer (m, &tmp, sizeof (u32));
824 clib_mem_unaligned (d, u32) = tmp;
826 else if (n_bytes == 8)
829 unserialize (m, unserialize_64, &x);
830 clib_mem_unaligned (d, u64) = x;
838 unserialize_cstring (m, &t);
840 n_bytes = strlen (t) + 1;
841 clib_memcpy (d, t, clib_min (n_bytes, vec_len (t)));
850 unserialize (m, unserialize_f32, &x);
851 clib_mem_unaligned (d, f32) = x;
853 else if (n_bytes == 8)
856 unserialize (m, unserialize_f64, &x);
857 clib_mem_unaligned (d, f64) = x;
874 serialize_elog_event_type (serialize_main_t * m, va_list * va)
876 elog_event_type_t * t = va_arg (*va, elog_event_type_t *);
877 int n = va_arg (*va, int);
879 for (i = 0; i < n; i++)
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]);
891 unserialize_elog_event_type (serialize_main_t * m, va_list * va)
893 elog_event_type_t * t = va_arg (*va, elog_event_type_t *);
894 int n = va_arg (*va, int);
896 for (i = 0; i < n; i++)
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]);
909 serialize_elog_track (serialize_main_t * m, va_list * va)
911 elog_track_t * t = va_arg (*va, elog_track_t *);
912 int n = va_arg (*va, int);
914 for (i = 0; i < n; i++)
916 serialize_cstring (m, t[i].name);
921 unserialize_elog_track (serialize_main_t * m, va_list * va)
923 elog_track_t * t = va_arg (*va, elog_track_t *);
924 int n = va_arg (*va, int);
926 for (i = 0; i < n; i++)
928 unserialize_cstring (m, &t[i].name);
933 serialize_elog_time_stamp (serialize_main_t * m, va_list * va)
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);
941 unserialize_elog_time_stamp (serialize_main_t * m, va_list * va)
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);
948 static char * elog_serialize_magic = "elog v0";
951 serialize_elog_main (serialize_main_t * m, va_list * va)
953 elog_main_t * em = va_arg (*va, elog_main_t *);
956 serialize_magic (m, elog_serialize_magic, strlen (elog_serialize_magic));
958 serialize_integer (m, em->event_ring_size, sizeof (u32));
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);
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);
968 /* Free old events (cached) in case they have changed. */
969 vec_free (em->events);
970 elog_get_events (em);
972 serialize_integer (m, vec_len (em->events), sizeof (u32));
974 /* SMP logs can easily have local time paradoxes... */
975 vec_sort_with_function (em->events, elog_cmp);
977 vec_foreach (e, em->events)
978 serialize (m, serialize_elog_event, em, e);
982 unserialize_elog_main (serialize_main_t * m, va_list * va)
984 elog_main_t * em = va_arg (*va, elog_main_t *);
988 unserialize_check_magic (m, elog_serialize_magic,
989 strlen (elog_serialize_magic));
991 unserialize_integer (m, &rs, sizeof (u32));
992 em->event_ring_size = rs;
993 elog_init (em, em->event_ring_size);
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);
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);
1003 vec_unserialize (m, &em->tracks, unserialize_elog_track);
1004 vec_unserialize (m, &em->string_table, unserialize_vec_8);
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);