diff options
Diffstat (limited to 'src/vppinfra/elog.h')
-rw-r--r-- | src/vppinfra/elog.h | 460 |
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: + */ |