summaryrefslogtreecommitdiffstats
path: root/src/vppinfra/elog.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/vppinfra/elog.h')
-rw-r--r--src/vppinfra/elog.h460
1 files changed, 460 insertions, 0 deletions
diff --git a/src/vppinfra/elog.h b/src/vppinfra/elog.h
new file mode 100644
index 00000000000..9756fb83a8d
--- /dev/null
+++ b/src/vppinfra/elog.h
@@ -0,0 +1,460 @@
+/*
+ * Copyright (c) 2015 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ Copyright (c) 2005,2009 Eliot Dresselhaus
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+/* High speed event logging with much thanks to Dave Barach. */
+
+#ifndef included_clib_elog_h
+#define included_clib_elog_h
+
+#include <vppinfra/cache.h>
+#include <vppinfra/error.h> /* for ASSERT */
+#include <vppinfra/serialize.h>
+#include <vppinfra/time.h> /* for clib_cpu_time_now */
+#include <vppinfra/mhash.h>
+
+typedef struct
+{
+ union
+ {
+ /* Absolute time stamp in CPU clock cycles. */
+ u64 time_cycles;
+
+ /* Absolute time as floating point number in seconds. */
+ f64 time;
+ };
+
+ /* Event type index. */
+ u16 type;
+
+ /* Track for this event. Tracks allow events to be sorted and
+ displayed by track. Think of 2 dimensional display with time and
+ track being the x and y axes. */
+ u16 track;
+
+ /* 20-bytes of data follows and pads to 32 bytes. */
+ u8 data[20];
+} elog_event_t;
+
+typedef struct
+{
+ /* Type index plus one assigned to this type.
+ This is used to mark type as seen. */
+ u32 type_index_plus_one;
+
+ /* String table as a vector constructed when type is registered. */
+ char **enum_strings_vector;
+
+ /* Format string. (example: "my-event (%d,%d)"). */
+ char *format;
+
+ /* Specifies how arguments to format are parsed from event data.
+ String of characters '0' '1' or '2' '3' to specify log2 size of data
+ (e.g. for u8, u16, u32 or u64),
+ 's' means a null-terminated C string
+ 't' means argument is an index into enum string table for this type.
+ 'e' is a float,
+ 'f' is a double. */
+ char *format_args;
+
+ /* Function name generating event. */
+ char *function;
+
+ /* Number of elements in string enum table. */
+ u32 n_enum_strings;
+
+ /* String table for enum/number to string formatting. */
+ char *enum_strings[];
+} elog_event_type_t;
+
+typedef struct
+{
+ /* Track name vector. */
+ char *name;
+
+ /* Set to one when track has been added to
+ main structure. */
+ u32 track_index_plus_one;
+} elog_track_t;
+
+typedef struct
+{
+ /* CPU cycle counter. */
+ u64 cpu;
+
+ /* OS timer in nano secs since epoch Jan 1 1970. */
+ u64 os_nsec;
+} elog_time_stamp_t;
+
+typedef struct
+{
+ /* Total number of events in buffer. */
+ u32 n_total_events;
+
+ /* When count reaches limit logging is disabled. This is
+ used for event triggers. */
+ u32 n_total_events_disable_limit;
+
+ /* Dummy event to use when logger is disabled. */
+ elog_event_t dummy_event;
+
+ /* Power of 2 number of elements in ring. */
+ uword event_ring_size;
+
+ /* Vector of events (circular buffer). Power of 2 size.
+ Used when events are being collected. */
+ elog_event_t *event_ring;
+
+ /* Vector of event types. */
+ elog_event_type_t *event_types;
+
+ /* Hash table mapping type format to type index. */
+ uword *event_type_by_format;
+
+ /* Events may refer to strings in string table. */
+ char *string_table;
+
+ /* Vector of tracks. */
+ elog_track_t *tracks;
+
+ /* Default track. */
+ elog_track_t default_track;
+
+ /* Place holder for CPU clock frequency. */
+ clib_time_t cpu_timer;
+
+ elog_time_stamp_t init_time, serialize_time;
+
+ /* SMP lock, non-zero means locking required */
+ uword *lock;
+
+ /* Use serialize_time and init_time to give estimate for
+ cpu clock frequency. */
+ f64 nsec_per_cpu_clock;
+
+ /* Vector of events converted to generic form after collection. */
+ elog_event_t *events;
+} elog_main_t;
+
+always_inline uword
+elog_n_events_in_buffer (elog_main_t * em)
+{
+ return clib_min (em->n_total_events, em->event_ring_size);
+}
+
+always_inline uword
+elog_buffer_capacity (elog_main_t * em)
+{
+ return em->event_ring_size;
+}
+
+always_inline void
+elog_reset_buffer (elog_main_t * em)
+{
+ em->n_total_events = 0;
+ em->n_total_events_disable_limit = ~0;
+}
+
+always_inline void
+elog_enable_disable (elog_main_t * em, int is_enabled)
+{
+ em->n_total_events = 0;
+ em->n_total_events_disable_limit = is_enabled ? ~0 : 0;
+}
+
+/* Disable logging after specified number of ievents have been logged.
+ This is used as a "debug trigger" when a certain event has occurred.
+ Events will be logged both before and after the "event" but the
+ event will not be lost as long as N < RING_SIZE. */
+always_inline void
+elog_disable_after_events (elog_main_t * em, uword n)
+{
+ em->n_total_events_disable_limit = em->n_total_events + n;
+}
+
+/* Signal a trigger. We do this when we encounter an event that we want to save
+ context around (before and after). */
+always_inline void
+elog_disable_trigger (elog_main_t * em)
+{
+ em->n_total_events_disable_limit =
+ em->n_total_events + vec_len (em->event_ring) / 2;
+}
+
+/* External function to register types/tracks. */
+word elog_event_type_register (elog_main_t * em, elog_event_type_t * t);
+word elog_track_register (elog_main_t * em, elog_track_t * t);
+
+always_inline uword
+elog_is_enabled (elog_main_t * em)
+{
+ return em->n_total_events < em->n_total_events_disable_limit;
+}
+
+/* Add an event to the log. Returns a pointer to the
+ data for caller to write into. */
+always_inline void *
+elog_event_data_inline (elog_main_t * em,
+ elog_event_type_t * type,
+ elog_track_t * track, u64 cpu_time)
+{
+ elog_event_t *e;
+ uword ei;
+ word type_index, track_index;
+
+ /* Return the user dummy memory to scribble data into. */
+ if (PREDICT_FALSE (!elog_is_enabled (em)))
+ return em->dummy_event.data;
+
+ type_index = (word) type->type_index_plus_one - 1;
+ track_index = (word) track->track_index_plus_one - 1;
+ if (PREDICT_FALSE ((type_index | track_index) < 0))
+ {
+ if (type_index < 0)
+ type_index = elog_event_type_register (em, type);
+ if (track_index < 0)
+ track_index = elog_track_register (em, track);
+ }
+
+ ASSERT (type_index < vec_len (em->event_types));
+ ASSERT (track_index < vec_len (em->tracks));
+ ASSERT (is_pow2 (vec_len (em->event_ring)));
+
+ if (em->lock)
+ ei = clib_smp_atomic_add (&em->n_total_events, 1);
+ else
+ ei = em->n_total_events++;
+
+ ei &= em->event_ring_size - 1;
+ e = vec_elt_at_index (em->event_ring, ei);
+
+ e->time_cycles = cpu_time;
+ e->type = type_index;
+ e->track = track_index;
+
+ /* Return user data for caller to fill in. */
+ return e->data;
+}
+
+/* External version of inline. */
+void *elog_event_data (elog_main_t * em,
+ elog_event_type_t * type,
+ elog_track_t * track, u64 cpu_time);
+
+/* Non-inline version. */
+always_inline void *
+elog_event_data_not_inline (elog_main_t * em,
+ elog_event_type_t * type,
+ elog_track_t * track, u64 cpu_time)
+{
+ /* Return the user dummy memory to scribble data into. */
+ if (PREDICT_FALSE (!elog_is_enabled (em)))
+ return em->dummy_event.data;
+ return elog_event_data (em, type, track, cpu_time);
+}
+
+/* Most common forms: log a single 32 bit datum, w / w-out track */
+always_inline void
+elog (elog_main_t * em, elog_event_type_t * type, u32 data)
+{
+ u32 *d = elog_event_data_not_inline (em,
+ type,
+ &em->default_track,
+ clib_cpu_time_now ());
+ d[0] = data;
+}
+
+/* Inline version of above. */
+always_inline void
+elog_inline (elog_main_t * em, elog_event_type_t * type, u32 data)
+{
+ u32 *d = elog_event_data_inline (em,
+ type,
+ &em->default_track,
+ clib_cpu_time_now ());
+ d[0] = data;
+}
+
+always_inline void
+elog_track (elog_main_t * em, elog_event_type_t * type, elog_track_t * track,
+ u32 data)
+{
+ u32 *d = elog_event_data_not_inline (em,
+ type,
+ track,
+ clib_cpu_time_now ());
+ d[0] = data;
+}
+
+always_inline void
+elog_track_inline (elog_main_t * em, elog_event_type_t * type,
+ elog_track_t * track, u32 data)
+{
+ u32 *d = elog_event_data_inline (em,
+ type,
+ track,
+ clib_cpu_time_now ());
+ d[0] = data;
+}
+
+always_inline void *
+elog_data (elog_main_t * em, elog_event_type_t * type, elog_track_t * track)
+{
+ return elog_event_data_not_inline (em, type, track, clib_cpu_time_now ());
+}
+
+always_inline void *
+elog_data_inline (elog_main_t * em, elog_event_type_t * type,
+ elog_track_t * track)
+{
+ return elog_event_data_inline (em, type, track, clib_cpu_time_now ());
+}
+
+/* Macro shorthands for generating/declaring events. */
+#define __ELOG_TYPE_VAR(f) f
+#define __ELOG_TRACK_VAR(f) f
+
+#define ELOG_TYPE_DECLARE(f) static elog_event_type_t __ELOG_TYPE_VAR(f)
+
+#define ELOG_TYPE_INIT_FORMAT_AND_FUNCTION(fmt,func) \
+ { .format = fmt, .function = func, }
+
+#define ELOG_TYPE_INIT(fmt) \
+ ELOG_TYPE_INIT_FORMAT_AND_FUNCTION(fmt,(char *) __FUNCTION__)
+
+#define ELOG_TYPE_DECLARE_HELPER(f,fmt,func) \
+ static elog_event_type_t __ELOG_TYPE_VAR(f) = \
+ ELOG_TYPE_INIT_FORMAT_AND_FUNCTION (fmt, func)
+
+#define ELOG_TYPE_DECLARE_FORMAT_AND_FUNCTION(f,fmt) \
+ ELOG_TYPE_DECLARE_HELPER (f, fmt, (char *) __FUNCTION__)
+
+#define ELOG_TYPE_DECLARE_FORMAT(f,fmt) \
+ ELOG_TYPE_DECLARE_HELPER (f, fmt, 0)
+
+/* Shorthands with and without __FUNCTION__.
+ D for decimal; X for hex. F for __FUNCTION__. */
+#define ELOG_TYPE(f,fmt) ELOG_TYPE_DECLARE_FORMAT_AND_FUNCTION(f,fmt)
+#define ELOG_TYPE_D(f) ELOG_TYPE_DECLARE_FORMAT (f, #f " %d")
+#define ELOG_TYPE_X(f) ELOG_TYPE_DECLARE_FORMAT (f, #f " 0x%x")
+#define ELOG_TYPE_DF(f) ELOG_TYPE_DECLARE_FORMAT_AND_FUNCTION (f, #f " %d")
+#define ELOG_TYPE_XF(f) ELOG_TYPE_DECLARE_FORMAT_AND_FUNCTION (f, #f " 0x%x")
+#define ELOG_TYPE_FD(f) ELOG_TYPE_DECLARE_FORMAT_AND_FUNCTION (f, #f " %d")
+#define ELOG_TYPE_FX(f) ELOG_TYPE_DECLARE_FORMAT_AND_FUNCTION (f, #f " 0x%x")
+
+#define ELOG_TRACK_DECLARE(f) static elog_track_t __ELOG_TRACK_VAR(f)
+#define ELOG_TRACK(f) ELOG_TRACK_DECLARE(f) = { .name = #f, }
+
+/* Log 32 bits of data. */
+#define ELOG(em,f,data) elog ((em), &__ELOG_TYPE_VAR(f), data)
+#define ELOG_INLINE(em,f,data) elog_inline ((em), &__ELOG_TYPE_VAR(f), data)
+
+/* Return data pointer to fill in. */
+#define ELOG_TRACK_DATA(em,f,track) \
+ elog_data ((em), &__ELOG_TYPE_VAR(f), &__ELOG_TRACK_VAR(track))
+#define ELOG_TRACK_DATA_INLINE(em,f,track) \
+ elog_data_inline ((em), &__ELOG_TYPE_VAR(f), &__ELOG_TRACK_VAR(track))
+
+/* Shorthand with default track. */
+#define ELOG_DATA(em,f) elog_data ((em), &__ELOG_TYPE_VAR (f), &(em)->default_track)
+#define ELOG_DATA_INLINE(em,f) elog_data_inline ((em), &__ELOG_TYPE_VAR (f), &(em)->default_track)
+
+u32 elog_string (elog_main_t * em, char *format, ...);
+void elog_time_now (elog_time_stamp_t * et);
+
+/* Convert ievents to events and return them as a vector.
+ Sets em->events to resulting vector. */
+elog_event_t *elog_get_events (elog_main_t * em);
+
+/* Convert ievents to events and return them as a vector with no side effects. */
+elog_event_t *elog_peek_events (elog_main_t * em);
+
+/* Merge two logs, add supplied track tags. */
+void elog_merge (elog_main_t * dst, u8 * dst_tag,
+ elog_main_t * src, u8 * src_tag);
+
+/* 2 arguments elog_main_t and elog_event_t to format event or track name. */
+u8 *format_elog_event (u8 * s, va_list * va);
+u8 *format_elog_track (u8 * s, va_list * va);
+
+void serialize_elog_main (serialize_main_t * m, va_list * va);
+void unserialize_elog_main (serialize_main_t * m, va_list * va);
+
+void elog_init (elog_main_t * em, u32 n_events);
+void elog_alloc (elog_main_t * em, u32 n_events);
+
+#ifdef CLIB_UNIX
+always_inline clib_error_t *
+elog_write_file (elog_main_t * em, char *unix_file)
+{
+ serialize_main_t m;
+ clib_error_t *error;
+
+ error = serialize_open_unix_file (&m, unix_file);
+ if (error)
+ return error;
+ error = serialize (&m, serialize_elog_main, em);
+ if (!error)
+ serialize_close (&m);
+ return error;
+}
+
+always_inline clib_error_t *
+elog_read_file (elog_main_t * em, char *unix_file)
+{
+ serialize_main_t m;
+ clib_error_t *error;
+
+ error = unserialize_open_unix_file (&m, unix_file);
+ if (error)
+ return error;
+ error = unserialize (&m, unserialize_elog_main, em);
+ if (!error)
+ unserialize_close (&m);
+ return error;
+}
+
+#endif /* CLIB_UNIX */
+
+#endif /* included_clib_elog_h */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */