vlib_mains == 0 special cases be gone
[vpp.git] / src / vlibmemory / memory_vlib.c
1 /*
2  *------------------------------------------------------------------
3  * memory_vlib.c
4  *
5  * Copyright (c) 2009 Cisco and/or its affiliates.
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at:
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  *------------------------------------------------------------------
18  */
19
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <unistd.h>
24 #include <sys/types.h>
25 #include <sys/stat.h>
26 #include <fcntl.h>
27 #include <signal.h>
28 #include <pthread.h>
29 #include <vppinfra/vec.h>
30 #include <vppinfra/hash.h>
31 #include <vppinfra/pool.h>
32 #include <vppinfra/format.h>
33 #include <vppinfra/byte_order.h>
34 #include <vppinfra/elog.h>
35 #include <stdarg.h>
36 #include <vlib/vlib.h>
37 #include <vlib/unix/unix.h>
38 #include <vlibapi/api.h>
39 #include <vlibmemory/api.h>
40
41 #define TRACE_VLIB_MEMORY_QUEUE 0
42
43 #include <vlibmemory/vl_memory_msg_enum.h>      /* enumerate all vlib messages */
44
45 #define vl_typedefs             /* define message structures */
46 #include <vlibmemory/vl_memory_api_h.h>
47 #undef vl_typedefs
48
49 /* instantiate all the print functions we know about */
50 #define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__)
51 #define vl_printfun
52 #include <vlibmemory/vl_memory_api_h.h>
53 #undef vl_printfun
54
55 static inline void *
56 vl_api_memclnt_create_t_print (vl_api_memclnt_create_t * a, void *handle)
57 {
58   vl_print (handle, "vl_api_memclnt_create_t:\n");
59   vl_print (handle, "name: %s\n", a->name);
60   vl_print (handle, "input_queue: 0x%wx\n", a->input_queue);
61   vl_print (handle, "context: %u\n", (unsigned) a->context);
62   vl_print (handle, "ctx_quota: %ld\n", (long) a->ctx_quota);
63   return handle;
64 }
65
66 static inline void *
67 vl_api_memclnt_delete_t_print (vl_api_memclnt_delete_t * a, void *handle)
68 {
69   vl_print (handle, "vl_api_memclnt_delete_t:\n");
70   vl_print (handle, "index: %u\n", (unsigned) a->index);
71   vl_print (handle, "handle: 0x%wx\n", a->handle);
72   return handle;
73 }
74
75 static inline void *
76 vl_api_trace_plugin_msg_ids_t_print (vl_api_trace_plugin_msg_ids_t * a,
77                                      void *handle)
78 {
79   vl_print (handle, "vl_api_trace_plugin_msg_ids: %s first %u last %u\n",
80             a->plugin_name,
81             clib_host_to_net_u16 (a->first_msg_id),
82             clib_host_to_net_u16 (a->last_msg_id));
83   return handle;
84 }
85
86 /* instantiate all the endian swap functions we know about */
87 #define vl_endianfun
88 #include <vlibmemory/vl_memory_api_h.h>
89 #undef vl_endianfun
90
91 void vl_socket_api_send (vl_api_registration_t * rp, u8 * elem)
92   __attribute__ ((weak));
93
94 void
95 vl_socket_api_send (vl_api_registration_t * rp, u8 * elem)
96 {
97   static int count;
98
99   if (count++ < 5)
100     clib_warning ("need to link against -lvlibsocket, msg not sent!");
101 }
102
103 void
104 vl_msg_api_send (vl_api_registration_t * rp, u8 * elem)
105 {
106   if (PREDICT_FALSE (rp->registration_type > REGISTRATION_TYPE_SHMEM))
107     {
108       vl_socket_api_send (rp, elem);
109     }
110   else
111     {
112       vl_msg_api_send_shmem (rp->vl_input_queue, elem);
113     }
114 }
115
116 u8 *
117 vl_api_serialize_message_table (api_main_t * am, u8 * vector)
118 {
119   serialize_main_t _sm, *sm = &_sm;
120   hash_pair_t *hp;
121   u32 nmsg = hash_elts (am->msg_index_by_name_and_crc);
122
123   serialize_open_vector (sm, vector);
124
125   /* serialize the count */
126   serialize_integer (sm, nmsg, sizeof (u32));
127
128   /* *INDENT-OFF* */
129   hash_foreach_pair (hp, am->msg_index_by_name_and_crc,
130   ({
131     serialize_likely_small_unsigned_integer (sm, hp->value[0]);
132     serialize_cstring (sm, (char *) hp->key);
133   }));
134   /* *INDENT-ON* */
135
136   return serialize_close_vector (sm);
137 }
138
139 /*
140  * vl_api_memclnt_create_t_handler
141  */
142 void
143 vl_api_memclnt_create_t_handler (vl_api_memclnt_create_t * mp)
144 {
145   vl_api_registration_t **regpp;
146   vl_api_registration_t *regp;
147   vl_api_memclnt_create_reply_t *rp;
148   svm_region_t *svm;
149   unix_shared_memory_queue_t *q;
150   int rv = 0;
151   void *oldheap;
152   api_main_t *am = &api_main;
153   u8 *serialized_message_table = 0;
154
155   /*
156    * This is tortured. Maintain a vlib-address-space private
157    * pool of client registrations. We use the shared-memory virtual
158    * address of client structure as a handle, to allow direct
159    * manipulation of context quota vbls from the client library.
160    *
161    * This scheme causes trouble w/ API message trace replay, since
162    * some random VA from clib_mem_alloc() certainly won't
163    * occur in the Linux sim. The (very) few places
164    * that care need to use the pool index.
165    *
166    * Putting the registration object(s) into a pool in shared memory and
167    * using the pool index as a handle seems like a great idea.
168    * Unfortunately, each and every reference to that pool would need
169    * to be protected by a mutex:
170    *
171    *     Client                      VLIB
172    *     ------                      ----
173    *     convert pool index to
174    *     pointer.
175    *     <deschedule>
176    *                                 expand pool
177    *                                 <deschedule>
178    *     kaboom!
179    */
180
181   pool_get (am->vl_clients, regpp);
182
183   svm = am->vlib_rp;
184
185   if (am->serialized_message_table_in_shmem == 0)
186     serialized_message_table = vl_api_serialize_message_table (am, 0);
187
188   pthread_mutex_lock (&svm->mutex);
189   oldheap = svm_push_data_heap (svm);
190   *regpp = clib_mem_alloc (sizeof (vl_api_registration_t));
191
192   regp = *regpp;
193   memset (regp, 0, sizeof (*regp));
194   regp->registration_type = REGISTRATION_TYPE_SHMEM;
195   regp->vl_api_registration_pool_index = regpp - am->vl_clients;
196
197   q = regp->vl_input_queue = (unix_shared_memory_queue_t *) (uword)
198     mp->input_queue;
199
200   regp->name = format (0, "%s", mp->name);
201   vec_add1 (regp->name, 0);
202   if (serialized_message_table)
203     am->serialized_message_table_in_shmem =
204       vec_dup (serialized_message_table);
205
206   pthread_mutex_unlock (&svm->mutex);
207   svm_pop_heap (oldheap);
208
209   vec_free (serialized_message_table);
210
211   rp = vl_msg_api_alloc (sizeof (*rp));
212   rp->_vl_msg_id = ntohs (VL_API_MEMCLNT_CREATE_REPLY);
213   rp->handle = (uword) regp;
214   rp->index = vl_msg_api_handle_from_index_and_epoch
215     (regp->vl_api_registration_pool_index,
216      am->shmem_hdr->application_restarts);
217   rp->context = mp->context;
218   rp->response = ntohl (rv);
219   rp->message_table = (u64) am->serialized_message_table_in_shmem;
220
221   vl_msg_api_send_shmem (q, (u8 *) & rp);
222 }
223
224 /* Application callback to clean up leftover registrations from this client */
225 int vl_api_memclnt_delete_callback (u32 client_index) __attribute__ ((weak));
226
227 int
228 vl_api_memclnt_delete_callback (u32 client_index)
229 {
230   return 0;
231 }
232
233 /*
234  * vl_api_memclnt_delete_t_handler
235  */
236 void
237 vl_api_memclnt_delete_t_handler (vl_api_memclnt_delete_t * mp)
238 {
239   vl_api_registration_t **regpp;
240   vl_api_registration_t *regp;
241   vl_api_memclnt_delete_reply_t *rp;
242   svm_region_t *svm;
243   void *oldheap;
244   api_main_t *am = &api_main;
245   u32 handle, client_index, epoch;
246
247   handle = mp->index;
248
249   if (vl_api_memclnt_delete_callback (handle))
250     return;
251
252   epoch = vl_msg_api_handle_get_epoch (handle);
253   client_index = vl_msg_api_handle_get_index (handle);
254
255   if (epoch != (am->shmem_hdr->application_restarts & VL_API_EPOCH_MASK))
256     {
257       clib_warning
258         ("Stale clnt delete index %d old epoch %d cur epoch %d",
259          client_index, epoch,
260          (am->shmem_hdr->application_restarts & VL_API_EPOCH_MASK));
261       return;
262     }
263
264   regpp = am->vl_clients + client_index;
265
266   if (!pool_is_free (am->vl_clients, regpp))
267     {
268       regp = *regpp;
269       svm = am->vlib_rp;
270
271       /* $$$ check the input queue for e.g. punted sf's */
272
273       rp = vl_msg_api_alloc (sizeof (*rp));
274       rp->_vl_msg_id = ntohs (VL_API_MEMCLNT_DELETE_REPLY);
275       rp->handle = mp->handle;
276       rp->response = 1;
277
278       vl_msg_api_send_shmem (regp->vl_input_queue, (u8 *) & rp);
279
280       if (client_index != regp->vl_api_registration_pool_index)
281         {
282           clib_warning ("mismatch client_index %d pool_index %d",
283                         client_index, regp->vl_api_registration_pool_index);
284           vl_msg_api_free (rp);
285           return;
286         }
287
288       /* No dangling references, please */
289       *regpp = 0;
290
291       pool_put_index (am->vl_clients, regp->vl_api_registration_pool_index);
292
293       pthread_mutex_lock (&svm->mutex);
294       oldheap = svm_push_data_heap (svm);
295       /* Poison the old registration */
296       memset (regp, 0xF1, sizeof (*regp));
297       clib_mem_free (regp);
298       pthread_mutex_unlock (&svm->mutex);
299       svm_pop_heap (oldheap);
300     }
301   else
302     {
303       clib_warning ("unknown client ID %d", mp->index);
304     }
305 }
306
307 void
308 vl_api_get_first_msg_id_t_handler (vl_api_get_first_msg_id_t * mp)
309 {
310   vl_api_get_first_msg_id_reply_t *rmp;
311   unix_shared_memory_queue_t *q;
312   uword *p;
313   api_main_t *am = &api_main;
314   vl_api_msg_range_t *rp;
315   u8 name[64];
316   u16 first_msg_id = ~0;
317   int rv = -7;                  /* VNET_API_ERROR_INVALID_VALUE */
318
319   q = vl_api_client_index_to_input_queue (mp->client_index);
320   if (!q)
321     return;
322
323   if (am->msg_range_by_name == 0)
324     goto out;
325
326   strncpy ((char *) name, (char *) mp->name, ARRAY_LEN (name) - 1);
327
328   p = hash_get_mem (am->msg_range_by_name, name);
329   if (p == 0)
330     goto out;
331
332   rp = vec_elt_at_index (am->msg_ranges, p[0]);
333
334   first_msg_id = rp->first_msg_id;
335   rv = 0;
336
337 out:
338
339   rmp = vl_msg_api_alloc (sizeof (*rmp));
340   rmp->_vl_msg_id = ntohs (VL_API_GET_FIRST_MSG_ID_REPLY);
341   rmp->context = mp->context;
342   rmp->retval = ntohl (rv);
343   rmp->first_msg_id = ntohs (first_msg_id);
344   vl_msg_api_send_shmem (q, (u8 *) & rmp);
345 }
346
347 #define foreach_vlib_api_msg                    \
348 _(MEMCLNT_CREATE, memclnt_create)               \
349 _(MEMCLNT_DELETE, memclnt_delete)               \
350 _(GET_FIRST_MSG_ID, get_first_msg_id)
351
352 /*
353  * vl_api_init
354  */
355 static int
356 memory_api_init (char *region_name)
357 {
358   int rv;
359   vl_msg_api_msg_config_t cfg;
360   vl_msg_api_msg_config_t *c = &cfg;
361
362   memset (c, 0, sizeof (*c));
363
364   if ((rv = vl_map_shmem (region_name, 1 /* is_vlib */ )) < 0)
365     return rv;
366
367 #define _(N,n) do {                                             \
368     c->id = VL_API_##N;                                         \
369     c->name = #n;                                               \
370     c->handler = vl_api_##n##_t_handler;                        \
371     c->cleanup = vl_noop_handler;                               \
372     c->endian = vl_api_##n##_t_endian;                          \
373     c->print = vl_api_##n##_t_print;                            \
374     c->size = sizeof(vl_api_##n##_t);                           \
375     c->traced = 1; /* trace, so these msgs print */             \
376     c->replay = 0; /* don't replay client create/delete msgs */ \
377     c->message_bounce = 0; /* don't bounce this message */      \
378     vl_msg_api_config(c);} while (0);
379
380   foreach_vlib_api_msg;
381 #undef _
382
383   return 0;
384 }
385
386 #define foreach_histogram_bucket                \
387 _(400)                                          \
388 _(200)                                          \
389 _(100)                                          \
390 _(10)
391
392 typedef enum
393 {
394 #define _(n) SLEEP_##n##_US,
395   foreach_histogram_bucket
396 #undef _
397     SLEEP_N_BUCKETS,
398 } histogram_index_t;
399
400 static u64 vector_rate_histogram[SLEEP_N_BUCKETS];
401
402 static void memclnt_queue_callback (vlib_main_t * vm);
403
404 /*
405  * Callback to send ourselves a plugin numbering-space trace msg
406  */
407 static void
408 send_one_plugin_msg_ids_msg (u8 * name, u16 first_msg_id, u16 last_msg_id)
409 {
410   vl_api_trace_plugin_msg_ids_t *mp;
411   api_main_t *am = &api_main;
412   vl_shmem_hdr_t *shmem_hdr = am->shmem_hdr;
413   unix_shared_memory_queue_t *q;
414
415   mp = vl_msg_api_alloc_as_if_client (sizeof (*mp));
416   memset (mp, 0, sizeof (*mp));
417
418   mp->_vl_msg_id = clib_host_to_net_u16 (VL_API_TRACE_PLUGIN_MSG_IDS);
419   strncpy ((char *) mp->plugin_name, (char *) name,
420            sizeof (mp->plugin_name) - 1);
421   mp->first_msg_id = clib_host_to_net_u16 (first_msg_id);
422   mp->last_msg_id = clib_host_to_net_u16 (last_msg_id);
423
424   q = shmem_hdr->vl_input_queue;
425
426   vl_msg_api_send_shmem (q, (u8 *) & mp);
427 }
428
429 static uword
430 memclnt_process (vlib_main_t * vm,
431                  vlib_node_runtime_t * node, vlib_frame_t * f)
432 {
433   uword mp;
434   vl_shmem_hdr_t *shm;
435   unix_shared_memory_queue_t *q;
436   clib_error_t *e;
437   int rv;
438   api_main_t *am = &api_main;
439   f64 dead_client_scan_time;
440   f64 sleep_time, start_time;
441   f64 vector_rate;
442   int i;
443
444   vlib_set_queue_signal_callback (vm, memclnt_queue_callback);
445
446   if ((rv = memory_api_init (am->region_name)) < 0)
447     {
448       clib_warning ("memory_api_init returned %d, wait for godot...", rv);
449       vlib_process_suspend (vm, 1e70);
450     }
451
452   shm = am->shmem_hdr;
453   ASSERT (shm);
454   q = shm->vl_input_queue;
455   ASSERT (q);
456
457   e = vlib_call_init_exit_functions
458     (vm, vm->api_init_function_registrations, 1 /* call_once */ );
459   if (e)
460     clib_error_report (e);
461
462   sleep_time = 20.0;
463   dead_client_scan_time = vlib_time_now (vm) + 20.0;
464
465   /*
466    * Send plugin message range messages for each plugin we loaded
467    */
468   for (i = 0; i < vec_len (am->msg_ranges); i++)
469     {
470       vl_api_msg_range_t *rp = am->msg_ranges + i;
471       send_one_plugin_msg_ids_msg (rp->name, rp->first_msg_id,
472                                    rp->last_msg_id);
473     }
474
475   /* $$$ pay attention to frame size, control CPU usage */
476   while (1)
477     {
478       uword event_type __attribute__ ((unused));
479       i8 *headp;
480       int need_broadcast;
481
482       /*
483        * There's a reason for checking the queue before
484        * sleeping. If the vlib application crashes, it's entirely
485        * possible for a client to enqueue a connect request
486        * during the process restart interval.
487        *
488        * Unless some force of physics causes the new incarnation
489        * of the application to process the request, the client will
490        * sit and wait for Godot...
491        */
492       vector_rate = vlib_last_vector_length_per_node (vm);
493       start_time = vlib_time_now (vm);
494       while (1)
495         {
496           pthread_mutex_lock (&q->mutex);
497           if (q->cursize == 0)
498             {
499               vm->api_queue_nonempty = 0;
500               pthread_mutex_unlock (&q->mutex);
501
502               if (TRACE_VLIB_MEMORY_QUEUE)
503                 {
504                   /* *INDENT-OFF* */
505                   ELOG_TYPE_DECLARE (e) =
506                     {
507                       .format = "q-underflow: len %d",
508                       .format_args = "i4",
509                     };
510                   /* *INDENT-ON* */
511                   struct
512                   {
513                     u32 len;
514                   } *ed;
515                   ed = ELOG_DATA (&vm->elog_main, e);
516                   ed->len = 0;
517                 }
518               sleep_time = 20.0;
519               break;
520             }
521
522           headp = (i8 *) (q->data + sizeof (uword) * q->head);
523           clib_memcpy (&mp, headp, sizeof (uword));
524
525           q->head++;
526           need_broadcast = (q->cursize == q->maxsize / 2);
527           q->cursize--;
528
529           if (PREDICT_FALSE (q->head == q->maxsize))
530             q->head = 0;
531           pthread_mutex_unlock (&q->mutex);
532           if (need_broadcast)
533             (void) pthread_cond_broadcast (&q->condvar);
534
535           vl_msg_api_handler_with_vm_node (am, (void *) mp, vm, node);
536
537           /* Allow no more than 10us without a pause */
538           if (vlib_time_now (vm) > start_time + 10e-6)
539             {
540               int index = SLEEP_400_US;
541               if (vector_rate > 40.0)
542                 sleep_time = 400e-6;
543               else if (vector_rate > 20.0)
544                 {
545                   index = SLEEP_200_US;
546                   sleep_time = 200e-6;
547                 }
548               else if (vector_rate >= 1.0)
549                 {
550                   index = SLEEP_100_US;
551                   sleep_time = 100e-6;
552                 }
553               else
554                 {
555                   index = SLEEP_10_US;
556                   sleep_time = 10e-6;
557                 }
558               vector_rate_histogram[index] += 1;
559               break;
560             }
561         }
562
563       event_type = vlib_process_wait_for_event_or_clock (vm, sleep_time);
564       vm->queue_signal_pending = 0;
565       vlib_process_get_events (vm, 0 /* event_data */ );
566
567       if (vlib_time_now (vm) > dead_client_scan_time)
568         {
569           vl_api_registration_t **regpp;
570           vl_api_registration_t *regp;
571           unix_shared_memory_queue_t *q;
572           static u32 *dead_indices;
573           static u32 *confused_indices;
574
575           vec_reset_length (dead_indices);
576           vec_reset_length (confused_indices);
577
578           /* *INDENT-OFF* */
579           pool_foreach (regpp, am->vl_clients,
580           ({
581             regp = *regpp;
582             if (regp)
583               {
584                 q = regp->vl_input_queue;
585                 if (kill (q->consumer_pid, 0) < 0)
586                   {
587                     vec_add1(dead_indices, regpp - am->vl_clients);
588                   }
589               }
590             else
591               {
592                 clib_warning ("NULL client registration index %d",
593                               regpp - am->vl_clients);
594                 vec_add1 (confused_indices, regpp - am->vl_clients);
595               }
596           }));
597           /* *INDENT-ON* */
598           /* This should "never happen," but if it does, fix it... */
599           if (PREDICT_FALSE (vec_len (confused_indices) > 0))
600             {
601               int i;
602               for (i = 0; i < vec_len (confused_indices); i++)
603                 {
604                   pool_put_index (am->vl_clients, confused_indices[i]);
605                 }
606             }
607
608           if (PREDICT_FALSE (vec_len (dead_indices) > 0))
609             {
610               int i;
611               svm_region_t *svm;
612               void *oldheap;
613
614               /* Allow the application to clean up its registrations */
615               for (i = 0; i < vec_len (dead_indices); i++)
616                 {
617                   regpp = pool_elt_at_index (am->vl_clients, dead_indices[i]);
618                   if (regpp)
619                     {
620                       u32 handle;
621
622                       handle = vl_msg_api_handle_from_index_and_epoch
623                         (dead_indices[i], shm->application_restarts);
624                       (void) vl_api_memclnt_delete_callback (handle);
625                     }
626                 }
627
628               svm = am->vlib_rp;
629               pthread_mutex_lock (&svm->mutex);
630               oldheap = svm_push_data_heap (svm);
631
632               for (i = 0; i < vec_len (dead_indices); i++)
633                 {
634                   regpp = pool_elt_at_index (am->vl_clients, dead_indices[i]);
635                   if (regpp)
636                     {
637                       /* Poison the old registration */
638                       memset (*regpp, 0xF3, sizeof (**regpp));
639                       clib_mem_free (*regpp);
640                       /* no dangling references, please */
641                       *regpp = 0;
642                     }
643                   else
644                     {
645                       svm_pop_heap (oldheap);
646                       clib_warning ("Duplicate free, client index %d",
647                                     regpp - am->vl_clients);
648                       oldheap = svm_push_data_heap (svm);
649                     }
650                 }
651
652               svm_client_scan_this_region_nolock (am->vlib_rp);
653
654               pthread_mutex_unlock (&svm->mutex);
655               svm_pop_heap (oldheap);
656               for (i = 0; i < vec_len (dead_indices); i++)
657                 pool_put_index (am->vl_clients, dead_indices[i]);
658             }
659
660           dead_client_scan_time = vlib_time_now (vm) + 20.0;
661         }
662
663       if (TRACE_VLIB_MEMORY_QUEUE)
664         {
665           /* *INDENT-OFF* */
666           ELOG_TYPE_DECLARE (e) = {
667             .format = "q-awake: len %d",
668             .format_args = "i4",
669           };
670           /* *INDENT-ON* */
671           struct
672           {
673             u32 len;
674           } *ed;
675           ed = ELOG_DATA (&vm->elog_main, e);
676           ed->len = q->cursize;
677         }
678     }
679
680   return 0;
681 }
682
683 static clib_error_t *
684 vl_api_show_histogram_command (vlib_main_t * vm,
685                                unformat_input_t * input,
686                                vlib_cli_command_t * cli_cmd)
687 {
688   u64 total_counts = 0;
689   int i;
690
691   for (i = 0; i < SLEEP_N_BUCKETS; i++)
692     {
693       total_counts += vector_rate_histogram[i];
694     }
695
696   if (total_counts == 0)
697     {
698       vlib_cli_output (vm, "No control-plane activity.");
699       return 0;
700     }
701
702 #define _(n)                                                    \
703     do {                                                        \
704         f64 percent;                                            \
705         percent = ((f64) vector_rate_histogram[SLEEP_##n##_US]) \
706             / (f64) total_counts;                               \
707         percent *= 100.0;                                       \
708         vlib_cli_output (vm, "Sleep %3d us: %llu, %.2f%%",n,    \
709                          vector_rate_histogram[SLEEP_##n##_US], \
710                          percent);                              \
711     } while (0);
712   foreach_histogram_bucket;
713 #undef _
714
715   return 0;
716 }
717
718 /* *INDENT-OFF* */
719 VLIB_CLI_COMMAND (cli_show_api_histogram_command, static) = {
720     .path = "show api histogram",
721     .short_help = "show api histogram",
722     .function = vl_api_show_histogram_command,
723 };
724 /* *INDENT-ON* */
725
726 static clib_error_t *
727 vl_api_clear_histogram_command (vlib_main_t * vm,
728                                 unformat_input_t * input,
729                                 vlib_cli_command_t * cli_cmd)
730 {
731   int i;
732
733   for (i = 0; i < SLEEP_N_BUCKETS; i++)
734     vector_rate_histogram[i] = 0;
735   return 0;
736 }
737
738 /* *INDENT-OFF* */
739 VLIB_CLI_COMMAND (cli_clear_api_histogram_command, static) = {
740     .path = "clear api histogram",
741     .short_help = "clear api histogram",
742     .function = vl_api_clear_histogram_command,
743 };
744 /* *INDENT-ON* */
745
746
747 /* *INDENT-OFF* */
748 VLIB_REGISTER_NODE (memclnt_node,static) = {
749     .function = memclnt_process,
750     .type = VLIB_NODE_TYPE_PROCESS,
751     .name = "api-rx-from-ring",
752     .state = VLIB_NODE_STATE_DISABLED,
753 };
754 /* *INDENT-ON* */
755
756 static void
757 memclnt_queue_callback (vlib_main_t * vm)
758 {
759   static volatile int *cursizep;
760
761   if (PREDICT_FALSE (cursizep == 0))
762     {
763       api_main_t *am = &api_main;
764       vl_shmem_hdr_t *shmem_hdr = am->shmem_hdr;
765       unix_shared_memory_queue_t *q;
766
767       if (shmem_hdr == 0)
768         return;
769
770       q = shmem_hdr->vl_input_queue;
771       if (q == 0)
772         return;
773       cursizep = &q->cursize;
774     }
775
776   if (*cursizep >= 1)
777     {
778       vm->queue_signal_pending = 1;
779       vm->api_queue_nonempty = 1;
780       vlib_process_signal_event (vm, memclnt_node.index,
781                                  /* event_type */ 0, /* event_data */ 0);
782     }
783 }
784
785 void
786 vl_enable_disable_memory_api (vlib_main_t * vm, int enable)
787 {
788   vlib_node_set_state (vm, memclnt_node.index,
789                        (enable
790                         ? VLIB_NODE_STATE_POLLING
791                         : VLIB_NODE_STATE_DISABLED));
792 }
793
794 static uword
795 api_rx_from_node (vlib_main_t * vm,
796                   vlib_node_runtime_t * node, vlib_frame_t * frame)
797 {
798   uword n_packets = frame->n_vectors;
799   uword n_left_from;
800   u32 *from;
801   static u8 *long_msg;
802
803   vec_validate (long_msg, 4095);
804   n_left_from = frame->n_vectors;
805   from = vlib_frame_args (frame);
806
807   while (n_left_from > 0)
808     {
809       u32 bi0;
810       vlib_buffer_t *b0;
811       void *msg;
812       uword msg_len;
813
814       bi0 = from[0];
815       b0 = vlib_get_buffer (vm, bi0);
816       from += 1;
817       n_left_from -= 1;
818
819       msg = b0->data + b0->current_data;
820       msg_len = b0->current_length;
821       if (b0->flags & VLIB_BUFFER_NEXT_PRESENT)
822         {
823           ASSERT (long_msg != 0);
824           _vec_len (long_msg) = 0;
825           vec_add (long_msg, msg, msg_len);
826           while (b0->flags & VLIB_BUFFER_NEXT_PRESENT)
827             {
828               b0 = vlib_get_buffer (vm, b0->next_buffer);
829               msg = b0->data + b0->current_data;
830               msg_len = b0->current_length;
831               vec_add (long_msg, msg, msg_len);
832             }
833           msg = long_msg;
834         }
835       vl_msg_api_handler_no_trace_no_free (msg);
836     }
837
838   /* Free what we've been given. */
839   vlib_buffer_free (vm, vlib_frame_args (frame), n_packets);
840
841   return n_packets;
842 }
843
844 /* *INDENT-OFF* */
845 VLIB_REGISTER_NODE (api_rx_from_node_node,static) = {
846     .function = api_rx_from_node,
847     .type = VLIB_NODE_TYPE_INTERNAL,
848     .vector_size = 4,
849     .name = "api-rx-from-node",
850 };
851 /* *INDENT-ON* */
852
853 static clib_error_t *
854 setup_memclnt_exit (vlib_main_t * vm)
855 {
856   atexit (vl_unmap_shmem);
857   return 0;
858 }
859
860 VLIB_INIT_FUNCTION (setup_memclnt_exit);
861
862
863 static clib_error_t *
864 vl_api_ring_command (vlib_main_t * vm,
865                      unformat_input_t * input, vlib_cli_command_t * cli_cmd)
866 {
867   int i;
868   ring_alloc_t *ap;
869   vl_shmem_hdr_t *shmem_hdr;
870   api_main_t *am = &api_main;
871
872   shmem_hdr = am->shmem_hdr;
873
874   if (shmem_hdr == 0)
875     {
876       vlib_cli_output (vm, "Shared memory segment not initialized...\n");
877       return 0;
878     }
879
880   vlib_cli_output (vm, "%8s %8s %8s %8s %8s\n",
881                    "Owner", "Size", "Nitems", "Hits", "Misses");
882
883   ap = shmem_hdr->vl_rings;
884
885   for (i = 0; i < vec_len (shmem_hdr->vl_rings); i++)
886     {
887       vlib_cli_output (vm, "%8s %8d %8d %8d %8d\n",
888                        "vlib", ap->size, ap->nitems, ap->hits, ap->misses);
889       ap++;
890     }
891
892   ap = shmem_hdr->client_rings;
893
894   for (i = 0; i < vec_len (shmem_hdr->client_rings); i++)
895     {
896       vlib_cli_output (vm, "%8s %8d %8d %8d %8d\n",
897                        "clnt", ap->size, ap->nitems, ap->hits, ap->misses);
898       ap++;
899     }
900
901   vlib_cli_output (vm, "%d ring miss fallback allocations\n",
902                    am->ring_misses);
903
904   vlib_cli_output
905     (vm, "%d application restarts, %d reclaimed msgs, %d garbage collects\n",
906      shmem_hdr->application_restarts,
907      shmem_hdr->restart_reclaims, shmem_hdr->garbage_collects);
908   return 0;
909 }
910
911 void dump_socket_clients (vlib_main_t * vm, api_main_t * am)
912   __attribute__ ((weak));
913
914 void
915 dump_socket_clients (vlib_main_t * vm, api_main_t * am)
916 {
917 }
918
919 static clib_error_t *
920 vl_api_client_command (vlib_main_t * vm,
921                        unformat_input_t * input, vlib_cli_command_t * cli_cmd)
922 {
923   vl_api_registration_t **regpp, *regp;
924   unix_shared_memory_queue_t *q;
925   char *health;
926   api_main_t *am = &api_main;
927   u32 *confused_indices = 0;
928
929   if (!pool_elts (am->vl_clients))
930     goto socket_clients;
931   vlib_cli_output (vm, "Shared memory clients");
932   vlib_cli_output (vm, "%16s %8s %14s %18s %s",
933                    "Name", "PID", "Queue Length", "Queue VA", "Health");
934
935   /* *INDENT-OFF* */
936   pool_foreach (regpp, am->vl_clients,
937   ({
938     regp = *regpp;
939
940     if (regp)
941       {
942         q = regp->vl_input_queue;
943         if (kill (q->consumer_pid, 0) < 0)
944           {
945             health = "DEAD";
946           }
947         else
948           {
949             health = "alive";
950           }
951         vlib_cli_output (vm, "%16s %8d %14d 0x%016llx %s\n",
952                          regp->name, q->consumer_pid, q->cursize,
953                          q, health);
954       }
955     else
956       {
957         clib_warning ("NULL client registration index %d",
958                       regpp - am->vl_clients);
959         vec_add1 (confused_indices, regpp - am->vl_clients);
960       }
961   }));
962   /* *INDENT-ON* */
963
964   /* This should "never happen," but if it does, fix it... */
965   if (PREDICT_FALSE (vec_len (confused_indices) > 0))
966     {
967       int i;
968       for (i = 0; i < vec_len (confused_indices); i++)
969         {
970           pool_put_index (am->vl_clients, confused_indices[i]);
971         }
972     }
973   vec_free (confused_indices);
974
975   if (am->missing_clients)
976     vlib_cli_output (vm, "%u messages with missing clients",
977                      am->missing_clients);
978 socket_clients:
979   dump_socket_clients (vm, am);
980
981   return 0;
982 }
983
984 static clib_error_t *
985 vl_api_status_command (vlib_main_t * vm,
986                        unformat_input_t * input, vlib_cli_command_t * cli_cmd)
987 {
988   api_main_t *am = &api_main;
989
990   // check if rx_trace and tx_trace are not null pointers
991
992   if (am->rx_trace == 0)
993     {
994       vlib_cli_output (vm, "RX Trace disabled\n");
995     }
996   else
997     {
998       if (am->rx_trace->enabled == 0)
999         vlib_cli_output (vm, "RX Trace disabled\n");
1000       else
1001         vlib_cli_output (vm, "RX Trace enabled\n");
1002     }
1003
1004   if (am->tx_trace == 0)
1005     {
1006       vlib_cli_output (vm, "TX Trace disabled\n");
1007     }
1008   else
1009     {
1010       if (am->tx_trace->enabled == 0)
1011         vlib_cli_output (vm, "TX Trace disabled\n");
1012       else
1013         vlib_cli_output (vm, "TX Trace enabled\n");
1014     }
1015
1016   return 0;
1017 }
1018
1019 /* *INDENT-OFF* */
1020 VLIB_CLI_COMMAND (cli_show_api_command, static) = {
1021     .path = "show api",
1022     .short_help = "Show API information",
1023 };
1024 /* *INDENT-ON* */
1025
1026 /* *INDENT-OFF* */
1027 VLIB_CLI_COMMAND (cli_show_api_ring_command, static) = {
1028     .path = "show api ring-stats",
1029     .short_help = "Message ring statistics",
1030     .function = vl_api_ring_command,
1031 };
1032 /* *INDENT-ON* */
1033
1034 /* *INDENT-OFF* */
1035 VLIB_CLI_COMMAND (cli_show_api_clients_command, static) = {
1036     .path = "show api clients",
1037     .short_help = "Client information",
1038     .function = vl_api_client_command,
1039 };
1040 /* *INDENT-ON* */
1041
1042 /* *INDENT-OFF* */
1043 VLIB_CLI_COMMAND (cli_show_api_status_command, static) = {
1044     .path = "show api status",
1045     .short_help = "Show API trace status",
1046     .function = vl_api_status_command,
1047 };
1048 /* *INDENT-ON* */
1049
1050 static clib_error_t *
1051 vl_api_message_table_command (vlib_main_t * vm,
1052                               unformat_input_t * input,
1053                               vlib_cli_command_t * cli_cmd)
1054 {
1055   api_main_t *am = &api_main;
1056   int i;
1057   int verbose = 0;
1058
1059   if (unformat (input, "verbose"))
1060     verbose = 1;
1061
1062
1063   if (verbose == 0)
1064     vlib_cli_output (vm, "%-4s %s", "ID", "Name");
1065   else
1066     vlib_cli_output (vm, "%-4s %-40s %6s %7s", "ID", "Name", "Bounce",
1067                      "MP-safe");
1068
1069   for (i = 1; i < vec_len (am->msg_names); i++)
1070     {
1071       if (verbose == 0)
1072         {
1073           vlib_cli_output (vm, "%-4d %s", i,
1074                            am->msg_names[i] ? am->msg_names[i] :
1075                            "  [no handler]");
1076         }
1077       else
1078         {
1079           vlib_cli_output (vm, "%-4d %-40s %6d %7d", i,
1080                            am->msg_names[i] ? am->msg_names[i] :
1081                            "  [no handler]", am->message_bounce[i],
1082                            am->is_mp_safe[i]);
1083         }
1084     }
1085
1086   return 0;
1087 }
1088
1089 /* *INDENT-OFF* */
1090 VLIB_CLI_COMMAND (cli_show_api_message_table_command, static) = {
1091     .path = "show api message-table",
1092     .short_help = "Message Table",
1093     .function = vl_api_message_table_command,
1094 };
1095 /* *INDENT-ON* */
1096
1097 static clib_error_t *
1098 vl_api_trace_command (vlib_main_t * vm,
1099                       unformat_input_t * input, vlib_cli_command_t * cli_cmd)
1100 {
1101   u32 nitems = 1024;
1102   vl_api_trace_which_t which = VL_API_TRACE_RX;
1103   api_main_t *am = &api_main;
1104
1105   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1106     {
1107       if (unformat (input, "rx nitems %u", &nitems) || unformat (input, "rx"))
1108         goto configure;
1109       else if (unformat (input, "tx nitems %u", &nitems)
1110                || unformat (input, "tx"))
1111         {
1112           which = VL_API_TRACE_RX;
1113           goto configure;
1114         }
1115       else if (unformat (input, "on rx"))
1116         {
1117           vl_msg_api_trace_onoff (am, VL_API_TRACE_RX, 1);
1118         }
1119       else if (unformat (input, "on tx"))
1120         {
1121           vl_msg_api_trace_onoff (am, VL_API_TRACE_TX, 1);
1122         }
1123       else if (unformat (input, "on"))
1124         {
1125           vl_msg_api_trace_onoff (am, VL_API_TRACE_RX, 1);
1126         }
1127       else if (unformat (input, "off"))
1128         {
1129           vl_msg_api_trace_onoff (am, VL_API_TRACE_RX, 0);
1130           vl_msg_api_trace_onoff (am, VL_API_TRACE_TX, 0);
1131         }
1132       else if (unformat (input, "free"))
1133         {
1134           vl_msg_api_trace_onoff (am, VL_API_TRACE_RX, 0);
1135           vl_msg_api_trace_onoff (am, VL_API_TRACE_TX, 0);
1136           vl_msg_api_trace_free (am, VL_API_TRACE_RX);
1137           vl_msg_api_trace_free (am, VL_API_TRACE_TX);
1138         }
1139       else if (unformat (input, "debug on"))
1140         {
1141           am->msg_print_flag = 1;
1142         }
1143       else if (unformat (input, "debug off"))
1144         {
1145           am->msg_print_flag = 0;
1146         }
1147       else
1148         return clib_error_return (0, "unknown input `%U'",
1149                                   format_unformat_error, input);
1150     }
1151   return 0;
1152
1153 configure:
1154   if (vl_msg_api_trace_configure (am, which, nitems))
1155     {
1156       vlib_cli_output (vm, "warning: trace configure error (%d, %d)",
1157                        which, nitems);
1158     }
1159
1160   return 0;
1161 }
1162
1163 /* *INDENT-OFF* */
1164 VLIB_CLI_COMMAND (trace, static) = {
1165     .path = "set api-trace",
1166     .short_help = "API trace",
1167     .function = vl_api_trace_command,
1168 };
1169 /* *INDENT-ON* */
1170
1171 clib_error_t *
1172 vlibmemory_init (vlib_main_t * vm)
1173 {
1174   api_main_t *am = &api_main;
1175   svm_map_region_args_t _a, *a = &_a;
1176
1177   memset (a, 0, sizeof (*a));
1178   a->root_path = am->root_path;
1179   a->name = SVM_GLOBAL_REGION_NAME;
1180   a->baseva = (am->global_baseva != 0) ?
1181     am->global_baseva : SVM_GLOBAL_REGION_BASEVA;
1182   a->size = (am->global_size != 0) ? am->global_size : SVM_GLOBAL_REGION_SIZE;
1183   a->flags = SVM_FLAGS_NODATA;
1184   a->uid = am->api_uid;
1185   a->gid = am->api_gid;
1186   a->pvt_heap_size =
1187     (am->global_pvt_heap_size !=
1188      0) ? am->global_pvt_heap_size : SVM_PVT_MHEAP_SIZE;
1189
1190   svm_region_init_args (a);
1191   return 0;
1192 }
1193
1194 VLIB_INIT_FUNCTION (vlibmemory_init);
1195
1196 void
1197 vl_set_memory_region_name (char *name)
1198 {
1199   api_main_t *am = &api_main;
1200
1201   am->region_name = name;
1202 }
1203
1204 static int
1205 range_compare (vl_api_msg_range_t * a0, vl_api_msg_range_t * a1)
1206 {
1207   int len0, len1, clen;
1208
1209   len0 = vec_len (a0->name);
1210   len1 = vec_len (a1->name);
1211   clen = len0 < len1 ? len0 : len1;
1212   return (strncmp ((char *) a0->name, (char *) a1->name, clen));
1213 }
1214
1215 static u8 *
1216 format_api_msg_range (u8 * s, va_list * args)
1217 {
1218   vl_api_msg_range_t *rp = va_arg (*args, vl_api_msg_range_t *);
1219
1220   if (rp == 0)
1221     s = format (s, "%-20s%9s%9s", "Name", "First-ID", "Last-ID");
1222   else
1223     s = format (s, "%-20s%9d%9d", rp->name, rp->first_msg_id,
1224                 rp->last_msg_id);
1225
1226   return s;
1227 }
1228
1229 static clib_error_t *
1230 vl_api_show_plugin_command (vlib_main_t * vm,
1231                             unformat_input_t * input,
1232                             vlib_cli_command_t * cli_cmd)
1233 {
1234   api_main_t *am = &api_main;
1235   vl_api_msg_range_t *rp = 0;
1236   int i;
1237
1238   if (vec_len (am->msg_ranges) == 0)
1239     {
1240       vlib_cli_output (vm, "No plugin API message ranges configured...");
1241       return 0;
1242     }
1243
1244   rp = vec_dup (am->msg_ranges);
1245
1246   vec_sort_with_function (rp, range_compare);
1247
1248   vlib_cli_output (vm, "Plugin API message ID ranges...\n");
1249   vlib_cli_output (vm, "%U", format_api_msg_range, 0 /* header */ );
1250
1251   for (i = 0; i < vec_len (rp); i++)
1252     vlib_cli_output (vm, "%U", format_api_msg_range, rp + i);
1253
1254   vec_free (rp);
1255
1256   return 0;
1257 }
1258
1259 /* *INDENT-OFF* */
1260 VLIB_CLI_COMMAND (cli_show_api_plugin_command, static) = {
1261     .path = "show api plugin",
1262     .short_help = "show api plugin",
1263     .function = vl_api_show_plugin_command,
1264 };
1265 /* *INDENT-ON* */
1266
1267 static void
1268 vl_api_rpc_call_t_handler (vl_api_rpc_call_t * mp)
1269 {
1270   vl_api_rpc_reply_t *rmp;
1271   int (*fp) (void *);
1272   i32 rv = 0;
1273   vlib_main_t *vm = vlib_get_main ();
1274
1275   if (mp->function == 0)
1276     {
1277       rv = -1;
1278       clib_warning ("rpc NULL function pointer");
1279     }
1280
1281   else
1282     {
1283       if (mp->need_barrier_sync)
1284         vlib_worker_thread_barrier_sync (vm);
1285
1286       fp = uword_to_pointer (mp->function, int (*)(void *));
1287       rv = fp (mp->data);
1288
1289       if (mp->need_barrier_sync)
1290         vlib_worker_thread_barrier_release (vm);
1291     }
1292
1293   if (mp->send_reply)
1294     {
1295       unix_shared_memory_queue_t *q =
1296         vl_api_client_index_to_input_queue (mp->client_index);
1297       if (q)
1298         {
1299           rmp = vl_msg_api_alloc_as_if_client (sizeof (*rmp));
1300           rmp->_vl_msg_id = ntohs (VL_API_RPC_REPLY);
1301           rmp->context = mp->context;
1302           rmp->retval = rv;
1303           vl_msg_api_send_shmem (q, (u8 *) & rmp);
1304         }
1305     }
1306   if (mp->multicast)
1307     {
1308       clib_warning ("multicast not yet implemented...");
1309     }
1310 }
1311
1312 static void
1313 vl_api_rpc_reply_t_handler (vl_api_rpc_reply_t * mp)
1314 {
1315   clib_warning ("unimplemented");
1316 }
1317
1318 void
1319 vl_api_rpc_call_main_thread (void *fp, u8 * data, u32 data_length)
1320 {
1321   vl_api_rpc_call_t *mp;
1322   api_main_t *am = &api_main;
1323   vl_shmem_hdr_t *shmem_hdr = am->shmem_hdr;
1324   unix_shared_memory_queue_t *q;
1325
1326   /* Main thread: call the function directly */
1327   if (os_get_cpu_number () == 0)
1328     {
1329       vlib_main_t *vm = vlib_get_main ();
1330       void (*call_fp) (void *);
1331
1332       vlib_worker_thread_barrier_sync (vm);
1333
1334       call_fp = fp;
1335       call_fp (data);
1336
1337       vlib_worker_thread_barrier_release (vm);
1338       return;
1339     }
1340
1341   /* Any other thread, actually do an RPC call... */
1342   mp = vl_msg_api_alloc_as_if_client (sizeof (*mp) + data_length);
1343
1344   memset (mp, 0, sizeof (*mp));
1345   clib_memcpy (mp->data, data, data_length);
1346   mp->_vl_msg_id = ntohs (VL_API_RPC_CALL);
1347   mp->function = pointer_to_uword (fp);
1348   mp->need_barrier_sync = 1;
1349
1350   /*
1351    * Use the "normal" control-plane mechanism for the main thread.
1352    * Well, almost. if the main input queue is full, we cannot
1353    * block. Otherwise, we can expect a barrier sync timeout.
1354    */
1355   q = shmem_hdr->vl_input_queue;
1356
1357   while (pthread_mutex_trylock (&q->mutex))
1358     vlib_worker_thread_barrier_check ();
1359
1360   while (PREDICT_FALSE (unix_shared_memory_queue_is_full (q)))
1361     {
1362       pthread_mutex_unlock (&q->mutex);
1363       vlib_worker_thread_barrier_check ();
1364       while (pthread_mutex_trylock (&q->mutex))
1365         vlib_worker_thread_barrier_check ();
1366     }
1367
1368   vl_msg_api_send_shmem_nolock (q, (u8 *) & mp);
1369
1370   pthread_mutex_unlock (&q->mutex);
1371 }
1372
1373 static void
1374 vl_api_trace_plugin_msg_ids_t_handler (vl_api_trace_plugin_msg_ids_t * mp)
1375 {
1376   api_main_t *am = &api_main;
1377   vl_api_msg_range_t *rp;
1378   uword *p;
1379
1380   /* Noop (except for tracing) during normal operation */
1381   if (am->replay_in_progress == 0)
1382     return;
1383
1384   p = hash_get_mem (am->msg_range_by_name, mp->plugin_name);
1385   if (p == 0)
1386     {
1387       clib_warning ("WARNING: traced plugin '%s' not in current image",
1388                     mp->plugin_name);
1389       return;
1390     }
1391
1392   rp = vec_elt_at_index (am->msg_ranges, p[0]);
1393   if (rp->first_msg_id != clib_net_to_host_u16 (mp->first_msg_id))
1394     {
1395       clib_warning ("WARNING: traced plugin '%s' first message id %d not %d",
1396                     mp->plugin_name, clib_net_to_host_u16 (mp->first_msg_id),
1397                     rp->first_msg_id);
1398     }
1399
1400   if (rp->last_msg_id != clib_net_to_host_u16 (mp->last_msg_id))
1401     {
1402       clib_warning ("WARNING: traced plugin '%s' last message id %d not %d",
1403                     mp->plugin_name, clib_net_to_host_u16 (mp->last_msg_id),
1404                     rp->last_msg_id);
1405     }
1406 }
1407
1408 #define foreach_rpc_api_msg                     \
1409 _(RPC_CALL,rpc_call)                            \
1410 _(RPC_REPLY,rpc_reply)
1411
1412 #define foreach_plugin_trace_msg                \
1413 _(TRACE_PLUGIN_MSG_IDS,trace_plugin_msg_ids)
1414
1415 static clib_error_t *
1416 rpc_api_hookup (vlib_main_t * vm)
1417 {
1418 #define _(N,n)                                                  \
1419     vl_msg_api_set_handlers(VL_API_##N, #n,                     \
1420                            vl_api_##n##_t_handler,              \
1421                            vl_noop_handler,                     \
1422                            vl_noop_handler,                     \
1423                            vl_api_##n##_t_print,                \
1424                            sizeof(vl_api_##n##_t), 0 /* do not trace */);
1425   foreach_rpc_api_msg;
1426 #undef _
1427
1428 #define _(N,n)                                                  \
1429     vl_msg_api_set_handlers(VL_API_##N, #n,                     \
1430                            vl_api_##n##_t_handler,              \
1431                            vl_noop_handler,                     \
1432                            vl_noop_handler,                     \
1433                            vl_api_##n##_t_print,                \
1434                            sizeof(vl_api_##n##_t), 1 /* do trace */);
1435   foreach_plugin_trace_msg;
1436 #undef _
1437   return 0;
1438 }
1439
1440 VLIB_API_INIT_FUNCTION (rpc_api_hookup);
1441
1442 typedef enum
1443 {
1444   DUMP,
1445   CUSTOM_DUMP,
1446   REPLAY,
1447   INITIALIZERS,
1448 } vl_api_replay_t;
1449
1450 u8 *
1451 format_vl_msg_api_trace_status (u8 * s, va_list * args)
1452 {
1453   api_main_t *am = va_arg (*args, api_main_t *);
1454   vl_api_trace_which_t which = va_arg (*args, vl_api_trace_which_t);
1455   vl_api_trace_t *tp;
1456   char *trace_name;
1457
1458   switch (which)
1459     {
1460     case VL_API_TRACE_TX:
1461       tp = am->tx_trace;
1462       trace_name = "TX trace";
1463       break;
1464
1465     case VL_API_TRACE_RX:
1466       tp = am->rx_trace;
1467       trace_name = "RX trace";
1468       break;
1469
1470     default:
1471       abort ();
1472     }
1473
1474   if (tp == 0)
1475     {
1476       s = format (s, "%s: not yet configured.\n", trace_name);
1477       return s;
1478     }
1479
1480   s = format (s, "%s: used %d of %d items, %s enabled, %s wrapped\n",
1481               trace_name, vec_len (tp->traces), tp->nitems,
1482               tp->enabled ? "is" : "is not", tp->wrapped ? "has" : "has not");
1483   return s;
1484 }
1485
1486 void vl_msg_api_custom_dump_configure (api_main_t * am)
1487   __attribute__ ((weak));
1488 void
1489 vl_msg_api_custom_dump_configure (api_main_t * am)
1490 {
1491 }
1492
1493 static void
1494 vl_msg_api_process_file (vlib_main_t * vm, u8 * filename,
1495                          u32 first_index, u32 last_index,
1496                          vl_api_replay_t which)
1497 {
1498   vl_api_trace_file_header_t *hp;
1499   int i, fd;
1500   struct stat statb;
1501   size_t file_size;
1502   u8 *msg;
1503   u8 endian_swap_needed = 0;
1504   api_main_t *am = &api_main;
1505   u8 *tmpbuf = 0;
1506   u32 nitems;
1507   void **saved_print_handlers = 0;
1508
1509   fd = open ((char *) filename, O_RDONLY);
1510
1511   if (fd < 0)
1512     {
1513       vlib_cli_output (vm, "Couldn't open %s\n", filename);
1514       return;
1515     }
1516
1517   if (fstat (fd, &statb) < 0)
1518     {
1519       vlib_cli_output (vm, "Couldn't stat %s\n", filename);
1520       close (fd);
1521       return;
1522     }
1523
1524   if (!(statb.st_mode & S_IFREG) || (statb.st_size < sizeof (*hp)))
1525     {
1526       vlib_cli_output (vm, "File not plausible: %s\n", filename);
1527       close (fd);
1528       return;
1529     }
1530
1531   file_size = statb.st_size;
1532   file_size = (file_size + 4095) & ~(4096);
1533
1534   hp = mmap (0, file_size, PROT_READ, MAP_PRIVATE, fd, 0);
1535
1536   if (hp == (vl_api_trace_file_header_t *) MAP_FAILED)
1537     {
1538       vlib_cli_output (vm, "mmap failed: %s\n", filename);
1539       close (fd);
1540       return;
1541     }
1542   close (fd);
1543
1544   if ((clib_arch_is_little_endian && hp->endian == VL_API_BIG_ENDIAN)
1545       || (clib_arch_is_big_endian && hp->endian == VL_API_LITTLE_ENDIAN))
1546     endian_swap_needed = 1;
1547
1548   if (endian_swap_needed)
1549     nitems = ntohl (hp->nitems);
1550   else
1551     nitems = hp->nitems;
1552
1553   if (last_index == (u32) ~ 0)
1554     {
1555       last_index = nitems - 1;
1556     }
1557
1558   if (first_index >= nitems || last_index >= nitems)
1559     {
1560       vlib_cli_output (vm, "Range (%d, %d) outside file range (0, %d)\n",
1561                        first_index, last_index, nitems - 1);
1562       munmap (hp, file_size);
1563       return;
1564     }
1565   if (hp->wrapped)
1566     vlib_cli_output (vm,
1567                      "Note: wrapped/incomplete trace, results may vary\n");
1568
1569   if (which == CUSTOM_DUMP)
1570     {
1571       saved_print_handlers = (void **) vec_dup (am->msg_print_handlers);
1572       vl_msg_api_custom_dump_configure (am);
1573     }
1574
1575
1576   msg = (u8 *) (hp + 1);
1577
1578   for (i = 0; i < first_index; i++)
1579     {
1580       trace_cfg_t *cfgp;
1581       int size;
1582       u16 msg_id;
1583
1584       size = clib_host_to_net_u32 (*(u32 *) msg);
1585       msg += sizeof (u32);
1586
1587       if (clib_arch_is_little_endian)
1588         msg_id = ntohs (*((u16 *) msg));
1589       else
1590         msg_id = *((u16 *) msg);
1591
1592       cfgp = am->api_trace_cfg + msg_id;
1593       if (!cfgp)
1594         {
1595           vlib_cli_output (vm, "Ugh: msg id %d no trace config\n", msg_id);
1596           munmap (hp, file_size);
1597           return;
1598         }
1599       msg += size;
1600     }
1601
1602   if (which == REPLAY)
1603     am->replay_in_progress = 1;
1604
1605   for (; i <= last_index; i++)
1606     {
1607       trace_cfg_t *cfgp;
1608       u16 *msg_idp;
1609       u16 msg_id;
1610       int size;
1611
1612       if (which == DUMP)
1613         vlib_cli_output (vm, "---------- trace %d -----------\n", i);
1614
1615       size = clib_host_to_net_u32 (*(u32 *) msg);
1616       msg += sizeof (u32);
1617
1618       if (clib_arch_is_little_endian)
1619         msg_id = ntohs (*((u16 *) msg));
1620       else
1621         msg_id = *((u16 *) msg);
1622
1623       cfgp = am->api_trace_cfg + msg_id;
1624       if (!cfgp)
1625         {
1626           vlib_cli_output (vm, "Ugh: msg id %d no trace config\n", msg_id);
1627           munmap (hp, file_size);
1628           vec_free (tmpbuf);
1629           am->replay_in_progress = 0;
1630           return;
1631         }
1632
1633       /* Copy the buffer (from the read-only mmap'ed file) */
1634       vec_validate (tmpbuf, size - 1 + sizeof (uword));
1635       clib_memcpy (tmpbuf + sizeof (uword), msg, size);
1636       memset (tmpbuf, 0xf, sizeof (uword));
1637
1638       /*
1639        * Endian swap if needed. All msg data is supposed to be
1640        * in network byte order. All msg handlers are supposed to
1641        * know that. The generic message dumpers don't know that.
1642        * One could fix apigen, I suppose.
1643        */
1644       if ((which == DUMP && clib_arch_is_little_endian) || endian_swap_needed)
1645         {
1646           void (*endian_fp) (void *);
1647           if (msg_id >= vec_len (am->msg_endian_handlers)
1648               || (am->msg_endian_handlers[msg_id] == 0))
1649             {
1650               vlib_cli_output (vm, "Ugh: msg id %d no endian swap\n", msg_id);
1651               munmap (hp, file_size);
1652               vec_free (tmpbuf);
1653               am->replay_in_progress = 0;
1654               return;
1655             }
1656           endian_fp = am->msg_endian_handlers[msg_id];
1657           (*endian_fp) (tmpbuf + sizeof (uword));
1658         }
1659
1660       /* msg_id always in network byte order */
1661       if (clib_arch_is_little_endian)
1662         {
1663           msg_idp = (u16 *) (tmpbuf + sizeof (uword));
1664           *msg_idp = msg_id;
1665         }
1666
1667       switch (which)
1668         {
1669         case CUSTOM_DUMP:
1670         case DUMP:
1671           if (msg_id < vec_len (am->msg_print_handlers) &&
1672               am->msg_print_handlers[msg_id])
1673             {
1674               u8 *(*print_fp) (void *, void *);
1675
1676               print_fp = (void *) am->msg_print_handlers[msg_id];
1677               (*print_fp) (tmpbuf + sizeof (uword), vm);
1678             }
1679           else
1680             {
1681               vlib_cli_output (vm, "Skipping msg id %d: no print fcn\n",
1682                                msg_id);
1683               break;
1684             }
1685           break;
1686
1687         case INITIALIZERS:
1688           if (msg_id < vec_len (am->msg_print_handlers) &&
1689               am->msg_print_handlers[msg_id])
1690             {
1691               u8 *s;
1692               int j;
1693               u8 *(*print_fp) (void *, void *);
1694
1695               print_fp = (void *) am->msg_print_handlers[msg_id];
1696
1697               vlib_cli_output (vm, "/*");
1698
1699               (*print_fp) (tmpbuf + sizeof (uword), vm);
1700               vlib_cli_output (vm, "*/\n");
1701
1702               s = format (0, "static u8 * vl_api_%s_%d[%d] = {",
1703                           am->msg_names[msg_id], i,
1704                           am->api_trace_cfg[msg_id].size);
1705
1706               for (j = 0; j < am->api_trace_cfg[msg_id].size; j++)
1707                 {
1708                   if ((j & 7) == 0)
1709                     s = format (s, "\n    ");
1710                   s = format (s, "0x%02x,", tmpbuf[sizeof (uword) + j]);
1711                 }
1712               s = format (s, "\n};\n%c", 0);
1713               vlib_cli_output (vm, (char *) s);
1714               vec_free (s);
1715             }
1716           break;
1717
1718         case REPLAY:
1719           if (msg_id < vec_len (am->msg_print_handlers) &&
1720               am->msg_print_handlers[msg_id] && cfgp->replay_enable)
1721             {
1722               void (*handler) (void *);
1723
1724               handler = (void *) am->msg_handlers[msg_id];
1725
1726               if (!am->is_mp_safe[msg_id])
1727                 vl_msg_api_barrier_sync ();
1728               (*handler) (tmpbuf + sizeof (uword));
1729               if (!am->is_mp_safe[msg_id])
1730                 vl_msg_api_barrier_release ();
1731             }
1732           else
1733             {
1734               if (cfgp->replay_enable)
1735                 vlib_cli_output (vm, "Skipping msg id %d: no handler\n",
1736                                  msg_id);
1737               break;
1738             }
1739           break;
1740         }
1741
1742       _vec_len (tmpbuf) = 0;
1743       msg += size;
1744     }
1745
1746   if (saved_print_handlers)
1747     {
1748       clib_memcpy (am->msg_print_handlers, saved_print_handlers,
1749                    vec_len (am->msg_print_handlers) * sizeof (void *));
1750       vec_free (saved_print_handlers);
1751     }
1752
1753   munmap (hp, file_size);
1754   vec_free (tmpbuf);
1755   am->replay_in_progress = 0;
1756 }
1757
1758 static clib_error_t *
1759 api_trace_command_fn (vlib_main_t * vm,
1760                       unformat_input_t * input, vlib_cli_command_t * cmd)
1761 {
1762   u32 nitems = 256 << 10;
1763   api_main_t *am = &api_main;
1764   vl_api_trace_which_t which = VL_API_TRACE_RX;
1765   u8 *filename;
1766   u32 first = 0;
1767   u32 last = (u32) ~ 0;
1768   FILE *fp;
1769   int rv;
1770
1771   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1772     {
1773       if (unformat (input, "on") || unformat (input, "enable"))
1774         {
1775           if (unformat (input, "nitems %d", &nitems))
1776             ;
1777           vl_msg_api_trace_configure (am, which, nitems);
1778           vl_msg_api_trace_onoff (am, which, 1 /* on */ );
1779         }
1780       else if (unformat (input, "off"))
1781         {
1782           vl_msg_api_trace_onoff (am, which, 0);
1783         }
1784       else if (unformat (input, "save %s", &filename))
1785         {
1786           u8 *chroot_filename;
1787           if (strstr ((char *) filename, "..")
1788               || index ((char *) filename, '/'))
1789             {
1790               vlib_cli_output (vm, "illegal characters in filename '%s'",
1791                                filename);
1792               return 0;
1793             }
1794
1795           chroot_filename = format (0, "/tmp/%s%c", filename, 0);
1796
1797           vec_free (filename);
1798
1799           fp = fopen ((char *) chroot_filename, "w");
1800           if (fp == NULL)
1801             {
1802               vlib_cli_output (vm, "Couldn't create %s\n", chroot_filename);
1803               return 0;
1804             }
1805           rv = vl_msg_api_trace_save (am, which, fp);
1806           fclose (fp);
1807           if (rv == -1)
1808             vlib_cli_output (vm, "API Trace data not present\n");
1809           else if (rv == -2)
1810             vlib_cli_output (vm, "File for writing is closed\n");
1811           else if (rv == -10)
1812             vlib_cli_output (vm, "Error while writing header to file\n");
1813           else if (rv == -11)
1814             vlib_cli_output (vm, "Error while writing trace to file\n");
1815           else if (rv == -12)
1816             vlib_cli_output (vm,
1817                              "Error while writing end of buffer trace to file\n");
1818           else if (rv == -13)
1819             vlib_cli_output (vm,
1820                              "Error while writing start of buffer trace to file\n");
1821           else if (rv < 0)
1822             vlib_cli_output (vm, "Unkown error while saving: %d", rv);
1823           else
1824             vlib_cli_output (vm, "API trace saved to %s\n", chroot_filename);
1825           vec_free (chroot_filename);
1826         }
1827       else if (unformat (input, "dump %s", &filename))
1828         {
1829           vl_msg_api_process_file (vm, filename, first, last, DUMP);
1830         }
1831       else if (unformat (input, "custom-dump %s", &filename))
1832         {
1833           vl_msg_api_process_file (vm, filename, first, last, CUSTOM_DUMP);
1834         }
1835       else if (unformat (input, "replay %s", &filename))
1836         {
1837           vl_msg_api_process_file (vm, filename, first, last, REPLAY);
1838         }
1839       else if (unformat (input, "initializers %s", &filename))
1840         {
1841           vl_msg_api_process_file (vm, filename, first, last, INITIALIZERS);
1842         }
1843       else if (unformat (input, "tx"))
1844         {
1845           which = VL_API_TRACE_TX;
1846         }
1847       else if (unformat (input, "first %d", &first))
1848         {
1849           ;
1850         }
1851       else if (unformat (input, "last %d", &last))
1852         {
1853           ;
1854         }
1855       else if (unformat (input, "status"))
1856         {
1857           vlib_cli_output (vm, "%U", format_vl_msg_api_trace_status,
1858                            am, which);
1859         }
1860       else if (unformat (input, "free"))
1861         {
1862           vl_msg_api_trace_onoff (am, which, 0);
1863           vl_msg_api_trace_free (am, which);
1864         }
1865       else if (unformat (input, "post-mortem-on"))
1866         vl_msg_api_post_mortem_dump_enable_disable (1 /* enable */ );
1867       else if (unformat (input, "post-mortem-off"))
1868         vl_msg_api_post_mortem_dump_enable_disable (0 /* enable */ );
1869       else
1870         return clib_error_return (0, "unknown input `%U'",
1871                                   format_unformat_error, input);
1872     }
1873   return 0;
1874 }
1875
1876 /* *INDENT-OFF* */
1877 VLIB_CLI_COMMAND (api_trace_command, static) = {
1878     .path = "api trace",
1879     .short_help =
1880     "api trace [on|off][dump|save|replay <file>][status][free][post-mortem-on]",
1881     .function = api_trace_command_fn,
1882 };
1883 /* *INDENT-ON* */
1884
1885 static clib_error_t *
1886 api_config_fn (vlib_main_t * vm, unformat_input_t * input)
1887 {
1888   u32 nitems = 256 << 10;
1889   vl_api_trace_which_t which = VL_API_TRACE_RX;
1890   api_main_t *am = &api_main;
1891
1892   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1893     {
1894       if (unformat (input, "on") || unformat (input, "enable"))
1895         {
1896           if (unformat (input, "nitems %d", &nitems))
1897             ;
1898           vl_msg_api_trace_configure (am, which, nitems);
1899           vl_msg_api_trace_onoff (am, which, 1 /* on */ );
1900           vl_msg_api_post_mortem_dump_enable_disable (1 /* enable */ );
1901         }
1902       else
1903         return clib_error_return (0, "unknown input `%U'",
1904                                   format_unformat_error, input);
1905     }
1906   return 0;
1907 }
1908
1909 VLIB_CONFIG_FUNCTION (api_config_fn, "api-trace");
1910
1911 /*
1912  * fd.io coding-style-patch-verification: ON
1913  *
1914  * Local Variables:
1915  * eval: (c-set-style "gnu")
1916  * End:
1917  */