From 36217e3ca8a1ca2e7a341b6b44ffc25e6497191c Mon Sep 17 00:00:00 2001 From: Filip Tehlar Date: Fri, 23 Jul 2021 08:51:10 +0000 Subject: [PATCH] api: API trace improvements 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 Signed-off-by: Ole Troan --- docs/about.rst | 4 +- .../developers/binary_api_support.rst | 11 +- docs/reference/cmdreference/trace/trace.rst | 117 ++--- src/plugins/hs_apps/sapi/vpp_echo_bapi.c | 9 +- src/plugins/mactime/mactime_top.c | 14 +- src/plugins/tracedump/tracedump_test.c | 12 +- src/tools/vppapigen/vppapigen_c.py | 71 ++- src/tools/vppapitrace/vppapitrace | 1 - src/tools/vppapitrace/vppapitrace.py | 492 -------------------- src/vat/api_format.c | 4 +- src/vat2/CMakeLists.txt | 4 - src/vat2/jsonconvert.h | 105 ----- src/vat2/main.c | 68 ++- src/vat2/test/vat2_test.c | 1 + src/vcl/vcl_bapi.c | 9 +- src/vlibapi/api.h | 9 +- src/vlibapi/api_common.h | 25 +- src/vlibapi/api_shared.c | 283 ++++++++---- src/vlibmemory/CMakeLists.txt | 6 + src/vlibmemory/memclnt.api | 4 +- src/vlibmemory/memclnt_api.c | 34 +- src/vlibmemory/memory_api.c | 30 +- src/vlibmemory/memory_client.c | 16 +- src/vlibmemory/socket_api.c | 31 +- src/vlibmemory/socket_client.c | 13 +- src/vlibmemory/vlib_api_cli.c | 506 +++++++++++++++++---- src/vnet/srmpls/sr_mpls_api.c | 31 +- src/vpp-api/client/client.c | 8 - src/vppinfra/CMakeLists.txt | 4 +- src/vppinfra/cJSON.c | 85 ++-- src/vppinfra/cJSON.h | 4 + src/{vat2/jsonconvert.c => vppinfra/jsonformat.c} | 37 +- src/vppinfra/jsonformat.h | 116 +++++ test/test_api_trace.py | 63 +++ 34 files changed, 1165 insertions(+), 1062 deletions(-) delete mode 120000 src/tools/vppapitrace/vppapitrace delete mode 100755 src/tools/vppapitrace/vppapitrace.py delete mode 100644 src/vat2/jsonconvert.h rename src/{vat2/jsonconvert.c => vppinfra/jsonformat.c} (95%) create mode 100644 src/vppinfra/jsonformat.h create mode 100644 test/test_api_trace.py diff --git a/docs/about.rst b/docs/about.rst index 7f9a88d378e..dc04d09d0fc 100644 --- a/docs/about.rst +++ b/docs/about.rst @@ -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 diff --git a/docs/gettingstarted/developers/binary_api_support.rst b/docs/gettingstarted/developers/binary_api_support.rst index 732ce978daa..f93bbc296fc 100644 --- a/docs/gettingstarted/developers/binary_api_support.rst +++ b/docs/gettingstarted/developers/binary_api_support.rst @@ -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 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. diff --git a/docs/reference/cmdreference/trace/trace.rst b/docs/reference/cmdreference/trace/trace.rst index 9d120ed9cab..927342fa3d7 100644 --- a/docs/reference/cmdreference/trace/trace.rst +++ b/docs/reference/cmdreference/trace/trace.rst @@ -1,58 +1,59 @@ -.. _interface: - -.. toctree:: - -.. note:: For a complete list of CLI Debug commands refer to the Debug CLI section of the `Source Code Documents `_ . - - -API Trace -=========== - -Summary/Usage --------------- - -api trace [on|off][first <*n*>][last <*n*>][status][free][post-mortem-on][dump|custom-dump|save|replay <*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 \ 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 `_ . + + +API Trace +=========== + +Summary/Usage +-------------- + +api trace [tx][on|off][first ][last ][status][free] + [post-mortem-on][dump|dump-file|dump-json|save|tojson|save-json|replay ][nitems ][initializers ] + +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 diff --git a/src/plugins/hs_apps/sapi/vpp_echo_bapi.c b/src/plugins/hs_apps/sapi/vpp_echo_bapi.c index 38fb522351c..30f3b78aff3 100644 --- a/src/plugins/hs_apps/sapi/vpp_echo_bapi.c +++ b/src/plugins/hs_apps/sapi/vpp_echo_bapi.c @@ -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 _ } diff --git a/src/plugins/mactime/mactime_top.c b/src/plugins/mactime/mactime_top.c index 72d1964f32f..ed4c7345721 100644 --- a/src/plugins/mactime/mactime_top.c +++ b/src/plugins/mactime/mactime_top.c @@ -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 _ diff --git a/src/plugins/tracedump/tracedump_test.c b/src/plugins/tracedump/tracedump_test.c index 3bf50efb4ac..05191a49ac4 100644 --- a/src/plugins/tracedump/tracedump_test.c +++ b/src/plugins/tracedump/tracedump_test.c @@ -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 diff --git a/src/tools/vppapigen/vppapigen_c.py b/src/tools/vppapigen/vppapigen_c.py index db684adb06d..ae47625a474 100644 --- a/src/tools/vppapigen/vppapigen_c.py +++ b/src/tools/vppapigen/vppapigen_c.py @@ -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 \n\n') - write('#include \n\n') + write('#include \n\n') + if self.module == 'interface_types': + write('#define vl_printfun\n') + write('#include \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 \n\n') - write('#include \n\n') + write('#include \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 index d0ece85a809..00000000000 --- a/src/tools/vppapitrace/vppapitrace +++ /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 index 8089b3a2236..00000000000 --- a/src/tools/vppapitrace/vppapitrace.py +++ /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("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() diff --git a/src/vat/api_format.c b/src/vat/api_format.c index 1458fde5190..61ac92fac38 100644 --- a/src/vat/api_format.c +++ b/src/vat/api_format.c @@ -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; diff --git a/src/vat2/CMakeLists.txt b/src/vat2/CMakeLists.txt index 9ee2d8211eb..c44d2452ea1 100644 --- a/src/vat2/CMakeLists.txt +++ b/src/vat2/CMakeLists.txt @@ -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 index 038ad74ac0e..00000000000 --- a/src/vat2/jsonconvert.h +++ /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 -#include -#include -#include -#include -#include - -#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 diff --git a/src/vat2/main.c b/src/vat2/main.c index 208e0c5d2c2..667f473c635 100644 --- a/src/vat2/main.c +++ b/src/vat2/main.c @@ -18,7 +18,7 @@ #include #include #include -#include +#include #include #include #include @@ -30,6 +30,33 @@ #include #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); } diff --git a/src/vat2/test/vat2_test.c b/src/vat2/test/vat2_test.c index 1ac46527b3c..7aa5e71296e 100644 --- a/src/vat2/test/vat2_test.c +++ b/src/vat2/test/vat2_test.c @@ -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) diff --git a/src/vcl/vcl_bapi.c b/src/vcl/vcl_bapi.c index 60fbe737a41..72440989727 100644 --- a/src/vcl/vcl_bapi.c +++ b/src/vcl/vcl_bapi.c @@ -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 _ } diff --git a/src/vlibapi/api.h b/src/vlibapi/api.h index 431155c5e09..d05395a213c 100644 --- a/src/vlibapi/api.h +++ b/src/vlibapi/api.h @@ -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 diff --git a/src/vlibapi/api_common.h b/src/vlibapi/api_common.h index 3fdc1bbdd36..a955636ba3f 100644 --- a/src/vlibapi/api_common.h +++ b/src/vlibapi/api_common.h @@ -27,6 +27,7 @@ #include #include +#include #include #include #include @@ -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; diff --git a/src/vlibapi/api_shared.c b/src/vlibapi/api_shared.c index 65288d89f67..52e66f41342 100644 --- a/src/vlibapi/api_shared.c +++ b/src/vlibapi/api_shared.c @@ -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) { diff --git a/src/vlibmemory/CMakeLists.txt b/src/vlibmemory/CMakeLists.txt index 456cba9baeb..6d6483dc61f 100644 --- a/src/vlibmemory/CMakeLists.txt +++ b/src/vlibmemory/CMakeLists.txt @@ -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 +) diff --git a/src/vlibmemory/memclnt.api b/src/vlibmemory/memclnt.api index 07c6d47b9fc..a5194cd58c4 100644 --- a/src/vlibmemory/memclnt.api +++ b/src/vlibmemory/memclnt.api @@ -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; diff --git a/src/vlibmemory/memclnt_api.c b/src/vlibmemory/memclnt_api.c index 5ebc31f71dc..23d0088308f 100644 --- a/src/vlibmemory/memclnt_api.c +++ b/src/vlibmemory/memclnt_api.c @@ -52,16 +52,6 @@ #include #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 @@ -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; diff --git a/src/vlibmemory/memory_api.c b/src/vlibmemory/memory_api.c index 9db27ebd574..6c066a152f4 100644 --- a/src/vlibmemory/memory_api.c +++ b/src/vlibmemory/memory_api.c @@ -38,26 +38,6 @@ #include #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 diff --git a/src/vlibmemory/memory_client.c b/src/vlibmemory/memory_client.c index 64650b64eca..f0b05b70695 100644 --- a/src/vlibmemory/memory_client.c +++ b/src/vlibmemory/memory_client.c @@ -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 _ } diff --git a/src/vlibmemory/socket_api.c b/src/vlibmemory/socket_api.c index 60ca650d92f..ce834a70aac 100644 --- a/src/vlibmemory/socket_api.c +++ b/src/vlibmemory/socket_api.c @@ -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 _ diff --git a/src/vlibmemory/socket_client.c b/src/vlibmemory/socket_client.c index 69126f88963..2fb6b8a0c4e 100644 --- a/src/vlibmemory/socket_client.c +++ b/src/vlibmemory/socket_client.c @@ -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 _ } diff --git a/src/vlibmemory/vlib_api_cli.c b/src/vlibmemory/vlib_api_cli.c index 0057c85adcf..74ad3c5cd76 100644 --- a/src/vlibmemory/vlib_api_cli.c +++ b/src/vlibmemory/vlib_api_cli.c @@ -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 ][last ][status][free]" - "[post-mortem-on][dump|save|replay ]", + .short_help = "api trace [tx][on|off][first ][last ][status][free]" + "[post-mortem-on][dump|dump-file|dump-json|save|tojson|save-" + "json|replay |replay-json ][nitems ]" + "[initializers ]", .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) { diff --git a/src/vnet/srmpls/sr_mpls_api.c b/src/vnet/srmpls/sr_mpls_api.c index d6216c68391..7d42f1ba451 100644 --- a/src/vnet/srmpls/sr_mpls_api.c +++ b/src/vnet/srmpls/sr_mpls_api.c @@ -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 diff --git a/src/vpp-api/client/client.c b/src/vpp-api/client/client.c index 542df9d414b..7a30792402c 100644 --- a/src/vpp-api/client/client.c +++ b/src/vpp-api/client/client.c @@ -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) { diff --git a/src/vppinfra/CMakeLists.txt b/src/vppinfra/CMakeLists.txt index 4be291e1e8d..1114092e246 100644 --- a/src/vppinfra/CMakeLists.txt +++ b/src/vppinfra/CMakeLists.txt @@ -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 diff --git a/src/vppinfra/cJSON.c b/src/vppinfra/cJSON.c index 5b26a4be4e1..448435de4dc 100644 --- a/src/vppinfra/cJSON.c +++ b/src/vppinfra/cJSON.c @@ -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); +} diff --git a/src/vppinfra/cJSON.h b/src/vppinfra/cJSON.h index e97e5f4cdc4..1474c4e5c49 100644 --- a/src/vppinfra/cJSON.h +++ b/src/vppinfra/cJSON.h @@ -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 } diff --git a/src/vat2/jsonconvert.c b/src/vppinfra/jsonformat.c similarity index 95% rename from src/vat2/jsonconvert.c rename to src/vppinfra/jsonformat.c index 1437b90688a..17b3ee9d3f8 100644 --- a/src/vat2/jsonconvert.c +++ b/src/vppinfra/jsonformat.c @@ -18,19 +18,21 @@ #include #include #include -#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 index 00000000000..d27785f0058 --- /dev/null +++ b/src/vppinfra/jsonformat.h @@ -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 +#include +#include +#include +#include +#include + +#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 index 00000000000..6524878a10d --- /dev/null +++ b/test/test_api_trace.py @@ -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) -- 2.16.6