blob: 8b8698b175df930035f54f77fa6d9fed31adf91e [file] [log] [blame]
Ed Warnickecb9cada2015-12-08 15:45:58 -07001/*
2 *------------------------------------------------------------------
3 * api_shared.c - API message handling, common code for both clients
4 * and the vlib process itself.
5 *
6 *
7 * Copyright (c) 2009 Cisco and/or its affiliates.
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at:
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 *------------------------------------------------------------------
20 */
21
22#include <stdio.h>
23#include <stdlib.h>
24#include <string.h>
25#include <sys/types.h>
26#include <sys/mman.h>
27#include <sys/stat.h>
28#include <fcntl.h>
29#include <unistd.h>
30#include <vppinfra/format.h>
31#include <vppinfra/byte_order.h>
32#include <vppinfra/error.h>
33#include <vlib/vlib.h>
34#include <vlib/unix/unix.h>
35#include <vlibapi/api.h>
36#include <vppinfra/elog.h>
37
38api_main_t api_main;
39
40void vl_msg_api_barrier_sync(void) __attribute__((weak));
41void vl_msg_api_barrier_sync(void) { }
42
43void vl_msg_api_barrier_release(void) __attribute__((weak));
44void vl_msg_api_barrier_release(void) { }
45
46void vl_msg_api_increment_missing_client_counter(void)
47{
48 api_main_t * am = &api_main;
49 am->missing_clients++;
50}
51
52typedef enum {
53 DUMP,
54 CUSTOM_DUMP,
55 REPLAY,
56 INITIALIZERS,
57} vl_api_replay_t;
58
59int vl_msg_api_rx_trace_enabled(api_main_t *am)
60{
61 return (am->rx_trace && am->rx_trace->enabled);
62}
63
64int vl_msg_api_tx_trace_enabled(api_main_t *am)
65{
66 return (am->tx_trace && am->tx_trace->enabled);
67}
68
69/*
70 * vl_msg_api_trace
71 */
72void vl_msg_api_trace(api_main_t *am, vl_api_trace_t *tp, void *msg)
73{
74 u8 **this_trace;
75 u8 **old_trace;
76 u8 *msg_copy;
77 trace_cfg_t *cfgp;
78 u16 msg_id = ntohs(*((u16 *)msg));
79
80 cfgp = am->api_trace_cfg + msg_id;
81
82 if (!cfgp || !cfgp->trace_enable)
83 return;
84
85 msg_copy = 0;
86
87 if (tp->nitems == 0) {
88 clib_warning ("tp->nitems is 0");
89 return;
90 }
91
92 if (vec_len(tp->traces) < tp->nitems) {
93 vec_add1(tp->traces, 0);
94 this_trace = tp->traces + vec_len(tp->traces) - 1;
95 } else {
96 tp->wrapped = 1;
97 old_trace = tp->traces + tp->curindex++;
98 if (tp->curindex == tp->nitems)
99 tp->curindex = 0;
100 vec_free(*old_trace);
101 this_trace = old_trace;
102 }
103
104 vec_validate(msg_copy, cfgp->size - 1);
105 memcpy(msg_copy, msg, cfgp->size);
106 *this_trace = msg_copy;
107}
108
109int vl_msg_api_trace_onoff(api_main_t *am, vl_api_trace_which_t which,
110 int onoff)
111{
112 vl_api_trace_t *tp;
113 int rv;
114
115 switch(which)
116 {
117 case VL_API_TRACE_TX:
118 tp = am->tx_trace;
119 if (tp == 0) {
120 vl_msg_api_trace_configure (am, which, 1024);
121 tp = am->tx_trace;
122 }
123 break;
124
125 case VL_API_TRACE_RX:
126 tp = am->rx_trace;
127 if (tp == 0) {
128 vl_msg_api_trace_configure (am, which, 1024);
129 tp = am->rx_trace;
130 }
131 break;
132
133 default:
134 /* duh? */
135 return -1;
136 }
137
138 /* Configured? */
139 if (tp == 0 || tp->nitems == 0)
140 return -1;
141
142 rv = tp->enabled;
143 tp->enabled = onoff;
144
145 return rv;
146}
147
148int vl_msg_api_trace_free(api_main_t *am, vl_api_trace_which_t which)
149{
150 vl_api_trace_t *tp;
151 int i;
152
153 switch(which)
154 {
155 case VL_API_TRACE_TX:
156 tp = am->tx_trace;
157 break;
158
159 case VL_API_TRACE_RX:
160 tp = am->rx_trace;
161 break;
162
163 default:
164 /* duh? */
165 return -1;
166 }
167
168 /* Configured? */
169 if (!tp || tp->nitems == 0)
170 return -1;
171
172 tp->curindex = 0;
173 tp->wrapped = 0;
174
175 for (i = 0; i < vec_len(tp->traces); i++) {
176 vec_free(tp->traces[i]);
177 }
178 vec_free(tp->traces);
179
180 return 0;
181}
182
183int vl_msg_api_trace_save(api_main_t *am,
184 vl_api_trace_which_t which, FILE *fp)
185{
186 vl_api_trace_t *tp;
187 vl_api_trace_file_header_t fh;
188 int i;
189 u8 *msg;
190
191 switch(which)
192 {
193 case VL_API_TRACE_TX:
194 tp = am->tx_trace;
195 break;
196
197 case VL_API_TRACE_RX:
198 tp = am->rx_trace;
199 break;
200
201 default:
202 /* duh? */
203 return -1;
204 }
205
206 /* Configured, data present? */
207 if (tp == 0 || tp->nitems == 0 || vec_len(tp->traces) == 0)
208 return -1;
209
210 /* "Dare to be stupid" check */
211 if (fp == 0) {
212 return -2;
213 }
214
215 /* Write the file header */
216 fh.nitems = vec_len(tp->traces);
217 fh.endian = tp->endian;
218 fh.wrapped = tp->wrapped;
219
220 if (fwrite(&fh, sizeof(fh), 1, fp) != 1) {
221 return (-10);
222 }
223
224 /* No-wrap case */
225 if (tp->wrapped == 0) {
226 /*
227 * Note: vec_len return 0 when fed a NULL pointer.
228 * Unfortunately, the static analysis tool doesn't
229 * figure it out, hence the suppressed warnings.
230 * What a great use of my time.
231 */
232 for (i = 0; i < vec_len(tp->traces); i++) {
233 /*sa_ignore NO_NULL_CHK*/
234 msg = tp->traces[i];
235 /*
236 * This retarded check required to pass
237 * [sic] SA-checking.
238 */
239 if (!msg)
240 continue;
241 if (fwrite(msg, 1, vec_len(msg), fp) != vec_len(msg)) {
242 return (-11);
243 }
244 }
245 } else {
246 /* Wrap case: write oldest -> end of buffer */
247 for (i = tp->curindex; i < vec_len(tp->traces); i++) {
248 msg = tp->traces[i];
249 /*
250 * This retarded check required to pass
251 * [sic] SA-checking
252 */
253 if (!msg)
254 continue;
255
256 if (fwrite(msg, 1, vec_len(msg), fp) != vec_len(msg)) {
257 return (-12);
258 }
259 }
260 /* write beginning of buffer -> oldest-1 */
261 for (i = 0; i < tp->curindex; i++) {
262 /*sa_ignore NO_NULL_CHK*/
263 msg = tp->traces[i];
264 /*
265 * This retarded check required to pass
266 * [sic] SA-checking
267 */
268 if (!msg)
269 continue;
270
271 if (fwrite(msg, 1, vec_len(msg), fp) != vec_len(msg)) {
272 return (-13);
273 }
274 }
275 }
276 return 0;
277}
278
279int vl_msg_api_trace_configure(api_main_t *am, vl_api_trace_which_t which,
280 u32 nitems)
281{
282 vl_api_trace_t *tp;
283 int was_on = 0;
284
285 switch(which)
286 {
287 case VL_API_TRACE_TX:
288 tp = am->tx_trace;
289 if (tp == 0) {
290 vec_validate(am->tx_trace, 0);
291 tp = am->tx_trace;
292 }
293 break;
294
295 case VL_API_TRACE_RX:
296 tp = am->rx_trace;
297 if (tp == 0) {
298 vec_validate(am->rx_trace, 0);
299 tp = am->rx_trace;
300 }
301
302 break;
303
304 default:
305 return -1;
306
307 }
308
309 if (tp->enabled) {
310 was_on = vl_msg_api_trace_onoff(am, which, 0);
311 }
312 if (tp->traces) {
313 vl_msg_api_trace_free(am, which);
314 }
315
316 memset(tp, 0, sizeof(*tp));
317
318 if (clib_arch_is_big_endian) {
319 tp->endian = VL_API_BIG_ENDIAN;
320 } else {
321 tp->endian = VL_API_LITTLE_ENDIAN;
322 }
323
324 tp->nitems = nitems;
325 if (was_on) {
326 (void)vl_msg_api_trace_onoff(am, which, was_on);
327 }
328 return 0;
329}
330
331always_inline void msg_handler_internal (api_main_t *am,
332 void *the_msg,
333 int trace_it,
334 int do_it,
335 int free_it)
336{
337 u16 id = ntohs(*((u16 *)the_msg));
338 u8 *(*print_fp)(void *, void *);
339
340 if (id < vec_len(am->msg_handlers) &&
341 am->msg_handlers[id]) {
342 if (trace_it)
343 vl_msg_api_trace(am, am->rx_trace, the_msg);
344
345 if (am->msg_print_flag) {
346 fformat (stdout, "[%d]: %s\n", id,
347 am->msg_names[id]);
348 print_fp = (void *)am->msg_print_handlers[id];
349 if (print_fp == 0) {
350 fformat(stdout, " [no registered print fn]\n");
351 } else {
352 (*print_fp)(the_msg, stdout);
353 }
354 }
355
356 if (do_it) {
357 if (!am->is_mp_safe[id])
358 vl_msg_api_barrier_sync();
359 (*am->msg_handlers[id])(the_msg);
360 if (!am->is_mp_safe[id])
361 vl_msg_api_barrier_release();
362 }
363 } else {
364 clib_warning("no handler for msg id %d", id);
365 }
366
367 if (free_it)
368 vl_msg_api_free(the_msg);
369}
370
371/* set to 1 if you want before/after message handler event logging */
372#define ELOG_API_MESSAGE_HANDLERS 0
373
374#if ELOG_API_MESSAGE_HANDLERS > 0
375static u32 elog_id_for_msg_name (vlib_main_t * vm, char *msg_name)
376{
377 uword * p, r;
378 static uword * h;
379 u8 *name_copy;
380
381 if (! h)
382 h = hash_create_string (0, sizeof (uword));
383
384 p = hash_get_mem (h, msg_name);
385 if (p)
386 return p[0];
387 r = elog_string (&vm->elog_main, "%s", msg_name);
388
389 name_copy = format (0, "%s%c", msg_name, 0);
390
391 hash_set_mem (h, name_copy, r);
392
393 return r;
394}
395#endif
396
397/* This is only to be called from a vlib/vnet app */
398void vl_msg_api_handler_with_vm_node (api_main_t *am,
399 void *the_msg, vlib_main_t *vm,
400 vlib_node_runtime_t *node)
401{
402 u16 id = ntohs(*((u16 *)the_msg));
403 u8 *(*handler)(void *, void *, void *);
404
405#if ELOG_API_MESSAGE_HANDLERS > 0
406 {
407 ELOG_TYPE_DECLARE (e) = {
408 .format = "api-msg: %s",
409 .format_args = "T4",
410 };
411 struct { u32 c; } * ed;
412 ed = ELOG_DATA (&vm->elog_main, e);
413 if (id < vec_len (am->msg_names))
414 ed->c = elog_id_for_msg_name (vm, am->msg_names[id]);
415 else
416 ed->c = elog_id_for_msg_name (vm, "BOGUS");
417 }
418#endif
419
420 if (id < vec_len(am->msg_handlers) &&
421 am->msg_handlers[id]) {
422 handler = (void *)am->msg_handlers[id];
423
424 if (am->rx_trace && am->rx_trace->enabled)
425 vl_msg_api_trace(am, am->rx_trace, the_msg);
426
427 if (!am->is_mp_safe[id])
428 vl_msg_api_barrier_sync();
429 (*handler)(the_msg, vm, node);
430 if (!am->is_mp_safe[id])
431 vl_msg_api_barrier_release();
432 } else {
433 clib_warning("no hander for msg id %d", id);
434 }
435 /*
436 * Special-case, so we can e.g. bounce messages off the vnet
437 * main thread without copying them...
438 */
439 if (!(am->message_bounce[id]))
440 vl_msg_api_free(the_msg);
441
442#if ELOG_API_MESSAGE_HANDLERS > 0
443 {
444 ELOG_TYPE_DECLARE (e) = {
445 .format = "api-msg-done: %s",
446 .format_args = "T4",
447 };
448 struct { u32 c; } * ed;
449 ed = ELOG_DATA (&vm->elog_main, e);
450 if (id < vec_len (am->msg_names))
451 ed->c = elog_id_for_msg_name (vm, am->msg_names[id]);
452 else
453 ed->c = elog_id_for_msg_name (vm, "BOGUS");
454 }
455#endif
456}
457
458void vl_msg_api_handler (void *the_msg)
459{
460 api_main_t *am = &api_main;
461
462 msg_handler_internal (am, the_msg,
463 (am->rx_trace
464 && am->rx_trace->enabled) /* trace_it */,
465 1 /* do_it */, 1 /* free_it */);
466}
467
468void vl_msg_api_handler_no_free (void *the_msg)
469{
470 api_main_t *am = &api_main;
471 msg_handler_internal (am, the_msg,
472 (am->rx_trace
473 && am->rx_trace->enabled) /* trace_it */,
474 1 /* do_it */, 0 /* free_it */);
475}
476
477void vl_msg_api_handler_no_trace_no_free (void *the_msg)
478{
479 api_main_t *am = &api_main;
480 msg_handler_internal (am, the_msg, 0/* trace_it */, 1 /* do_it */,
481 0 /* free_it */);
482}
483
484/*
485 * Add a trace record to the API message trace buffer, if
486 * API message tracing is enabled. Handy for adding sufficient
487 * data to the trace to reproduce autonomous state, as opposed to
488 * state downloaded via control-plane API messages. Example: the NAT
489 * application creates database entries based on packet traffic, not
490 * control-plane messages.
491 *
492 */
493void vl_msg_api_trace_only (void *the_msg)
494{
495 api_main_t *am = &api_main;
496
497 msg_handler_internal (am, the_msg,
498 (am->rx_trace
499 && am->rx_trace->enabled) /* trace_it */,
500 0 /* do_it */, 0 /* free_it */);
501}
502
503void vl_msg_api_cleanup_handler (void *the_msg)
504{
505 api_main_t *am = &api_main;
506 u16 id = ntohs(*((u16 *)the_msg));
507
508 if (PREDICT_FALSE(id >= vec_len(am->msg_cleanup_handlers))) {
509 clib_warning ("_vl_msg_id too large: %d\n", id);
510 return;
511 }
512 if (am->msg_cleanup_handlers[id])
513 (*am->msg_cleanup_handlers[id])(the_msg);
514
515 vl_msg_api_free(the_msg);
516}
517
518/*
519 * vl_msg_api_replay_handler
520 */
521void vl_msg_api_replay_handler(void *the_msg)
522{
523 api_main_t *am = &api_main;
524
525 u16 id = ntohs(*((u16 *)the_msg));
526
527 if (PREDICT_FALSE(id >= vec_len(am->msg_handlers))) {
528 clib_warning ("_vl_msg_id too large: %d\n", id);
529 return;
530 }
531 /* do NOT trace the message... */
532 if (am->msg_handlers[id])
533 (*am->msg_handlers[id])(the_msg);
534 /* do NOT free the message buffer... */
535}
536/*
537 * vl_msg_api_socket_handler
538 */
539void vl_msg_api_socket_handler(void *the_msg)
540{
541 api_main_t *am = &api_main;
542
543 msg_handler_internal (am, the_msg,
544 (am->rx_trace
545 && am->rx_trace->enabled) /* trace_it */,
546 1 /* do_it */, 0 /* free_it */);
547}
548
549#define foreach_msg_api_vector \
550_(msg_names) \
551_(msg_handlers) \
552_(msg_cleanup_handlers) \
553_(msg_endian_handlers) \
554_(msg_print_handlers) \
555_(api_trace_cfg) \
556_(message_bounce) \
557_(is_mp_safe)
558
559void vl_msg_api_config (vl_msg_api_msg_config_t *c)
560{
561 api_main_t *am = &api_main;
562
563 ASSERT(c->id > 0);
564
565#define _(a) vec_validate (am->a, c->id);
566 foreach_msg_api_vector;
567#undef _
568
569 am->msg_names[c->id] = c->name;
570 am->msg_handlers[c->id] = c->handler;
571 am->msg_cleanup_handlers[c->id] = c->cleanup;
572 am->msg_endian_handlers[c->id] = c->endian;
573 am->msg_print_handlers[c->id] = c->print;
574 am->message_bounce[c->id] = c->message_bounce;
575 am->is_mp_safe[c->id] = c->is_mp_safe;
576
577 am->api_trace_cfg[c->id].size = c->size;
578 am->api_trace_cfg[c->id].trace_enable = c->traced;
579 am->api_trace_cfg[c->id].replay_enable = c->replay;
580}
581
582/*
583 * vl_msg_api_set_handlers
584 * preserve the old API for a while
585 */
586void vl_msg_api_set_handlers(int id, char *name, void *handler, void *cleanup,
587 void *endian, void *print, int size, int traced)
588{
589 vl_msg_api_msg_config_t cfg;
590 vl_msg_api_msg_config_t *c = &cfg;
591
592 c->id = id;
593 c->name = name;
594 c->handler = handler;
595 c->cleanup = cleanup;
596 c->endian = endian;
597 c->print = print;
598 c->size = size;
599 c->traced = traced;
600 c->replay = 1;
601 c->message_bounce = 0;
602 c->is_mp_safe = 0;
603 vl_msg_api_config (c);
604}
605
606void vl_msg_api_set_cleanup_handler(int msg_id, void *fp)
607{
608 api_main_t *am = &api_main;
609 ASSERT(msg_id > 0);
610
611 vec_validate(am->msg_cleanup_handlers, msg_id);
612 am->msg_cleanup_handlers[msg_id] = fp;
613}
614
615void vl_msg_api_queue_handler(unix_shared_memory_queue_t *q)
616{
617 uword msg;
618
619 while (!unix_shared_memory_queue_sub(q, (u8 *)&msg, 0))
620 vl_msg_api_handler((void *)msg);
621}
622
623vl_api_trace_t *vl_msg_api_trace_get(api_main_t *am, vl_api_trace_which_t which)
624{
625 switch(which)
626 {
627 case VL_API_TRACE_RX:
628 return am->rx_trace;
629 case VL_API_TRACE_TX:
630 return am->tx_trace;
631 default:
632 return 0;
633 }
634}
635
636void vl_noop_handler (void *mp) { }
637
638clib_error_t *
639vl_api_init (vlib_main_t *vm)
640{
641 static u8 once;
642 api_main_t *am = &api_main;
643
644 if (once)
645 return 0;
646
647 once = 1;
648
649 am->region_name = "/unset";
650
651 return (0);
652}
653
654void vl_msg_api_custom_dump_configure (api_main_t *am) __attribute__((weak));
655void vl_msg_api_custom_dump_configure (api_main_t *am) { }
656
657VLIB_INIT_FUNCTION (vl_api_init);
658
659static void vl_msg_api_process_file (vlib_main_t *vm, u8 *filename,
660 u32 first_index, u32 last_index,
661 vl_api_replay_t which)
662{
663 vl_api_trace_file_header_t * hp;
664 int i, fd;
665 struct stat statb;
666 size_t file_size;
667 u8 *msg;
668 u8 endian_swap_needed = 0;
669 api_main_t * am = &api_main;
670 static u8 *tmpbuf;
671 u32 nitems;
672 void **saved_print_handlers = 0;
673
674 fd = open ((char *) filename, O_RDONLY);
675
676 if (fd < 0) {
677 vlib_cli_output (vm, "Couldn't open %s\n", filename);
678 return;
679 }
680
681 if (fstat(fd, &statb) < 0) {
682 vlib_cli_output (vm, "Couldn't stat %s\n", filename);
683 return;
684 }
685
686 if (! (statb.st_mode & S_IFREG) || (statb.st_size < sizeof (*hp))) {
687 vlib_cli_output (vm, "File not plausible: %s\n", filename);
688 return;
689 }
690
691 file_size = statb.st_size;
692 file_size = (file_size + 4095) & ~(4096);
693
694 hp = mmap (0, file_size, PROT_READ, MAP_PRIVATE, fd, 0);
695
696 if (hp == (vl_api_trace_file_header_t *)MAP_FAILED) {
697 vlib_cli_output (vm, "mmap failed: %s\n", filename);
698 close(fd);
699 return;
700 }
701 close(fd);
702
703 if ((clib_arch_is_little_endian && hp->endian == VL_API_BIG_ENDIAN)
704 || (clib_arch_is_big_endian && hp->endian == VL_API_LITTLE_ENDIAN))
705 endian_swap_needed = 1;
706
707 if (endian_swap_needed)
708 nitems = ntohl(hp->nitems);
709 else
710 nitems = hp->nitems;
711
712 if (last_index == (u32) ~0) {
713 last_index = nitems - 1;
714 }
715
716 if (first_index >= nitems || last_index >= nitems) {
717 vlib_cli_output (vm, "Range (%d, %d) outside file range (0, %d)\n",
718 first_index, last_index, nitems-1);
719 return;
720 }
721 if (hp->wrapped)
722 vlib_cli_output (vm,
723 "Note: wrapped/incomplete trace, results may vary\n");
724
725 if (which == CUSTOM_DUMP) {
726 saved_print_handlers = (void **) vec_dup (am->msg_print_handlers);
727 vl_msg_api_custom_dump_configure (am);
728 }
729
730
731 msg = (u8 *)(hp+1);
732
733 for (i = 0; i < first_index; i++) {
734 trace_cfg_t *cfgp;
735 int size;
736 u16 msg_id;
737
738 if (clib_arch_is_little_endian)
739 msg_id = ntohs(*((u16 *)msg));
740 else
741 msg_id = *((u16 *)msg);
742
743 cfgp = am->api_trace_cfg + msg_id;
744 if (!cfgp) {
745 vlib_cli_output (vm, "Ugh: msg id %d no trace config\n", msg_id);
746 return;
747 }
748 size = cfgp->size;
749 msg += size;
750 }
751
752 for (; i <= last_index; i++) {
753 trace_cfg_t *cfgp;
754 u16 *msg_idp;
755 u16 msg_id;
756 int size;
757
758 if (which == DUMP)
759 vlib_cli_output (vm, "---------- trace %d -----------\n", i);
760
761 if (clib_arch_is_little_endian)
762 msg_id = ntohs(*((u16 *)msg));
763 else
764 msg_id = *((u16 *)msg);
765
766 cfgp = am->api_trace_cfg + msg_id;
767 if (!cfgp) {
768 vlib_cli_output (vm, "Ugh: msg id %d no trace config\n", msg_id);
769 return;
770 }
771 size = cfgp->size;
772
773 /* Copy the buffer (from the read-only mmap'ed file) */
774 vec_validate (tmpbuf, size-1 + sizeof(uword));
775 memcpy (tmpbuf+sizeof(uword), msg, size);
776 memset (tmpbuf, 0xf, sizeof(uword));
777
778 /*
779 * Endian swap if needed. All msg data is supposed to be
780 * in network byte order. All msg handlers are supposed to
781 * know that. The generic message dumpers don't know that.
782 * One could fix apigen, I suppose.
783 */
784 if ((which == DUMP && clib_arch_is_little_endian)
785 || endian_swap_needed) {
786 void (*endian_fp)(void *);
787 if (msg_id >= vec_len (am->msg_endian_handlers)
788 || (am->msg_endian_handlers[msg_id] == 0)) {
789 vlib_cli_output (vm, "Ugh: msg id %d no endian swap\n", msg_id);
790 return;
791 }
792 endian_fp = am->msg_endian_handlers[msg_id];
793 (*endian_fp)(tmpbuf+sizeof(uword));
794 }
795
796 /* msg_id always in network byte order */
797 if (clib_arch_is_little_endian) {
798 msg_idp = (u16 *)(tmpbuf+sizeof(uword));
799 *msg_idp = msg_id;
800 }
801
802 switch (which) {
803 case CUSTOM_DUMP:
804 case DUMP:
805 if (msg_id < vec_len(am->msg_print_handlers) &&
806 am->msg_print_handlers [msg_id]) {
807 u8 *(*print_fp)(void *, void *);
808
809 print_fp = (void *)am->msg_print_handlers[msg_id];
810 (*print_fp)(tmpbuf+sizeof(uword), vm);
811 } else {
812 vlib_cli_output (vm, "Skipping msg id %d: no print fcn\n",
813 msg_id);
814 break;
815 }
816 break;
817
818 case INITIALIZERS:
819 if (msg_id < vec_len(am->msg_print_handlers) &&
820 am->msg_print_handlers [msg_id]) {
821 u8 * s;
822 int j;
823 u8 *(*print_fp)(void *, void *);
824
825 print_fp = (void *)am->msg_print_handlers[msg_id];
826
827 vlib_cli_output (vm, "/*");
828
829 (*print_fp)(tmpbuf+sizeof(uword), vm);
830 vlib_cli_output (vm, "*/\n");
831
832 s = format (0, "static u8 * vl_api_%s_%d[%d] = {",
833 am->msg_names[msg_id], i,
834 am->api_trace_cfg[msg_id].size);
835
836 for (j = 0; j < am->api_trace_cfg[msg_id].size; j++) {
837 if ((j & 7) == 0)
838 s = format (s, "\n ");
839 s = format (s, "0x%02x,", tmpbuf[sizeof(uword)+j]);
840 }
841 s = format (s, "\n};\n%c", 0);
842 vlib_cli_output (vm, (char *)s);
843 vec_free(s);
844 }
845 break;
846
847 case REPLAY:
848 if (msg_id < vec_len(am->msg_print_handlers) &&
849 am->msg_print_handlers [msg_id] && cfgp->replay_enable) {
850 void (*handler)(void *);
851
852 handler = (void *)am->msg_handlers[msg_id];
853
854 if (!am->is_mp_safe[msg_id])
855 vl_msg_api_barrier_sync();
856 (*handler)(tmpbuf+sizeof(uword));
857 if (!am->is_mp_safe[msg_id])
858 vl_msg_api_barrier_release();
859 } else {
860 if (cfgp->replay_enable)
861 vlib_cli_output (vm, "Skipping msg id %d: no handler\n",
862 msg_id);
863 break;
864 }
865 break;
866 }
867
868 _vec_len(tmpbuf) = 0;
869 msg += size;
870 }
871
872 if (saved_print_handlers) {
873 memcpy (am->msg_print_handlers, saved_print_handlers,
874 vec_len(am->msg_print_handlers) * sizeof (void *));
875 vec_free (saved_print_handlers);
876 }
877
878 munmap (hp, file_size);
879}
880
881u8 * format_vl_msg_api_trace_status (u8 * s, va_list * args)
882{
883 api_main_t * am = va_arg (*args, api_main_t *);
884 vl_api_trace_which_t which = va_arg (*args, vl_api_trace_which_t);
885 vl_api_trace_t *tp;
886 char *trace_name;
887
888 switch(which)
889 {
890 case VL_API_TRACE_TX:
891 tp = am->tx_trace;
892 trace_name = "TX trace";
893 break;
894
895 case VL_API_TRACE_RX:
896 tp = am->rx_trace;
897 trace_name = "RX trace";
898 break;
899
900 default:
901 abort();
902 }
903
904 if (tp == 0) {
905 s = format (s, "%s: not yet configured.\n", trace_name);
906 return s;
907 }
908
909 s = format (s, "%s: used %d of %d items, %s enabled, %s wrapped\n",
910 trace_name, vec_len (tp->traces), tp->nitems,
911 tp->enabled ? "is" : "is not",
912 tp->wrapped ? "has" : "has not");
913 return s;
914}
915
916static u8 post_mortem_dump_enabled;
917
918static clib_error_t *
919api_trace_command_fn (vlib_main_t * vm,
920 unformat_input_t * input,
921 vlib_cli_command_t * cmd)
922{
923 u32 nitems = 256<<10;
924 api_main_t * am = &api_main;
925 vl_api_trace_which_t which = VL_API_TRACE_RX;
926 u8 *filename;
927 u32 first = 0;
928 u32 last = (u32)~0;
929 FILE *fp;
930 int rv;
931
932 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
933 if (unformat (input, "on") || unformat (input, "enable")) {
934 if (unformat (input, "nitems %d", &nitems))
935 ;
936 vl_msg_api_trace_configure (am, which, nitems);
937 vl_msg_api_trace_onoff (am, which, 1 /* on */);
938 } else if (unformat (input, "off")) {
939 vl_msg_api_trace_onoff (am, which, 0);
940 } else if (unformat (input, "save %s", &filename)) {
941 u8 * chroot_filename;
942 if (strstr((char *)filename, "..")
943 || index((char *)filename, '/'))
944 {
945 vlib_cli_output (vm, "illegal characters in filename '%s'",
946 filename);
947 return 0;
948 }
949
950 chroot_filename = format (0, "/tmp/%s%c", filename, 0);
951
952 vec_free(filename);
953
954 fp = fopen ((char *)chroot_filename, "w");
955 if (fp == NULL) {
956 vlib_cli_output (vm, "Couldn't create %s\n", chroot_filename);
957 return 0;
958 }
959 rv = vl_msg_api_trace_save (am, which, fp);
960 fclose (fp);
961 if (rv < 0)
962 vlib_cli_output (vm, "ERROR: %d", rv);
963 else
964 vlib_cli_output (vm, "API trace saved to %s\n",
965 chroot_filename);
966 vec_free (chroot_filename);
967 } else if (unformat (input, "dump %s", &filename)) {
968 vl_msg_api_process_file (vm, filename, first, last, DUMP);
969 } else if (unformat (input, "custom-dump %s", &filename)) {
970 vl_msg_api_process_file (vm, filename, first, last, CUSTOM_DUMP);
971 } else if (unformat (input, "replay %s", &filename)) {
972 vl_msg_api_process_file (vm, filename, first, last, REPLAY);
973 } else if (unformat (input, "initializers %s", &filename)) {
974 vl_msg_api_process_file (vm, filename, first, last, INITIALIZERS);
975 } else if (unformat (input, "tx")) {
976 which = VL_API_TRACE_TX;
977 } else if (unformat (input, "first %d", &first)) {
978 ;
979 } else if (unformat (input, "last %d", &last)) {
980 ;
981 } else if (unformat (input, "status")) {
982 vlib_cli_output (vm, "%U", format_vl_msg_api_trace_status,
983 am, which);
984 } else if (unformat (input, "free")) {
985 vl_msg_api_trace_onoff (am, which, 0);
986 vl_msg_api_trace_free (am, which);
987 } else if (unformat (input, "post-mortem-on"))
988 post_mortem_dump_enabled = 1;
989 else if (unformat (input, "post-mortem-off"))
990 post_mortem_dump_enabled = 0;
991 else
992 return clib_error_return (0, "unknown input `%U'",
993 format_unformat_error, input);
994 }
995 return 0;
996}
997
998VLIB_CLI_COMMAND (api_trace_command, static) = {
999 .path = "api trace",
1000 .short_help =
1001 "api trace [on|off][dump|save|replay <file>][status][free][post-mortem-on]",
1002 .function = api_trace_command_fn,
1003};
1004
1005static clib_error_t *
1006api_config_fn (vlib_main_t * vm, unformat_input_t * input)
1007{
1008 u32 nitems = 256<<10;
1009 vl_api_trace_which_t which = VL_API_TRACE_RX;
1010 api_main_t * am = &api_main;
1011
1012 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
1013 if (unformat (input, "on") || unformat (input, "enable")) {
1014 if (unformat (input, "nitems %d", &nitems))
1015 ;
1016 vl_msg_api_trace_configure (am, which, nitems);
1017 vl_msg_api_trace_onoff (am, which, 1 /* on */);
1018 post_mortem_dump_enabled = 1;
1019 } else
1020 return clib_error_return (0, "unknown input `%U'",
1021 format_unformat_error, input);
1022 }
1023 return 0;
1024}
1025
1026VLIB_CONFIG_FUNCTION (api_config_fn, "api-trace");
1027
1028void vl_msg_api_post_mortem_dump (void)
1029{
1030 api_main_t * am = &api_main;
1031 FILE *fp;
1032 char filename[64];
1033 int rv;
1034
1035 if (post_mortem_dump_enabled == 0)
1036 return;
1037
1038 snprintf (filename, sizeof(filename), "/tmp/api_post_mortem.%d",
1039 getpid());
1040
1041 fp = fopen (filename, "w");
1042 if (fp == NULL) {
1043 rv = write (2, "Couldn't create ", 16);
1044 rv = write (2, filename, strlen(filename));
1045 rv = write (2, "\n", 1);
1046 return;
1047 }
1048 rv = vl_msg_api_trace_save (am, VL_API_TRACE_RX, fp);
1049 fclose (fp);
1050 if (rv < 0) {
1051 rv = write (2, "Failed to save post-mortem API trace to ", 40);
1052 rv = write (2, filename, strlen(filename));
1053 rv = write (2, "\n", 1);
1054 }
1055
1056}
1057
1058/* Layered message handling support */
1059
1060void vl_msg_api_register_pd_handler (void *fp, u16 msg_id_host_byte_order)
1061{
1062 api_main_t * am = &api_main;
1063
1064 /* Mild idiot proofing */
1065 if (msg_id_host_byte_order > 10000)
1066 clib_warning ("msg_id_host_byte_order endian issue? %d arg vs %d",
1067 msg_id_host_byte_order,
1068 clib_net_to_host_u16 (msg_id_host_byte_order));
1069 vec_validate (am->pd_msg_handlers, msg_id_host_byte_order);
1070 am->pd_msg_handlers[msg_id_host_byte_order] = fp;
1071}
1072
1073int vl_msg_api_pd_handler (void *mp, int rv)
1074{
1075 api_main_t * am = &api_main;
1076 int (*fp)(void *, int);
1077 u16 msg_id;
1078
1079 if (clib_arch_is_little_endian)
1080 msg_id = clib_net_to_host_u16(*((u16 *)mp));
1081 else
1082 msg_id = *((u16 *)mp);
1083
1084 if (msg_id >= vec_len (am->pd_msg_handlers)
1085 || am->pd_msg_handlers[msg_id] == 0)
1086 return rv;
1087
1088 fp = am->pd_msg_handlers [msg_id];
1089 rv = (*fp)(mp, rv);
1090 return rv;
1091}
1092
1093void vl_msg_api_set_first_available_msg_id (u16 first_avail)
1094{
1095 api_main_t * am = &api_main;
1096
1097 am->first_available_msg_id = first_avail;
1098}
1099
1100u16 vl_msg_api_get_msg_ids (char * name, int n)
1101{
1102 api_main_t * am = &api_main;
1103 u8 * name_copy;
1104 vl_api_msg_range_t * rp;
1105 uword * p;
1106 u16 rv;
1107
1108 if (am->msg_range_by_name == 0)
1109 am->msg_range_by_name = hash_create_string (0, sizeof(uword));
1110
1111 name_copy = format (0, "%s%c", name, 0);
1112
1113 p = hash_get_mem (am->msg_range_by_name, name_copy);
1114 if (p) {
1115 clib_warning ("WARNING: duplicate message range registration for '%s'",
1116 name_copy);
1117 vec_free(name_copy);
1118 return ((u16) ~0);
1119 }
1120
1121 if (n < 0 || n > 1024) {
1122 clib_warning
1123 ("WARNING: bad number of message-IDs (%d) requested by '%s'",
1124 n, name_copy);
1125 vec_free(name_copy);
1126 return ((u16) ~0);
1127 }
1128
1129 vec_add2 (am->msg_ranges, rp, 1);
1130
1131 rv = rp->first_msg_id = am->first_available_msg_id;
1132 am->first_available_msg_id += n;
1133 rp->last_msg_id = am->first_available_msg_id - 1;
1134 rp->name = name_copy;
1135
1136 hash_set_mem (am->msg_range_by_name, name_copy, rp - am->msg_ranges);
1137
1138 return rv;
1139}