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))
58 /* Non-inline version. */
60 elog_event_data (elog_main_t * em,
61 elog_event_type_t * type,
64 { return elog_event_data_inline (em, type, track, cpu_time); }
66 static void new_event_type (elog_main_t * em, uword i)
68 elog_event_type_t * t = vec_elt_at_index (em->event_types, i);
70 if (! em->event_type_by_format)
71 em->event_type_by_format = hash_create_vec (/* size */ 0, sizeof (u8), sizeof (uword));
73 hash_set_mem (em->event_type_by_format, t->format, i);
77 find_or_create_type (elog_main_t * em, elog_event_type_t * t)
79 uword * p = hash_get_mem (em->event_type_by_format, t->format);
86 i = vec_len (em->event_types);
87 vec_add1 (em->event_types, t[0]);
88 new_event_type (em, i);
94 /* External function to register types. */
95 word elog_event_type_register (elog_main_t * em, elog_event_type_t * t)
97 elog_event_type_t * static_type = t;
102 l = vec_len (em->event_types);
104 t->type_index_plus_one = 1 + l;
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)
115 l = strlen (t->format);
116 for (i = 0; i < l; i++)
118 if (t->format[i] != '%')
122 if (t->format[i+1] == '%') /* %% */
125 switch (t->format[i+1]) {
127 case 'd': case 'x': case 'u':
128 this_arg = "i4"; /* size of u32 */
131 this_arg = "f8"; /* defaults to f64 */
134 this_arg = "s0"; /* defaults to null terminated string. */
138 t->format_args = (char *) format ((u8 *) t->format_args, "%s", this_arg);
141 /* Null terminate. */
142 vec_add1 (t->format_args, 0);
145 vec_add1 (em->event_types, t[0]);
147 t = em->event_types + l;
149 /* Make copies of strings for hashing etc. */
151 t->format = (char *) format (0, "%s %s%c", t->function, t->format, 0);
153 t->format = (char *) format (0, "%s%c", t->format, 0);
155 t->format_args = (char *) format (0, "%s%c", t->format_args, 0);
157 /* Construct string table. */
160 t->n_enum_strings = static_type->n_enum_strings;
161 for (i = 0; i < t->n_enum_strings; i++)
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));
170 new_event_type (em, l);
177 word elog_track_register (elog_main_t * em, elog_track_t * t)
183 l = vec_len (em->tracks);
185 t->track_index_plus_one = 1 + l;
189 vec_add1 (em->tracks, t[0]);
193 t->name = (char *) format (0, "%s%c", t->name, 0);
200 static uword parse_2digit_decimal (char * p, uword * number)
205 digits[0] = digits[1] = 0;
206 while (p[i] >= '0' && p[i] <= '9')
210 digits[i] = p[i] - '0';
214 if (i >= 1 && i <= 2)
219 *number = 10 * digits[0] + digits[1];
226 static u8 * fixed_format (u8 * s, char * fmt, char * result, uword * result_len)
236 if (f[0] == '%' && f[1] != '%')
241 vec_add (s, fmt, f - fmt);
249 /* Skip possible +-= justification. */
250 f += f[0] == '+' || f[0] == '-' || f[0] == '=';
252 /* Skip possible X.Y width. */
253 while ((f[0] >= '0' && f[0] <= '9') || f[0] == '.')
256 /* Skip wlL as in e.g. %Ld. */
257 f += f[0] == 'w' || f[0] == 'l' || f[0] == 'L';
259 /* Finally skip format letter. */
262 ASSERT (*result_len > f - percent);
263 l = clib_min (f - percent, *result_len - 1);
264 memcpy (result, percent, l);
268 *result_len = f - fmt;
272 u8 * format_elog_event (u8 * s, va_list * va)
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;
278 void * d = (u8 *) e->data;
281 t = vec_elt_at_index (em->event_types, e->type);
287 uword n_bytes = 0, n_digits, f_bytes = 0;
289 f_bytes = sizeof (arg_format);
290 s = fixed_format (s, f, arg_format, &f_bytes);
293 if (a == 0 || a[0] == 0)
295 /* Format must also be at end. */
300 /* Don't go past end of event data. */
301 ASSERT (d < (void *) (e->data + sizeof (e->data)));
303 n_digits = parse_2digit_decimal (a + 1, &n_bytes);
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);
325 char * e = vec_elt (t->enum_strings_vector, n_bytes == 8 ? l : i);
326 s = format (s, arg_format, e);
328 else if (a[0] == 'T')
330 char * e = vec_elt_at_index (em->string_table, n_bytes == 8 ? l : i);
331 s = format (s, arg_format, e);
333 else if (n_bytes == 8)
334 s = format (s, arg_format, l);
336 s = format (s, arg_format, i);
344 x = clib_mem_unaligned (d, f32);
345 else if (n_bytes == 8)
346 x = clib_mem_unaligned (d, f64);
349 s = format (s, arg_format, x);
354 s = format (s, arg_format, d);
356 n_bytes = strlen (d) + 1;
364 ASSERT (n_digits > 0 && n_digits <= 2);
372 u8 * format_elog_track (u8 * s, va_list * va)
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);
380 void elog_time_now (elog_time_stamp_t * et)
382 u64 cpu_time_now, os_time_now_nsec;
386 #include <sys/syscall.h>
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;
393 cpu_time_now = clib_cpu_time_now ();
394 os_time_now_nsec = 0;
397 et->cpu = cpu_time_now;
398 et->os_nsec = os_time_now_nsec;
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; }
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; }
412 elog_nsec_per_clock (elog_main_t * em)
414 return ((f64) elog_time_stamp_diff_os_nsec (&em->serialize_time,
416 / (f64) elog_time_stamp_diff_cpu (&em->serialize_time,
420 static void elog_alloc (elog_main_t * em, u32 n_events)
423 vec_free (em->event_ring);
425 /* Ring size must be a power of 2. */
426 em->event_ring_size = n_events = max_pow2 (n_events);
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);
433 void elog_init (elog_main_t * em, u32 n_events)
435 memset (em, 0, sizeof (em[0]));
440 elog_alloc (em, n_events);
442 clib_time_init (&em->cpu_timer);
444 em->n_total_events_disable_limit = ~0;
447 em->default_track.name = "default";
448 elog_track_register (em, &em->default_track);
450 elog_time_now (&em->init_time);
453 /* Returns number of events in ring and start index. */
454 static uword elog_event_range (elog_main_t * em, uword * lo)
456 uword l = em->event_ring_size;
457 u64 i = em->n_total_events;
459 /* Ring never wrapped? */
467 if (lo) *lo = i & (l - 1);
472 elog_event_t * elog_peek_events (elog_main_t * em)
474 elog_event_t * e, * f, * es = 0;
477 n = elog_event_range (em, &j);
478 for (i = 0; i < n; i++)
481 f = vec_elt_at_index (em->event_ring, j);
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;
487 j = (j + 1) & (em->event_ring_size - 1);
493 /* Add a formatted string to the string table. */
494 u32 elog_string (elog_main_t * em, char * fmt, ...)
500 offset = vec_len (em->string_table);
501 em->string_table = (char *) va_format ((u8 *) em->string_table, fmt, &va);
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);
511 elog_event_t * elog_get_events (elog_main_t * em)
514 em->events = elog_peek_events (em);
518 static void maybe_fix_string_table_offset (elog_event_t * e,
519 elog_event_type_t * t,
522 void * d = (u8 *) e->data;
532 uword n_bytes = 0, n_digits;
537 /* Don't go past end of event data. */
538 ASSERT (d < (void *) (e->data + sizeof (e->data)));
540 n_digits = parse_2digit_decimal (a + 1, &n_bytes);
544 ASSERT (n_bytes == 4);
545 clib_mem_unaligned (d, u32) += offset;
559 ASSERT (n_digits > 0 && n_digits <= 2);
565 void elog_merge (elog_main_t * dst, u8 * dst_tag,
566 elog_main_t * src, u8 * src_tag)
570 u32 string_table_offset_for_src_events;
571 u32 track_offset_for_src_tracks;
575 elog_get_events (src);
576 elog_get_events (dst);
578 string_table_offset_for_src_events = vec_len (dst->string_table);
579 vec_append (dst->string_table, src->string_table);
581 l = vec_len (dst->events);
582 vec_add (dst->events, src->events, vec_len (src->events));
584 /* Prepend the supplied tag (if any) to all dst track names */
587 for (i = 0; i < vec_len(dst->tracks); i++)
589 elog_track_t * t = vec_elt_at_index (dst->tracks, i);
592 new_name = (char *) format (0, "%s:%s%c", dst_tag, t->name, 0);
598 track_offset_for_src_tracks = vec_len (dst->tracks);
600 /* Copy / tag source tracks */
601 for (i = 0; i < vec_len (src->tracks); i++)
603 elog_track_t * t = vec_elt_at_index (src->tracks, i);
605 newt.name = (char *) format (0, "%s:%s%c", src_tag, t->name, 0);
607 newt.name = (char *) format (0, "%s%c", t->name, 0);
608 (void) elog_track_register (dst, &newt);
609 vec_free (newt.name);
612 /* Across all (copied) src events... */
613 for (e = dst->events + l; e < vec_end (dst->events); e++)
615 elog_event_type_t * t = vec_elt_at_index (src->event_types, e->type);
617 /* Remap type from src -> dst. */
618 e->type = find_or_create_type (dst, t);
620 /* Remap string table offsets for 'T' format args */
621 maybe_fix_string_table_offset (e, t, string_table_offset_for_src_events);
624 e->track += track_offset_for_src_tracks;
627 /* Adjust event times for relative starting times of event streams. */
629 f64 dt_event, dt_os_nsec, dt_clock_nsec;
631 /* Set clock parameters if dst was not generated by unserialize. */
632 if (dst->serialize_time.cpu == 0)
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;
639 dt_os_nsec = elog_time_stamp_diff_os_nsec (&src->init_time, &dst->init_time);
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));
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;
653 /* Convert to seconds. */
658 /* Src started after dst. */
659 for (e = dst->events + l; e < vec_end (dst->events); e++)
664 /* Dst started after src. */
665 for (e = dst->events + 0; e < dst->events + l; e++)
670 /* Sort events by increasing time. */
671 vec_sort (dst->events, e1, e2, e1->time < e2->time ? -1 : (e1->time > e2->time ? +1 : 0));
673 /* Recreate the event ring or the results won't serialize */
677 ASSERT (dst->cpu_timer.seconds_per_clock);
679 elog_alloc (dst, vec_len (dst->events));
680 for (i = 0; i < vec_len(dst->events); i++)
682 elog_event_t *es, *ed;
684 es = dst->events + i;
685 ed = dst->event_ring + i;
689 /* Invert elog_peek_events calculation */
691 (es->time/dst->cpu_timer.seconds_per_clock) + dst->init_time.cpu;
693 dst->n_total_events = vec_len (dst->events);
698 serialize_elog_event (serialize_main_t * m, va_list * va)
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);
704 u8 * p = (u8 *) t->format_args;
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);
712 uword n_digits, n_bytes = 0;
714 n_digits = parse_2digit_decimal ((char *) p + 1, &n_bytes);
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));
734 serialize_cstring (m, (char *) d);
736 n_bytes = strlen ((char *) d) + 1;
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));
759 unserialize_elog_event (serialize_main_t * m, va_list * va)
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;
769 unserialize_integer (m, &tmp[0], sizeof (e->type));
770 unserialize_integer (m, &tmp[1], sizeof (e->track));
775 /* Make sure it fits. */
776 ASSERT (e->type == tmp[0]);
777 ASSERT (e->track == tmp[1]);
780 t = vec_elt_at_index (em->event_types, e->type);
782 unserialize (m, unserialize_f64, &e->time);
785 p = (u8 *) t->format_args;
789 uword n_digits, n_bytes = 0;
792 n_digits = parse_2digit_decimal ((char *) p + 1, &n_bytes);
801 unserialize_integer (m, &tmp, sizeof (u8));
804 else if (n_bytes == 2)
806 unserialize_integer (m, &tmp, sizeof (u16));
807 clib_mem_unaligned (d, u16) = tmp;
809 else if (n_bytes == 4)
811 unserialize_integer (m, &tmp, sizeof (u32));
812 clib_mem_unaligned (d, u32) = tmp;
814 else if (n_bytes == 8)
817 unserialize (m, unserialize_64, &x);
818 clib_mem_unaligned (d, u64) = x;
826 unserialize_cstring (m, &t);
828 n_bytes = strlen (t) + 1;
829 memcpy (d, t, clib_min (n_bytes, vec_len (t)));
838 unserialize (m, unserialize_f32, &x);
839 clib_mem_unaligned (d, f32) = x;
841 else if (n_bytes == 8)
844 unserialize (m, unserialize_f64, &x);
845 clib_mem_unaligned (d, f64) = x;
862 serialize_elog_event_type (serialize_main_t * m, va_list * va)
864 elog_event_type_t * t = va_arg (*va, elog_event_type_t *);
865 int n = va_arg (*va, int);
867 for (i = 0; i < n; i++)
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]);
879 unserialize_elog_event_type (serialize_main_t * m, va_list * va)
881 elog_event_type_t * t = va_arg (*va, elog_event_type_t *);
882 int n = va_arg (*va, int);
884 for (i = 0; i < n; i++)
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]);
897 serialize_elog_track (serialize_main_t * m, va_list * va)
899 elog_track_t * t = va_arg (*va, elog_track_t *);
900 int n = va_arg (*va, int);
902 for (i = 0; i < n; i++)
904 serialize_cstring (m, t[i].name);
909 unserialize_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 unserialize_cstring (m, &t[i].name);
921 serialize_elog_time_stamp (serialize_main_t * m, va_list * va)
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);
929 unserialize_elog_time_stamp (serialize_main_t * m, va_list * va)
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);
936 static char * elog_serialize_magic = "elog v0";
939 serialize_elog_main (serialize_main_t * m, va_list * va)
941 elog_main_t * em = va_arg (*va, elog_main_t *);
944 serialize_magic (m, elog_serialize_magic, strlen (elog_serialize_magic));
946 serialize_integer (m, em->event_ring_size, sizeof (u32));
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);
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);
956 /* Free old events (cached) in case they have changed. */
957 vec_free (em->events);
958 elog_get_events (em);
960 serialize_integer (m, vec_len (em->events), sizeof (u32));
962 /* SMP logs can easily have local time paradoxes... */
963 vec_sort (em->events, e0, e1, e0->time - e1->time);
965 vec_foreach (e, em->events)
966 serialize (m, serialize_elog_event, em, e);
970 unserialize_elog_main (serialize_main_t * m, va_list * va)
972 elog_main_t * em = va_arg (*va, elog_main_t *);
976 unserialize_check_magic (m, elog_serialize_magic,
977 strlen (elog_serialize_magic));
979 unserialize_integer (m, &rs, sizeof (u32));
980 em->event_ring_size = rs;
981 elog_init (em, em->event_ring_size);
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);
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);
991 vec_unserialize (m, &em->tracks, unserialize_elog_track);
992 vec_unserialize (m, &em->string_table, unserialize_vec_8);
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);