api: API trace improvements 52/32652/27
authorFilip Tehlar <ftehlar@cisco.com>
Fri, 23 Jul 2021 08:51:10 +0000 (08:51 +0000)
committerFilip Tehlar <ftehlar@cisco.com>
Tue, 28 Sep 2021 16:06:19 +0000 (16:06 +0000)
Type: improvement

 * add support for JSON format in API trace
 * add ability to replay JSON API trace in both VPP and VAT2
 * use CRC for backward compatibility check during JSON API replay
 * fix API trace CLI (and remove duplicits)
 * remove custom dump
 * remove vppapitrace.py
 * update docs accordingly

Change-Id: I5294f68bebe6cbe738630f457f3a87720e06486b
Signed-off-by: Filip Tehlar <ftehlar@cisco.com>
Signed-off-by: Ole Troan <ot@cisco.com>
34 files changed:
docs/about.rst
docs/gettingstarted/developers/binary_api_support.rst
docs/reference/cmdreference/trace/trace.rst
src/plugins/hs_apps/sapi/vpp_echo_bapi.c
src/plugins/mactime/mactime_top.c
src/plugins/tracedump/tracedump_test.c
src/tools/vppapigen/vppapigen_c.py
src/tools/vppapitrace/vppapitrace [deleted symlink]
src/tools/vppapitrace/vppapitrace.py [deleted file]
src/vat/api_format.c
src/vat2/CMakeLists.txt
src/vat2/jsonconvert.h [deleted file]
src/vat2/main.c
src/vat2/test/vat2_test.c
src/vcl/vcl_bapi.c
src/vlibapi/api.h
src/vlibapi/api_common.h
src/vlibapi/api_shared.c
src/vlibmemory/CMakeLists.txt
src/vlibmemory/memclnt.api
src/vlibmemory/memclnt_api.c
src/vlibmemory/memory_api.c
src/vlibmemory/memory_client.c
src/vlibmemory/socket_api.c
src/vlibmemory/socket_client.c
src/vlibmemory/vlib_api_cli.c
src/vnet/srmpls/sr_mpls_api.c
src/vpp-api/client/client.c
src/vppinfra/CMakeLists.txt
src/vppinfra/cJSON.c
src/vppinfra/cJSON.h
src/vppinfra/jsonformat.c [moved from src/vat2/jsonconvert.c with 95% similarity]
src/vppinfra/jsonformat.h [new file with mode: 0644]
test/test_api_trace.py [new file with mode: 0644]

index 7f9a88d..dc04d09 100644 (file)
@@ -4,6 +4,6 @@
 About
 =====
 
-**VPP Version:** 21.06-rc0~304-ga73e7568c
+**VPP Version:** 21.10-rc0~204-g13e841847
 
-**Built on:** Fri Feb 19 23:40:45 GMT 2021
+**Built on:** Thu Jul 22 23:44:06 GMT 2021
index 732ce97..f93bbc2 100644 (file)
@@ -206,11 +206,10 @@ out the set of plugins:
 
 .. code-block:: console
 
-   DBGvpp# api trace custom-dump /tmp/api_trace
-   vl_api_trace_plugin_msg_ids: abf_54307ba2 first 846 last 855
-   vl_api_trace_plugin_msg_ids: acl_0d7265b0 first 856 last 893
-   vl_api_trace_plugin_msg_ids: cdp_8f707b96 first 894 last 895
-   vl_api_trace_plugin_msg_ids: flowprobe_f2f0286c first 898 last 901
+   DBGvpp# api trace dump /tmp/api_trace
+   vl_api_trace_plugin_msg_ids: arp_cfdf7292 first 49 last 56
+   vl_api_trace_plugin_msg_ids: ip6_nd_ac628462 first 57 last 69
+   vl_api_trace_plugin_msg_ids: rd_cp_8a996e86 first 70 last 71
    <etc>
 
 Here, we see the "abf," "acl," "cdp," and "flowprobe" plugins. Use the
@@ -239,7 +238,7 @@ __________________________
 Along the same lines, it may be necessary to manufacture [simulated]
 physical interfaces so that an API trace will replay correctly. "show
 interface" on the trace origin system can help. An API trace
-"custom-dump" as shown above may make it obvious how many loopback
+dump as shown above may make it obvious how many loopback
 interfaces to create. If you see vhost interfaces being created and
 then configured, the first such configuration message in the trace
 will tell you how many physical interfaces were involved.
index 9d120ed..927342f 100644 (file)
@@ -1,58 +1,59 @@
-.. _interface:\r
-\r
-.. toctree::\r
-\r
-.. note:: For a complete list of CLI Debug commands refer to the Debug CLI section of the `Source Code Documents <https://docs.fd.io/vpp/18.07/clicmd.html>`_ .\r
-\r
-\r
-API Trace \r
-===========\r
-\r
-Summary/Usage\r
---------------\r
-\r
-api trace [on|off][first <*n*>][last <*n*>][status][free][post-mortem-on][dump|custom-dump|save|replay <*file*>]\r
-\r
-Description\r
-------------\r
-\r
-Display, replay, or save a binary API trace.\r
-\r
-Declaration and Implementation\r
--------------------------------\r
-\r
-**Declaration:** api_trace_command (src/vlibmemory/vlib_api_cli.c line 783)\r
-\r
-**Implementation:** api_trace_command_fn\r
-\r
-Clear Trace\r
-=============\r
-\r
-Summary/Usage\r
---------------\r
-Clear trace buffer and free memory.\r
-Declaration and implementation\r
-\r
-**Declaration:** clear_trace_cli (src/vlib/trace.c line 519)\r
-\r
-**Implementation:** cli_clear_trace_buffer\r
-\r
-Show Trace\r
-===========\r
-\r
-`Show Trace <../show/show.html#show-trace>`_\r
-\r
-Trace Add\r
-===========\r
-\r
-Summary/Usage\r
---------------\r
-\r
-Trace given number of packets.\r
-\r
-Declaration and Implementation\r
--------------------------------\r
-\r
-**Declaration:** add_trace_cli (src/vlib/trace.c line 405)\r
-\r
-**Implementation:** cli_add_trace_buffer
\ No newline at end of file
+.. _interface:
+
+.. toctree::
+
+.. note:: For a complete list of CLI Debug commands refer to the Debug CLI section of the `Source Code Documents <https://docs.fd.io/vpp/18.07/clicmd.html>`_ .
+
+
+API Trace
+===========
+
+Summary/Usage
+--------------
+
+api trace [tx][on|off][first <n>][last <n>][status][free]
+       [post-mortem-on][dump|dump-file|dump-json|save|tojson|save-json|replay <file>][nitems <n>][initializers <file>]
+
+Description
+------------
+
+Display, replay, or save a binary API trace.
+
+Declaration and Implementation
+-------------------------------
+
+**Declaration:** api_trace_command (src/vlibmemory/vlib_api_cli.c line 783)
+
+**Implementation:** api_trace_command_fn
+
+Clear Trace
+=============
+
+Summary/Usage
+--------------
+Clear trace buffer and free memory.
+Declaration and implementation
+
+**Declaration:** clear_trace_cli (src/vlib/trace.c line 519)
+
+**Implementation:** cli_clear_trace_buffer
+
+Show Trace
+===========
+
+`Show Trace <../show/show.html#show-trace>`_
+
+Trace Add
+===========
+
+Summary/Usage
+--------------
+
+Trace given number of packets.
+
+Declaration and Implementation
+-------------------------------
+
+**Declaration:** add_trace_cli (src/vlib/trace.c line 405)
+
+**Implementation:** cli_add_trace_buffer
index 38fb522..30f3b78 100644 (file)
@@ -569,10 +569,11 @@ echo_api_hookup (echo_main_t * em)
     return;
 
 #define _(N, n)                                                               \
-  vl_msg_api_set_handlers (REPLY_MSG_ID_BASE + VL_API_##N, #n,                \
-                          vl_api_##n##_t_handler, vl_noop_handler,           \
-                          vl_api_##n##_t_endian, vl_api_##n##_t_print,       \
-                          sizeof (vl_api_##n##_t), 1);
+  vl_msg_api_set_handlers (                                                   \
+    REPLY_MSG_ID_BASE + VL_API_##N, #n, vl_api_##n##_t_handler,               \
+    vl_noop_handler, vl_api_##n##_t_endian, vl_api_##n##_t_print,             \
+    sizeof (vl_api_##n##_t), 1, vl_api_##n##_t_print_json,                    \
+    vl_api_##n##_t_tojson, vl_api_##n##_t_fromjson);
   foreach_quic_echo_msg;
 #undef _
 }
index 72d1964..ed4c734 100644 (file)
@@ -143,14 +143,12 @@ connect_to_vpp (char *name)
   if (mm->msg_id_base == (u16) ~ 0)
     return -1;
 
-#define _(N,n)                                                  \
-    vl_msg_api_set_handlers((VL_API_##N + mm->msg_id_base),     \
-                           #n,                                 \
-                           vl_api_##n##_t_handler,              \
-                           vl_noop_handler,                     \
-                           vl_api_##n##_t_endian,               \
-                           vl_api_##n##_t_print,                \
-                           sizeof(vl_api_##n##_t), 1);
+#define _(N, n)                                                               \
+  vl_msg_api_set_handlers ((VL_API_##N + mm->msg_id_base), #n,                \
+                          vl_api_##n##_t_handler, vl_noop_handler,           \
+                          vl_api_##n##_t_endian, vl_api_##n##_t_print,       \
+                          sizeof (vl_api_##n##_t), 1, vl_api_##n##_t_tojson, \
+                          vl_api_##n##_t_fromjson);
   foreach_mactime_api_msg;
 #undef _
 
index 3bf50ef..05191a4 100644 (file)
@@ -246,12 +246,12 @@ api_trace_clear_capture (vat_main_t * vam)
 void
 manual_setup_message_id_table (vat_main_t * vam)
 {
-  vl_msg_api_set_handlers (VL_API_TRACE_DETAILS
-                          + tracedump_test_main.msg_id_base, "trace_details",
-                          vl_api_trace_details_t_handler, vl_noop_handler,
-                          vl_api_trace_details_t_endian,
-                          vl_api_trace_details_t_print,
-                          sizeof (vl_api_trace_details_t), 1);
+  vl_msg_api_set_handlers (
+    VL_API_TRACE_DETAILS + tracedump_test_main.msg_id_base, "trace_details",
+    vl_api_trace_details_t_handler, vl_noop_handler,
+    vl_api_trace_details_t_endian, vl_api_trace_details_t_print,
+    sizeof (vl_api_trace_details_t), 1, vl_api_trace_details_t_print_json,
+    vl_api_trace_details_t_tojson, vl_api_trace_details_t_fromjson);
 }
 
 #define VL_API_LOCAL_SETUP_MESSAGE_ID_TABLE manual_setup_message_id_table
index db684ad..ae47625 100644 (file)
@@ -66,7 +66,10 @@ class ToJSON():
         write('#ifndef included_{}_api_tojson_h\n'.format(self.module))
         write('#define included_{}_api_tojson_h\n'.format(self.module))
         write('#include <vppinfra/cJSON.h>\n\n')
-        write('#include <vat2/jsonconvert.h>\n\n')
+        write('#include <vppinfra/jsonformat.h>\n\n')
+        if self.module == 'interface_types':
+            write('#define vl_printfun\n')
+            write('#include <vnet/interface_types.api.h>\n\n')
 
     def footer(self):
         '''Output the bottom boilerplate.'''
@@ -231,6 +234,8 @@ class ToJSON():
         write('    cJSON *o = cJSON_CreateObject();\n')
         write('    cJSON_AddStringToObject(o, "_msgname", "{}");\n'
               .format(o.name))
+        write('    cJSON_AddStringToObject(o, "_crc", "{crc:08x}");\n'
+              .format(crc=o.crc))
 
         for t in o.block:
             self._dispatch[t.type](self, t)
@@ -312,7 +317,7 @@ class FromJSON():
         write('#ifndef included_{}_api_fromjson_h\n'.format(self.module))
         write('#define included_{}_api_fromjson_h\n'.format(self.module))
         write('#include <vppinfra/cJSON.h>\n\n')
-        write('#include <vat2/jsonconvert.h>\n\n')
+        write('#include <vppinfra/jsonformat.h>\n\n')
         write('#pragma GCC diagnostic ignored "-Wunused-label"\n')
 
     def is_base_type(self, t):
@@ -338,7 +343,7 @@ class FromJSON():
         if o.modern_vla:
             write('    char *p = cJSON_GetStringValue(item);\n')
             write('    size_t plen = strlen(p);\n')
-            write('    {msgvar} = realloc({msgvar}, {msgsize} + plen);\n'
+            write('    {msgvar} = cJSON_realloc({msgvar}, {msgsize} + plen, {msgsize});\n'
                   .format(msgvar=msgvar, msgsize=msgsize))
             write('    if ({msgvar} == 0) goto error;\n'.format(msgvar=msgvar))
             write('    vl_api_c_string_to_api_string(p, (void *){msgvar} + '
@@ -393,7 +398,7 @@ class FromJSON():
         cJSON *array = cJSON_GetObjectItem(o, "{n}");
         int size = cJSON_GetArraySize(array);
         {lfield} = size;
-        {realloc} = realloc({realloc}, {msgsize} + sizeof({t}) * size);
+        {realloc} = cJSON_realloc({realloc}, {msgsize} + sizeof({t}) * size, {msgsize});
         {t} *d = (void *){realloc} + {msgsize};
         {msgsize} += sizeof({t}) * size;
         for (i = 0; i < size; i++) {{
@@ -419,8 +424,8 @@ class FromJSON():
                 write('    if (!s) goto error;\n')
                 write('    {} = vec_len(s);\n'.format(lfield))
 
-                write('    {realloc} = realloc({realloc}, {msgsize} + '
-                      'vec_len(s));\n'.format(msgvar=msgvar, msgsize=msgsize, realloc=realloc))
+                write('    {realloc} = cJSON_realloc({realloc}, {msgsize} + '
+                      'vec_len(s), {msgsize});\n'.format(msgvar=msgvar, msgsize=msgsize, realloc=realloc))
                 write('    memcpy((void *){realloc} + {msgsize}, s, '
                       'vec_len(s));\n'.format(realloc=realloc, msgsize=msgsize))
                 write('    {msgsize} += vec_len(s);\n'.format(msgsize=msgsize))
@@ -553,7 +558,7 @@ class FromJSON():
         write('    cJSON *item __attribute__ ((unused));\n')
         write('    u8 *s __attribute__ ((unused));\n')
         write('    int l = sizeof(vl_api_{}_t);\n'.format(o.name))
-        write('    vl_api_{}_t *a = malloc(l);\n'.format(o.name))
+        write('    vl_api_{}_t *a = cJSON_malloc(l);\n'.format(o.name))
         write('\n')
 
         for t in o.block:
@@ -573,7 +578,7 @@ class FromJSON():
 
         if error:
             write('\n  error:\n')
-            write('    free(a);\n')
+            write('    cJSON_free(a);\n')
             write('    return 0;\n')
         write('}\n')
 
@@ -928,10 +933,13 @@ def printfun(objs, stream, modulename):
 #define _uword_cast long
 #endif
 
+#include "{module}.api_tojson.h"
+#include "{module}.api_fromjson.h"
+
 '''
 
     signature = '''\
-static inline void *vl_api_{name}_t_print (vl_api_{name}_t *a, void *handle)
+static inline void *vl_api_{name}_t_print{suffix} (vl_api_{name}_t *a, void *handle)
 {{
     u8 *s = 0;
     u32 indent __attribute__((unused)) = 2;
@@ -946,7 +954,7 @@ static inline void *vl_api_{name}_t_print (vl_api_{name}_t *a, void *handle)
         if t.manual_print:
             write("/***** manual: vl_api_%s_t_print  *****/\n\n" % t.name)
             continue
-        write(signature.format(name=t.name))
+        write(signature.format(name=t.name, suffix=''))
         write('    /* Message definition: vl_api_{}_t: */\n'.format(t.name))
         write("    s = format(s, \"vl_api_%s_t:\");\n" % t.name)
         for o in t.block:
@@ -957,6 +965,16 @@ static inline void *vl_api_{name}_t_print (vl_api_{name}_t *a, void *handle)
         write('    return handle;\n')
         write('}\n\n')
 
+        write(signature.format(name=t.name, suffix='_json'))
+        write('    cJSON * o = vl_api_{}_t_tojson(a);\n'.format(t.name))
+        write('    (void)s;\n');
+        write('    char *out = cJSON_Print(o);\n')
+        write('    vl_print(handle, out);\n');
+        write('    cJSON_Delete(o);\n')
+        write('    cJSON_free(out);\n');
+        write('    return handle;\n')
+        write('}\n\n');
+
     write("\n#endif")
     write("\n#endif /* vl_printfun */\n")
 
@@ -1346,6 +1364,9 @@ def generate_c_boilerplate(services, defines, counters, file_crc,
               '   .print = vl_api_{n}_t_print,\n'
               '   .traced = 1,\n'
               '   .replay = 1,\n'
+              '   .print_json = vl_api_{n}_t_print_json,\n'
+              '   .tojson = vl_api_{n}_t_tojson,\n'
+              '   .fromjson = vl_api_{n}_t_fromjson,\n'
               '   .is_autoendian = {auto}}};\n'
               .format(n=s.caller, ID=s.caller.upper(),
                       auto=d.autoendian))
@@ -1359,6 +1380,11 @@ def generate_c_boilerplate(services, defines, counters, file_crc,
                   '  .cleanup = vl_noop_handler,\n'
                   '  .endian = vl_api_{n}_t_endian,\n'
                   '  .print = vl_api_{n}_t_print,\n'
+                  '  .traced = 1,\n'
+                  '  .replay = 1,\n'
+                  '  .print_json = vl_api_{n}_t_print_json,\n'
+                  '  .tojson = vl_api_{n}_t_tojson,\n'
+                  '  .fromjson = vl_api_{n}_t_fromjson,\n'
                   '  .is_autoendian = {auto}}};\n'
                   .format(n=s.reply, ID=s.reply.upper(),
                           auto=d.autoendian))
@@ -1455,7 +1481,10 @@ def generate_c_test_boilerplate(services, defines, file_crc, module, plugin,
               '                           vl_noop_handler,\n'
               '                           vl_api_{n}_t_endian, '
               '                           vl_api_{n}_t_print,\n'
-              '                           sizeof(vl_api_{n}_t), 1);\n'
+              '                           sizeof(vl_api_{n}_t), 1,\n'
+              '                           vl_api_{n}_t_print_json,\n'
+              '                           vl_api_{n}_t_tojson,\n'
+              '                           vl_api_{n}_t_fromjson);\n'
               .format(n=s.reply, ID=s.reply.upper()))
         write('   hash_set_mem (vam->function_by_name, "{n}", api_{n});\n'
               .format(n=s.caller))
@@ -1474,7 +1503,10 @@ def generate_c_test_boilerplate(services, defines, file_crc, module, plugin,
                   '                           vl_noop_handler,\n'
                   '                           vl_api_{n}_t_endian, '
                   '                           vl_api_{n}_t_print,\n'
-                  '                           sizeof(vl_api_{n}_t), 1);\n'
+                  '                           sizeof(vl_api_{n}_t), 1,\n'
+                  '                           vl_api_{n}_t_print_json,\n'
+                  '                           vl_api_{n}_t_tojson,\n'
+                  '                           vl_api_{n}_t_fromjson);\n'
                   .format(n=e, ID=e.upper()))
 
     write('}\n')
@@ -1526,7 +1558,7 @@ api_{n} (cJSON *o)
   mp->_vl_msg_id = vac_get_msg_index(VL_API_{N}_CRC);
   vl_api_{n}_t_endian(mp);
   vac_write((char *)mp, len);
-  free(mp);
+  cJSON_free(mp);
 
   /* Read reply */
   char *p;
@@ -1559,7 +1591,7 @@ api_{n} (cJSON *o)
   mp->_vl_msg_id = msg_id;
   vl_api_{n}_t_endian(mp);
   vac_write((char *)mp, len);
-  free(mp);
+  cJSON_free(mp);
 
   vat2_control_ping(123); // FIX CONTEXT
   cJSON *reply = cJSON_CreateArray();
@@ -1615,7 +1647,7 @@ api_{n} (cJSON *o)
 
   vl_api_{n}_t_endian(mp);
   vac_write((char *)mp, len);
-  free(mp);
+  cJSON_free(mp);
 
   cJSON *reply = cJSON_CreateArray();
 
@@ -1713,13 +1745,16 @@ def generate_c_test2_boilerplate(services, defines, module, stream):
             continue
         c_test_api_service(s, s.stream, stream)
 
-    write('void vat2_register_function(char *, cJSON * (*)(cJSON *), cJSON * (*)(void *));\n')
+    write('void vat2_register_function(char *, cJSON * (*)(cJSON *), cJSON * (*)(void *), u32);\n')
     # write('__attribute__((constructor))')
     write('clib_error_t *\n')
     write('vat2_register_plugin (void) {\n')
     for s in services:
-        write('   vat2_register_function("{n}", api_{n}, (cJSON * (*)(void *))vl_api_{n}_t_tojson);\n'
-              .format(n=s.caller))
+        if s.reply not in define_hash:
+            continue
+        crc = define_hash[s.caller].crc
+        write('   vat2_register_function("{n}", api_{n}, (cJSON * (*)(void *))vl_api_{n}_t_tojson, 0x{crc:08x});\n'
+              .format(n=s.caller, crc=crc))
     write('   return 0;\n')
     write('}\n')
 
diff --git a/src/tools/vppapitrace/vppapitrace b/src/tools/vppapitrace/vppapitrace
deleted file mode 120000 (symlink)
index d0ece85..0000000
+++ /dev/null
@@ -1 +0,0 @@
-vppapitrace.py
\ No newline at end of file
diff --git a/src/tools/vppapitrace/vppapitrace.py b/src/tools/vppapitrace/vppapitrace.py
deleted file mode 100755 (executable)
index 8089b3a..0000000
+++ /dev/null
@@ -1,492 +0,0 @@
-#!/usr/bin/env python3
-
-#
-# Copyright (c) 2019 Cisco and/or its affiliates.
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at:
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-#
-# Convert from VPP API trace to JSON.
-
-import argparse
-import struct
-import sys
-import logging
-import json
-from ipaddress import *
-from collections import namedtuple
-from vpp_papi import MACAddress, VPPApiJSONFiles
-import base64
-import os
-import textwrap
-
-def serialize_likely_small_unsigned_integer(x):
-    r = x
-
-    # Low bit set means it fits into 1 byte.
-    if r < (1 << 7):
-        return struct.pack("B", 1 + 2 * r)
-
-    # Low 2 bits 1 0 means it fits into 2 bytes.
-    r -= (1 << 7)
-    if r < (1 << 14):
-        return struct.pack("<H", 4 * r + 2)
-
-    r -= (1 << 14)
-    if r < (1 << 29):
-        return struct.pack("<I", 8 * r + 4)
-
-    return struct.pack("<BQ", 0, x)
-
-
-def unserialize_likely_small_unsigned_integer(data, offset):
-    y = struct.unpack_from("B", data, offset)[0]
-    if y & 1:
-        return y // 2, 1
-    r = 1 << 7
-    if y & 2:
-        p = struct.unpack_from("B", data, offset + 1)[0]
-        r += (y // 4) + (p << 6)
-        return r, 2
-    r += 1 << 14
-    if y & 4:
-        (p1, p2, p3) = struct.unpack_from("BBB", data, offset+1)
-        r += ((y // 8) + (p1 << (5 + 8 * 0))
-              + (p2 << (5 + 8 * 1)) + (p3 << (5 + 8 * 2)))
-        return r, 3
-    return struct.unpack_from(">Q", data, offset+1)[0], 8
-
-
-def serialize_cstring(s):
-    bstring = s.encode('utf8')
-    l = len(bstring)
-    b = serialize_likely_small_unsigned_integer(l)
-    b += struct.pack('{}s'.format(l), bstring)
-    return b
-
-
-def unserialize_cstring(data, offset):
-    l, size = unserialize_likely_small_unsigned_integer(data, offset)
-    name = struct.unpack_from('{}s'.format(l), data, offset+size)[0]
-    return name.decode('utf8'), size + len(name)
-
-
-def unserialize_msgtbl(data, offset):
-    msgtable_by_id = {}
-    msgtable_by_name = {}
-    i = 0
-    nmsg = struct.unpack_from(">I", data, offset)[0]
-    o = 4
-    while i < nmsg:
-        (msgid, size) = unserialize_likely_small_unsigned_integer(
-            data, offset + o)
-        o += size
-        (name, size) = unserialize_cstring(data, offset + o)
-        o += size
-        msgtable_by_id[msgid] = name
-        msgtable_by_name[name] = msgid
-
-        i += 1
-    return msgtable_by_id, msgtable_by_name, o
-
-
-def serialize_msgtbl(messages):
-    offset = 0
-    # XXX 100K?
-    data = bytearray(100000)
-    nmsg = len(messages)
-    data = struct.pack(">I", nmsg)
-
-    for k, v in messages.items():
-        name = k + '_' + v.crc[2:]
-        data += serialize_likely_small_unsigned_integer(v._vl_msg_id)
-        data += serialize_cstring(name)
-    return data
-
-
-def apitrace2json(messages, filename):
-    result = []
-    with open(filename, 'rb') as file:
-        bytes_read = file.read()
-        # Read header
-        (nitems, msgtbl_size, wrapped) = struct.unpack_from(">IIB",
-                                                            bytes_read, 0)
-        logging.debug('nitems: {} message table size: {} wrapped: {}'
-                      .format(nitems, msgtbl_size, wrapped))
-        if wrapped:
-            sys.stdout.write('Wrapped/incomplete trace, results may vary')
-        offset = 9
-
-        msgtbl_by_id, msgtbl_by_name, size = unserialize_msgtbl(bytes_read,
-                                                                offset)
-        offset += size
-
-        i = 0
-        while i < nitems:
-            size = struct.unpack_from(">I", bytes_read, offset)[0]
-            offset += 4
-            if size == 0:
-                break
-            msgid = struct.unpack_from(">H", bytes_read, offset)[0]
-            name = msgtbl_by_id[msgid]
-            n = name[:name.rfind("_")]
-            msgobj = messages[n]
-            if n + '_' + msgobj.crc[2:] != name:
-                sys.exit("CRC Mismatch between JSON API definition "
-                         "and trace. {}".format(name))
-
-            x, s = msgobj.unpack(bytes_read[offset:offset+size])
-            msgname = type(x).__name__
-            offset += size
-            # Replace named tuple illegal _0
-            y = x._asdict()
-            y.pop('_0')
-            result.append({'name': msgname, 'args': y})
-            i += 1
-
-    file.close()
-    return result
-
-
-def json2apitrace(messages, filename):
-    """Input JSON file and API message definition. Output API trace
-    bytestring."""
-
-    msgs = []
-    with open(filename, 'r') as file:
-        msgs = json.load(file, object_hook=vpp_decode)
-    result = b''
-    for m in msgs:
-        name = m['name']
-        msgobj = messages[name]
-        m['args']['_vl_msg_id'] = messages[name]._vl_msg_id
-        b = msgobj.pack(m['args'])
-
-        result += struct.pack('>I', len(b))
-        result += b
-    return len(msgs), result
-
-
-class VPPEncoder(json.JSONEncoder):
-    def default(self, o):
-        if type(o) is bytes:
-            return "base64:" + base64.b64encode(o).decode('utf-8')
-        # Let the base class default method raise the TypeError
-        return json.JSONEncoder.default(self, o)
-
-    def encode(self, obj):
-        def hint_tuples(item):
-            if isinstance(item, tuple):
-                return hint_tuples(item._asdict())
-            if isinstance(item, list):
-                return [hint_tuples(e) for e in item]
-            if isinstance(item, dict):
-                return {key: hint_tuples(value) for key, value in item.items()}
-            else:
-                return item
-
-        return super(VPPEncoder, self).encode(hint_tuples(obj))
-
-
-def vpp_decode(obj):
-    for k, v in obj.items():
-        if type(v) is str and v.startswith('base64:'):
-            s = v.lstrip('base64:')
-            obj[k] = base64.b64decode(v[7:])
-    return obj
-
-
-def vpp_encoder(obj):
-    if isinstance(obj, IPv6Network):
-        return str(obj)
-    if isinstance(obj, IPv4Network):
-        return str(obj)
-    if isinstance(obj, IPv6Address):
-        return str(obj)
-    if isinstance(obj, IPv4Address):
-        return str(obj)
-    if isinstance(obj, MACAddress):
-        return str(obj)
-    if type(obj) is bytes:
-        return "base64:" + base64.b64encode(obj).decode('ascii')
-    raise TypeError('Unknown object {} {}\n'.format(type(obj), obj))
-
-message_filter = {
-    'control_ping',
-    'memclnt_create',
-    'memclnt_delete',
-    'get_first_msg_id',
-}
-
-argument_filter = {
-    'client_index',
-    'context',
-}
-
-def topython(messages, services):
-    import pprint
-    pp = pprint.PrettyPrinter()
-
-    s = '''\
-#!/usr/bin/env python3
-from vpp_papi import VPP, VppEnum
-vpp = VPP(use_socket=True)
-vpp.connect(name='vppapitrace')
-'''
-
-    for m in messages:
-        if m['name'] not in services:
-            s += '# ignoring reply message: {}\n'.format(m['name'])
-            continue
-        if m['name'] in message_filter:
-            s += '# ignoring message {}\n'.format(m['name'])
-            continue
-        for k in argument_filter:
-            try:
-                m['args'].pop(k)
-            except KeyError:
-                pass
-        a = pp.pformat(m['args'])
-        s += 'rv = vpp.api.{}(**{})\n'.format(m['name'], a)
-        s += 'print("RV:", rv)\n'
-    s += 'vpp.disconnect()\n'
-
-    return s
-
-def todump_items(k, v, level):
-    klen = len(k) if k else 0
-    spaces = '  ' * level + ' ' * (klen + 3)
-    wrapper = textwrap.TextWrapper(initial_indent="", subsequent_indent=spaces, width=60)
-    s = ''
-    if type(v) is dict:
-        if k:
-            s += '   ' * level + '{}:\n'.format(k)
-        for k2, v2 in v.items():
-            s += todump_items(k2, v2, level + 1)
-        return s
-
-    if type(v) is list:
-        for v2 in v:
-            s += '{}'.format(todump_items(k, v2, level))
-        return s
-
-    if type(v) is bytes:
-        w = wrapper.fill(bytes.hex(v))
-        s += '   ' * level + '{}: {}\n'.format(k, w)
-    else:
-        if type(v) is str:
-            v = wrapper.fill(v)
-        s += '   ' * level + '{}: {}\n'.format(k, v)
-    return s
-
-
-def todump(messages, services):
-    import pprint
-    pp = pprint.PrettyPrinter()
-
-    s = ''
-    for m in messages:
-        if m['name'] not in services:
-            s += '# ignoring reply message: {}\n'.format(m['name'])
-            continue
-        #if m['name'] in message_filter:
-        #    s += '# ignoring message {}\n'.format(m['name'])
-        #    continue
-        for k in argument_filter:
-            try:
-                m['args'].pop(k)
-            except KeyError:
-                pass
-        a = pp.pformat(m['args'])
-        s += '{}:\n'.format(m['name'])
-        s += todump_items(None, m['args'], 0)
-    return s
-
-
-def init_api(apidir):
-    # Read API definitions
-    apifiles = VPPApiJSONFiles.find_api_files(api_dir=apidir)
-    messages = {}
-    services = {}
-    for file in apifiles:
-        with open(file) as apidef_file:
-            m, s = VPPApiJSONFiles.process_json_file(apidef_file)
-            messages.update(m)
-            services.update(s)
-    return messages, services
-
-
-def replaymsgs(vpp, msgs):
-    for m in msgs:
-        name = m['name']
-        if name not in vpp.services:
-            continue
-        if name == 'control_ping':
-            continue
-        try:
-            m['args'].pop('client_index')
-        except KeyError:
-            pass
-        if m['args']['context'] == 0:
-            m['args']['context'] = 1
-        f = vpp.get_function(name)
-        rv = f(**m['args'])
-        print('RV {}'.format(rv))
-
-
-def replay(args):
-    """Replay into running VPP instance"""
-
-    from vpp_papi import VPP
-
-    JSON = 1
-    APITRACE = 2
-
-    filename, file_extension = os.path.splitext(args.input)
-    input_type = JSON if file_extension == '.json' else APITRACE
-
-    vpp = VPP(use_socket=args.socket)
-    rv = vpp.connect(name='vppapireplay', chroot_prefix=args.shmprefix)
-    if rv != 0:
-        sys.exit('Cannot connect to VPP')
-
-    if input_type == JSON:
-        with open(args.input, 'r') as file:
-            msgs = json.load(file, object_hook=vpp_decode)
-    else:
-        msgs = apitrace2json(messages, args.input)
-
-    replaymsgs(vpp, msgs)
-
-    vpp.disconnect()
-
-
-def generate(args):
-    """Generate JSON"""
-
-    JSON = 1
-    APITRACE = 2
-    PYTHON = 3
-    DUMP = 4
-
-    filename, file_extension = os.path.splitext(args.input)
-    input_type = JSON if file_extension == '.json' else APITRACE
-    filename, file_extension = os.path.splitext(args.output)
-
-    if args.todump:
-        output_type = DUMP
-    else:
-        if file_extension == '.json' or filename == '-':
-            output_type = JSON
-        elif file_extension == '.py':
-            output_type = PYTHON
-        else:
-            output_type = APITRACE
-
-    if input_type == output_type:
-        sys.exit("error: Nothing to convert between")
-
-    if input_type != JSON and output_type == APITRACE:
-        sys.exit("error: Input file must be JSON file: {}".format(args.input))
-
-    messages, services = init_api(args.apidir)
-
-    if input_type == JSON and output_type == APITRACE:
-        i = 0
-        for k, v in messages.items():
-            v._vl_msg_id = i
-            i += 1
-
-        n, result = json2apitrace(messages, args.input)
-        msgtbl = serialize_msgtbl(messages)
-
-        print('API messages: {}'.format(n))
-        header = struct.pack(">IIB", n, len(msgtbl), 0)
-
-        with open(args.output, 'wb') as outfile:
-            outfile.write(header)
-            outfile.write(msgtbl)
-            outfile.write(result)
-
-        return
-
-    if input_type == APITRACE:
-        result = apitrace2json(messages, args.input)
-        if output_type == PYTHON:
-            s = json.dumps(result, cls=VPPEncoder, default=vpp_encoder)
-            x = json.loads(s, object_hook=vpp_decode)
-            s = topython(x, services)
-        elif output_type == DUMP:
-            s = json.dumps(result, cls=VPPEncoder, default=vpp_encoder)
-            x = json.loads(s, object_hook=vpp_decode)
-            s = todump(x, services)
-        else:
-            s = json.dumps(result, cls=VPPEncoder,
-                           default=vpp_encoder, indent=4 * ' ')
-    elif output_type == PYTHON:
-        with open(args.input, 'r') as file:
-            x = json.load(file, object_hook=vpp_decode)
-            s = topython(x, services)
-    else:
-        sys.exit('Input file must be API trace file: {}'.format(args.input))
-
-    if args.output == '-':
-        sys.stdout.write(s + '\n')
-    else:
-        print('Generating {} from API trace: {}'
-              .format(args.output, args.input))
-        with open(args.output, 'w') as outfile:
-            outfile.write(s)
-
-def general(args):
-    return
-
-def main():
-    parser = argparse.ArgumentParser()
-    parser.add_argument('--debug', action='store_true',
-                        help='enable debug mode')
-    parser.add_argument('--apidir',
-                        help='Location of JSON API definitions')
-
-    parser.set_defaults(func=general)
-    subparsers = parser.add_subparsers(title='subcommands',
-                                       description='valid subcommands',
-                                       help='additional help')
-
-    parser_convert = subparsers.add_parser('convert',
-                                           help='Convert API trace to JSON or Python and back')
-    parser_convert.add_argument('input',
-                                help='Input file (API trace | JSON)')
-    parser_convert.add_argument('--todump', action='store_true', help='Output text format')
-    parser_convert.add_argument('output',
-                                help='Output file (Python | JSON | API trace)')
-    parser_convert.set_defaults(func=generate)
-
-
-    parser_replay = subparsers.add_parser('replay',
-                                          help='Replay messages to running VPP instance')
-    parser_replay.add_argument('input', help='Input file (API trace | JSON)')
-    parser_replay.add_argument('--socket', action='store_true',
-                               help='use default socket to connect to VPP')
-    parser_replay.add_argument('--shmprefix',
-                               help='connect to VPP on shared memory prefix')
-    parser_replay.set_defaults(func=replay)
-
-    args = parser.parse_args()
-    if args.debug:
-        logging.basicConfig(stream=sys.stdout, level=logging.DEBUG)
-
-    args.func(args)
-
-
-main()
index 1458fde..61ac92f 100644 (file)
@@ -2627,7 +2627,9 @@ vat_api_hookup (vat_main_t * vam)
 #define _(N, n)                                                               \
   vl_msg_api_set_handlers (VL_API_##N + 1, #n, vl_api_##n##_t_handler_uni,    \
                           vl_noop_handler, vl_api_##n##_t_endian,            \
-                          vl_api_##n##_t_print, sizeof (vl_api_##n##_t), 1);
+                          vl_api_##n##_t_print, sizeof (vl_api_##n##_t), 1,  \
+                          vl_api_##n##_t_print_json, vl_api_##n##_t_tojson,  \
+                          vl_api_##n##_t_fromjson);
   foreach_vpe_api_reply_msg;
 #if VPP_API_TEST_BUILTIN == 0
   foreach_standalone_reply_msg;
index 9ee2d82..c44d245 100644 (file)
@@ -18,7 +18,6 @@ add_vpp_executable(vat2 ENABLE_EXPORTS
   SOURCES
   main.c
   plugin.c
-  jsonconvert.c
 
   DEPENDS api_headers
 
@@ -41,7 +40,6 @@ vpp_generate_api_c_header (test/vat2_test.api)
 add_vpp_executable(test_vat2 ENABLE_EXPORTS NO_INSTALL
   SOURCES
   test/vat2_test.c
-  jsonconvert.c
 
   DEPENDS api_headers
 
@@ -57,7 +55,6 @@ add_vpp_executable(test_vat2 ENABLE_EXPORTS NO_INSTALL
 
 if("${CMAKE_VERSION}" VERSION_GREATER_EQUAL "3.13" AND "${CMAKE_C_COMPILER_ID}" MATCHES "(Apple)?[Cc]lang")
   set(TARGET_NAME test_vat2)
-  set(COV_SOURCES ${CMAKE_SOURCE_DIR}/vat2/jsonconvert.c)
 
   message("Building with llvm Code Coverage Tools ${TARGET_NAME}")
   target_compile_options(${TARGET_NAME} PRIVATE -fprofile-instr-generate -fcoverage-mapping)
@@ -96,7 +93,6 @@ endif()
 ##############################################################################
 install(
   FILES
-  jsonconvert.h
   vat2_helpers.h
   DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/vat2
   COMPONENT vpp-dev
diff --git a/src/vat2/jsonconvert.h b/src/vat2/jsonconvert.h
deleted file mode 100644 (file)
index 038ad74..0000000
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * Copyright (c) 2020 Cisco and/or its affiliates.
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef included_json_convert_h
-#define included_json_convert_h
-
-#include <stdbool.h>
-#include <vppinfra/cJSON.h>
-#include <vnet/ethernet/mac_address.h>
-#include <vnet/ip/ip6_packet.h>
-#include <vnet/ip/ip_types.api_types.h>
-#include <vnet/ethernet/ethernet_types.api_types.h>
-
-#define foreach_vat2_fromjson                   \
-  _(i8)                                         \
-  _(u8)                                         \
-  _(i16)                                        \
-  _(u16)                                        \
-  _(i32)                                        \
-  _(u32)                                        \
-  _(u64)                                        \
-  _(f64)
-
-#define _(T)                                    \
-  int vl_api_ ##T## _fromjson(cJSON *o, T *d);
-foreach_vat2_fromjson
-#undef _
-
-  /* Prototypes */
-  int
-  vl_api_bool_fromjson (cJSON *o, bool *d);
-int vl_api_ip4_address_t_fromjson (void **mp, int *len, cJSON *o,
-                                  vl_api_ip4_address_t *a);
-int vl_api_ip4_prefix_t_fromjson (void **mp, int *len, cJSON *o,
-                                 vl_api_ip4_prefix_t *a);
-int vl_api_ip4_address_with_prefix_t_fromjson (void **mp, int *len, cJSON *o,
-                                              vl_api_ip4_prefix_t *a);
-int vl_api_ip6_address_t_fromjson (void **mp, int *len, cJSON *o,
-                                  vl_api_ip6_address_t *a);
-int vl_api_ip6_prefix_t_fromjson (void **mp, int *len, cJSON *o,
-                                 vl_api_ip6_prefix_t *a);
-int vl_api_ip6_address_with_prefix_t_fromjson (void **mp, int *len, cJSON *o,
-                                              vl_api_ip6_prefix_t *a);
-int vl_api_address_t_fromjson (void **mp, int *len, cJSON *o,
-                              vl_api_address_t *a);
-int vl_api_prefix_t_fromjson (void **mp, int *len, cJSON *o,
-                             vl_api_prefix_t *a);
-int vl_api_address_with_prefix_t_fromjson (void **mp, int *len, cJSON *o,
-                                          vl_api_prefix_t *a);
-int vl_api_mac_address_t_fromjson (void **mp, int *len, cJSON *o,
-                                  vl_api_mac_address_t *a);
-
-uword unformat_ip4_address (unformat_input_t *input, va_list *args);
-uword unformat_ip6_address (unformat_input_t *input, va_list *args);
-u8 *format_ip6_address (u8 *s, va_list *args);
-uword unformat_mac_address (unformat_input_t *input, va_list *args);
-u8 *format_ip4_address (u8 *s, va_list *args);
-u8 *format_vl_api_interface_index_t (u8 *s, va_list *args);
-u8 *format_vl_api_timestamp_t (u8 *s, va_list *args);
-u8 *format_vl_api_timedelta_t (u8 *s, va_list *args);
-uword unformat_vl_api_timedelta_t (unformat_input_t *input, va_list *args);
-uword unformat_vl_api_timestamp_t (unformat_input_t *input, va_list *args);
-u8 *format_vl_api_gbp_scope_t (u8 *s, va_list *args);
-uword unformat_vl_api_gbp_scope_t (unformat_input_t *input, va_list *args);
-
-int vl_api_c_string_to_api_string (const char *buf, vl_api_string_t *str);
-void vl_api_string_cJSON_AddToObject (cJSON *const object,
-                                     const char *const name,
-                                     vl_api_string_t *astr);
-
-u8 *u8string_fromjson (cJSON *o, char *fieldname);
-int u8string_fromjson2 (cJSON *o, char *fieldname, u8 *data);
-int vl_api_u8_string_fromjson (cJSON *o, u8 *s, int len);
-
-#define foreach_vat2_tojson                     \
-  _(ip4_address)                                \
-  _(ip4_prefix)                                 \
-  _(ip6_address)                                \
-  _(ip6_prefix)                                 \
-  _(address)                                    \
-  _(prefix)                                     \
-  _(mac_address)
-
-#define _(T)                                    \
-  cJSON *vl_api_ ##T## _t_tojson(vl_api_ ##T## _t *);
-  foreach_vat2_tojson
-#undef _
-
-cJSON *vl_api_ip4_address_with_prefix_t_tojson (vl_api_ip4_prefix_t *a);
-cJSON *vl_api_ip6_address_with_prefix_t_tojson (vl_api_ip6_prefix_t *a);
-cJSON *vl_api_address_with_prefix_t_tojson (vl_api_prefix_t *a);
-
-#endif
index 208e0c5..667f473 100644 (file)
@@ -18,7 +18,7 @@
 #include <stdbool.h>
 #include <ctype.h>
 #include <getopt.h>
-#include <assert.h>
+#include <string.h>
 #include <vlib/vlib.h>
 #include <vlibapi/api_types.h>
 #include <vppinfra/hash.h>
 #include <limits.h>
 #include "vat2.h"
 
+/*
+ * Filter these messages as they are used to manage the API connection to VPP
+ */
+char *filter_messages_strings[] = { "memclnt_create",
+                                   "memclnt_delete",
+                                   "sockclnt_create",
+                                   "sockclnt_delete",
+                                   "memclnt_rx_thread_suspend",
+                                   "memclnt_read_timeout",
+                                   "rx_thread_exit",
+                                   "trace_plugin_msg_ids",
+                                   0 };
+
+static bool
+filter_message (char *msgname)
+{
+  char **p = filter_messages_strings;
+
+  while (*p)
+    {
+      if (strcmp (*p, msgname) == 0)
+       return true;
+      p++;
+    }
+  return false;
+}
+
 uword *function_by_name;
 bool debug = false;
 
@@ -89,15 +116,16 @@ struct apifuncs_s
 {
   cJSON (*f) (cJSON *);
   cJSON (*tojson) (void *);
+  u32 crc;
 };
 
 struct apifuncs_s *apifuncs = 0;
 
 void
 vat2_register_function (char *name, cJSON (*f) (cJSON *),
-                       cJSON (*tojson) (void *))
+                       cJSON (*tojson) (void *), u32 crc)
 {
-  struct apifuncs_s funcs = { .f = f, .tojson = tojson };
+  struct apifuncs_s funcs = { .f = f, .tojson = tojson, .crc = crc };
   vec_add1 (apifuncs, funcs);
   hash_set_mem (function_by_name, name, vec_len (apifuncs) - 1);
 }
@@ -105,12 +133,28 @@ vat2_register_function (char *name, cJSON (*f) (cJSON *),
 static int
 vat2_exec_command_by_name (char *msgname, cJSON *o)
 {
+  if (filter_message (msgname))
+    return 0;
+
+  cJSON *crc_obj = cJSON_GetObjectItem (o, "_crc");
+  if (!crc_obj)
+    {
+      fprintf (stderr, "Missing '_crc' element!\n");
+      return -1;
+    }
+  char *crc_str = cJSON_GetStringValue (crc_obj);
+  u32 crc = (u32) strtol (crc_str, NULL, 16);
+
   uword *p = hash_get_mem (function_by_name, msgname);
   if (!p)
     {
-      fprintf (stderr, "No such command %s", msgname);
+      fprintf (stderr, "No such command %s\n", msgname);
       return -1;
     }
+  if (crc != apifuncs[p[0]].crc)
+    {
+      fprintf (stderr, "API CRC does not match: %s!\n", msgname);
+    }
 
   cJSON *(*fp) (cJSON *);
   fp = (void *) apifuncs[p[0]].f;
@@ -143,9 +187,10 @@ vat2_exec_command (cJSON *o)
     }
 
   char *name = cJSON_GetStringValue (msg_id_obj);
-  assert (name);
+
   return vat2_exec_command_by_name (name, o);
 }
+
 static void
 print_template (char *msgname)
 {
@@ -307,6 +352,12 @@ int main (int argc, char **argv)
     }
   }
 
+  if (!msgname && !filename)
+    {
+      print_help ();
+      exit (-1);
+    }
+
   /* Read message from file */
   if (filename) {
       if (argc > index)
@@ -325,6 +376,7 @@ int main (int argc, char **argv)
       fprintf(stderr, "%s: can't open file: %s\n", argv[0], filename);
       exit(-1);
     }
+
     chunksize = bufsize = 1024;
     char *buf = malloc(bufsize);
     while ((n = fread (buf + n_read, 1, chunksize, f)))
@@ -339,17 +391,17 @@ int main (int argc, char **argv)
     fclose(f);
     if (n_read) {
       o = cJSON_Parse(buf);
-      free(buf);
       if (!o) {
         fprintf(stderr, "%s: Failed parsing JSON input: %s\n", argv[0], cJSON_GetErrorPtr());
         exit(-1);
       }
     }
+    free (buf);
   }
 
-  if (!msgname && !filename)
+  if (!o)
     {
-      print_help ();
+      fprintf (stderr, "%s: Failed parsing JSON input\n", argv[0]);
       exit (-1);
     }
 
index 1ac4652..7aa5e71 100644 (file)
@@ -196,6 +196,7 @@ struct tests tests[] = {
         "[\"2001:db8::23\", \"2001:db8::23\"] }" },
   { .s = "{\"_msgname\": \"test_empty\"}" },
   { .s = "{\"_msgname\": \"test_interface\", \"sw_if_index\": 100 }" },
+  { .s = "{\"_msgname\": \"test_interface\", \"sw_if_index\": 4294967295 }" },
 };
 
 int main (int argc, char **argv)
index 60fbe73..7244098 100644 (file)
@@ -299,10 +299,11 @@ vcl_bapi_hookup (void)
     return;
 
 #define _(N, n)                                                               \
-  vl_msg_api_set_handlers (REPLY_MSG_ID_BASE + VL_API_##N, #n,                \
-                          vl_api_##n##_t_handler, vl_noop_handler,           \
-                          vl_api_##n##_t_endian, vl_api_##n##_t_print,       \
-                          sizeof (vl_api_##n##_t), 1);
+  vl_msg_api_set_handlers (                                                   \
+    REPLY_MSG_ID_BASE + VL_API_##N, #n, vl_api_##n##_t_handler,               \
+    vl_noop_handler, vl_api_##n##_t_endian, vl_api_##n##_t_print,             \
+    sizeof (vl_api_##n##_t), 1, vl_api_##n##_t_print_json,                    \
+    vl_api_##n##_t_tojson, vl_api_##n##_t_fromjson);
   foreach_sock_msg;
 #undef _
 }
index 431155c..d05395a 100644 (file)
@@ -36,8 +36,8 @@ typedef CLIB_PACKED ( struct {
 }) vl_api_trace_file_header_t;
 /* *INDENT-ON* */
 
-int vl_msg_api_trace_save (api_main_t * am,
-                          vl_api_trace_which_t which, FILE * fp);
+int vl_msg_api_trace_save (api_main_t *am, vl_api_trace_which_t which,
+                          FILE *fp, u8 is_json);
 
 #define VLIB_API_INIT_FUNCTION(x) VLIB_DECLARE_INIT_FUNCTION(x,api_init)
 
@@ -123,6 +123,11 @@ vlib_node_t ***vlib_node_unserialize (u8 * vector);
 
 u32 vl_msg_api_get_msg_length (void *msg_arg);
 
+typedef int (*vl_msg_traverse_trace_fn) (u8 *, void *);
+
+int vl_msg_traverse_trace (vl_api_trace_t *tp, vl_msg_traverse_trace_fn fn,
+                          void *ctx);
+
 #endif /* included_api_h */
 /*
  * fd.io coding-style-patch-verification: ON
index 3fdc1bb..a955636 100644 (file)
@@ -27,6 +27,7 @@
 
 #include <vppinfra/clib_error.h>
 #include <vppinfra/elog.h>
+#include <vppinfra/cJSON.h>
 #include <vlibapi/api_types.h>
 #include <svm/svm_common.h>
 #include <svm/queue.h>
@@ -128,6 +129,9 @@ typedef struct
   void *cleanup;               /**< non-default message cleanup handler */
   void *endian;                        /**< message endian function  */
   void *print;                 /**< message print function  */
+  void *print_json;            /**< message print function (JSON format)  */
+  void *tojson;                        /**< binary to JSON convert function */
+  void *fromjson;              /**< JSON to binary convert function */
   int size;                    /**< message size  */
   int traced;                  /**< is this message to be traced?  */
   int replay;                  /**< is this message to be replayed?  */
@@ -173,11 +177,10 @@ void vl_msg_api_trace_only (void *the_msg);
 void vl_msg_api_cleanup_handler (void *the_msg);
 void vl_msg_api_replay_handler (void *the_msg);
 void vl_msg_api_socket_handler (void *the_msg);
-void vl_msg_api_set_handlers (int msg_id, char *msg_name,
-                             void *handler,
-                             void *cleanup,
-                             void *endian,
-                             void *print, int msg_size, int traced);
+void vl_msg_api_set_handlers (int msg_id, char *msg_name, void *handler,
+                             void *cleanup, void *endian, void *print,
+                             int msg_size, int traced, void *print_json,
+                             void *tojson, void *fromjson);
 void vl_msg_api_clean_handlers (int msg_id);
 void vl_msg_api_config (vl_msg_api_msg_config_t *);
 void vl_msg_api_set_cleanup_handler (int msg_id, void *fp);
@@ -241,9 +244,21 @@ typedef struct api_main_t
   /** Message print function vector */
   void (**msg_print_handlers) (void *, void *);
 
+  /** Message print function vector in JSON */
+  void (**msg_print_json_handlers) (void *, void *);
+
+  /** Message convert function vector */
+  cJSON *(**msg_tojson_handlers) (void *);
+
+  /** Message convert function vector */
+  void *(**msg_fromjson_handlers) (cJSON *, int *);
+
   /** Message name vector */
   const char **msg_names;
 
+  /** API message ID by name hash table */
+  uword *msg_id_by_name;
+
   /** Don't automatically free message buffer vetor */
   u8 *message_bounce;
 
index 65288d8..52e66f4 100644 (file)
@@ -223,13 +223,160 @@ vl_api_serialize_message_table (api_main_t * am, u8 * vector)
   return serialize_close_vector (sm);
 }
 
+static int
+vl_msg_api_trace_write_one (api_main_t *am, u8 *msg, FILE *fp)
+{
+  u8 *tmpmem = 0;
+  int tlen, slen;
+  cJSON *(*tojson_fn) (void *);
+
+  u32 msg_length = vec_len (msg);
+  vec_validate (tmpmem, msg_length - 1);
+  clib_memcpy_fast (tmpmem, msg, msg_length);
+  u16 id = clib_net_to_host_u16 (*((u16 *) msg));
+
+  void (*endian_fp) (void *);
+  endian_fp = am->msg_endian_handlers[id];
+  (*endian_fp) (tmpmem);
+
+  if (id < vec_len (am->msg_tojson_handlers) && am->msg_tojson_handlers[id])
+    {
+      tojson_fn = am->msg_tojson_handlers[id];
+      cJSON *o = tojson_fn (tmpmem);
+      char *s = cJSON_Print (o);
+      slen = strlen (s);
+      tlen = fwrite (s, 1, slen, fp);
+      cJSON_free (s);
+      cJSON_Delete (o);
+      vec_free (tmpmem);
+      if (tlen != slen)
+       {
+         fformat (stderr, "writing to file error\n");
+         return -11;
+       }
+    }
+  else
+    fformat (stderr, "  [no registered tojson fn]\n");
+
+  return 0;
+}
+
+#define vl_msg_fwrite(_s, _f) fwrite (_s, 1, sizeof (_s) - 1, _f)
+
+typedef struct
+{
+  FILE *fp;
+  u32 n_traces;
+  u32 i;
+} vl_msg_write_json_args_t;
+
+static int
+vl_msg_write_json_fn (u8 *msg, void *ctx)
+{
+  vl_msg_write_json_args_t *arg = ctx;
+  FILE *fp = arg->fp;
+  api_main_t *am = vlibapi_get_main ();
+  int rc = vl_msg_api_trace_write_one (am, msg, fp);
+  if (rc < 0)
+    return rc;
+
+  if (arg->i < arg->n_traces - 1)
+    vl_msg_fwrite (",\n", fp);
+  arg->i++;
+  return 0;
+}
+
+static int
+vl_msg_api_trace_write_json (api_main_t *am, vl_api_trace_t *tp, FILE *fp)
+{
+  vl_msg_write_json_args_t args;
+  clib_memset (&args, 0, sizeof (args));
+  args.fp = fp;
+  args.n_traces = vec_len (tp->traces);
+  vl_msg_fwrite ("[\n", fp);
+
+  int rv = vl_msg_traverse_trace (tp, vl_msg_write_json_fn, &args);
+  if (rv < 0)
+    return rv;
+
+  vl_msg_fwrite ("]", fp);
+  return 0;
+}
+
 int
-vl_msg_api_trace_save (api_main_t * am, vl_api_trace_which_t which, FILE * fp)
+vl_msg_traverse_trace (vl_api_trace_t *tp, vl_msg_traverse_trace_fn fn,
+                      void *ctx)
 {
-  vl_api_trace_t *tp;
-  vl_api_trace_file_header_t fh;
   int i;
   u8 *msg;
+  int rv = 0;
+
+  /* No-wrap case */
+  if (tp->wrapped == 0)
+    {
+      for (i = 0; i < vec_len (tp->traces); i++)
+       {
+         /*sa_ignore NO_NULL_CHK */
+         msg = tp->traces[i];
+         if (!msg)
+           continue;
+
+         rv = fn (msg, ctx);
+         if (rv < 0)
+           return rv;
+       }
+    }
+  else
+    {
+      /* Wrap case: write oldest -> end of buffer */
+      for (i = tp->curindex; i < vec_len (tp->traces); i++)
+       {
+         msg = tp->traces[i];
+         if (!msg)
+           continue;
+
+         rv = fn (msg, ctx);
+         if (rv < 0)
+           return rv;
+       }
+      /* write beginning of buffer -> oldest-1 */
+      for (i = 0; i < tp->curindex; i++)
+       {
+         /*sa_ignore NO_NULL_CHK */
+         msg = tp->traces[i];
+         if (!msg)
+           continue;
+
+         rv = fn (msg, ctx);
+         if (rv < 0)
+           return rv;
+       }
+    }
+  return 0;
+}
+
+static int
+vl_api_msg_write_fn (u8 *msg, void *ctx)
+{
+  FILE *fp = ctx;
+  u32 msg_length = clib_host_to_net_u32 (vec_len (msg));
+  if (fwrite (&msg_length, 1, sizeof (msg_length), fp) != sizeof (msg_length))
+    {
+      return (-14);
+    }
+  if (fwrite (msg, 1, vec_len (msg), fp) != vec_len (msg))
+    {
+      return (-14);
+    }
+  return 0;
+}
+
+int
+vl_msg_api_trace_save (api_main_t *am, vl_api_trace_which_t which, FILE *fp,
+                      u8 is_json)
+{
+  vl_api_trace_t *tp;
+  vl_api_trace_file_header_t fh;
 
   switch (which)
     {
@@ -256,9 +403,13 @@ vl_msg_api_trace_save (api_main_t * am, vl_api_trace_which_t which, FILE * fp)
       return -2;
     }
 
+  if (is_json)
+    return vl_msg_api_trace_write_json (am, tp, fp);
+
   /* Write the file header */
   fh.wrapped = tp->wrapped;
   fh.nitems = clib_host_to_net_u32 (vec_len (tp->traces));
+
   u8 *m = vl_api_serialize_message_table (am, 0);
   fh.msgtbl_size = clib_host_to_net_u32 (vec_len (m));
 
@@ -274,92 +425,7 @@ vl_msg_api_trace_save (api_main_t * am, vl_api_trace_which_t which, FILE * fp)
     }
   vec_free (m);
 
-  /* No-wrap case */
-  if (tp->wrapped == 0)
-    {
-      /*
-       * Note: vec_len return 0 when fed a NULL pointer.
-       * Unfortunately, the static analysis tool doesn't
-       * figure it out, hence the suppressed warnings.
-       * What a great use of my time.
-       */
-      for (i = 0; i < vec_len (tp->traces); i++)
-       {
-         u32 msg_length;
-         /*sa_ignore NO_NULL_CHK */
-         msg = tp->traces[i];
-         /*
-          * This retarded check required to pass
-          * [sic] SA-checking.
-          */
-         if (!msg)
-           continue;
-
-         msg_length = clib_host_to_net_u32 (vec_len (msg));
-         if (fwrite (&msg_length, 1, sizeof (msg_length), fp)
-             != sizeof (msg_length))
-           {
-             return (-14);
-           }
-         if (fwrite (msg, 1, vec_len (msg), fp) != vec_len (msg))
-           {
-             return (-11);
-           }
-       }
-    }
-  else
-    {
-      /* Wrap case: write oldest -> end of buffer */
-      for (i = tp->curindex; i < vec_len (tp->traces); i++)
-       {
-         u32 msg_length;
-         msg = tp->traces[i];
-         /*
-          * This retarded check required to pass
-          * [sic] SA-checking
-          */
-         if (!msg)
-           continue;
-
-         msg_length = clib_host_to_net_u32 (vec_len (msg));
-         if (fwrite (&msg_length, 1, sizeof (msg_length), fp)
-             != sizeof (msg_length))
-           {
-             return (-14);
-           }
-
-         if (fwrite (msg, 1, vec_len (msg), fp) != vec_len (msg))
-           {
-             return (-12);
-           }
-       }
-      /* write beginning of buffer -> oldest-1 */
-      for (i = 0; i < tp->curindex; i++)
-       {
-         u32 msg_length;
-         /*sa_ignore NO_NULL_CHK */
-         msg = tp->traces[i];
-         /*
-          * This retarded check required to pass
-          * [sic] SA-checking
-          */
-         if (!msg)
-           continue;
-
-         msg_length = clib_host_to_net_u32 (vec_len (msg));
-         if (fwrite (&msg_length, 1, sizeof (msg_length), fp)
-             != sizeof (msg_length))
-           {
-             return (-14);
-           }
-
-         if (fwrite (msg, 1, vec_len (msg), fp) != vec_len (msg))
-           {
-             return (-13);
-           }
-       }
-    }
-  return 0;
+  return vl_msg_traverse_trace (tp, vl_api_msg_write_fn, fp);
 }
 
 int
@@ -807,16 +873,19 @@ vl_msg_api_socket_handler (void *the_msg)
                        1 /* do_it */ , 0 /* free_it */ );
 }
 
-#define foreach_msg_api_vector                  \
-_(msg_names)                                    \
-_(msg_handlers)                                 \
-_(msg_cleanup_handlers)                         \
-_(msg_endian_handlers)                          \
-_(msg_print_handlers)                           \
-_(api_trace_cfg)                               \
-_(message_bounce)                              \
-_(is_mp_safe)                                  \
-_(is_autoendian)
+#define foreach_msg_api_vector                                                \
+  _ (msg_names)                                                               \
+  _ (msg_handlers)                                                            \
+  _ (msg_cleanup_handlers)                                                    \
+  _ (msg_endian_handlers)                                                     \
+  _ (msg_print_handlers)                                                      \
+  _ (msg_print_json_handlers)                                                 \
+  _ (msg_tojson_handlers)                                                     \
+  _ (msg_fromjson_handlers)                                                   \
+  _ (api_trace_cfg)                                                           \
+  _ (message_bounce)                                                          \
+  _ (is_mp_safe)                                                              \
+  _ (is_autoendian)
 
 void
 vl_msg_api_config (vl_msg_api_msg_config_t * c)
@@ -855,6 +924,9 @@ vl_msg_api_config (vl_msg_api_msg_config_t * c)
   am->msg_cleanup_handlers[c->id] = c->cleanup;
   am->msg_endian_handlers[c->id] = c->endian;
   am->msg_print_handlers[c->id] = c->print;
+  am->msg_print_json_handlers[c->id] = c->print_json;
+  am->msg_tojson_handlers[c->id] = c->tojson;
+  am->msg_fromjson_handlers[c->id] = c->fromjson;
   am->message_bounce[c->id] = c->message_bounce;
   am->is_mp_safe[c->id] = c->is_mp_safe;
   am->is_autoendian[c->id] = c->is_autoendian;
@@ -862,6 +934,11 @@ vl_msg_api_config (vl_msg_api_msg_config_t * c)
   am->api_trace_cfg[c->id].size = c->size;
   am->api_trace_cfg[c->id].trace_enable = c->traced;
   am->api_trace_cfg[c->id].replay_enable = c->replay;
+
+  if (!am->msg_id_by_name)
+    am->msg_id_by_name = hash_create_string (0, sizeof (uword));
+
+  hash_set_mem (am->msg_id_by_name, c->name, c->id);
 }
 
 /*
@@ -870,7 +947,8 @@ vl_msg_api_config (vl_msg_api_msg_config_t * c)
  */
 void
 vl_msg_api_set_handlers (int id, char *name, void *handler, void *cleanup,
-                        void *endian, void *print, int size, int traced)
+                        void *endian, void *print, int size, int traced,
+                        void *print_json, void *tojson, void *fromjson)
 {
   vl_msg_api_msg_config_t cfg;
   vl_msg_api_msg_config_t *c = &cfg;
@@ -888,6 +966,9 @@ vl_msg_api_set_handlers (int id, char *name, void *handler, void *cleanup,
   c->message_bounce = 0;
   c->is_mp_safe = 0;
   c->is_autoendian = 0;
+  c->tojson = tojson;
+  c->fromjson = fromjson;
+  c->print_json = print_json;
   vl_msg_api_config (c);
 }
 
@@ -987,7 +1068,7 @@ vl_msg_api_post_mortem_dump (void)
       rv = write (2, "\n", 1);
       return;
     }
-  rv = vl_msg_api_trace_save (am, VL_API_TRACE_RX, fp);
+  rv = vl_msg_api_trace_save (am, VL_API_TRACE_RX, fp, 0);
   fclose (fp);
   if (rv < 0)
     {
index 456cba9..6d6483d 100644 (file)
@@ -57,3 +57,9 @@ add_dependencies(vlibmemoryclient vlibmemory_api_headers)
 add_vat_test_library(vlib
   vlibapi_test.c
 )
+##############################################################################
+# VAT2 plugins
+##############################################################################
+add_vpp_test_library(vlibmemoryclient
+  memclnt.api
+)
index 07c6d47..a5194cd 100644 (file)
@@ -29,7 +29,6 @@ service {
 /*
  * Create a client registration 
  */
-manual_print 
 define memclnt_create {
     u32 context;                /* opaque value to be returned in the reply */
     i32 ctx_quota;              /* requested punt context quota */
@@ -49,7 +48,6 @@ define memclnt_create_reply {
 /*
  * Delete a client registration 
  */
-manual_print
 define memclnt_delete {
     u32 index;                  /* index, used e.g. by API trace replay */
     u64 handle;               /* handle by which vlib knows this client */
@@ -137,7 +135,7 @@ define api_versions_reply {
  * at api trace replay time
  */
 
-manual_print define trace_plugin_msg_ids
+define trace_plugin_msg_ids
 {
     u32 client_index;
     u32 context;
index 5ebc31f..23d0088 100644 (file)
 #include <vlibmemory/vl_memory_api_h.h>
 #undef vl_printfun
 
-static inline void *
-vl_api_trace_plugin_msg_ids_t_print (vl_api_trace_plugin_msg_ids_t *a,
-                                    void *handle)
-{
-  vl_print (handle, "vl_api_trace_plugin_msg_ids: %s first %u last %u\n",
-           a->plugin_name, clib_host_to_net_u16 (a->first_msg_id),
-           clib_host_to_net_u16 (a->last_msg_id));
-  return handle;
-}
-
 /* instantiate all the endian swap functions we know about */
 #define vl_endianfun
 #include <vlibmemory/vl_memory_api_h.h>
@@ -154,6 +144,12 @@ vlib_api_init (void)
   vl_msg_api_msg_config_t cfg;
   vl_msg_api_msg_config_t *c = &cfg;
 
+  cJSON_Hooks cjson_hooks = {
+    .malloc_fn = clib_mem_alloc,
+    .free_fn = clib_mem_free,
+  };
+  cJSON_InitHooks (&cjson_hooks);
+
   clib_memset (c, 0, sizeof (*c));
 
 #define _(N, n)                                                               \
@@ -689,19 +685,25 @@ rpc_api_hookup (vlib_main_t *vm)
 {
   api_main_t *am = vlibapi_get_main ();
 #define _(N, n)                                                               \
-  vl_msg_api_set_handlers (                                                   \
-    VL_API_##N, #n, vl_api_##n##_t_handler, vl_noop_handler, vl_noop_handler, \
-    vl_api_##n##_t_print, sizeof (vl_api_##n##_t), 0 /* do not trace */);
+  vl_msg_api_set_handlers (VL_API_##N, #n, vl_api_##n##_t_handler,            \
+                          vl_noop_handler, vl_noop_handler,                  \
+                          vl_api_##n##_t_print, sizeof (vl_api_##n##_t),     \
+                          0 /* do not trace */, vl_api_##n##_t_print_json,   \
+                          vl_api_##n##_t_tojson, vl_api_##n##_t_fromjson);
   foreach_rpc_api_msg;
 #undef _
 
 #define _(N, n)                                                               \
-  vl_msg_api_set_handlers (                                                   \
-    VL_API_##N, #n, vl_api_##n##_t_handler, vl_noop_handler, vl_noop_handler, \
-    vl_api_##n##_t_print, sizeof (vl_api_##n##_t), 1 /* do trace */);
+  vl_msg_api_set_handlers (VL_API_##N, #n, vl_api_##n##_t_handler,            \
+                          vl_noop_handler, vl_noop_handler,                  \
+                          vl_api_##n##_t_print, sizeof (vl_api_##n##_t),     \
+                          1 /* do trace */, vl_api_##n##_t_print_json,       \
+                          vl_api_##n##_t_tojson, vl_api_##n##_t_fromjson);
   foreach_plugin_trace_msg;
 #undef _
 
+  am->api_trace_cfg[VL_API_TRACE_PLUGIN_MSG_IDS].replay_enable = 0;
+
   /* No reason to halt the parade to create a trace record... */
   am->is_mp_safe[VL_API_TRACE_PLUGIN_MSG_IDS] = 1;
   rpc_call_main_thread_cb_fn = vl_api_rpc_call_main_thread;
index 9db27eb..6c066a1 100644 (file)
 #include <vlibmemory/vl_memory_api_h.h>
 #undef vl_endianfun
 
-static inline void *
-vl_api_memclnt_create_t_print (vl_api_memclnt_create_t * a, void *handle)
-{
-  vl_print (handle, "vl_api_memclnt_create_t:\n");
-  vl_print (handle, "name: %s\n", a->name);
-  vl_print (handle, "input_queue: 0x%wx\n", a->input_queue);
-  vl_print (handle, "context: %u\n", (unsigned) a->context);
-  vl_print (handle, "ctx_quota: %ld\n", (long) a->ctx_quota);
-  return handle;
-}
-
-static inline void *
-vl_api_memclnt_delete_t_print (vl_api_memclnt_delete_t * a, void *handle)
-{
-  vl_print (handle, "vl_api_memclnt_delete_t:\n");
-  vl_print (handle, "index: %u\n", (unsigned) a->index);
-  vl_print (handle, "handle: 0x%wx\n", a->handle);
-  return handle;
-}
-
 volatile int **vl_api_queue_cursizes;
 
 static void
@@ -417,11 +397,11 @@ vl_api_memclnt_keepalive_t_handler (vl_api_memclnt_keepalive_t * mp)
  * don't trace memclnt_keepalive[_reply] msgs
  */
 
-#define foreach_vlib_api_msg                            \
-_(MEMCLNT_CREATE, memclnt_create, 1)                    \
-_(MEMCLNT_DELETE, memclnt_delete, 1)                    \
-_(MEMCLNT_KEEPALIVE, memclnt_keepalive, 0)              \
-_(MEMCLNT_KEEPALIVE_REPLY, memclnt_keepalive_reply, 0)
+#define foreach_vlib_api_msg                                                  \
+  _ (MEMCLNT_CREATE, memclnt_create, 0)                                       \
+  _ (MEMCLNT_DELETE, memclnt_delete, 0)                                       \
+  _ (MEMCLNT_KEEPALIVE, memclnt_keepalive, 0)                                 \
+  _ (MEMCLNT_KEEPALIVE_REPLY, memclnt_keepalive_reply, 0)
 
 /*
  * memory_api_init
index 64650b6..f0b05b7 100644 (file)
@@ -362,14 +362,14 @@ _(MEMCLNT_KEEPALIVE, memclnt_keepalive)
 void
 vl_client_install_client_message_handlers (void)
 {
-
-#define _(N,n)                                                  \
-    vl_msg_api_set_handlers(VL_API_##N, #n,                     \
-                            vl_api_##n##_t_handler,             \
-                            noop_handler,                       \
-                            vl_api_##n##_t_endian,              \
-                            vl_api_##n##_t_print,               \
-                            sizeof(vl_api_##n##_t), 1);
+  api_main_t *am = vlibapi_get_main ();
+#define _(N, n)                                                               \
+  vl_msg_api_set_handlers (VL_API_##N, #n, vl_api_##n##_t_handler,            \
+                          noop_handler, vl_api_##n##_t_endian,               \
+                          vl_api_##n##_t_print, sizeof (vl_api_##n##_t), 0,  \
+                          vl_api_##n##_t_print_json, vl_api_##n##_t_tojson,  \
+                          vl_api_##n##_t_fromjson);                          \
+  am->api_trace_cfg[VL_API_##N].replay_enable = 0;
   foreach_api_msg;
 #undef _
 }
index 60ca650..ce834a7 100644 (file)
@@ -495,7 +495,13 @@ vl_api_sockclnt_create_t_handler (vl_api_sockclnt_create_t * mp)
 
   regp = socket_main.current_rp;
 
-  ASSERT (regp->registration_type == REGISTRATION_TYPE_SOCKET_SERVER);
+  /* client already connected through shared memory? */
+  if (!regp || regp->registration_type != REGISTRATION_TYPE_SOCKET_SERVER)
+    {
+      clib_warning (
+       "unsupported API call: already connected though shared memory?");
+      return;
+    }
 
   regp->name = format (0, "%s%c", mp->name, 0);
 
@@ -765,14 +771,15 @@ reply:
   vl_sock_api_send_fd_msg (cf->file_descriptor, &memfd->fd, 1);
 }
 
-#define foreach_vlib_api_msg                           \
-  _(SOCKCLNT_CREATE, sockclnt_create, 1)               \
-  _(SOCKCLNT_DELETE, sockclnt_delete, 1)               \
-  _(SOCK_INIT_SHM, sock_init_shm, 1)
+#define foreach_vlib_api_msg                                                  \
+  _ (SOCKCLNT_CREATE, sockclnt_create, 0)                                     \
+  _ (SOCKCLNT_DELETE, sockclnt_delete, 0)                                     \
+  _ (SOCK_INIT_SHM, sock_init_shm, 0)
 
 clib_error_t *
 vl_sock_api_init (vlib_main_t * vm)
 {
+  api_main_t *am = vlibapi_get_main ();
   clib_file_main_t *fm = &file_main;
   clib_file_t template = { 0 };
   vl_api_registration_t *rp;
@@ -784,13 +791,13 @@ vl_sock_api_init (vlib_main_t * vm)
   if (sm->socket_name == 0)
     return 0;
 
-#define _(N,n,t)                                               \
-    vl_msg_api_set_handlers(VL_API_##N, #n,                     \
-                           vl_api_##n##_t_handler,              \
-                           vl_noop_handler,                     \
-                           vl_api_##n##_t_endian,               \
-                           vl_api_##n##_t_print,                \
-                           sizeof(vl_api_##n##_t), t);
+#define _(N, n, t)                                                            \
+  vl_msg_api_set_handlers (VL_API_##N, #n, vl_api_##n##_t_handler,            \
+                          vl_noop_handler, vl_api_##n##_t_endian,            \
+                          vl_api_##n##_t_print, sizeof (vl_api_##n##_t), t,  \
+                          vl_api_##n##_t_print_json, vl_api_##n##_t_tojson,  \
+                          vl_api_##n##_t_fromjson);                          \
+  am->api_trace_cfg[VL_API_##N].replay_enable = 0;
   foreach_vlib_api_msg;
 #undef _
 
index 69126f8..2fb6b8a 100644 (file)
@@ -432,13 +432,12 @@ void
 vl_sock_client_install_message_handlers (void)
 {
 
-#define _(N,n)                                                  \
-    vl_msg_api_set_handlers(VL_API_##N, #n,                     \
-                            vl_api_##n##_t_handler,             \
-                            noop_handler,                       \
-                            vl_api_##n##_t_endian,              \
-                            vl_api_##n##_t_print,               \
-                            sizeof(vl_api_##n##_t), 1);
+#define _(N, n)                                                               \
+  vl_msg_api_set_handlers (VL_API_##N, #n, vl_api_##n##_t_handler,            \
+                          noop_handler, vl_api_##n##_t_endian,               \
+                          vl_api_##n##_t_print, sizeof (vl_api_##n##_t), 0,  \
+                          vl_api_##n##_t_print_json, vl_api_##n##_t_tojson,  \
+                          vl_api_##n##_t_fromjson);
   foreach_sock_client_api_msg;
 #undef _
 }
index 0057c85..74ad3c5 100644 (file)
@@ -344,6 +344,7 @@ VLIB_CLI_COMMAND (cli_show_api_plugin_command, static) =
 typedef enum
 {
   DUMP,
+  DUMP_JSON,
   REPLAY,
   INITIALIZERS,
 } vl_api_replay_t;
@@ -391,6 +392,7 @@ vl_msg_api_process_file (vlib_main_t * vm, u8 * filename,
 {
   vl_api_trace_file_header_t *hp;
   int i, fd;
+  u16 *msgid_vec = 0;
   struct stat statb;
   size_t file_size;
   u8 *msg;
@@ -453,13 +455,31 @@ vl_msg_api_process_file (vlib_main_t * vm, u8 * filename,
     vlib_cli_output (vm,
                     "Note: wrapped/incomplete trace, results may vary\n");
 
+  size_t file_size_left = file_size;
+
+#define assert_size(size_left, s)                                             \
+  do                                                                          \
+    {                                                                         \
+      if ((s) >= size_left)                                                   \
+       {                                                                     \
+         vlib_cli_output (vm, "corrupted file");                             \
+         munmap (hp, file_size);                                             \
+         vec_free (msgid_vec);                                               \
+         return;                                                             \
+       }                                                                     \
+      size_left -= s;                                                         \
+    }                                                                         \
+  while (0);
+
+  assert_size (file_size_left, sizeof (hp[0]));
   msg = (u8 *) (hp + 1);
 
-  u16 *msgid_vec = 0;
   serialize_main_t _sm, *sm = &_sm;
   u32 msgtbl_size = ntohl (hp->msgtbl_size);
   u8 *name_and_crc;
 
+  assert_size (file_size_left, msgtbl_size);
+
   unserialize_open_data (sm, msg, msgtbl_size);
   unserialize_integer (sm, &nitems_msgtbl, sizeof (u32));
 
@@ -480,9 +500,11 @@ vl_msg_api_process_file (vlib_main_t * vm, u8 * filename,
       int size;
       u16 msg_id;
 
+      assert_size (file_size_left, sizeof (u32));
       size = clib_host_to_net_u32 (*(u32 *) msg);
       msg += sizeof (u32);
 
+      assert_size (file_size_left, size);
       msg_id = ntohs (*((u16 *) msg));
       if (msg_id < vec_len (msgid_vec))
        msg_id = msgid_vec[msg_id];
@@ -491,6 +513,7 @@ vl_msg_api_process_file (vlib_main_t * vm, u8 * filename,
        {
          vlib_cli_output (vm, "Ugh: msg id %d no trace config\n", msg_id);
          munmap (hp, file_size);
+         vec_free (msgid_vec);
          return;
        }
       msg += size;
@@ -536,7 +559,8 @@ vl_msg_api_process_file (vlib_main_t * vm, u8 * filename,
        * Endian swap if needed. All msg data is supposed to be in
        * network byte order.
        */
-      if (((which == DUMP) && clib_arch_is_little_endian))
+      if (((which == DUMP || which == DUMP_JSON) &&
+          clib_arch_is_little_endian))
        {
          void (*endian_fp) (void *);
          if (msg_id >= vec_len (am->msg_endian_handlers)
@@ -561,6 +585,23 @@ vl_msg_api_process_file (vlib_main_t * vm, u8 * filename,
 
       switch (which)
        {
+       case DUMP_JSON:
+         if (msg_id < vec_len (am->msg_print_json_handlers) &&
+             am->msg_print_json_handlers[msg_id])
+           {
+             u8 *(*print_fp) (void *, void *);
+
+             print_fp = (void *) am->msg_print_json_handlers[msg_id];
+             (*print_fp) (tmpbuf + sizeof (uword), vm);
+           }
+         else
+           {
+             vlib_cli_output (vm, "Skipping msg id %d: no JSON print fcn\n",
+                              msg_id);
+             break;
+           }
+         break;
+
        case DUMP:
          if (msg_id < vec_len (am->msg_print_handlers) &&
              am->msg_print_handlers[msg_id])
@@ -639,9 +680,319 @@ vl_msg_api_process_file (vlib_main_t * vm, u8 * filename,
 
   munmap (hp, file_size);
   vec_free (tmpbuf);
+  vec_free (msgid_vec);
   am->replay_in_progress = 0;
 }
 
+static int
+file_exists (u8 *fname)
+{
+  FILE *fp = 0;
+  fp = fopen ((char *) fname, "r");
+  if (fp)
+    {
+      fclose (fp);
+      return 1;
+    }
+  return 0;
+}
+
+typedef struct
+{
+  vlib_main_t *vm;
+  u8 is_json;
+} vl_msg_print_args;
+
+static int
+vl_msg_print_trace (u8 *msg, void *ctx)
+{
+  vl_msg_print_args *a = ctx;
+  api_main_t *am = vlibapi_get_main ();
+  u16 msg_id = ntohs (*((u16 *) msg));
+  void (*print_fp) (void *, void *);
+  void (**handlers) (void *, void *);
+  u8 is_json = a->is_json;
+  u8 *tmpbuf = 0;
+
+  if (clib_arch_is_little_endian)
+    {
+      u32 msg_length = vec_len (msg);
+      vec_validate (tmpbuf, msg_length - 1);
+      clib_memcpy_fast (tmpbuf, msg, msg_length);
+      msg = tmpbuf;
+
+      void (*endian_fp) (void *);
+      endian_fp = am->msg_endian_handlers[msg_id];
+      (*endian_fp) (tmpbuf);
+    }
+
+  if (is_json)
+    handlers = am->msg_print_json_handlers;
+  else
+    handlers = am->msg_print_handlers;
+
+  if (msg_id < vec_len (handlers) && handlers[msg_id])
+    {
+      print_fp = (void *) handlers[msg_id];
+      (*print_fp) (msg, a->vm);
+    }
+  else
+    {
+      vlib_cli_output (a->vm, "Skipping msg id %d: no print fcn\n", msg_id);
+    }
+
+  vec_free (tmpbuf);
+  return 0;
+}
+
+static int
+vl_msg_api_dump_trace (vlib_main_t *vm, vl_api_trace_which_t which, u8 is_json)
+{
+  api_main_t *am = vlibapi_get_main ();
+  vl_api_trace_t *tp;
+
+  switch (which)
+    {
+    case VL_API_TRACE_TX:
+      tp = am->tx_trace;
+      break;
+    case VL_API_TRACE_RX:
+      tp = am->rx_trace;
+      break;
+    default:
+      return -1;
+    }
+
+  if (tp == 0 || tp->nitems == 0 || vec_len (tp->traces) == 0)
+    return -1;
+
+  vl_msg_print_args args;
+  clib_memset (&args, 0, sizeof (args));
+  args.is_json = is_json;
+  args.vm = vm;
+  vl_msg_traverse_trace (tp, vl_msg_print_trace, &args);
+
+  return 0;
+}
+
+static char *
+vl_msg_read_file (FILE *f)
+{
+  const size_t bufsize = 1024;
+  char *buf[bufsize], *v = 0;
+  size_t n;
+
+  while ((n = fread (buf, 1, bufsize, f)))
+    vec_add (v, buf, n);
+
+  return v;
+}
+
+static u16
+vl_msg_find_id_by_name_and_crc (vlib_main_t *vm, api_main_t *am, char *name)
+{
+  uword *p;
+  p = hash_get_mem (am->msg_index_by_name_and_crc, name);
+  if (!p)
+    return (u16) ~0;
+
+  return p[0];
+}
+
+static u16
+vl_msg_find_id_by_name (vlib_main_t *vm, api_main_t *am, char *name)
+{
+  uword *p;
+
+  if (!am->msg_id_by_name)
+    {
+      vlib_cli_output (vm, "message id table not yet initialized!\n");
+      return (u16) ~0;
+    }
+
+  p = hash_get_mem (am->msg_id_by_name, name);
+  if (!p)
+    return (u16) ~0;
+
+  return p[0];
+}
+
+static int
+vl_msg_exec_json_command (vlib_main_t *vm, cJSON *o)
+{
+  api_main_t *am = vlibapi_get_main ();
+  u16 msg_id;
+  void *(*fromjson) (cJSON *, int *);
+  int len = 0, rv = -1;
+  trace_cfg_t *cfgp;
+  u8 *msg = 0;
+
+  cJSON *msg_id_obj = cJSON_GetObjectItem (o, "_msgname");
+  if (!msg_id_obj)
+    {
+      vlib_cli_output (vm, "Missing '_msgname' element!\n");
+      return rv;
+    }
+  char *name = cJSON_GetStringValue (msg_id_obj);
+
+  cJSON *crc_obj = cJSON_GetObjectItem (o, "_crc");
+  if (!msg_id_obj)
+    {
+      vlib_cli_output (vm, "Missing '_crc' element!\n");
+      return rv;
+    }
+  char *crc = cJSON_GetStringValue (crc_obj);
+  u8 proc_warning = 0;
+
+  u8 *name_crc = format (0, "%s_%s%c", name, crc, 0);
+  msg_id = vl_msg_find_id_by_name_and_crc (vm, am, (char *) name_crc);
+  if (msg_id == (u16) ~0)
+    {
+      msg_id = vl_msg_find_id_by_name (vm, am, name);
+      if (msg_id == (u16) ~0)
+       {
+         vlib_cli_output (vm, "unknown msg id %d!\n", msg_id);
+         vec_free (name_crc);
+         return rv;
+       }
+      proc_warning = 1;
+    }
+  vec_free (name_crc);
+
+  cfgp = am->api_trace_cfg + msg_id;
+  if (!cfgp)
+    {
+      vlib_cli_output (vm, "msg id %d no trace config\n", msg_id);
+      return rv;
+    }
+
+  if (cfgp->replay_enable)
+    {
+
+      if (proc_warning)
+       vlib_cli_output (vm, "warning: msg %d has different signature\n");
+
+      fromjson = am->msg_fromjson_handlers[msg_id];
+      if (!fromjson)
+       {
+         vlib_cli_output (vm, "missing fromjson convert function! id %d\n",
+                          msg_id);
+         return rv;
+       }
+
+      msg = (u8 *) fromjson (o, &len);
+      if (!msg)
+       {
+         vlib_cli_output (vm, "failed to convert JSON (msg id %d)!\n",
+                          msg_id);
+         return rv;
+       }
+
+      if (clib_arch_is_little_endian)
+       {
+         void (*endian_fp) (void *);
+         endian_fp = am->msg_endian_handlers[msg_id];
+         (*endian_fp) (msg);
+       }
+
+      void (*handler) (void *, vlib_main_t *);
+      handler = (void *) am->msg_handlers[msg_id];
+      if (!handler)
+       {
+         vlib_cli_output (vm, "no handler for msg id %d!\n", msg_id);
+         goto end;
+       }
+
+      if (!am->is_mp_safe[msg_id])
+       vl_msg_api_barrier_sync ();
+      (*handler) (msg, vm);
+      if (!am->is_mp_safe[msg_id])
+       vl_msg_api_barrier_release ();
+    }
+
+  rv = 0;
+end:
+  if (msg)
+    cJSON_free (msg);
+  return rv;
+}
+
+static void
+vl_msg_replay_json (vlib_main_t *vm, u8 *filename)
+{
+  api_main_t *am = vlibapi_get_main ();
+  cJSON *o = 0;
+  int rv = 0;
+  FILE *f = fopen ((char *) filename, "r");
+
+  if (!f)
+    {
+      vlib_cli_output (vm, "failed to open %s!\n", filename);
+      return;
+    }
+
+  char *buf = vl_msg_read_file (f);
+  fclose (f);
+
+  o = cJSON_Parse (buf);
+  vec_free (buf);
+  if (!o)
+    {
+      vlib_cli_output (vm, "%s: Failed parsing JSON input: %s\n", filename,
+                      cJSON_GetErrorPtr ());
+      return;
+    }
+
+  if (cJSON_IsArray (o))
+    {
+      am->replay_in_progress = 1;
+      size_t size = cJSON_GetArraySize (o);
+      for (int i = 0; i < size; i++)
+       {
+         rv = vl_msg_exec_json_command (vm, cJSON_GetArrayItem (o, i));
+         if (rv < 0)
+           {
+             am->replay_in_progress = 0;
+             break;
+           }
+       }
+    }
+  else
+    {
+      rv = vl_msg_exec_json_command (vm, o);
+    }
+
+  if (rv < 0)
+    vlib_cli_output (vm, "error during replaying API trace");
+
+  cJSON_Delete (o);
+}
+
+static void
+vl_msg_dump_file_json (vlib_main_t *vm, u8 *filename)
+{
+  FILE *f = fopen ((char *) filename, "r");
+  char *buf;
+
+  if (!f)
+    {
+      vlib_cli_output (vm, "failed to open %s!\n", filename);
+      return;
+    }
+
+  buf = vl_msg_read_file (f);
+  fclose (f);
+
+  if (!buf)
+    {
+      vlib_cli_output (vm, "no content in %s!\n", filename);
+      return;
+    }
+
+  vlib_cli_output (vm, buf);
+  vec_free (buf);
+}
+
 /** api_trace_command_fn - control the binary API trace / replay feature
 
     Note: this command MUST be marked thread-safe. Replay with
@@ -688,6 +1039,43 @@ api_trace_command_fn (vlib_main_t * vm,
          vl_msg_api_trace_onoff (am, which, 0);
          vlib_worker_thread_barrier_release (vm);
        }
+      else if (unformat (line_input, "save-json %s", &filename))
+       {
+         if (strstr ((char *) filename, "..") ||
+             index ((char *) filename, '/'))
+           {
+             vlib_cli_output (vm, "illegal characters in filename '%s'",
+                              filename);
+             goto out;
+           }
+
+         chroot_filename = format (0, "/tmp/%s%c", filename, 0);
+
+         vec_free (filename);
+
+         if (file_exists (chroot_filename))
+           {
+             vlib_cli_output (vm, "file exists: %s\n", chroot_filename);
+             goto out;
+           }
+
+         fp = fopen ((char *) chroot_filename, "w");
+         if (fp == NULL)
+           {
+             vlib_cli_output (vm, "Couldn't create %s\n", chroot_filename);
+             goto out;
+           }
+         vlib_worker_thread_barrier_sync (vm);
+         rv = vl_msg_api_trace_save (am, which, fp, 1);
+         if (rv == -1)
+           vlib_cli_output (vm, "API Trace data not present\n");
+         else if (rv < 0)
+           vlib_cli_output (vm, "failed to save api trace\n");
+         else
+           vlib_cli_output (vm, "API trace saved to %s\n", chroot_filename);
+         vlib_worker_thread_barrier_release (vm);
+         fclose (fp);
+       }
       else if (unformat (line_input, "save %s", &filename))
        {
          if (strstr ((char *) filename, "..")
@@ -702,6 +1090,12 @@ api_trace_command_fn (vlib_main_t * vm,
 
          vec_free (filename);
 
+         if (file_exists (chroot_filename))
+           {
+             vlib_cli_output (vm, "file exists: %s\n", chroot_filename);
+             goto out;
+           }
+
          fp = fopen ((char *) chroot_filename, "w");
          if (fp == NULL)
            {
@@ -709,7 +1103,7 @@ api_trace_command_fn (vlib_main_t * vm,
              goto out;
            }
          vlib_worker_thread_barrier_sync (vm);
-         rv = vl_msg_api_trace_save (am, which, fp);
+         rv = vl_msg_api_trace_save (am, which, fp, 0);
          vlib_worker_thread_barrier_release (vm);
          fclose (fp);
          if (rv == -1)
@@ -732,10 +1126,30 @@ api_trace_command_fn (vlib_main_t * vm,
            vlib_cli_output (vm, "API trace saved to %s\n", chroot_filename);
          goto out;
        }
-      else if (unformat (line_input, "dump %s", &filename))
+      else if (unformat (line_input, "tojson %s", &filename))
+       {
+         vl_msg_api_process_file (vm, filename, first, last, DUMP_JSON);
+       }
+      else if (unformat (line_input, "dump-file-json %s", &filename))
+       {
+         vl_msg_dump_file_json (vm, filename);
+       }
+      else if (unformat (line_input, "dump-file %s", &filename))
        {
          vl_msg_api_process_file (vm, filename, first, last, DUMP);
        }
+      else if (unformat (line_input, "dump-json"))
+       {
+         vl_msg_api_dump_trace (vm, which, 1);
+       }
+      else if (unformat (line_input, "dump"))
+       {
+         vl_msg_api_dump_trace (vm, which, 0);
+       }
+      else if (unformat (line_input, "replay-json %s", &filename))
+       {
+         vl_msg_replay_json (vm, filename);
+       }
       else if (unformat (line_input, "replay %s", &filename))
        {
          vl_msg_api_process_file (vm, filename, first, last, REPLAY);
@@ -790,91 +1204,15 @@ out:
 /* *INDENT-OFF* */
 VLIB_CLI_COMMAND (api_trace_command, static) = {
   .path = "api trace",
-  .short_help = "api trace [on|off][first <n>][last <n>][status][free]"
-               "[post-mortem-on][dump|save|replay <file>]",
+  .short_help = "api trace [tx][on|off][first <n>][last <n>][status][free]"
+               "[post-mortem-on][dump|dump-file|dump-json|save|tojson|save-"
+               "json|replay <file>|replay-json <file>][nitems <n>]"
+               "[initializers <file>]",
   .function = api_trace_command_fn,
   .is_mp_safe = 1,
 };
 /* *INDENT-ON* */
 
-static clib_error_t *
-vl_api_trace_command (vlib_main_t * vm,
-                     unformat_input_t * input, vlib_cli_command_t * cli_cmd)
-{
-  u32 nitems = 1024;
-  vl_api_trace_which_t which = VL_API_TRACE_RX;
-  api_main_t *am = vlibapi_get_main ();
-
-  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
-    {
-      if (unformat (input, "rx nitems %u", &nitems) || unformat (input, "rx"))
-       goto configure;
-      else if (unformat (input, "tx nitems %u", &nitems)
-              || unformat (input, "tx"))
-       {
-         which = VL_API_TRACE_RX;
-         goto configure;
-       }
-      else if (unformat (input, "on rx"))
-       {
-         vl_msg_api_trace_onoff (am, VL_API_TRACE_RX, 1);
-       }
-      else if (unformat (input, "on tx"))
-       {
-         vl_msg_api_trace_onoff (am, VL_API_TRACE_TX, 1);
-       }
-      else if (unformat (input, "on"))
-       {
-         vl_msg_api_trace_onoff (am, VL_API_TRACE_RX, 1);
-       }
-      else if (unformat (input, "off"))
-       {
-         vl_msg_api_trace_onoff (am, VL_API_TRACE_RX, 0);
-         vl_msg_api_trace_onoff (am, VL_API_TRACE_TX, 0);
-       }
-      else if (unformat (input, "free"))
-       {
-         vl_msg_api_trace_onoff (am, VL_API_TRACE_RX, 0);
-         vl_msg_api_trace_onoff (am, VL_API_TRACE_TX, 0);
-         vl_msg_api_trace_free (am, VL_API_TRACE_RX);
-         vl_msg_api_trace_free (am, VL_API_TRACE_TX);
-       }
-      else if (unformat (input, "debug on"))
-       {
-         am->msg_print_flag = 1;
-       }
-      else if (unformat (input, "debug off"))
-       {
-         am->msg_print_flag = 0;
-       }
-      else
-       return clib_error_return (0, "unknown input `%U'",
-                                 format_unformat_error, input);
-    }
-  return 0;
-
-configure:
-  if (vl_msg_api_trace_configure (am, which, nitems))
-    {
-      vlib_cli_output (vm, "warning: trace configure error (%d, %d)",
-                      which, nitems);
-    }
-
-  return 0;
-}
-
-/*?
- * Control the binary API trace mechanism
-?*/
-/* *INDENT-OFF* */
-VLIB_CLI_COMMAND (trace, static) =
-{
-  .path = "set api-trace",
-  .short_help = "API trace [on][on tx][on rx][off][free][debug on][debug off]",
-  .function = vl_api_trace_command,
-};
-/* *INDENT-ON* */
-
 static clib_error_t *
 api_trace_config_fn (vlib_main_t * vm, unformat_input_t * input)
 {
index d6216c6..7d42f1b 100644 (file)
@@ -190,10 +190,11 @@ sr_mpls_api_hookup (vlib_main_t * vm)
   vec_free (name);
 
 #define _(N, n)                                                               \
-  vl_msg_api_set_handlers (REPLY_MSG_ID_BASE + VL_API_##N, #n,                \
-                          vl_api_##n##_t_handler, vl_noop_handler,           \
-                          vl_api_##n##_t_endian, vl_api_##n##_t_print,       \
-                          sizeof (vl_api_##n##_t), 1);
+  vl_msg_api_set_handlers (                                                   \
+    REPLY_MSG_ID_BASE + VL_API_##N, #n, vl_api_##n##_t_handler,               \
+    vl_noop_handler, vl_api_##n##_t_endian, vl_api_##n##_t_print,             \
+    sizeof (vl_api_##n##_t), 1, vl_api_##n##_t_print_json,                    \
+    vl_api_##n##_t_tojson, vl_api_##n##_t_fromjson);
   foreach_vpe_api_msg;
 #undef _
 
@@ -201,21 +202,23 @@ sr_mpls_api_hookup (vlib_main_t * vm)
    * Manually register the sr policy add msg, so we trace enough bytes
    * to capture a typical segment list
    */
-  vl_msg_api_set_handlers (REPLY_MSG_ID_BASE + VL_API_SR_MPLS_POLICY_ADD,
-                          "sr_mpls_policy_add",
-                          vl_api_sr_mpls_policy_add_t_handler,
-                          vl_noop_handler, vl_api_sr_mpls_policy_add_t_endian,
-                          vl_api_sr_mpls_policy_add_t_print, 256, 1);
+  vl_msg_api_set_handlers (
+    REPLY_MSG_ID_BASE + VL_API_SR_MPLS_POLICY_ADD, "sr_mpls_policy_add",
+    vl_api_sr_mpls_policy_add_t_handler, vl_noop_handler,
+    vl_api_sr_mpls_policy_add_t_endian, vl_api_sr_mpls_policy_add_t_print, 256,
+    1, vl_api_sr_mpls_policy_add_t_print_json,
+    vl_api_sr_mpls_policy_mod_t_tojson, vl_api_sr_mpls_policy_mod_t_fromjson);
 
   /*
    * Manually register the sr policy mod msg, so we trace enough bytes
    * to capture a typical segment list
    */
-  vl_msg_api_set_handlers (REPLY_MSG_ID_BASE + VL_API_SR_MPLS_POLICY_MOD,
-                          "sr_mpls_policy_mod",
-                          vl_api_sr_mpls_policy_mod_t_handler,
-                          vl_noop_handler, vl_api_sr_mpls_policy_mod_t_endian,
-                          vl_api_sr_mpls_policy_mod_t_print, 256, 1);
+  vl_msg_api_set_handlers (
+    REPLY_MSG_ID_BASE + VL_API_SR_MPLS_POLICY_MOD, "sr_mpls_policy_mod",
+    vl_api_sr_mpls_policy_mod_t_handler, vl_noop_handler,
+    vl_api_sr_mpls_policy_mod_t_endian, vl_api_sr_mpls_policy_mod_t_print, 256,
+    1, vl_api_sr_mpls_policy_mod_t_print_json,
+    vl_api_sr_mpls_policy_mod_t_tojson, vl_api_sr_mpls_policy_mod_t_fromjson);
 
   /*
    * Set up the (msg_name, crc, message-id) table
index 542df9d..7a30792 100644 (file)
@@ -101,14 +101,6 @@ cleanup (void)
   clib_memset(pm, 0, sizeof(*pm));
 }
 
-/*
- * Satisfy external references when -lvlib is not available.
- */
-void vlib_cli_output (struct vlib_main_t * vm, char * fmt, ...)
-{
-  clib_warning ("vlib_cli_output called...");
-}
-
 void
 vac_free (void * msg)
 {
index 4be291e..1114092 100644 (file)
@@ -39,7 +39,7 @@ install(
 add_definitions(-fvisibility=hidden)
 
 # Ensure symbols from cJSON are exported
-set_source_files_properties( cJSON.c PROPERTIES
+set_source_files_properties( cJSON.c jsonformat.c PROPERTIES
   COMPILE_DEFINITIONS " CJSON_API_VISIBILITY " )
 
 
@@ -62,6 +62,7 @@ set(VPPINFRA_SRCS
   hash.c
   heap.c
   interrupt.c
+  jsonformat.c
   longjmp.S
   macros.c
   maplog.c
@@ -140,6 +141,7 @@ set(VPPINFRA_HEADERS
   hash.h
   heap.h
   interrupt.h
+  jsonformat.h
   lb_hash_hash.h
   llist.h
   lock.h
index 5b26a4b..448435d 100644 (file)
@@ -157,7 +157,7 @@ typedef struct internal_hooks
 {
     void *(CJSON_CDECL *allocate)(size_t size);
     void (CJSON_CDECL *deallocate)(void *pointer);
-    void *(CJSON_CDECL *reallocate)(void *pointer, size_t size);
+    void *(CJSON_CDECL *reallocate)(void *pointer, size_t new_size, size_t old_size);
 } internal_hooks;
 
 #if defined(_MSC_VER)
@@ -170,16 +170,20 @@ static void CJSON_CDECL internal_free(void *pointer)
 {
     free(pointer);
 }
-static void * CJSON_CDECL internal_realloc(void *pointer, size_t size)
-{
-    return realloc(pointer, size);
-}
 #else
 #define internal_malloc malloc
 #define internal_free free
-#define internal_realloc realloc
 #endif
 
+static void * CJSON_CDECL internal_realloc(void *pointer, size_t new_size,
+    size_t old_size)
+{
+    return realloc(pointer, new_size);
+}
+
+static void *
+cjson_realloc_internal (void *ptr, size_t new_size, size_t old_size);
+
 /* strlen of character literals resolved at compile time */
 #define static_strlen(string_literal) (sizeof(string_literal) - sizeof(""))
 
@@ -213,7 +217,7 @@ CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks)
         /* Reset hooks */
         global_hooks.allocate = malloc;
         global_hooks.deallocate = free;
-        global_hooks.reallocate = realloc;
+        global_hooks.reallocate = internal_realloc;
         return;
     }
 
@@ -233,7 +237,11 @@ CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks)
     global_hooks.reallocate = NULL;
     if ((global_hooks.allocate == malloc) && (global_hooks.deallocate == free))
     {
-        global_hooks.reallocate = realloc;
+        global_hooks.reallocate = internal_realloc;
+    }
+    else
+    {
+        global_hooks.reallocate = cjson_realloc_internal;
     }
 }
 
@@ -435,6 +443,27 @@ typedef struct
     internal_hooks hooks;
 } printbuffer;
 
+static void *
+cjson_realloc_internal (void *ptr, size_t new_size, size_t old_size)
+{
+    size_t copy_size;
+    if (old_size < new_size)
+      copy_size = old_size;
+    else
+      copy_size = new_size;
+
+    unsigned char *newbuffer = global_hooks.allocate(new_size);
+    if (!newbuffer)
+    {
+        global_hooks.deallocate(ptr);
+        return NULL;
+    }
+
+    memcpy (newbuffer, ptr, copy_size);
+    global_hooks.deallocate (ptr);
+    return newbuffer;
+}
+
 /* realloc printbuffer if necessary to have at least "needed" bytes more */
 static unsigned char* ensure(printbuffer * const p, size_t needed)
 {
@@ -486,34 +515,13 @@ static unsigned char* ensure(printbuffer * const p, size_t needed)
         newsize = needed * 2;
     }
 
-    if (p->hooks.reallocate != NULL)
+    newbuffer = p->hooks.reallocate (p->buffer, newsize, p->length);
+    if (newbuffer == NULL)
     {
-        /* reallocate with realloc if available */
-        newbuffer = (unsigned char*)p->hooks.reallocate(p->buffer, newsize);
-        if (newbuffer == NULL)
-        {
-            p->hooks.deallocate(p->buffer);
-            p->length = 0;
-            p->buffer = NULL;
-
-            return NULL;
-        }
-    }
-    else
-    {
-        /* otherwise reallocate manually */
-        newbuffer = (unsigned char*)p->hooks.allocate(newsize);
-        if (!newbuffer)
-        {
-            p->hooks.deallocate(p->buffer);
-            p->length = 0;
-            p->buffer = NULL;
-
-            return NULL;
-        }
-
-       memcpy (newbuffer, p->buffer, p->offset + 1);
-       p->hooks.deallocate (p->buffer);
+        p->hooks.deallocate(p->buffer);
+        p->length = 0;
+        p->buffer = NULL;
+        return NULL;
     }
     p->length = newsize;
     p->buffer = newbuffer;
@@ -1208,7 +1216,7 @@ static unsigned char *print(const cJSON * const item, cJSON_bool format, const i
     /* check if reallocate is available */
     if (hooks->reallocate != NULL)
     {
-        printed = (unsigned char*) hooks->reallocate(buffer->buffer, buffer->offset + 1);
+        printed = (unsigned char*) hooks->reallocate(buffer->buffer, buffer->offset + 1, default_buffer_size);
         if (printed == NULL) {
             goto fail;
         }
@@ -3112,3 +3120,8 @@ CJSON_PUBLIC(void) cJSON_free(void *object)
 {
     global_hooks.deallocate(object);
 }
+
+CJSON_PUBLIC(void *) cJSON_realloc(void *object, size_t new_size, size_t old_size)
+{
+    return global_hooks.reallocate(object, new_size, old_size);
+}
index e97e5f4..1474c4e 100644 (file)
@@ -127,6 +127,8 @@ typedef struct cJSON_Hooks
       /* malloc/free are CDECL on Windows regardless of the default calling convention of the compiler, so ensure the hooks allow passing those functions directly. */
       void *(CJSON_CDECL *malloc_fn)(size_t sz);
       void (CJSON_CDECL *free_fn)(void *ptr);
+      void *(CJSON_CDECL *realloc_fn) (void *ptr, size_t new_size,
+                                      size_t old_size);
 } cJSON_Hooks;
 
 typedef int cJSON_bool;
@@ -285,6 +287,8 @@ CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring)
 /* malloc/free objects using the malloc/free functions that have been set with cJSON_InitHooks */
 CJSON_PUBLIC(void *) cJSON_malloc(size_t size);
 CJSON_PUBLIC(void) cJSON_free(void *object);
+CJSON_PUBLIC (void *)
+cJSON_realloc (void *object, size_t new_size, size_t old_size);
 
 #ifdef __cplusplus
 }
similarity index 95%
rename from src/vat2/jsonconvert.c
rename to src/vppinfra/jsonformat.c
index 1437b90..17b3ee9 100644 (file)
 #include <vnet/ip/ip6_packet.h>
 #include <vnet/ip/ip_format_fns.h>
 #include <vpp/api/types.h>
-#include "jsonconvert.h"
-
-#define _(T)                                    \
-int vl_api_ ##T## _fromjson(cJSON *o, T *d)     \
-{                                               \
-    if (!cJSON_IsNumber(o)) return -1;          \
-    memcpy(d, &o->valueint, sizeof(T));         \
-    return 0;                                   \
-}
-  foreach_vat2_fromjson
+#include "jsonformat.h"
+
+#define _(T)                                                                  \
+  int vl_api_##T##_fromjson (cJSON *o, T *d)                                  \
+  {                                                                           \
+    if (!cJSON_IsNumber (o))                                                  \
+      return -1;                                                              \
+    d[0] = (T) cJSON_GetNumberValue (o);                                      \
+    return 0;                                                                 \
+  }
+foreach_type_fromjson
 #undef _
 
-int vl_api_bool_fromjson(cJSON *o, bool *d)
+  int
+  vl_api_bool_fromjson (cJSON *o, bool *d)
 {
     if (!cJSON_IsBool(o)) return -1;
     *d = o->valueint ? true : false;
@@ -437,13 +439,6 @@ vl_api_c_string_to_api_string (const char *buf, vl_api_string_t * str)
   return len + sizeof (u32);
 }
 
-u8 *
-format_vl_api_interface_index_t (u8 *s, va_list *args)
-{
-  u32 *a = va_arg (*args, u32 *);
-  return format (s, "%u", *a);
-}
-
 void
 vl_api_string_cJSON_AddToObject(cJSON * const object, const char * const name, vl_api_string_t *astr)
 {
@@ -490,10 +485,6 @@ unformat_vl_api_timestamp_t(unformat_input_t * input, va_list * args)
 {
     return 0;
 }
-u8 *format_vl_api_gbp_scope_t(u8 * s, va_list * args)
-{
-    return 0;
-}
 uword unformat_vl_api_gbp_scope_t(unformat_input_t * input, va_list * args)
 {
     return 0;
@@ -527,5 +518,5 @@ format_vl_api_mac_address_t (u8 * s, va_list * args)
   vec_free(s);                                              \
   return o;                                                 \
   }
-foreach_vat2_tojson
+foreach_type_tojson
 #undef _
diff --git a/src/vppinfra/jsonformat.h b/src/vppinfra/jsonformat.h
new file mode 100644 (file)
index 0000000..d27785f
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2020 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef included_json_convert_h
+#define included_json_convert_h
+
+#include <stdbool.h>
+#include <vppinfra/cJSON.h>
+#include <vnet/ethernet/mac_address.h>
+#include <vnet/ip/ip6_packet.h>
+#include <vnet/ip/ip_types.api_types.h>
+#include <vnet/ethernet/ethernet_types.api_types.h>
+
+#define foreach_type_fromjson                                                 \
+  _ (i8)                                                                      \
+  _ (u8)                                                                      \
+  _ (i16)                                                                     \
+  _ (u16)                                                                     \
+  _ (i32)                                                                     \
+  _ (u32)                                                                     \
+  _ (u64)                                                                     \
+  _ (f64)
+
+#define _(T) CJSON_PUBLIC (int) vl_api_##T##_fromjson (cJSON *o, T *d);
+foreach_type_fromjson
+#undef _
+
+/* Prototypes */
+CJSON_PUBLIC (int) vl_api_bool_fromjson (cJSON *o, bool *d);
+CJSON_PUBLIC (int)
+vl_api_ip4_address_t_fromjson (void **mp, int *len, cJSON *o,
+                              vl_api_ip4_address_t *a);
+CJSON_PUBLIC (int)
+vl_api_ip4_prefix_t_fromjson (void **mp, int *len, cJSON *o,
+                             vl_api_ip4_prefix_t *a);
+CJSON_PUBLIC (int)
+vl_api_ip4_address_with_prefix_t_fromjson (void **mp, int *len, cJSON *o,
+                                          vl_api_ip4_prefix_t *a);
+CJSON_PUBLIC (int)
+vl_api_ip6_address_t_fromjson (void **mp, int *len, cJSON *o,
+                              vl_api_ip6_address_t *a);
+CJSON_PUBLIC (int)
+vl_api_ip6_prefix_t_fromjson (void **mp, int *len, cJSON *o,
+                             vl_api_ip6_prefix_t *a);
+CJSON_PUBLIC (int)
+vl_api_ip6_address_with_prefix_t_fromjson (void **mp, int *len, cJSON *o,
+                                          vl_api_ip6_prefix_t *a);
+CJSON_PUBLIC (int)
+vl_api_address_t_fromjson (void **mp, int *len, cJSON *o, vl_api_address_t *a);
+CJSON_PUBLIC (int)
+vl_api_prefix_t_fromjson (void **mp, int *len, cJSON *o, vl_api_prefix_t *a);
+CJSON_PUBLIC (int)
+vl_api_address_with_prefix_t_fromjson (void **mp, int *len, cJSON *o,
+                                      vl_api_prefix_t *a);
+CJSON_PUBLIC (int)
+vl_api_mac_address_t_fromjson (void **mp, int *len, cJSON *o,
+                              vl_api_mac_address_t *a);
+
+CJSON_PUBLIC (uword)
+unformat_ip4_address (unformat_input_t *input, va_list *args);
+CJSON_PUBLIC (uword)
+unformat_ip6_address (unformat_input_t *input, va_list *args);
+CJSON_PUBLIC (u8 *) format_ip6_address (u8 *s, va_list *args);
+CJSON_PUBLIC (uword)
+unformat_mac_address (unformat_input_t *input, va_list *args);
+CJSON_PUBLIC (u8 *) format_ip4_address (u8 *s, va_list *args);
+CJSON_PUBLIC (uword)
+unformat_vl_api_timedelta_t (unformat_input_t *input, va_list *args);
+CJSON_PUBLIC (uword)
+unformat_vl_api_timestamp_t (unformat_input_t *input, va_list *args);
+CJSON_PUBLIC (uword)
+unformat_vl_api_gbp_scope_t (unformat_input_t *input, va_list *args);
+
+CJSON_PUBLIC (int)
+vl_api_c_string_to_api_string (const char *buf, vl_api_string_t *str);
+CJSON_PUBLIC (void)
+vl_api_string_cJSON_AddToObject (cJSON *const object, const char *const name,
+                                vl_api_string_t *astr);
+
+CJSON_PUBLIC (u8 *) u8string_fromjson (cJSON *o, char *fieldname);
+CJSON_PUBLIC (int) u8string_fromjson2 (cJSON *o, char *fieldname, u8 *data);
+CJSON_PUBLIC (int) vl_api_u8_string_fromjson (cJSON *o, u8 *s, int len);
+
+#define foreach_type_tojson                                                   \
+  _ (ip4_address)                                                             \
+  _ (ip4_prefix)                                                              \
+  _ (ip6_address)                                                             \
+  _ (ip6_prefix)                                                              \
+  _ (address)                                                                 \
+  _ (prefix)                                                                  \
+  _ (mac_address)
+
+#define _(T) CJSON_PUBLIC (cJSON *) vl_api_##T##_t_tojson (vl_api_##T##_t *);
+foreach_type_tojson
+#undef _
+
+CJSON_PUBLIC (cJSON *)
+  vl_api_ip4_address_with_prefix_t_tojson (vl_api_ip4_prefix_t *a);
+CJSON_PUBLIC (cJSON *)
+vl_api_ip6_address_with_prefix_t_tojson (vl_api_ip6_prefix_t *a);
+CJSON_PUBLIC (cJSON *)
+vl_api_address_with_prefix_t_tojson (vl_api_prefix_t *a);
+
+#endif
diff --git a/test/test_api_trace.py b/test/test_api_trace.py
new file mode 100644 (file)
index 0000000..6524878
--- /dev/null
@@ -0,0 +1,63 @@
+import os
+import unittest
+from framework import VppTestCase, VppTestRunner
+from vpp_papi import VppEnum
+import json
+
+
+class TestJsonApiTrace(VppTestCase):
+    """ JSON API trace related tests """
+
+    @classmethod
+    def setUpClass(cls):
+        super(TestJsonApiTrace, cls).setUpClass()
+
+    def setUp(self):
+        self.vapi.cli("api trace free")
+        self.vapi.cli("api trace on")
+        self.vapi.cli("api trace tx on")
+
+    @classmethod
+    def tearDownClass(cls):
+        super(TestJsonApiTrace, cls).tearDownClass()
+
+    def test_json_api_trace_save(self):
+        self.vapi.show_version()
+
+        fname = 'test_api_trace-%d.json' % self.vpp.pid
+        tmp_api_trace = "/tmp/%s" % fname
+        fpath = '%s/%s' % (self.tempdir, fname)
+        self.vapi.cli("api trace save-json {}".format(fname))
+        os.rename(tmp_api_trace, fpath)
+        with open(fpath, encoding='utf-8') as f:
+            s = f.read()
+        trace = json.loads(s)
+        found = False
+        for o in trace:
+            if o['_msgname'] == 'show_version':
+                found = True
+                break
+        self.assertTrue(found)
+        self.assertEquals(o['_msgname'], 'show_version')
+
+    def test_json_api_trace_replay(self):
+        fname = '/tmp/create_loop.json'
+        req = """
+[
+{
+        "_msgname": "create_loopback",
+        "_crc": "42bb5d22",
+        "mac_address": "00:00:00:00:00:00"
+}]
+"""
+        with open(fname, 'w') as f:
+            f.write(req)
+        self.vapi.cli("api trace replay-json {}".format(fname))
+        r = self.vapi.sw_interface_dump(name_filter='loop',
+                                        name_filter_valid=True)
+        self.assertEqual(len(r), 1)
+        self.assertEqual(r[0].interface_name, 'loop0')
+
+
+if __name__ == '__main__':
+    unittest.main(testRunner=VppTestRunner)