21a0c3d0379d6f1ef86e6336d919f10cc291389a
[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 static int
37 trace_cmp (void *a1, void *a2)
38 {
39   vlib_trace_header_t **t1 = a1;
40   vlib_trace_header_t **t2 = a2;
41   i64 dt = t1[0]->time - t2[0]->time;
42   return dt < 0 ? -1 : (dt > 0 ? +1 : 0);
43 }
44
45 static void
46 toss_client_cache (tracedump_main_t * tdmp, u32 client_index,
47                    vlib_trace_header_t *** client_trace_cache)
48 {
49   vlib_trace_header_t **th;
50   int i;
51
52   /* Across each vlib main... */
53   for (i = 0; i < vec_len (client_trace_cache); i++)
54     {
55       th = client_trace_cache[i];
56       /* Toss the thread's cached data */
57       vec_free (th);
58     }
59   /* And toss the vector of threads */
60   vec_free (client_trace_cache);
61   tdmp->traces[client_index] = client_trace_cache;
62 }
63
64 static clib_error_t *
65 tracedump_cache_reaper (u32 client_index)
66 {
67   tracedump_main_t *tdmp = &tracedump_main;
68   vlib_trace_header_t ***client_trace_cache;
69
70   /* Its likely that we won't have a cache entry */
71   if (client_index >= vec_len (tdmp->traces))
72     return 0;
73
74   client_trace_cache = tdmp->traces[client_index];
75   toss_client_cache (tdmp, client_index, client_trace_cache);
76   return 0;
77 }
78
79 VL_MSG_API_REAPER_FUNCTION (tracedump_cache_reaper);
80
81 /* API message handler */
82 static void
83 vl_api_trace_dump_t_handler (vl_api_trace_dump_t * mp)
84 {
85   vl_api_registration_t *rp;
86   vl_api_trace_dump_reply_t *rmp;
87   vl_api_trace_details_t *dmp;
88   tracedump_main_t *tdmp = &tracedump_main;
89   vlib_trace_header_t ***client_trace_cache, **th;
90   int i, j;
91   u32 client_index;
92   u32 iterator_thread_id, iterator_position, max_records;
93   i32 retval = VNET_API_ERROR_NO_SUCH_ENTRY;
94   u32 last_thread_id = ~0, last_position = ~0;
95   u8 last_done = 0;
96   u8 last_more_this_thread = 0;
97   u8 last_more_threads = 0;
98   u8 *s = 0;
99
100   rp = vl_api_client_index_to_registration (mp->client_index);
101   if (rp == 0)
102     return;
103
104   /* Use the registration pool index... */
105   client_index = rp->vl_api_registration_pool_index;
106
107   vec_validate_init_empty (tdmp->traces, client_index, 0);
108
109   client_trace_cache = tdmp->traces[client_index];
110
111   /* Clear the per-client cache if requested */
112   if (mp->clear_cache)
113     {
114       toss_client_cache (tdmp, client_index, client_trace_cache);
115       client_trace_cache = 0;
116     }
117
118   /* Now, where were we? */
119   iterator_thread_id = clib_net_to_host_u32 (mp->thread_id);
120   iterator_position = clib_net_to_host_u32 (mp->position);
121   max_records = clib_net_to_host_u32 (mp->max_records);
122
123   /* Need a fresh cache for this client? */
124   if (vec_len (client_trace_cache) == 0
125       && (iterator_thread_id != ~0 || iterator_position != ~0))
126     {
127       vlib_worker_thread_barrier_sync (&vlib_global_main);
128
129       /* Make a slot for each worker thread */
130       vec_validate (client_trace_cache, vec_len (vlib_mains) - 1);
131       i = 0;
132
133       /* *INDENT-OFF* */
134       foreach_vlib_main (
135       ({
136         vlib_trace_main_t *tm = &this_vlib_main->trace_main;
137
138         /* Filter as directed */
139         trace_apply_filter(this_vlib_main);
140
141         pool_foreach (th, tm->trace_buffer_pool,
142         ({
143           vec_add1 (client_trace_cache[i], th[0]);
144         }));
145
146         /* Sort them by increasing time. */
147         if (vec_len (client_trace_cache[i]))
148           vec_sort_with_function (client_trace_cache[i], trace_cmp);
149
150         i++;
151       }));
152       /* *INDENT-ON* */
153       vlib_worker_thread_barrier_release (&vlib_global_main);
154     }
155
156   /* Save the cache, one way or the other */
157   tdmp->traces[client_index] = client_trace_cache;
158
159   for (i = iterator_thread_id; i < vec_len (client_trace_cache); i++)
160     {
161       for (j = iterator_position; j < vec_len (client_trace_cache[i]); j++)
162         {
163           if (max_records == 0)
164             break;
165
166           retval = 0;
167           th = &client_trace_cache[i][j];
168
169           vec_reset_length (s);
170
171           s = format (s, "Packet %d\n%U\n\n", j + 1, format_vlib_trace,
172                       &vlib_global_main, th[0]);
173
174           dmp = vl_msg_api_alloc (sizeof (*dmp) + vec_len (s));
175           dmp->_vl_msg_id =
176             htons (VL_API_TRACE_DETAILS + (tdmp->msg_id_base));
177           dmp->context = mp->context;
178           last_thread_id = dmp->thread_id = ntohl (i);
179           last_position = dmp->position = ntohl (j);
180           vl_api_vec_to_api_string (s, &dmp->trace_data);
181           dmp->more_threads = 0;
182           dmp->more_this_thread = 0;
183
184           /* Last record in the batch? */
185           if (max_records == 1)
186             {
187               /* More threads, but not more in this thread? */
188               if (j == (vec_len (client_trace_cache[i]) - 1))
189                 dmp->more_threads = 1;
190               else
191                 dmp->more_this_thread = 1;
192             }
193           /* Done, may or may not be at the end of a batch. */
194           dmp->done = 0;
195           if (i == (vec_len (client_trace_cache) - 1) &&
196               j == (vec_len (client_trace_cache[i]) - 1))
197             {
198               last_done = dmp->done = 1;
199               last_more_threads = dmp->more_threads = 0;
200               last_more_this_thread = dmp->more_this_thread = 0;
201               vl_api_send_msg (rp, (u8 *) dmp);
202               goto doublebreak;
203             }
204           last_done = dmp->done;
205           vl_api_send_msg (rp, (u8 *) dmp);
206
207           max_records--;
208         }
209       iterator_position = 0;
210     }
211
212 doublebreak:;
213
214   rmp = vl_msg_api_alloc (sizeof (*rmp));
215   rmp->_vl_msg_id = htons (VL_API_TRACE_DUMP_REPLY + (tdmp->msg_id_base));
216   rmp->context = mp->context;
217   rmp->retval = clib_host_to_net_u32 (retval);
218   rmp->last_thread_id = last_thread_id;
219   rmp->last_position = last_position;
220   rmp->done = last_done;
221   rmp->more_this_thread = last_more_this_thread;
222   rmp->more_threads = last_more_threads;
223
224   /* Tag cleanup flushes to make life easy for the client */
225   if (iterator_thread_id == ~0 && iterator_position == ~0)
226     {
227       rmp->retval = 0;
228       rmp->done = 1;
229       rmp->flush_only = 1;
230     }
231   vl_api_send_msg (rp, (u8 *) rmp);
232
233   vec_free (s);
234 }
235
236 /* API definitions */
237 #include <tracedump/tracedump.api.c>
238
239 static clib_error_t *
240 tracedump_init (vlib_main_t * vm)
241 {
242   tracedump_main_t *tdmp = &tracedump_main;
243   api_main_t *am = vlibapi_get_main ();
244
245   clib_error_t *error = 0;
246
247   tdmp->vlib_main = vm;
248   tdmp->vnet_main = vnet_get_main ();
249
250   /* Add our API messages to the global name_crc hash table */
251   tdmp->msg_id_base = setup_message_id_table ();
252
253   am->is_mp_safe[tdmp->msg_id_base + VL_API_TRACE_DUMP] = 1;
254
255   return error;
256 }
257
258 VLIB_INIT_FUNCTION (tracedump_init);
259 /* *INDENT-OFF* */
260 VLIB_PLUGIN_REGISTER () =
261 {
262   .version = VPP_BUILD_VER,
263   .description = "Streaming packet trace dump plugin",
264 };
265 /* *INDENT-ON* */
266
267 /*
268  * fd.io coding-style-patch-verification: ON
269  *
270  * Local Variables:
271  * eval: (c-set-style "gnu")
272  * End:
273  */