feature: Add packet trace API
[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 (filter)
106     {
107       if (vlib_enable_disable_pkt_trace_filter (1) < 0) /* enable */
108         {
109           /* FIXME: Make a new error like "UNSUPPORTED_NODE_OPERATION"? */
110           rv = VNET_API_ERROR_NO_SUCH_NODE;
111           goto done;
112         }
113     }
114
115   if (pre_clear)
116     vlib_trace_stop_and_clear ();
117
118   trace_update_capture_options (add, node_index, filter, verbose);
119
120 done:
121   REPLY_MACRO (VL_API_TRACE_CAPTURE_PACKETS_REPLY);
122 }
123
124
125 static void
126 vl_api_trace_clear_capture_t_handler (vl_api_trace_clear_capture_t * mp)
127 {
128   vl_api_trace_clear_capture_reply_t *rmp;
129   tracedump_main_t *tdmp = &tracedump_main;
130
131   vlib_trace_stop_and_clear ();
132
133   int rv = 0;
134   REPLY_MACRO (VL_API_TRACE_CLEAR_CAPTURE_REPLY);
135 }
136
137
138
139 static int
140 trace_cmp (void *a1, void *a2)
141 {
142   vlib_trace_header_t **t1 = a1;
143   vlib_trace_header_t **t2 = a2;
144   i64 dt = t1[0]->time - t2[0]->time;
145   return dt < 0 ? -1 : (dt > 0 ? +1 : 0);
146 }
147
148 static void
149 toss_client_cache (tracedump_main_t * tdmp, u32 client_index,
150                    vlib_trace_header_t *** client_trace_cache)
151 {
152   vlib_trace_header_t **th;
153   int i;
154
155   /* Across each vlib main... */
156   for (i = 0; i < vec_len (client_trace_cache); i++)
157     {
158       th = client_trace_cache[i];
159       /* Toss the thread's cached data */
160       vec_free (th);
161     }
162   /* And toss the vector of threads */
163   vec_free (client_trace_cache);
164   tdmp->traces[client_index] = client_trace_cache;
165 }
166
167 static clib_error_t *
168 tracedump_cache_reaper (u32 client_index)
169 {
170   tracedump_main_t *tdmp = &tracedump_main;
171   vlib_trace_header_t ***client_trace_cache;
172
173   /* Its likely that we won't have a cache entry */
174   if (client_index >= vec_len (tdmp->traces))
175     return 0;
176
177   client_trace_cache = tdmp->traces[client_index];
178   toss_client_cache (tdmp, client_index, client_trace_cache);
179   return 0;
180 }
181
182 VL_MSG_API_REAPER_FUNCTION (tracedump_cache_reaper);
183
184 /* API message handler */
185 static void
186 vl_api_trace_dump_t_handler (vl_api_trace_dump_t * mp)
187 {
188   vl_api_registration_t *rp;
189   vl_api_trace_dump_reply_t *rmp;
190   vl_api_trace_details_t *dmp;
191   tracedump_main_t *tdmp = &tracedump_main;
192   vlib_trace_header_t ***client_trace_cache, **th;
193   int i, j;
194   u32 client_index;
195   u32 iterator_thread_id, iterator_position, max_records;
196   i32 retval = VNET_API_ERROR_NO_SUCH_ENTRY;
197   u32 last_thread_id = ~0, last_position = ~0;
198   u8 last_done = 0;
199   u8 last_more_this_thread = 0;
200   u8 last_more_threads = 0;
201   u8 *s = 0;
202
203   rp = vl_api_client_index_to_registration (mp->client_index);
204   if (rp == 0)
205     return;
206
207   /* Use the registration pool index... */
208   client_index = rp->vl_api_registration_pool_index;
209
210   vec_validate_init_empty (tdmp->traces, client_index, 0);
211
212   client_trace_cache = tdmp->traces[client_index];
213
214   /* Clear the per-client cache if requested */
215   if (mp->clear_cache)
216     {
217       toss_client_cache (tdmp, client_index, client_trace_cache);
218       client_trace_cache = 0;
219     }
220
221   /* Now, where were we? */
222   iterator_thread_id = clib_net_to_host_u32 (mp->thread_id);
223   iterator_position = clib_net_to_host_u32 (mp->position);
224   max_records = clib_net_to_host_u32 (mp->max_records);
225
226   /* Don't overflow the existing queue space. */
227   svm_queue_t *q = rp->vl_input_queue;
228   u32 queue_slots_available = q->maxsize - q->cursize;
229   int chunk = (queue_slots_available > 0) ? queue_slots_available - 1 : 0;
230   if (chunk < max_records)
231     max_records = chunk;
232
233   /* Need a fresh cache for this client? */
234   if (vec_len (client_trace_cache) == 0
235       && (iterator_thread_id != ~0 || iterator_position != ~0))
236     {
237       vlib_worker_thread_barrier_sync (&vlib_global_main);
238
239       /* Make a slot for each worker thread */
240       vec_validate (client_trace_cache, vec_len (vlib_mains) - 1);
241       i = 0;
242
243       /* *INDENT-OFF* */
244       foreach_vlib_main (
245       ({
246         vlib_trace_main_t *tm = &this_vlib_main->trace_main;
247
248         /* Filter as directed */
249         trace_apply_filter(this_vlib_main);
250
251         pool_foreach (th, tm->trace_buffer_pool,
252         ({
253           vec_add1 (client_trace_cache[i], th[0]);
254         }));
255
256         /* Sort them by increasing time. */
257         if (vec_len (client_trace_cache[i]))
258           vec_sort_with_function (client_trace_cache[i], trace_cmp);
259
260         i++;
261       }));
262       /* *INDENT-ON* */
263       vlib_worker_thread_barrier_release (&vlib_global_main);
264     }
265
266   /* Save the cache, one way or the other */
267   tdmp->traces[client_index] = client_trace_cache;
268
269   for (i = iterator_thread_id; i < vec_len (client_trace_cache); i++)
270     {
271       for (j = iterator_position; j < vec_len (client_trace_cache[i]); j++)
272         {
273           if (max_records == 0)
274             break;
275
276           retval = 0;
277           th = &client_trace_cache[i][j];
278
279           vec_reset_length (s);
280
281           s = format (s, "%U", format_vlib_trace, &vlib_global_main, th[0]);
282
283           dmp = vl_msg_api_alloc (sizeof (*dmp) + vec_len (s));
284           dmp->_vl_msg_id =
285             htons (VL_API_TRACE_DETAILS + (tdmp->msg_id_base));
286           dmp->context = mp->context;
287           last_thread_id = dmp->thread_id = ntohl (i);
288           last_position = dmp->position = ntohl (j);
289           vl_api_vec_to_api_string (s, &dmp->trace_data);
290           dmp->packet_number = htonl (j);
291           dmp->more_threads = 0;
292           dmp->more_this_thread = 0;
293
294           /* Last record in the batch? */
295           if (max_records == 1)
296             {
297               /* More threads, but not more in this thread? */
298               if (j == (vec_len (client_trace_cache[i]) - 1))
299                 dmp->more_threads = 1;
300               else
301                 dmp->more_this_thread = 1;
302             }
303           /* Done, may or may not be at the end of a batch. */
304           dmp->done = 0;
305           if (i == (vec_len (client_trace_cache) - 1) &&
306               j == (vec_len (client_trace_cache[i]) - 1))
307             {
308               last_done = dmp->done = 1;
309               last_more_threads = dmp->more_threads = 0;
310               last_more_this_thread = dmp->more_this_thread = 0;
311               vl_api_send_msg (rp, (u8 *) dmp);
312               goto doublebreak;
313             }
314           last_done = dmp->done;
315           vl_api_send_msg (rp, (u8 *) dmp);
316
317           max_records--;
318         }
319       iterator_position = 0;
320     }
321
322 doublebreak:;
323
324   rmp = vl_msg_api_alloc (sizeof (*rmp));
325   rmp->_vl_msg_id = htons (VL_API_TRACE_DUMP_REPLY + (tdmp->msg_id_base));
326   rmp->context = mp->context;
327   rmp->retval = clib_host_to_net_u32 (retval);
328   rmp->last_thread_id = last_thread_id;
329   rmp->last_position = last_position;
330   rmp->done = last_done;
331   rmp->more_this_thread = last_more_this_thread;
332   rmp->more_threads = last_more_threads;
333
334   /* Tag cleanup flushes to make life easy for the client */
335   if (iterator_thread_id == ~0 && iterator_position == ~0)
336     {
337       rmp->retval = 0;
338       rmp->done = 1;
339       rmp->flush_only = 1;
340     }
341   vl_api_send_msg (rp, (u8 *) rmp);
342
343   vec_free (s);
344 }
345
346 /* API definitions */
347 #include <tracedump/tracedump.api.c>
348
349 static clib_error_t *
350 tracedump_init (vlib_main_t * vm)
351 {
352   tracedump_main_t *tdmp = &tracedump_main;
353   api_main_t *am = vlibapi_get_main ();
354
355   clib_error_t *error = 0;
356
357   tdmp->vlib_main = vm;
358   tdmp->vnet_main = vnet_get_main ();
359
360   /* Add our API messages to the global name_crc hash table */
361   tdmp->msg_id_base = setup_message_id_table ();
362
363   am->is_mp_safe[tdmp->msg_id_base + VL_API_TRACE_DUMP] = 1;
364
365   return error;
366 }
367
368 VLIB_INIT_FUNCTION (tracedump_init);
369 /* *INDENT-OFF* */
370 VLIB_PLUGIN_REGISTER () =
371 {
372   .version = VPP_BUILD_VER,
373   .description = "Streaming packet trace dump plugin",
374 };
375 /* *INDENT-ON* */
376
377 /*
378  * fd.io coding-style-patch-verification: ON
379  *
380  * Local Variables:
381  * eval: (c-set-style "gnu")
382  * End:
383  */