summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorFilip Tehlar <ftehlar@cisco.com>2021-07-23 08:51:10 +0000
committerFilip Tehlar <ftehlar@cisco.com>2021-09-28 16:06:19 +0000
commit36217e3ca8a1ca2e7a341b6b44ffc25e6497191c (patch)
treeba45e2b144e0d66a69c0502a7823c28239d0bc66 /src
parent3459ece6da90627b161e2128b5926f1e58e7db65 (diff)
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 <ftehlar@cisco.com> Signed-off-by: Ole Troan <ot@cisco.com>
Diffstat (limited to 'src')
-rw-r--r--src/plugins/hs_apps/sapi/vpp_echo_bapi.c9
-rw-r--r--src/plugins/mactime/mactime_top.c14
-rw-r--r--src/plugins/tracedump/tracedump_test.c12
-rw-r--r--src/tools/vppapigen/vppapigen_c.py71
l---------src/tools/vppapitrace/vppapitrace1
-rwxr-xr-xsrc/tools/vppapitrace/vppapitrace.py492
-rw-r--r--src/vat/api_format.c4
-rw-r--r--src/vat2/CMakeLists.txt4
-rw-r--r--src/vat2/jsonconvert.h105
-rw-r--r--src/vat2/main.c68
-rw-r--r--src/vat2/test/vat2_test.c1
-rw-r--r--src/vcl/vcl_bapi.c9
-rw-r--r--src/vlibapi/api.h9
-rw-r--r--src/vlibapi/api_common.h25
-rw-r--r--src/vlibapi/api_shared.c283
-rw-r--r--src/vlibmemory/CMakeLists.txt6
-rw-r--r--src/vlibmemory/memclnt.api4
-rw-r--r--src/vlibmemory/memclnt_api.c34
-rw-r--r--src/vlibmemory/memory_api.c30
-rw-r--r--src/vlibmemory/memory_client.c16
-rw-r--r--src/vlibmemory/socket_api.c31
-rw-r--r--src/vlibmemory/socket_client.c13
-rw-r--r--src/vlibmemory/vlib_api_cli.c506
-rw-r--r--src/vnet/srmpls/sr_mpls_api.c31
-rw-r--r--src/vpp-api/client/client.c8
-rw-r--r--src/vppinfra/CMakeLists.txt4
-rw-r--r--src/vppinfra/cJSON.c85
-rw-r--r--src/vppinfra/cJSON.h4
-rw-r--r--src/vppinfra/jsonformat.c (renamed from src/vat2/jsonconvert.c)37
-rw-r--r--src/vppinfra/jsonformat.h116
30 files changed, 1036 insertions, 996 deletions
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 <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
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("<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()
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 <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
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 <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>
@@ -30,6 +30,33 @@
#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);
}
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 <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;
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 <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;
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 <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
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,92 +1204,16 @@ 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)
{
u32 nitems = 256 << 10;
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
index 1437b90688a..17b3ee9d3f8 100644
--- a/src/vat2/jsonconvert.c
+++ b/src/vppinfra/jsonformat.c
@@ -18,19 +18,21 @@
#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
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 <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