vlib: introduce trace filter functions
[vpp.git] / src / plugins / tracedump / tracedump.c
1 /*
2  * tracedump.c - skeleton vpp engine plug-in
3  *
4  * Copyright (c) <current-year> <your-organization>
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at:
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17
18 #include <vnet/vnet.h>
19 #include <vnet/plugin/plugin.h>
20 #include <tracedump/tracedump.h>
21 #include <vlib/trace.h>
22
23 #include <vlibapi/api.h>
24 #include <vlibmemory/api.h>
25 #include <vpp/app/version.h>
26 #include <stdbool.h>
27
28 #include <tracedump/tracedump.api_enum.h>
29 #include <tracedump/tracedump.api_types.h>
30
31 #define REPLY_MSG_ID_BASE tdmp->msg_id_base
32 #include <vlibapi/api_helper_macros.h>
33
34 tracedump_main_t tracedump_main;
35
36
37 static void
38 vl_api_trace_set_filters_t_handler (vl_api_trace_set_filters_t * mp)
39 {
40   vlib_main_t *vm = vlib_get_main ();
41   tracedump_main_t *tdmp = &tracedump_main;
42   u32 node_index = clib_net_to_host_u32 (mp->node_index);
43   u32 flag = clib_net_to_host_u32 (mp->flag);
44   u32 count = clib_net_to_host_u32 (mp->count);
45   vl_api_trace_set_filters_reply_t *rmp;
46   int rv = 0;
47
48   if (flag == TRACE_FF_NONE)
49     {
50       count = node_index = 0;
51     }
52   else if (flag != TRACE_FF_INCLUDE_NODE && flag != TRACE_FF_EXCLUDE_NODE)
53     {
54       rv = VNET_API_ERROR_INVALID_VALUE;
55       goto done;
56     }
57
58   vlib_node_t *node;
59   node = vlib_get_node (vm, node_index);
60   if (!node)
61     {
62       rv = VNET_API_ERROR_NO_SUCH_NODE;
63       goto done;
64     }
65
66   trace_filter_set (node_index, flag, count);
67
68 done:
69   REPLY_MACRO (VL_API_TRACE_SET_FILTERS_REPLY);
70 }
71
72
73 static void
74 vl_api_trace_capture_packets_t_handler (vl_api_trace_capture_packets_t * mp)
75 {
76   vlib_main_t *vm = vlib_get_main ();
77   tracedump_main_t *tdmp = &tracedump_main;
78   u32 add = clib_net_to_host_u32 (mp->max_packets);
79   u32 node_index = clib_net_to_host_u32 (mp->node_index);
80   u8 filter = mp->use_filter;
81   u8 verbose = mp->verbose;
82   u8 pre_clear = mp->pre_capture_clear;
83   vl_api_trace_capture_packets_reply_t *rmp;
84   int rv = 0;
85
86   if (!vnet_trace_placeholder)
87     vec_validate_aligned (vnet_trace_placeholder, 2048,
88                           CLIB_CACHE_LINE_BYTES);
89
90   vlib_node_t *node;
91   node = vlib_get_node (vm, node_index);
92   if (!node)
93     {
94       rv = VNET_API_ERROR_NO_SUCH_NODE;
95       goto done;
96     }
97
98   if ((node->flags & VLIB_NODE_FLAG_TRACE_SUPPORTED) == 0)
99     {
100       /* FIXME: Make a new, better error like "UNSUPPORTED_NODE_OPERATION"? */
101       rv = VNET_API_ERROR_NO_SUCH_NODE;
102       goto done;
103     }
104
105   if (pre_clear)
106     vlib_trace_stop_and_clear ();
107
108   trace_update_capture_options (add, node_index, filter, verbose);
109
110 done:
111   REPLY_MACRO (VL_API_TRACE_CAPTURE_PACKETS_REPLY);
112 }
113
114
115 static void
116 vl_api_trace_clear_capture_t_handler (vl_api_trace_clear_capture_t * mp)
117 {
118   vl_api_trace_clear_capture_reply_t *rmp;
119   tracedump_main_t *tdmp = &tracedump_main;
120
121   vlib_trace_stop_and_clear ();
122
123   int rv = 0;
124   REPLY_MACRO (VL_API_TRACE_CLEAR_CAPTURE_REPLY);
125 }
126
127
128
129 static int
130 trace_cmp (void *a1, void *a2)
131 {
132   vlib_trace_header_t **t1 = a1;
133   vlib_trace_header_t **t2 = a2;
134   i64 dt = t1[0]->time - t2[0]->time;
135   return dt < 0 ? -1 : (dt > 0 ? +1 : 0);
136 }
137
138 static void
139 toss_client_cache (tracedump_main_t * tdmp, u32 client_index,
140                    vlib_trace_header_t *** client_trace_cache)
141 {
142   vlib_trace_header_t **th;
143   int i;
144
145   /* Across each vlib main... */
146   for (i = 0; i < vec_len (client_trace_cache); i++)
147     {
148       th = client_trace_cache[i];
149       /* Toss the thread's cached data */
150       vec_free (th);
151     }
152   /* And toss the vector of threads */
153   vec_free (client_trace_cache);
154   tdmp->traces[client_index] = client_trace_cache;
155 }
156
157 static clib_error_t *
158 tracedump_cache_reaper (u32 client_index)
159 {
160   tracedump_main_t *tdmp = &tracedump_main;
161   vlib_trace_header_t ***client_trace_cache;
162
163   /* Its likely that we won't have a cache entry */
164   if (client_index >= vec_len (tdmp->traces))
165     return 0;
166
167   client_trace_cache = tdmp->traces[client_index];
168   toss_client_cache (tdmp, client_index, client_trace_cache);
169   return 0;
170 }
171
172 VL_MSG_API_REAPER_FUNCTION (tracedump_cache_reaper);
173
174 /* API message handler */
175 static void
176 vl_api_trace_dump_t_handler (vl_api_trace_dump_t * mp)
177 {
178   vl_api_registration_t *rp;
179   vl_api_trace_dump_reply_t *rmp;
180   vl_api_trace_details_t *dmp;
181   tracedump_main_t *tdmp = &tracedump_main;
182   vlib_trace_header_t ***client_trace_cache, **th;
183   int i, j;
184   u32 client_index;
185   u32 iterator_thread_id, iterator_position, max_records;
186   i32 retval = VNET_API_ERROR_NO_SUCH_ENTRY;
187   u32 last_thread_id = ~0, last_position = ~0;
188   u8 last_done = 0;
189   u8 last_more_this_thread = 0;
190   u8 last_more_threads = 0;
191   u8 *s = 0;
192
193   rp = vl_api_client_index_to_registration (mp->client_index);
194   if (rp == 0)
195     return;
196
197   /* Use the registration pool index... */
198   client_index = rp->vl_api_registration_pool_index;
199
200   vec_validate_init_empty (tdmp->traces, client_index, 0);
201
202   client_trace_cache = tdmp->traces[client_index];
203
204   /* Clear the per-client cache if requested */
205   if (mp->clear_cache)
206     {
207       toss_client_cache (tdmp, client_index, client_trace_cache);
208       client_trace_cache = 0;
209     }
210
211   /* Now, where were we? */
212   iterator_thread_id = clib_net_to_host_u32 (mp->thread_id);
213   iterator_position = clib_net_to_host_u32 (mp->position);
214   max_records = clib_net_to_host_u32 (mp->max_records);
215
216   /* Don't overflow the existing queue space for shared memory API clients. */
217   if (rp->vl_input_queue)
218     {
219       svm_queue_t *q = rp->vl_input_queue;
220       u32 queue_slots_available = q->maxsize - q->cursize;
221       int chunk = (queue_slots_available > 0) ? queue_slots_available - 1 : 0;
222       if (chunk < max_records)
223         max_records = chunk;
224     }
225
226   /* Need a fresh cache for this client? */
227   if (vec_len (client_trace_cache) == 0
228       && (iterator_thread_id != ~0 || iterator_position != ~0))
229     {
230       vlib_worker_thread_barrier_sync (vlib_get_first_main ());
231
232       /* Make a slot for each worker thread */
233       vec_validate (client_trace_cache, vlib_get_n_threads () - 1);
234       i = 0;
235
236       foreach_vlib_main ()
237         {
238           vlib_trace_main_t *tm = &this_vlib_main->trace_main;
239
240           /* Filter as directed */
241           trace_apply_filter (this_vlib_main);
242
243           pool_foreach (th, tm->trace_buffer_pool)
244             {
245               vec_add1 (client_trace_cache[i], th[0]);
246             }
247
248           /* Sort them by increasing time. */
249           if (vec_len (client_trace_cache[i]))
250             vec_sort_with_function (client_trace_cache[i], trace_cmp);
251
252           i++;
253         }
254       vlib_worker_thread_barrier_release (vlib_get_first_main ());
255     }
256
257   /* Save the cache, one way or the other */
258   tdmp->traces[client_index] = client_trace_cache;
259
260   for (i = iterator_thread_id; i < vec_len (client_trace_cache); i++)
261     {
262       for (j = iterator_position; j < vec_len (client_trace_cache[i]); j++)
263         {
264           if (max_records == 0)
265             break;
266
267           retval = 0;
268           th = &client_trace_cache[i][j];
269
270           vec_reset_length (s);
271
272           s =
273             format (s, "%U", format_vlib_trace, vlib_get_first_main (), th[0]);
274
275           dmp = vl_msg_api_alloc (sizeof (*dmp) + vec_len (s));
276           dmp->_vl_msg_id =
277             htons (VL_API_TRACE_DETAILS + (tdmp->msg_id_base));
278           dmp->context = mp->context;
279           last_thread_id = dmp->thread_id = ntohl (i);
280           last_position = dmp->position = ntohl (j);
281           vl_api_vec_to_api_string (s, &dmp->trace_data);
282           dmp->packet_number = htonl (j);
283           dmp->more_threads = 0;
284           dmp->more_this_thread = 0;
285
286           /* Last record in the batch? */
287           if (max_records == 1)
288             {
289               /* More threads, but not more in this thread? */
290               if (j == (vec_len (client_trace_cache[i]) - 1))
291                 dmp->more_threads = 1;
292               else
293                 dmp->more_this_thread = 1;
294             }
295           /* Done, may or may not be at the end of a batch. */
296           dmp->done = 0;
297           if (i == (vec_len (client_trace_cache) - 1) &&
298               j == (vec_len (client_trace_cache[i]) - 1))
299             {
300               last_done = dmp->done = 1;
301               last_more_threads = dmp->more_threads = 0;
302               last_more_this_thread = dmp->more_this_thread = 0;
303               vl_api_send_msg (rp, (u8 *) dmp);
304               goto doublebreak;
305             }
306           last_done = dmp->done;
307           vl_api_send_msg (rp, (u8 *) dmp);
308
309           max_records--;
310         }
311       iterator_position = 0;
312     }
313
314 doublebreak:;
315
316   rmp = vl_msg_api_alloc (sizeof (*rmp));
317   rmp->_vl_msg_id = htons (VL_API_TRACE_DUMP_REPLY + (tdmp->msg_id_base));
318   rmp->context = mp->context;
319   rmp->retval = clib_host_to_net_u32 (retval);
320   rmp->last_thread_id = last_thread_id;
321   rmp->last_position = last_position;
322   rmp->done = last_done;
323   rmp->more_this_thread = last_more_this_thread;
324   rmp->more_threads = last_more_threads;
325
326   /* Tag cleanup flushes to make life easy for the client */
327   if (iterator_thread_id == ~0 && iterator_position == ~0)
328     {
329       rmp->retval = 0;
330       rmp->done = 1;
331       rmp->flush_only = 1;
332     }
333   vl_api_send_msg (rp, (u8 *) rmp);
334
335   vec_free (s);
336 }
337
338 /* API message handler */
339 static void
340 vl_api_trace_v2_dump_t_handler (vl_api_trace_v2_dump_t *mp)
341 {
342   vl_api_registration_t *rp;
343   vl_api_trace_v2_details_t *dmp;
344   tracedump_main_t *tdmp = &tracedump_main;
345   vlib_trace_header_t ***client_trace_cache, **th;
346   int i, j;
347   u32 client_index;
348   u32 first_position, max, first_thread_id, last_thread_id;
349   u32 n_threads = vlib_get_n_threads ();
350   u8 *s = 0;
351
352   rp = vl_api_client_index_to_registration (mp->client_index);
353   if (rp == 0)
354     return;
355
356   client_index = rp->vl_api_registration_pool_index;
357
358   vec_validate_init_empty (tdmp->traces, client_index, 0);
359
360   client_trace_cache = tdmp->traces[client_index];
361
362   if (mp->clear_cache)
363     {
364       toss_client_cache (tdmp, client_index, client_trace_cache);
365       client_trace_cache = 0;
366     }
367
368   /* Now, where were we? */
369   first_thread_id = last_thread_id = clib_net_to_host_u32 (mp->thread_id);
370   first_position = clib_net_to_host_u32 (mp->position);
371   max = clib_net_to_host_u32 (mp->max);
372
373   if (first_thread_id == ~0)
374     {
375       first_thread_id = 0;
376       last_thread_id = n_threads - 1;
377     }
378
379   /* Don't overflow the existing queue space for shared memory API clients. */
380   if (rp->vl_input_queue)
381     {
382       svm_queue_t *q = rp->vl_input_queue;
383       u32 queue_slots_available = q->maxsize - q->cursize;
384       int chunk = (queue_slots_available > 0) ? queue_slots_available - 1 : 0;
385       /* split available slots among requested threads */
386       if (chunk < max * (last_thread_id - first_thread_id + 1))
387         max = chunk / (last_thread_id - first_thread_id + 1);
388     }
389
390   /* Need a fresh cache for this client? */
391   if (vec_len (client_trace_cache) == 0 && first_position != ~0)
392     {
393       vlib_worker_thread_barrier_sync (vlib_get_first_main ());
394
395       /* Make a slot for each worker thread */
396       vec_validate (client_trace_cache, n_threads - 1);
397       i = 0;
398
399       foreach_vlib_main ()
400         {
401           vlib_trace_main_t *tm = &this_vlib_main->trace_main;
402
403           /* Filter as directed */
404           trace_apply_filter (this_vlib_main);
405
406           pool_foreach (th, tm->trace_buffer_pool)
407             {
408               vec_add1 (client_trace_cache[i], th[0]);
409             }
410
411           /* Sort them by increasing time. */
412           if (vec_len (client_trace_cache[i]))
413             vec_sort_with_function (client_trace_cache[i], trace_cmp);
414
415           i++;
416         }
417       vlib_worker_thread_barrier_release (vlib_get_first_main ());
418     }
419
420   /* Save the cache, one way or the other */
421   tdmp->traces[client_index] = client_trace_cache;
422
423   for (i = first_thread_id;
424        i <= last_thread_id && i < vec_len (client_trace_cache); i++)
425     {
426       // dump a number of 'max' packets per thead
427       for (j = first_position;
428            j < vec_len (client_trace_cache[i]) && j < first_position + max;
429            j++)
430         {
431           th = &client_trace_cache[i][j];
432
433           vec_reset_length (s);
434
435           s =
436             format (s, "%U", format_vlib_trace, vlib_get_first_main (), th[0]);
437
438           dmp = vl_msg_api_alloc (sizeof (*dmp) + vec_len (s));
439           dmp->_vl_msg_id =
440             htons (VL_API_TRACE_V2_DETAILS + (tdmp->msg_id_base));
441           dmp->context = mp->context;
442           dmp->thread_id = ntohl (i);
443           dmp->position = ntohl (j);
444           dmp->more = j < vec_len (client_trace_cache[i]) - 1;
445           vl_api_vec_to_api_string (s, &dmp->trace_data);
446
447           vl_api_send_msg (rp, (u8 *) dmp);
448         }
449     }
450
451   vec_free (s);
452 }
453
454 static void
455 vl_api_trace_clear_cache_t_handler (vl_api_trace_clear_cache_t *mp)
456 {
457   vl_api_registration_t *rp;
458   tracedump_main_t *tdmp = &tracedump_main;
459   vlib_trace_header_t ***client_trace_cache;
460   vl_api_trace_clear_cache_reply_t *rmp;
461   u32 client_index;
462
463   rp = vl_api_client_index_to_registration (mp->client_index);
464   if (rp == 0)
465     return;
466
467   client_index = rp->vl_api_registration_pool_index;
468   vec_validate_init_empty (tdmp->traces, client_index, 0);
469   client_trace_cache = tdmp->traces[client_index];
470   toss_client_cache (tdmp, client_index, client_trace_cache);
471
472   int rv = 0;
473   REPLY_MACRO (VL_API_TRACE_CLEAR_CACHE_REPLY);
474 }
475
476 static void
477 vl_api_trace_set_filter_function_t_handler (
478   vl_api_trace_set_filter_function_t *mp)
479 {
480   vl_api_trace_set_filter_function_reply_t *rmp;
481   tracedump_main_t *tdmp = &tracedump_main;
482   unformat_input_t input = { 0 };
483   vlib_is_packet_traced_fn_t *f;
484   char *filter_name;
485   int rv = 0;
486   filter_name = vl_api_from_api_to_new_c_string (&mp->filter_function_name);
487   unformat_init_cstring (&input, filter_name);
488   if (unformat (&input, "%U", unformat_vlib_trace_filter_function, &f) == 0)
489     {
490       rv = -1;
491       goto done;
492     }
493   vlib_set_trace_filter_function (f);
494 done:
495   unformat_free (&input);
496   vec_free (filter_name);
497   REPLY_MACRO (VL_API_TRACE_SET_FILTER_FUNCTION_REPLY);
498 }
499
500 static void
501 vl_api_trace_filter_function_dump_t_handler (
502   vl_api_trace_filter_function_dump_t *mp)
503 {
504   vl_api_registration_t *rp;
505   vl_api_trace_filter_function_details_t *dmp;
506   tracedump_main_t *tdmp = &tracedump_main;
507   vlib_trace_filter_main_t *tfm = &vlib_trace_filter_main;
508   vlib_trace_filter_function_registration_t *reg =
509     tfm->trace_filter_registration;
510   vlib_main_t *vm = vlib_get_main ();
511   vlib_is_packet_traced_fn_t *current =
512     vm->trace_main.current_trace_filter_function;
513   rp = vl_api_client_index_to_registration (mp->client_index);
514
515   if (rp == 0)
516     return;
517
518   while (reg)
519     {
520       dmp = vl_msg_api_alloc (sizeof (*dmp) + strlen (reg->name));
521       dmp->_vl_msg_id =
522         htons (VL_API_TRACE_FILTER_FUNCTION_DETAILS + (tdmp->msg_id_base));
523       dmp->context = mp->context;
524       vl_api_c_string_to_api_string (reg->name, &dmp->name);
525       dmp->selected = current == reg->function;
526       vl_api_send_msg (rp, (u8 *) dmp);
527       reg = reg->next;
528     }
529 }
530
531 /* API definitions */
532 #include <tracedump/tracedump.api.c>
533
534 static clib_error_t *
535 tracedump_init (vlib_main_t * vm)
536 {
537   tracedump_main_t *tdmp = &tracedump_main;
538   api_main_t *am = vlibapi_get_main ();
539
540   clib_error_t *error = 0;
541
542   tdmp->vlib_main = vm;
543   tdmp->vnet_main = vnet_get_main ();
544
545   /* Add our API messages to the global name_crc hash table */
546   tdmp->msg_id_base = setup_message_id_table ();
547
548   vl_api_set_msg_thread_safe (am, tdmp->msg_id_base + VL_API_TRACE_DUMP, 1);
549   vl_api_set_msg_thread_safe (am, tdmp->msg_id_base + VL_API_TRACE_V2_DUMP, 1);
550
551   return error;
552 }
553
554 VLIB_INIT_FUNCTION (tracedump_init);
555 /* *INDENT-OFF* */
556 VLIB_PLUGIN_REGISTER () =
557 {
558   .version = VPP_BUILD_VER,
559   .description = "Streaming packet trace dump plugin",
560 };
561 /* *INDENT-ON* */
562
563 /*
564  * fd.io coding-style-patch-verification: ON
565  *
566  * Local Variables:
567  * eval: (c-set-style "gnu")
568  * End:
569  */