Initial commit of vpp code.
[vpp.git] / vppinfra / vppinfra / elog.h
1 /*
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:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
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.
14  */
15 /*
16   Copyright (c) 2005,2009 Eliot Dresselhaus
17
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:
25
26   The above copyright notice and this permission notice shall be
27   included in all copies or substantial portions of the Software.
28
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.
36 */
37
38 /* High speed event logging with much thanks to Dave Barach. */
39
40 #ifndef included_clib_elog_h
41 #define included_clib_elog_h
42
43 #include <vppinfra/cache.h>
44 #include <vppinfra/error.h>             /* for ASSERT */
45 #include <vppinfra/serialize.h>
46 #include <vppinfra/time.h>              /* for clib_cpu_time_now */
47 #include <vppinfra/mhash.h>
48
49 typedef struct{
50   union {
51     /* Absolute time stamp in CPU clock cycles. */
52     u64 time_cycles;
53
54     /* Absolute time as floating point number in seconds. */
55     f64 time;
56   };
57
58   /* Event type index. */
59   u16 type;
60
61   /* Track for this event.  Tracks allow events to be sorted and
62      displayed by track.  Think of 2 dimensional display with time and
63      track being the x and y axes.*/
64   u16 track;
65
66   /* 20-bytes of data follows and pads to 32 bytes. */
67   u8 data[20];
68 } elog_event_t;
69
70 typedef struct {
71   /* Type index plus one assigned to this type.
72      This is used to mark type as seen. */
73   u32 type_index_plus_one;
74
75   /* String table as a vector constructed when type is registered. */
76   char ** enum_strings_vector;
77
78   /* Format string. (example: "my-event (%d,%d)"). */
79   char * format;
80
81   /* Specifies how arguments to format are parsed from event data.
82      String of characters '0' '1' or '2' '3' to specify log2 size of data
83      (e.g. for u8, u16, u32 or u64),
84      's' means a null-terminated C string
85      't' means argument is an index into enum string table for this type.
86      'e' is a float,
87      'f' is a double. */
88   char * format_args;
89
90   /* Function name generating event. */
91   char * function;
92
93   /* Number of elements in string enum table. */
94   u32 n_enum_strings;
95
96   /* String table for enum/number to string formatting. */
97   char * enum_strings[];
98 } elog_event_type_t;
99
100 typedef struct {
101   /* Track name vector. */
102   char * name;
103
104   /* Set to one when track has been added to
105      main structure. */
106   u32 track_index_plus_one;
107 } elog_track_t;
108
109 typedef struct {
110   /* CPU cycle counter. */
111   u64 cpu;
112
113   /* OS timer in nano secs since epoch Jan 1 1970. */
114   u64 os_nsec;
115 } elog_time_stamp_t;
116
117 typedef struct {
118   /* Total number of events in buffer. */
119   u32 n_total_events;
120
121   /* When count reaches limit logging is disabled.  This is
122      used for event triggers. */
123   u32 n_total_events_disable_limit;
124
125   /* Dummy event to use when logger is disabled. */
126   elog_event_t dummy_event;
127
128   /* Power of 2 number of elements in ring. */
129   uword event_ring_size;
130
131   /* Vector of events (circular buffer).  Power of 2 size.
132      Used when events are being collected. */
133   elog_event_t * event_ring;
134
135   /* Vector of event types. */
136   elog_event_type_t * event_types;
137
138   /* Hash table mapping type format to type index. */
139   uword * event_type_by_format;
140
141   /* Events may refer to strings in string table. */
142   char * string_table;
143
144   /* Vector of tracks. */
145   elog_track_t * tracks;
146
147   /* Default track. */
148   elog_track_t default_track;
149
150   /* Place holder for CPU clock frequency. */
151   clib_time_t cpu_timer;
152
153   elog_time_stamp_t init_time, serialize_time;
154
155   /* SMP lock, non-zero means locking required */
156   uword * lock;
157
158   /* Use serialize_time and init_time to give estimate for
159      cpu clock frequency. */
160   f64 nsec_per_cpu_clock;
161
162   /* Vector of events converted to generic form after collection. */
163   elog_event_t * events;
164 } elog_main_t;
165
166 always_inline uword
167 elog_n_events_in_buffer (elog_main_t * em)
168 { return clib_min (em->n_total_events, em->event_ring_size); }
169
170 always_inline uword
171 elog_buffer_capacity (elog_main_t * em)
172 { return em->event_ring_size; }
173
174 always_inline void
175 elog_reset_buffer (elog_main_t * em)
176 {
177   em->n_total_events = 0;
178   em->n_total_events_disable_limit = ~0;
179 }
180
181 always_inline void
182 elog_enable_disable (elog_main_t * em, int is_enabled)
183 {
184   em->n_total_events = 0;
185   em->n_total_events_disable_limit = is_enabled ? ~0ULL : 0ULL;
186 }
187
188 /* Disable logging after specified number of ievents have been logged.
189    This is used as a "debug trigger" when a certain event has occurred.
190    Events will be logged both before and after the "event" but the
191    event will not be lost as long as N < RING_SIZE. */
192 always_inline void
193 elog_disable_after_events (elog_main_t * em, uword n)
194 { em->n_total_events_disable_limit = em->n_total_events + n; }
195
196 /* Signal a trigger.  We do this when we encounter an event that we want to save
197    context around (before and after). */
198 always_inline void
199 elog_disable_trigger (elog_main_t * em)
200 { em->n_total_events_disable_limit = em->n_total_events + vec_len (em->event_ring) / 2; }
201
202 /* External function to register types/tracks. */
203 word elog_event_type_register (elog_main_t * em, elog_event_type_t * t);
204 word elog_track_register (elog_main_t * em, elog_track_t * t);
205
206 always_inline uword
207 elog_is_enabled (elog_main_t * em)
208 { return em->n_total_events < em->n_total_events_disable_limit; }
209
210 /* Add an event to the log.  Returns a pointer to the
211    data for caller to write into. */
212 always_inline void *
213 elog_event_data_inline (elog_main_t * em,
214                         elog_event_type_t * type,
215                         elog_track_t * track,
216                         u64 cpu_time)
217 {
218   elog_event_t * e;
219   uword ei;
220   word type_index, track_index;
221
222   /* Return the user dummy memory to scribble data into. */
223   if (PREDICT_FALSE (! elog_is_enabled (em)))
224     return em->dummy_event.data;
225
226   type_index = (word) type->type_index_plus_one - 1;
227   track_index = (word) track->track_index_plus_one - 1;
228   if (PREDICT_FALSE ((type_index | track_index) < 0))
229     {
230       if (type_index < 0)
231         type_index = elog_event_type_register (em, type);
232       if (track_index < 0)
233         track_index = elog_track_register (em, track);
234     }
235
236   ASSERT (type_index < vec_len (em->event_types));
237   ASSERT (track_index < vec_len (em->tracks));
238   ASSERT (is_pow2 (vec_len (em->event_ring)));
239
240   if (em->lock)
241     ei = clib_smp_atomic_add (&em->n_total_events, 1);
242   else
243     ei = em->n_total_events++;
244
245   ei &= em->event_ring_size - 1;
246   e = vec_elt_at_index (em->event_ring, ei);
247
248   e->time_cycles = cpu_time;
249   e->type = type_index;
250   e->track = track_index;
251
252   /* Return user data for caller to fill in. */
253   return e->data;
254 }
255
256 /* External version of inline. */
257 void *
258 elog_event_data (elog_main_t * em,
259                  elog_event_type_t * type,
260                  elog_track_t * track,
261                  u64 cpu_time);
262
263 /* Non-inline version. */
264 always_inline void *
265 elog_event_data_not_inline (elog_main_t * em,
266                             elog_event_type_t * type,
267                             elog_track_t * track,
268                             u64 cpu_time)
269 {
270   /* Return the user dummy memory to scribble data into. */
271   if (PREDICT_FALSE (! elog_is_enabled (em)))
272     return em->dummy_event.data;
273   return elog_event_data (em, type, track, cpu_time);
274 }
275
276 /* Most common form: log a single 32 bit datum. */
277 always_inline void
278 elog (elog_main_t * em, elog_event_type_t * type, u32 data)
279 {
280   u32 * d = elog_event_data_not_inline
281     (em,
282      type,
283      &em->default_track,
284      clib_cpu_time_now ());
285   d[0] = data;
286 }
287
288 /* Inline version of above. */
289 always_inline void
290 elog_inline (elog_main_t * em, elog_event_type_t * type, u32 data)
291 {
292   u32 * d = elog_event_data_inline
293     (em,
294      type,
295      &em->default_track,
296      clib_cpu_time_now ());
297   d[0] = data;
298 }
299
300 always_inline void *
301 elog_data (elog_main_t * em, elog_event_type_t * type, elog_track_t * track)
302 {
303   return elog_event_data_not_inline
304     (em, type, track,
305      clib_cpu_time_now ());
306 }
307
308 always_inline void *
309 elog_data_inline (elog_main_t * em, elog_event_type_t * type, elog_track_t * track)
310 {
311   return elog_event_data_inline
312     (em, type, track,
313      clib_cpu_time_now ());
314 }
315
316 /* Macro shorthands for generating/declaring events. */
317 #define __ELOG_TYPE_VAR(f) f
318 #define __ELOG_TRACK_VAR(f) f
319
320 #define ELOG_TYPE_DECLARE(f) static elog_event_type_t __ELOG_TYPE_VAR(f)
321
322 #define ELOG_TYPE_INIT_FORMAT_AND_FUNCTION(fmt,func) \
323   { .format = fmt, .function = func, }
324
325 #define ELOG_TYPE_INIT(fmt) \
326   ELOG_TYPE_INIT_FORMAT_AND_FUNCTION(fmt,(char *) __FUNCTION__)
327
328 #define ELOG_TYPE_DECLARE_HELPER(f,fmt,func)            \
329   static elog_event_type_t __ELOG_TYPE_VAR(f) =         \
330     ELOG_TYPE_INIT_FORMAT_AND_FUNCTION (fmt, func)
331
332 #define ELOG_TYPE_DECLARE_FORMAT_AND_FUNCTION(f,fmt)            \
333   ELOG_TYPE_DECLARE_HELPER (f, fmt, (char *) __FUNCTION__)
334
335 #define ELOG_TYPE_DECLARE_FORMAT(f,fmt)         \
336   ELOG_TYPE_DECLARE_HELPER (f, fmt, 0)
337
338 /* Shorthands with and without __FUNCTION__.
339    D for decimal; X for hex.  F for __FUNCTION__. */
340 #define ELOG_TYPE(f,fmt) ELOG_TYPE_DECLARE_FORMAT_AND_FUNCTION(f,fmt)
341 #define ELOG_TYPE_D(f)  ELOG_TYPE_DECLARE_FORMAT (f, #f " %d")
342 #define ELOG_TYPE_X(f)  ELOG_TYPE_DECLARE_FORMAT (f, #f " 0x%x")
343 #define ELOG_TYPE_DF(f) ELOG_TYPE_DECLARE_FORMAT_AND_FUNCTION (f, #f " %d")
344 #define ELOG_TYPE_XF(f) ELOG_TYPE_DECLARE_FORMAT_AND_FUNCTION (f, #f " 0x%x")
345 #define ELOG_TYPE_FD(f) ELOG_TYPE_DECLARE_FORMAT_AND_FUNCTION (f, #f " %d")
346 #define ELOG_TYPE_FX(f) ELOG_TYPE_DECLARE_FORMAT_AND_FUNCTION (f, #f " 0x%x")
347
348 #define ELOG_TRACK_DECLARE(f) static elog_track_t __ELOG_TRACK_VAR(f)
349 #define ELOG_TRACK(f) ELOG_TRACK_DECLARE(f) = { .name = #f, }
350
351 /* Log 32 bits of data. */
352 #define ELOG(em,f,data) elog ((em), &__ELOG_TYPE_VAR(f), data)
353 #define ELOG_INLINE(em,f,data) elog_inline ((em), &__ELOG_TYPE_VAR(f), data)
354
355 /* Return data pointer to fill in. */
356 #define ELOG_TRACK_DATA(em,f,track) \
357   elog_data ((em), &__ELOG_TYPE_VAR(f), &__ELOG_TRACK_VAR(track))
358 #define ELOG_TRACK_DATA_INLINE(em,f,track) \
359   elog_data_inline ((em), &__ELOG_TYPE_VAR(f), &__ELOG_TRACK_VAR(track))
360
361 /* Shorthand with default track. */
362 #define ELOG_DATA(em,f) elog_data ((em), &__ELOG_TYPE_VAR (f), &(em)->default_track)
363 #define ELOG_DATA_INLINE(em,f) elog_data_inline ((em), &__ELOG_TYPE_VAR (f), &(em)->default_track)
364
365 u32 elog_string (elog_main_t * em, char * format, ...);
366 void elog_time_now (elog_time_stamp_t * et);
367
368 /* Convert ievents to events and return them as a vector.
369    Sets em->events to resulting vector. */
370 elog_event_t * elog_get_events (elog_main_t * em);
371
372 /* Convert ievents to events and return them as a vector with no side effects. */
373 elog_event_t * elog_peek_events (elog_main_t * em);
374
375 /* Merge two logs, add supplied track tags. */
376 void elog_merge (elog_main_t * dst, u8 * dst_tag, 
377                  elog_main_t * src, u8 * src_tag);
378
379 /* 2 arguments elog_main_t and elog_event_t to format event or track name. */
380 u8 * format_elog_event (u8 * s, va_list * va);
381 u8 * format_elog_track (u8 * s, va_list * va);
382
383 void serialize_elog_main (serialize_main_t * m, va_list * va);
384 void unserialize_elog_main (serialize_main_t * m, va_list * va);
385
386 void elog_init (elog_main_t * em, u32 n_events);
387
388 #ifdef CLIB_UNIX
389 always_inline clib_error_t *
390 elog_write_file (elog_main_t * em, char * unix_file)
391 {
392   serialize_main_t m;
393   clib_error_t * error;
394
395   error = serialize_open_unix_file (&m, unix_file);
396   if (error)
397     return error;
398   error = serialize (&m, serialize_elog_main, em);
399   if (! error)
400     serialize_close (&m);
401   return error;
402 }
403
404 always_inline clib_error_t *
405 elog_read_file (elog_main_t * em, char * unix_file)
406 {
407   serialize_main_t m;
408   clib_error_t * error;
409
410   error = unserialize_open_unix_file (&m, unix_file);
411   if (error)
412     return error;
413   error = unserialize (&m, unserialize_elog_main, em);
414   if (! error)
415     unserialize_close (&m);
416   return error;
417 }
418
419 #endif /* CLIB_UNIX */
420
421 #endif /* included_clib_elog_h */