feature: Add packet trace API
[vpp.git] / src / plugins / tracedump / graph_api.c
1 /* Hey Emacs use -*- mode: C -*- */
2 /*
3  * Copyright 2020 Rubicon Communications, LLC.
4  *
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 <vlibmemory/api.h>
20
21 #include <tracedump/graph.api_enum.h>
22 #include <tracedump/graph.api_types.h>
23
24 #define REPLY_MSG_ID_BASE       gmp->msg_id_base
25 #include <vlibapi/api_helper_macros.h>
26
27 #include <tracedump/graph.h>
28
29
30 graph_main_t graph_main;
31
32
33 #define MIN(x,y)        (((x) < (y)) ? (x) : (y))
34
35
36 /*
37  * If ever the graph or set of nodes changes, this cache of
38  * nodes in sorted order should be invalidated.
39  */
40 void
41 graph_node_invalid_cache (void)
42 {
43   graph_main_t *gmp = &graph_main;
44
45   vec_free (gmp->sorted_node_vec);
46 }
47
48
49 static clib_error_t *
50 graph_node_cache_reaper (u32 client_index)
51 {
52   graph_node_invalid_cache ();
53   return 0;
54 }
55
56 VL_MSG_API_REAPER_FUNCTION (graph_node_cache_reaper);
57
58
59 static void
60 send_graph_node_reply (vl_api_registration_t * rp,
61                        u32 context, u32 retval, u32 cursor)
62 {
63   graph_main_t *gmp = &graph_main;
64   vl_api_graph_node_get_reply_t *rmp;
65
66   rmp = vl_msg_api_alloc (sizeof (*rmp));
67   rmp->_vl_msg_id = htons (VL_API_GRAPH_NODE_GET_REPLY + gmp->msg_id_base);
68   rmp->context = context;
69   rmp->retval = clib_host_to_net_u32 (retval);
70   rmp->cursor = htonl (cursor);
71
72   vl_api_send_msg (rp, (u8 *) rmp);
73 }
74
75
76 static void
77 send_graph_node_details (vlib_node_main_t * nm,
78                          vl_api_registration_t * reg,
79                          u32 context, vlib_node_t * n, bool want_arcs)
80 {
81   graph_main_t *gmp = &graph_main;
82   vl_api_graph_node_details_t *mp;
83   u32 msg_size;
84
85   msg_size = sizeof (*mp);
86   if (want_arcs)
87     msg_size += vec_len (n->next_nodes) * sizeof (*n->next_nodes);
88
89   mp = vl_msg_api_alloc (msg_size);
90   if (!mp)
91     return;
92
93   clib_memset (mp, 0, msg_size);
94
95   mp->_vl_msg_id = htons (VL_API_GRAPH_NODE_DETAILS + gmp->msg_id_base);
96   mp->context = context;
97   mp->index = htonl (n->index);
98   mp->flags = htonl (n->flags);
99
100   clib_strncpy ((char *) mp->name, (char *) n->name,
101                 MIN (sizeof (mp->name) - 1, vec_len (n->name)));
102
103   if (want_arcs)
104     {
105       int i;
106
107       mp->n_arcs = htonl (vec_len (n->next_nodes));
108       for (i = 0; i < vec_len (n->next_nodes); ++i)
109         {
110           mp->arcs_out[i] = htonl (n->next_nodes[i]);
111         }
112     }
113
114   vl_api_send_msg (reg, (u8 *) mp);
115 }
116
117
118 static int
119 node_cmp (void *a1, void *a2)
120 {
121   vlib_node_t **n1 = a1;
122   vlib_node_t **n2 = a2;
123
124   return vec_cmp (n1[0]->name, n2[0]->name);
125 }
126
127
128 /*
129  * When cursor == ~0, it begins a request:
130  *    if index != ~0, dump node with given index
131  *    if index == ~0 and name[0] != 0, dump node with given name
132  *    if index == ~0 and name[0] == 0, and flag != 0, dump flagged nodes
133  *    else
134  *        index == ~0 and name[0] == 0 and flag == 0, so dump all nodes.
135  *
136  * When cursor != ~0, it is the middle of a request:
137  *    The same (index, name, and flag) parameters are assumed,
138  *    The next results resume from cursor.
139  */
140 static void
141 vl_api_graph_node_get_t_handler (vl_api_graph_node_get_t * mp)
142 {
143   vl_api_registration_t *rp;
144
145   rp = vl_api_client_index_to_registration (mp->client_index);
146   if (!rp)
147     return;
148
149   vlib_main_t *vm = vlib_get_main ();
150   vlib_node_main_t *nm = &vm->node_main;
151   graph_main_t *gmp = &graph_main;
152   vlib_node_t *n;
153   u32 cursor;
154   u32 node_index;
155   bool want_arcs;
156
157   want_arcs = ! !mp->want_arcs;
158   cursor = ntohl (mp->cursor);
159   n = 0;
160
161   /*
162    * Return details on a specific node by index?
163    */
164   node_index = ntohl (mp->index);
165   if (cursor == ~0 && node_index != ~0)
166     {
167       if (node_index < vec_len (nm->nodes))
168         n = vlib_get_node (vm, node_index);
169       if (!n)
170         {
171           send_graph_node_reply (rp, mp->context,
172                                  VNET_API_ERROR_NO_SUCH_ENTRY, ~0);
173           return;
174         }
175       send_graph_node_details (nm, rp, mp->context, n, want_arcs);
176       send_graph_node_reply (rp, mp->context, 0, ~0);
177       return;
178     }
179
180   /*
181    * Return details on a specific node by name?
182    */
183   if (cursor == ~0 && mp->name[0] != 0)
184     {
185       n = vlib_get_node_by_name (vm, (u8 *) mp->name);
186       if (!n)
187         {
188           send_graph_node_reply (rp, mp->context,
189                                  VNET_API_ERROR_NO_SUCH_ENTRY, ~0);
190           return;
191         }
192
193       send_graph_node_details (nm, rp, mp->context, n, want_arcs);
194       send_graph_node_reply (rp, mp->context, 0, ~0);
195       return;
196     }
197
198   /*
199    * Inspect all nodes, but potentially limit them by flag selection.
200    * As iteration my need to occur over multiple streaming API calls,
201    * determine the API client index and cache a sorted list of nodes.
202    *
203    * First time through, make a sorted node list and cache it.
204    */
205   vlib_node_t **nodes = gmp->sorted_node_vec;
206   if (!nodes)
207     {
208       nodes = vec_dup (nm->nodes);
209       vec_sort_with_function (nodes, node_cmp);
210       gmp->sorted_node_vec = nodes;
211     }
212
213   u32 flags = ntohl (mp->flags);
214   u32 first_index = (cursor == ~0) ? 0 : cursor;
215
216   /* Don't overflow the existing queue space. */
217   svm_queue_t *q = rp->vl_input_queue;
218   u32 queue_slots_available = q->maxsize - q->cursize;
219   int chunk = (queue_slots_available > 0) ? queue_slots_available - 1 : 0;
220   u32 i;
221
222   for (i = first_index; i < vec_len (nodes); ++i)
223     {
224       if (chunk-- == 0)
225         {
226           /*
227            * Pick up again at cursor = i.
228            */
229           send_graph_node_reply (rp, mp->context, VNET_API_ERROR_EAGAIN, i);
230           return;
231         }
232
233       n = nodes[i];
234       if (flags == 0 || (n->flags & flags))
235         {
236           send_graph_node_details (nm, rp, mp->context, n, want_arcs);
237         }
238     }
239
240   send_graph_node_reply (rp, mp->context, 0, ~0);
241 }
242
243
244 #include <vnet/format_fns.h>
245 #include <tracedump/graph.api.c>
246
247 static clib_error_t *
248 graph_api_hookup (vlib_main_t * vm)
249 {
250   api_main_t *am = vlibapi_get_main ();
251   graph_main_t *gmp = &graph_main;
252
253   gmp->msg_id_base = setup_message_id_table ();
254
255   am->is_mp_safe[gmp->msg_id_base + VL_API_GRAPH_NODE_GET] = 1;
256
257   am->is_autoendian[gmp->msg_id_base + VL_API_GRAPH_NODE_DETAILS] = 1;
258
259   return 0;
260 }
261
262 VLIB_INIT_FUNCTION (graph_api_hookup);
263
264 /*
265  * fd.io coding-style-patch-verification: ON
266  *
267  * Local Variables:
268  * eval: (c-set-style "gnu")
269  * End:
270  */