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 elog_get_events (src);
586 elog_get_events (dst);
588 string_table_offset_for_src_events = vec_len (dst->string_table);
589 vec_append (dst->string_table, src->string_table);
591 l = vec_len (dst->events);
592 vec_add (dst->events, src->events, vec_len (src->events));
594 /* Prepend the supplied tag (if any) to all dst track names */
597 for (i = 0; i < vec_len(dst->tracks); i++)
599 elog_track_t * t = vec_elt_at_index (dst->tracks, i);
602 new_name = (char *) format (0, "%s:%s%c", dst_tag, t->name, 0);
608 track_offset_for_src_tracks = vec_len (dst->tracks);
610 /* Copy / tag source tracks */
611 for (i = 0; i < vec_len (src->tracks); i++)
613 elog_track_t * t = vec_elt_at_index (src->tracks, i);
615 newt.name = (char *) format (0, "%s:%s%c", src_tag, t->name, 0);
617 newt.name = (char *) format (0, "%s%c", t->name, 0);
618 (void) elog_track_register (dst, &newt);
619 vec_free (newt.name);
622 /* Across all (copied) src events... */
623 for (e = dst->events + l; e < vec_end (dst->events); e++)
625 elog_event_type_t * t = vec_elt_at_index (src->event_types, e->type);
627 /* Remap type from src -> dst. */
628 e->type = find_or_create_type (dst, t);
630 /* Remap string table offsets for 'T' format args */
631 maybe_fix_string_table_offset (e, t, string_table_offset_for_src_events);
634 e->track += track_offset_for_src_tracks;
637 /* Adjust event times for relative starting times of event streams. */
639 f64 dt_event, dt_os_nsec, dt_clock_nsec;
641 /* Set clock parameters if dst was not generated by unserialize. */
642 if (dst->serialize_time.cpu == 0)
644 dst->init_time = src->init_time;
645 dst->serialize_time = src->serialize_time;
646 dst->nsec_per_cpu_clock = src->nsec_per_cpu_clock;
649 dt_os_nsec = elog_time_stamp_diff_os_nsec (&src->init_time, &dst->init_time);
651 dt_event = dt_os_nsec;
652 dt_clock_nsec = (elog_time_stamp_diff_cpu (&src->init_time, &dst->init_time)
653 * .5*(dst->nsec_per_cpu_clock + src->nsec_per_cpu_clock));
655 /* Heuristic to see if src/dst came from same time source.
656 If frequencies are "the same" and os clock and cpu clock agree
657 to within 100e-9 secs about time difference between src/dst
658 init_time, then we use cpu clock. Otherwise we use OS clock. */
659 if (fabs (src->nsec_per_cpu_clock - dst->nsec_per_cpu_clock) < 1e-2
660 && fabs (dt_os_nsec - dt_clock_nsec) < 100)
661 dt_event = dt_clock_nsec;
663 /* Convert to seconds. */
668 /* Src started after dst. */
669 for (e = dst->events + l; e < vec_end (dst->events); e++)
674 /* Dst started after src. */
675 for (e = dst->events + 0; e < dst->events + l; e++)
680 /* Sort events by increasing time. */
681 vec_sort_with_function (dst->events, elog_cmp);
683 /* Recreate the event ring or the results won't serialize */
687 ASSERT (dst->cpu_timer.seconds_per_clock);
689 elog_alloc (dst, vec_len (dst->events));
690 for (i = 0; i < vec_len(dst->events); i++)
692 elog_event_t *es, *ed;
694 es = dst->events + i;
695 ed = dst->event_ring + i;
699 /* Invert elog_peek_events calculation */
701 (es->time/dst->cpu_timer.seconds_per_clock) + dst->init_time.cpu;
703 dst->n_total_events = vec_len (dst->events);
708 serialize_elog_event (serialize_main_t * m, va_list * va)
710 elog_main_t * em = va_arg (*va, elog_main_t *);
711 elog_event_t * e = va_arg (*va, elog_event_t *);
712 elog_event_type_t * t = vec_elt_at_index (em->event_types, e->type);
714 u8 * p = (u8 *) t->format_args;
716 serialize_integer (m, e->type, sizeof (e->type));
717 serialize_integer (m, e->track, sizeof (e->track));
718 serialize (m, serialize_f64, e->time);
722 uword n_digits, n_bytes = 0;
724 n_digits = parse_2digit_decimal ((char *) p + 1, &n_bytes);
732 serialize_integer (m, d[0], sizeof (u8));
733 else if (n_bytes == 2)
734 serialize_integer (m, clib_mem_unaligned (d, u16), sizeof (u16));
735 else if (n_bytes == 4)
736 serialize_integer (m, clib_mem_unaligned (d, u32), sizeof (u32));
737 else if (n_bytes == 8)
738 serialize (m, serialize_64, clib_mem_unaligned (d, u64));
744 serialize_cstring (m, (char *) d);
746 n_bytes = strlen ((char *) d) + 1;
751 serialize (m, serialize_f32, clib_mem_unaligned (d, f32));
752 else if (n_bytes == 8)
753 serialize (m, serialize_f64, clib_mem_unaligned (d, f64));
769 unserialize_elog_event (serialize_main_t * m, va_list * va)
771 elog_main_t * em = va_arg (*va, elog_main_t *);
772 elog_event_t * e = va_arg (*va, elog_event_t *);
773 elog_event_type_t * t;
779 unserialize_integer (m, &tmp[0], sizeof (e->type));
780 unserialize_integer (m, &tmp[1], sizeof (e->track));
785 /* Make sure it fits. */
786 ASSERT (e->type == tmp[0]);
787 ASSERT (e->track == tmp[1]);
790 t = vec_elt_at_index (em->event_types, e->type);
792 unserialize (m, unserialize_f64, &e->time);
795 p = (u8 *) t->format_args;
799 uword n_digits, n_bytes = 0;
802 n_digits = parse_2digit_decimal ((char *) p + 1, &n_bytes);
811 unserialize_integer (m, &tmp, sizeof (u8));
814 else if (n_bytes == 2)
816 unserialize_integer (m, &tmp, sizeof (u16));
817 clib_mem_unaligned (d, u16) = tmp;
819 else if (n_bytes == 4)
821 unserialize_integer (m, &tmp, sizeof (u32));
822 clib_mem_unaligned (d, u32) = tmp;
824 else if (n_bytes == 8)
827 unserialize (m, unserialize_64, &x);
828 clib_mem_unaligned (d, u64) = x;
836 unserialize_cstring (m, &t);
838 n_bytes = strlen (t) + 1;
839 clib_memcpy (d, t, clib_min (n_bytes, vec_len (t)));
848 unserialize (m, unserialize_f32, &x);
849 clib_mem_unaligned (d, f32) = x;
851 else if (n_bytes == 8)
854 unserialize (m, unserialize_f64, &x);
855 clib_mem_unaligned (d, f64) = x;
872 serialize_elog_event_type (serialize_main_t * m, va_list * va)
874 elog_event_type_t * t = va_arg (*va, elog_event_type_t *);
875 int n = va_arg (*va, int);
877 for (i = 0; i < n; i++)
879 serialize_cstring (m, t[i].format);
880 serialize_cstring (m, t[i].format_args);
881 serialize_integer (m, t[i].type_index_plus_one, sizeof (t->type_index_plus_one));
882 serialize_integer (m, t[i].n_enum_strings, sizeof (t[i].n_enum_strings));
883 for (j = 0; j < t[i].n_enum_strings; j++)
884 serialize_cstring (m, t[i].enum_strings_vector[j]);
889 unserialize_elog_event_type (serialize_main_t * m, va_list * va)
891 elog_event_type_t * t = va_arg (*va, elog_event_type_t *);
892 int n = va_arg (*va, int);
894 for (i = 0; i < n; i++)
896 unserialize_cstring (m, &t[i].format);
897 unserialize_cstring (m, &t[i].format_args);
898 unserialize_integer (m, &t[i].type_index_plus_one, sizeof (t->type_index_plus_one));
899 unserialize_integer (m, &t[i].n_enum_strings, sizeof (t[i].n_enum_strings));
900 vec_resize (t[i].enum_strings_vector, t[i].n_enum_strings);
901 for (j = 0; j < t[i].n_enum_strings; j++)
902 unserialize_cstring (m, &t[i].enum_strings_vector[j]);
907 serialize_elog_track (serialize_main_t * m, va_list * va)
909 elog_track_t * t = va_arg (*va, elog_track_t *);
910 int n = va_arg (*va, int);
912 for (i = 0; i < n; i++)
914 serialize_cstring (m, t[i].name);
919 unserialize_elog_track (serialize_main_t * m, va_list * va)
921 elog_track_t * t = va_arg (*va, elog_track_t *);
922 int n = va_arg (*va, int);
924 for (i = 0; i < n; i++)
926 unserialize_cstring (m, &t[i].name);
931 serialize_elog_time_stamp (serialize_main_t * m, va_list * va)
933 elog_time_stamp_t * st = va_arg (*va, elog_time_stamp_t *);
934 serialize (m, serialize_64, st->os_nsec);
935 serialize (m, serialize_64, st->cpu);
939 unserialize_elog_time_stamp (serialize_main_t * m, va_list * va)
941 elog_time_stamp_t * st = va_arg (*va, elog_time_stamp_t *);
942 unserialize (m, unserialize_64, &st->os_nsec);
943 unserialize (m, unserialize_64, &st->cpu);
946 static char * elog_serialize_magic = "elog v0";
949 serialize_elog_main (serialize_main_t * m, va_list * va)
951 elog_main_t * em = va_arg (*va, elog_main_t *);
954 serialize_magic (m, elog_serialize_magic, strlen (elog_serialize_magic));
956 serialize_integer (m, em->event_ring_size, sizeof (u32));
958 elog_time_now (&em->serialize_time);
959 serialize (m, serialize_elog_time_stamp, &em->serialize_time);
960 serialize (m, serialize_elog_time_stamp, &em->init_time);
962 vec_serialize (m, em->event_types, serialize_elog_event_type);
963 vec_serialize (m, em->tracks, serialize_elog_track);
964 vec_serialize (m, em->string_table, serialize_vec_8);
966 /* Free old events (cached) in case they have changed. */
967 vec_free (em->events);
968 elog_get_events (em);
970 serialize_integer (m, vec_len (em->events), sizeof (u32));
972 /* SMP logs can easily have local time paradoxes... */
973 vec_sort_with_function (em->events, elog_cmp);
975 vec_foreach (e, em->events)
976 serialize (m, serialize_elog_event, em, e);
980 unserialize_elog_main (serialize_main_t * m, va_list * va)
982 elog_main_t * em = va_arg (*va, elog_main_t *);
986 unserialize_check_magic (m, elog_serialize_magic,
987 strlen (elog_serialize_magic));
989 unserialize_integer (m, &rs, sizeof (u32));
990 em->event_ring_size = rs;
991 elog_init (em, em->event_ring_size);
993 unserialize (m, unserialize_elog_time_stamp, &em->serialize_time);
994 unserialize (m, unserialize_elog_time_stamp, &em->init_time);
995 em->nsec_per_cpu_clock = elog_nsec_per_clock (em);
997 vec_unserialize (m, &em->event_types, unserialize_elog_event_type);
998 for (i = 0; i < vec_len (em->event_types); i++)
999 new_event_type (em, i);
1001 vec_unserialize (m, &em->tracks, unserialize_elog_track);
1002 vec_unserialize (m, &em->string_table, unserialize_vec_8);
1008 unserialize_integer (m, &ne, sizeof (u32));
1009 vec_resize (em->events, ne);
1010 vec_foreach (e, em->events)
1011 unserialize (m, unserialize_elog_event, em, e);