bufmon: add buffer monitoring plugin
[vpp.git] / src / plugins / bufmon / bufmon.c
1 #include <vlib/vlib.h>
2 #include <vnet/plugin/plugin.h>
3 #include <vpp/app/version.h>
4
5 typedef struct
6 {
7   u64 in;
8   u64 out;
9   u64 alloc;
10   u64 free;
11 } bufmon_per_node_data_t;
12
13 typedef struct
14 {
15   CLIB_CACHE_LINE_ALIGN_MARK (cacheline0);
16   bufmon_per_node_data_t *pnd;
17   u32 cur_node;
18 } bufmon_per_thread_data_t;
19
20 typedef struct
21 {
22   bufmon_per_thread_data_t *ptd;
23   int enabled;
24 } bufmon_main_t;
25
26 static bufmon_main_t bufmon_main;
27
28 static u32
29 bufmon_alloc_free_callback (vlib_main_t *vm, u32 n_buffers, const int is_free)
30 {
31   bufmon_main_t *bm = &bufmon_main;
32   bufmon_per_thread_data_t *ptd;
33   bufmon_per_node_data_t *pnd;
34   u32 cur_node;
35
36   if (PREDICT_FALSE (vm->thread_index >= vec_len (bm->ptd)))
37     {
38       clib_warning ("bufmon: thread index %d unknown for buffer %s (%d)",
39                     vm->thread_index, is_free ? "free" : "alloc", n_buffers);
40       return n_buffers;
41     }
42
43   ptd = vec_elt_at_index (bm->ptd, vm->thread_index);
44
45   cur_node = ptd->cur_node;
46   if (cur_node >= vec_len (ptd->pnd))
47     {
48       cur_node = vlib_get_current_process_node_index (vm);
49       vec_validate_aligned (ptd->pnd, cur_node, CLIB_CACHE_LINE_BYTES);
50     }
51
52   pnd = vec_elt_at_index (ptd->pnd, cur_node);
53
54   if (is_free)
55     pnd->free += n_buffers;
56   else
57     pnd->alloc += n_buffers;
58
59   return n_buffers;
60 }
61
62 static u32
63 bufmon_alloc_callback (vlib_main_t *vm, u8 buffer_pool_index, u32 *buffers,
64                        u32 n_buffers)
65 {
66   return bufmon_alloc_free_callback (vm, n_buffers, 0 /* is_free */);
67 }
68
69 static u32
70 bufmon_free_callback (vlib_main_t *vm, u8 buffer_pool_index, u32 *buffers,
71                       u32 n_buffers)
72 {
73   return bufmon_alloc_free_callback (vm, n_buffers, 1 /* is_free */);
74 }
75
76 static u32
77 bufmon_count_buffers (vlib_main_t *vm, vlib_frame_t *frame)
78 {
79   vlib_buffer_t *b[VLIB_FRAME_SIZE];
80   u32 *from = vlib_frame_vector_args (frame);
81   const u32 n = frame->n_vectors;
82   u32 nc = 0;
83   u32 i;
84
85   vlib_get_buffers (vm, from, b, n);
86
87   for (i = 0; i < n; i++)
88     {
89       const vlib_buffer_t *cb = b[i];
90       while (cb->flags & VLIB_BUFFER_NEXT_PRESENT)
91         {
92           nc++;
93           cb = vlib_get_buffer (vm, cb->next_buffer);
94         }
95     }
96
97   return n + nc;
98 }
99
100 static uword
101 bufmon_dispatch_wrapper (vlib_main_t *vm, vlib_node_runtime_t *node,
102                          vlib_frame_t *frame)
103 {
104   vlib_node_main_t *nm = &vm->node_main;
105   bufmon_main_t *bm = &bufmon_main;
106   bufmon_per_thread_data_t *ptd;
107   bufmon_per_node_data_t *pnd;
108   int pending_frames;
109   uword rv;
110
111   vec_validate_aligned (bm->ptd, vm->thread_index, CLIB_CACHE_LINE_BYTES);
112   ptd = vec_elt_at_index (bm->ptd, vm->thread_index);
113   vec_validate_aligned (ptd->pnd, node->node_index, CLIB_CACHE_LINE_BYTES);
114   pnd = vec_elt_at_index (ptd->pnd, node->node_index);
115
116   if (frame)
117     pnd->in += bufmon_count_buffers (vm, frame);
118
119   pending_frames = vec_len (nm->pending_frames);
120   ptd->cur_node = node->node_index;
121
122   rv = node->function (vm, node, frame);
123
124   ptd->cur_node = ~0;
125   for (; pending_frames < vec_len (nm->pending_frames); pending_frames++)
126     {
127       vlib_pending_frame_t *p =
128         vec_elt_at_index (nm->pending_frames, pending_frames);
129       pnd->out += bufmon_count_buffers (vm, vlib_get_frame (vm, p->frame));
130     }
131
132   return rv;
133 }
134
135 static void
136 bufmon_unregister_callbacks (vlib_main_t *vm)
137 {
138   vlib_buffer_set_alloc_free_callback (vm, 0, 0);
139   foreach_vlib_main ()
140     vlib_node_set_dispatch_wrapper (this_vlib_main, 0);
141 }
142
143 static clib_error_t *
144 bufmon_register_callbacks (vlib_main_t *vm)
145 {
146   if (vlib_buffer_set_alloc_free_callback (vm, bufmon_alloc_callback,
147                                            bufmon_free_callback))
148     goto err0;
149
150   foreach_vlib_main ()
151     if (vlib_node_set_dispatch_wrapper (this_vlib_main,
152                                         bufmon_dispatch_wrapper))
153       goto err1;
154
155   return 0;
156
157 err1:
158   foreach_vlib_main ()
159     vlib_node_set_dispatch_wrapper (this_vlib_main, 0);
160 err0:
161   vlib_buffer_set_alloc_free_callback (vm, 0, 0);
162   return clib_error_return (0, "failed to register callback");
163 }
164
165 static clib_error_t *
166 bufmon_enable_disable (vlib_main_t *vm, int enable)
167 {
168   bufmon_main_t *bm = &bufmon_main;
169
170   if (enable)
171     {
172       if (bm->enabled)
173         return 0;
174       clib_error_t *error = bufmon_register_callbacks (vm);
175       if (error)
176         return error;
177       bm->enabled = 1;
178     }
179   else
180     {
181       if (!bm->enabled)
182         return 0;
183       bufmon_unregister_callbacks (vm);
184       bm->enabled = 0;
185     }
186
187   return 0;
188 }
189
190 static clib_error_t *
191 set_buffer_traces (vlib_main_t *vm, unformat_input_t *input,
192                    vlib_cli_command_t *cmd)
193 {
194   unformat_input_t _line_input, *line_input = &_line_input;
195   int on = 1;
196
197   if (unformat_user (input, unformat_line_input, line_input))
198     {
199       while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
200         {
201           if (unformat (line_input, "on"))
202             on = 1;
203           else if (unformat (line_input, "off"))
204             on = 0;
205           else
206             {
207               unformat_free (line_input);
208               return clib_error_return (0, "unknown input `%U'",
209                                         format_unformat_error, line_input);
210             }
211         }
212       unformat_free (line_input);
213     }
214
215   return bufmon_enable_disable (vm, on);
216 }
217
218 VLIB_CLI_COMMAND (set_buffer_traces_command, static) = {
219   .path = "set buffer traces",
220   .short_help = "set buffer traces [on|off]",
221   .function = set_buffer_traces,
222 };
223
224 static clib_error_t *
225 show_buffer_traces (vlib_main_t *vm, unformat_input_t *input,
226                     vlib_cli_command_t *cmd)
227 {
228   unformat_input_t _line_input, *line_input = &_line_input;
229   const bufmon_main_t *bm = &bufmon_main;
230   const bufmon_per_thread_data_t *ptd;
231   const bufmon_per_node_data_t *pnd;
232   int verbose = 0;
233   int status = 0;
234
235   if (unformat_user (input, unformat_line_input, line_input))
236     {
237       while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
238         {
239           if (unformat (line_input, "verbose"))
240             verbose = 1;
241           else if (unformat (line_input, "status"))
242             status = 1;
243           else
244             {
245               unformat_free (line_input);
246               return clib_error_return (0, "unknown input `%U'",
247                                         format_unformat_error, line_input);
248             }
249         }
250       unformat_free (line_input);
251     }
252
253   if (status)
254     {
255       vlib_cli_output (vm, "buffers tracing is %s",
256                        bm->enabled ? "on" : "off");
257       return 0;
258     }
259
260   vlib_cli_output (vm, "%U\n\n", format_vlib_buffer_pool_all, vm);
261   vlib_cli_output (vm, "%30s%20s%20s%20s%20s%20s", "Node", "Allocated",
262                    "Freed", "In", "Out", "Buffered");
263   vec_foreach (ptd, bm->ptd)
264     {
265       vec_foreach (pnd, ptd->pnd)
266         {
267           const u64 in = pnd->alloc + pnd->in;
268           const u64 out = pnd->free + pnd->out;
269           const i64 buffered = in - out;
270           if (0 == in && 0 == out)
271             continue; /* skip nodes w/o activity */
272           if (0 == buffered && !verbose)
273             continue; /* if not verbose, skip nodes w/o buffered buffers */
274           vlib_cli_output (vm, "%30U%20lu%20lu%20lu%20lu%20ld",
275                            format_vlib_node_name, vm, pnd - ptd->pnd,
276                            pnd->alloc, pnd->free, pnd->in, pnd->out, buffered);
277         }
278     }
279
280   return 0;
281 }
282
283 VLIB_CLI_COMMAND (show_buffer_traces_command, static) = {
284   .path = "show buffer traces",
285   .short_help = "show buffer traces [status|verbose]",
286   .function = show_buffer_traces,
287 };
288
289 static clib_error_t *
290 clear_buffer_traces (vlib_main_t *vm, unformat_input_t *input,
291                      vlib_cli_command_t *cmd)
292 {
293   const bufmon_main_t *bm = &bufmon_main;
294   const bufmon_per_thread_data_t *ptd;
295   const bufmon_per_node_data_t *pnd;
296
297   vec_foreach (ptd, bm->ptd)
298     vec_foreach (pnd, ptd->pnd)
299       vec_reset_length (pnd);
300
301   return 0;
302 }
303
304 VLIB_CLI_COMMAND (clear_buffers_trace_command, static) = {
305   .path = "clear buffer traces",
306   .short_help = "clear buffer traces",
307   .function = clear_buffer_traces,
308 };
309
310 VLIB_PLUGIN_REGISTER () = {
311   .version = VPP_BUILD_VER,
312   .description = "Buffers monitoring plugin",
313 };