Reorganize source tree to use single autotools instance
[vpp.git] / src / vppinfra / elog.h
diff --git a/src/vppinfra/elog.h b/src/vppinfra/elog.h
new file mode 100644 (file)
index 0000000..9756fb8
--- /dev/null
@@ -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:
+ */