From mboxrd@z Thu Jan  1 00:00:00 1970
Return-Path: <dev-bounces@dpdk.org>
Received: from mails.dpdk.org (mails.dpdk.org [217.70.189.124])
	by inbox.dpdk.org (Postfix) with ESMTP id 0621145A50;
	Sat, 28 Sep 2024 18:21:44 +0200 (CEST)
Received: from mails.dpdk.org (localhost [127.0.0.1])
	by mails.dpdk.org (Postfix) with ESMTP id 308384042E;
	Sat, 28 Sep 2024 18:20:59 +0200 (CEST)
Received: from mail-pg1-f178.google.com (mail-pg1-f178.google.com
 [209.85.215.178])
 by mails.dpdk.org (Postfix) with ESMTP id 5871A4042E
 for <dev@dpdk.org>; Sat, 28 Sep 2024 18:20:53 +0200 (CEST)
Received: by mail-pg1-f178.google.com with SMTP id
 41be03b00d2f7-7e6afa8baeaso2641927a12.3
 for <dev@dpdk.org>; Sat, 28 Sep 2024 09:20:53 -0700 (PDT)
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
 d=networkplumber-org.20230601.gappssmtp.com; s=20230601; t=1727540452;
 x=1728145252; darn=dpdk.org; 
 h=content-transfer-encoding:mime-version:references:in-reply-to
 :message-id:date:subject:cc:to:from:from:to:cc:subject:date
 :message-id:reply-to;
 bh=I803+si0iwEyeN9kakzBqmYSGbVYdWbtK1fFTc2eHEk=;
 b=nxtT7UnhXPN3YaR6tv40oL6gPNyl7CipwZGuZ9c6jf7hxMXxY2M85DvFCzwWl03xHB
 usPwHRtPV4LwV6ayDXFI5NI3QDzXBbgocamdWeF4EYCzCJEyOXpDelLmplFoVn899kgQ
 yFB7T/oQfqtXp9jAcmimYFvbN/AMiu+2HRVpnj6vdt7BKCVdb7LEU7JVWuiI1efTCiWH
 zrNvzpWVfY39Qr1/quQ6MSE03JBFUevKHvMq+VdfvYMKz7ectMyLg2gJ32mu6NRyk+Jb
 J9YuIfSeg84G6pwpmGmwK8kRgTS0dTsDvqhai8pU9tG/BXtMaIWGByznbtx1+xAy7b9Q
 EXIQ==
X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
 d=1e100.net; s=20230601; t=1727540452; x=1728145252;
 h=content-transfer-encoding:mime-version:references:in-reply-to
 :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc
 :subject:date:message-id:reply-to;
 bh=I803+si0iwEyeN9kakzBqmYSGbVYdWbtK1fFTc2eHEk=;
 b=V1o4hyb9U7qp54c2/s5G1GLka0ul8ErQRVFeHRHC7FtM0Goel0ntA8nWr53LtT2JZL
 GDbYBPkD1+4TYkG8MEazYfybgIbopCM1Eo9U4muUD6WhEVVRBcefGgK5JpVdxn8rOGU/
 GQCGKUxnOqOgdZOTKGIocsXmfOF7UHac82h3FGX1MK8CPhQKkGfp08fZZhmVTeJyvBDX
 jLythgIyw9RHW3X4hl7OyR2HVvz1j/6LTiW7UMO0Qx9Q00eQD+3f8pSiT5hi3ZEMv01t
 up6f5uho47qO3XqRIxQV7T33Z/1RenkS+3AGHIOg2pfAPoqIueeMIClwWfp+JWucBVVQ
 UWHw==
X-Gm-Message-State: AOJu0YxACjyzWf5NE517raj/nAvldgs9U5zXezpeg5vJv1LV+W6ctggr
 2btcHBiG/ZGvc50EnVP0VKJoguZ0tgWxl2cXZdRg7O/efDzUl++oKs+M0ZWnlulvUTDBoWPbnGG
 I
X-Google-Smtp-Source: AGHT+IGBJ0/eJDPlWUiP2RrakqGQ8M6AXUkI54ENWk5fR/mo3grghVHy+zzK7JWQm8u3FBM2gzAJUA==
X-Received: by 2002:a05:6a21:e8d:b0:1d2:e90a:f4cd with SMTP id
 adf61e73a8af0-1d4fa77a35fmr10240187637.35.1727540452509; 
 Sat, 28 Sep 2024 09:20:52 -0700 (PDT)
Received: from hermes.local (204-195-96-226.wavecable.com. [204.195.96.226])
 by smtp.gmail.com with ESMTPSA id
 41be03b00d2f7-7e6db2b9407sm2969107a12.23.2024.09.28.09.20.51
 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);
 Sat, 28 Sep 2024 09:20:52 -0700 (PDT)
From: Stephen Hemminger <stephen@networkplumber.org>
To: dev@dpdk.org
Cc: Stephen Hemminger <stephen@networkplumber.org>,
 Aman Singh <aman.deep.singh@intel.com>
Subject: [PATCH v9 8/8] test-pmd: add a JSON packet output
Date: Sat, 28 Sep 2024 09:18:38 -0700
Message-ID: <20240928162035.849326-9-stephen@networkplumber.org>
X-Mailer: git-send-email 2.45.2
In-Reply-To: <20240928162035.849326-1-stephen@networkplumber.org>
References: <20240312220129.70667-1-stephen@networkplumber.org>
 <20240928162035.849326-1-stephen@networkplumber.org>
MIME-Version: 1.0
Content-Transfer-Encoding: 8bit
X-BeenThere: dev@dpdk.org
X-Mailman-Version: 2.1.29
Precedence: list
List-Id: DPDK patches and discussions <dev.dpdk.org>
List-Unsubscribe: <https://mails.dpdk.org/options/dev>,
 <mailto:dev-request@dpdk.org?subject=unsubscribe>
List-Archive: <http://mails.dpdk.org/archives/dev/>
List-Post: <mailto:dev@dpdk.org>
List-Help: <mailto:dev-request@dpdk.org?subject=help>
List-Subscribe: <https://mails.dpdk.org/listinfo/dev>,
 <mailto:dev-request@dpdk.org?subject=subscribe>
Errors-To: dev-bounces@dpdk.org

When doing automated testing it is useful to show packet meta
data in JSON.

Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
---
 app/test-pmd/cmdline.c                      |   6 +-
 app/test-pmd/config.c                       |   3 +
 app/test-pmd/testpmd.h                      |   1 +
 app/test-pmd/util.c                         | 167 ++++++++++++++++++++
 doc/guides/testpmd_app_ug/testpmd_funcs.rst |   7 +-
 5 files changed, 178 insertions(+), 6 deletions(-)

diff --git a/app/test-pmd/cmdline.c b/app/test-pmd/cmdline.c
index 72be5a0c06..3d52ad4c72 100644
--- a/app/test-pmd/cmdline.c
+++ b/app/test-pmd/cmdline.c
@@ -305,7 +305,7 @@ static void cmd_help_long_parsed(void *parsed_result,
 			"set output (filename)\n"
 			"    Set the packet debug log file\n\n"
 
-			"set format (dissect|hex|verbose)\n"
+			"set format (dissect|hex|json|verbose)\n"
 			"    Set the format of packet log\\n"
 
 			"set log global|(type) (level)\n"
@@ -3918,12 +3918,12 @@ static cmdline_parse_token_string_t cmd_set_format_set =
 static cmdline_parse_token_string_t cmd_set_format_output =
 	TOKEN_STRING_INITIALIZER(struct cmd_set_format_result, format, "format");
 static cmdline_parse_token_string_t cmd_set_format_value =
-	TOKEN_STRING_INITIALIZER(struct cmd_set_format_result, value, "dissect#hex#verbose");
+	TOKEN_STRING_INITIALIZER(struct cmd_set_format_result, value, "dissect#hex#json#verbose");
 
 static cmdline_parse_inst_t cmd_set_format = {
 	.f = cmd_set_format_parsed,
 	.data = NULL,
-	.help_str = "set format dissect|hex|verbose",
+	.help_str = "set format dissect|hex|json|verbose",
 	.tokens = {
 		(void *)&cmd_set_format_set,
 		(void *)&cmd_set_format_output,
diff --git a/app/test-pmd/config.c b/app/test-pmd/config.c
index 36b7aa1307..10ed58c08b 100644
--- a/app/test-pmd/config.c
+++ b/app/test-pmd/config.c
@@ -6275,6 +6275,9 @@ set_output_format(const char *mode)
 		[OUTPUT_MODE_VERBOSE] = "verbose",
 		[OUTPUT_MODE_HEX]     = "hex",
 		[OUTPUT_MODE_DISSECT] = "dissect",
+#ifdef RTE_HAS_JANSSON
+		[OUTPUT_MODE_JSON]    = "json",
+#endif
 	};
 
 	printf("Change output format from %s to %s\n",
diff --git a/app/test-pmd/testpmd.h b/app/test-pmd/testpmd.h
index 66b0317b61..f8bf9b7126 100644
--- a/app/test-pmd/testpmd.h
+++ b/app/test-pmd/testpmd.h
@@ -492,6 +492,7 @@ enum output_mode {
 	OUTPUT_MODE_VERBOSE = 0,
 	OUTPUT_MODE_HEX,
 	OUTPUT_MODE_DISSECT,
+	OUTPUT_MODE_JSON,
 };
 
 extern uint8_t xstats_hide_zero; /**< Hide zero values for xstats display */
diff --git a/app/test-pmd/util.c b/app/test-pmd/util.c
index 551e684e4c..7da59988db 100644
--- a/app/test-pmd/util.c
+++ b/app/test-pmd/util.c
@@ -299,6 +299,168 @@ dump_pkt_verbose(FILE *outf, uint16_t port_id, uint16_t queue,
 	}
 }
 
+#ifdef RTE_HAS_JANSSON
+
+/* Encode offload flags as JSON array */
+static json_t *
+encode_ol_flags(uint64_t flags, int is_rx)
+{
+	json_t *ol_array = json_array();
+	unsigned int i;
+
+	if (is_rx)
+		flags &= ~RTE_MBUF_F_TX_OFFLOAD_MASK;
+	else
+		flags &= RTE_MBUF_F_TX_OFFLOAD_MASK;
+
+	for (i = 0; i < 64; i++) {
+		uint64_t mask = (uint64_t)1 << i;
+		const char *name;
+
+		if (!(mask & flags))
+			continue;
+
+		if (is_rx)
+			name = rte_get_rx_ol_flag_name(mask);
+		else
+			name = rte_get_tx_ol_flag_name(mask);
+		json_array_append_new(ol_array, json_string(name));
+	}
+	return ol_array;
+}
+
+/* Encode packet type fields as JSON object */
+static json_t *
+encode_ptype(uint32_t ptype)
+{
+	if ((ptype & RTE_PTYPE_ALL_MASK) == RTE_PTYPE_UNKNOWN)
+		return json_string("UNKNOWN");
+
+	json_t *ptypes = json_array();
+	if (ptype & RTE_PTYPE_L2_MASK)
+		json_array_append(ptypes, json_string(rte_get_ptype_l2_name(ptype)));
+	if (ptype & RTE_PTYPE_L3_MASK)
+		json_array_append(ptypes, json_string(rte_get_ptype_l3_name(ptype)));
+	if (ptype & RTE_PTYPE_L4_MASK)
+		json_array_append(ptypes, json_string(rte_get_ptype_l4_name(ptype)));
+	if (ptype & RTE_PTYPE_TUNNEL_MASK)
+		json_array_append(ptypes, json_string(rte_get_ptype_tunnel_name(ptype)));
+	if (ptype & RTE_PTYPE_INNER_L2_MASK)
+		json_array_append(ptypes, json_string(rte_get_ptype_inner_l2_name(ptype)));
+	if (ptype & RTE_PTYPE_INNER_L3_MASK)
+		json_array_append(ptypes, json_string(rte_get_ptype_inner_l3_name(ptype)));
+	if (ptype & RTE_PTYPE_INNER_L4_MASK)
+		json_array_append(ptypes, json_string(rte_get_ptype_inner_l4_name(ptype)));
+
+	return ptypes;
+}
+
+static void
+dump_pkt_json(FILE *outf, uint16_t port_id, uint16_t queue,
+	      struct rte_mbuf *pkts[], uint16_t nb_pkts, int is_rx)
+{
+	char buf[256];
+
+	for (uint16_t i = 0; i < nb_pkts; i++) {
+		const struct rte_mbuf *mb = pkts[i];
+		struct rte_net_hdr_lens hdr_lens;
+		const struct rte_ether_hdr *eth_hdr;
+		struct rte_ether_hdr _eth_hdr;
+		uint16_t eth_type;
+		uint64_t ol_flags = mb->ol_flags;
+		const char *reason = NULL;
+		json_t *jobj;
+
+		jobj = json_object();
+		json_object_set_new(jobj, "port", json_integer(port_id));
+		json_object_set_new(jobj, "queue", json_integer(queue));
+		json_object_set_new(jobj, "is_rx", json_boolean(is_rx));
+
+		if (rte_mbuf_check(mb, 1, &reason) < 0) {
+			json_object_set_new(jobj, "invalid", json_string(reason));
+			continue;
+		}
+
+		eth_hdr = rte_pktmbuf_read(mb, 0, sizeof(_eth_hdr), &_eth_hdr);
+		eth_type = RTE_BE_TO_CPU_16(eth_hdr->ether_type);
+
+		rte_ether_format_addr(buf, sizeof(buf), &eth_hdr->dst_addr);
+		json_object_set_new(jobj, "dst", json_string(buf));
+		rte_ether_format_addr(buf, sizeof(buf), &eth_hdr->src_addr);
+		json_object_set_new(jobj, "src", json_string(buf));
+
+		snprintf(buf, sizeof(buf), "0x%04x", eth_type);
+		json_object_set_new(jobj, "type", json_string(buf));
+
+		json_object_set_new(jobj, "length", json_integer(mb->pkt_len));
+		json_object_set_new(jobj, "nb_segs", json_integer(mb->nb_segs));
+
+		if (ol_flags & RTE_MBUF_F_RX_RSS_HASH)
+			json_object_set_new(jobj, "rss_hash", json_integer(mb->hash.rss));
+
+		if (ol_flags & RTE_MBUF_F_RX_FDIR) {
+			json_t *fdir = json_object();
+
+			if (ol_flags & RTE_MBUF_F_RX_FDIR_ID)
+				json_object_set_new(fdir, "id",
+						    json_integer(mb->hash.fdir.hi));
+			else if (ol_flags & RTE_MBUF_F_RX_FDIR_FLX) {
+				json_t *bytes = json_array();
+
+				json_array_append(bytes, json_integer(mb->hash.fdir.hi));
+				json_array_append(bytes, json_integer(mb->hash.fdir.lo));
+				json_object_set_new(fdir, "flex", bytes);
+			} else {
+				json_object_set_new(fdir, "hash", json_integer(mb->hash.fdir.hash));
+				json_object_set_new(fdir, "id",  json_integer(mb->hash.fdir.id));
+			}
+		}
+
+		if (is_timestamp_enabled(mb))
+			json_object_set_new(jobj, "timestamp", json_integer(get_timestamp(mb)));
+
+		if ((is_rx && (ol_flags & RTE_MBUF_F_RX_QINQ) != 0) ||
+		    (!is_rx && (ol_flags & RTE_MBUF_F_TX_QINQ) != 0)) {
+			json_object_set_new(jobj, "vlan_tci", json_integer(mb->vlan_tci));
+			json_object_set_new(jobj, "vlan_outer_tci",
+					    json_integer(mb->vlan_tci_outer));
+		} else if ((is_rx && (ol_flags & RTE_MBUF_F_RX_VLAN) != 0) ||
+			   (!is_rx && (ol_flags & RTE_MBUF_F_TX_VLAN) != 0)) {
+			json_object_set_new(jobj, "vlan_tci", json_integer(mb->vlan_tci));
+		}
+
+		if (mb->packet_type)
+			json_object_set_new(jobj, "hw_ptype", encode_ptype(mb->packet_type));
+
+		uint32_t sw_packet_type = rte_net_get_ptype(mb, &hdr_lens, RTE_PTYPE_ALL_MASK);
+		json_object_set_new(jobj, "sw_ptype", encode_ptype(sw_packet_type));
+
+		if (sw_packet_type & RTE_PTYPE_L2_MASK)
+			json_object_set_new(jobj, "l2_len", json_integer(hdr_lens.l2_len));
+		if (sw_packet_type & RTE_PTYPE_L3_MASK)
+			json_object_set_new(jobj, "l3_len", json_integer(hdr_lens.l3_len));
+		if (sw_packet_type & RTE_PTYPE_L4_MASK)
+			json_object_set_new(jobj, "l4_len", json_integer(hdr_lens.l4_len));
+		if (sw_packet_type & RTE_PTYPE_TUNNEL_MASK)
+			json_object_set_new(jobj, "tunnel_len", json_integer(hdr_lens.tunnel_len));
+		if (sw_packet_type & RTE_PTYPE_INNER_L2_MASK)
+			json_object_set_new(jobj, "inner_l2_len",
+					    json_integer(hdr_lens.inner_l2_len));
+		if (sw_packet_type & RTE_PTYPE_INNER_L3_MASK)
+			json_object_set_new(jobj, "inner_l3_len",
+					    json_integer(hdr_lens.inner_l3_len));
+		if (sw_packet_type & RTE_PTYPE_INNER_L4_MASK)
+			json_object_set_new(jobj, "inner_l4_len",
+					    json_integer(hdr_lens.inner_l4_len));
+
+		json_object_set_new(jobj, "ol_flags", encode_ol_flags(mb->ol_flags, is_rx));
+
+		json_dumpf(jobj, outf, JSON_INDENT(4));
+		json_decref(jobj);
+	}
+}
+#endif
+
 static void
 dump_pkt_hex(FILE *outf, uint16_t port_id, uint16_t queue,
 	     struct rte_mbuf *pkts[], uint16_t nb_pkts, int is_rx)
@@ -368,6 +530,11 @@ dump_pkt_burst(uint16_t port_id, uint16_t queue, struct rte_mbuf *pkts[],
 	case OUTPUT_MODE_DISSECT:
 		dump_pkt_brief(outf, port_id, queue, pkts, nb_pkts, is_rx);
 		break;
+#ifdef RTE_HAS_JANSSON
+	case OUTPUT_MODE_JSON:
+		dump_pkt_json(outf, port_id, queue, pkts, nb_pkts, is_rx);
+		break;
+#endif
 	default:
 		return;
 	}
diff --git a/doc/guides/testpmd_app_ug/testpmd_funcs.rst b/doc/guides/testpmd_app_ug/testpmd_funcs.rst
index 705b3dc3d5..bb62960582 100644
--- a/doc/guides/testpmd_app_ug/testpmd_funcs.rst
+++ b/doc/guides/testpmd_app_ug/testpmd_funcs.rst
@@ -677,13 +677,14 @@ set format
 
 Chose the output format for packet debug log::
 
-   testpmd> set format dissect|hex|verbose
+   testpmd> set format dissect|hex|json|verbose
 
 Available formats are:
 
-* ``verbose`` print the packet meta data information
-* ``hex`` print the mbuf flags and data in hex
 * ``dissect`` print the packet in tshark summary format
+* ``hex`` print the mbuf flags and data in hex
+* ``json`` print the packet meta data in json
+* ``verbose`` print the packet meta data information
 
 
 set verbose
-- 
2.45.2