netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Matthias Tafelmeier <matthias.tafelmeier@gmx.net>
To: netdev@vger.kernel.org
Cc: hagen@jauu.net, shemminger@osdl.org, fw@strlen.de,
	edumazet@google.com, daniel@iogearbox.net
Subject: [PATCH v7 02/10] ss: created formatters for json and hr
Date: Thu, 10 Sep 2015 21:35:00 +0200	[thread overview]
Message-ID: <1441913708-15532-3-git-send-email-matthias.tafelmeier@gmx.net> (raw)
In-Reply-To: <1441913708-15532-1-git-send-email-matthias.tafelmeier@gmx.net>

This patch creates a central formatter module that acts as a kind of
switch. From there, more specific handler modules for the certain output
formats are called. Up to now, humand readable and json do exist.

That prepares ss for potential output format extensions in the future.
With the help of such an apparatus, extensions should get done
conveniently as well.

For a completely new output format, a new handler module must be created
and should be constructed like its relatives (for ex.: ss_json_fmt.c).
Moreover, its functions need to get registered with the central output
distributor. The latter can be done in that the according fmt_op_hub of
the new handler module is registered in the fmt_op_hub array.

Solely extending tcp_stats output shall boil down to extending the
according handler function with the new predicate and its value. The
context of the output subparts are important. With JSON, for instance,
you have to ensure, that the comas are set at the right places.

Further, an interim solution for all tcp_stats extensions is to check
that all those muddle through to all fmt handlers by STATICAL_ASSERTING
that.  Interim is the solution, since a central structure would be much
more worthwile for maintainability and this method does not ensure
correct output fmt extension in a foolproof manner.

Extension Examples:
See according sub patches in these series.

Signed-off-by: Matthias Tafelmeier <matthias.tafelmeier@gmx.net>
Suggested-by: Hagen Paul Pfeifer <hagen@jauu.net>
---
 misc/Makefile      |   2 +-
 misc/ss_hr_fmt.c   | 258 +++++++++++++++++++++++++++++++++
 misc/ss_hr_fmt.h   |   9 ++
 misc/ss_json_fmt.c | 408 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 misc/ss_json_fmt.h |  26 ++++
 misc/ss_out_fmt.c  | 127 +++++++++++++++++
 misc/ss_out_fmt.h  |  83 +++++++++++
 7 files changed, 912 insertions(+), 1 deletion(-)
 create mode 100644 misc/ss_hr_fmt.c
 create mode 100644 misc/ss_hr_fmt.h
 create mode 100644 misc/ss_json_fmt.c
 create mode 100644 misc/ss_json_fmt.h
 create mode 100644 misc/ss_out_fmt.c
 create mode 100644 misc/ss_out_fmt.h

diff --git a/misc/Makefile b/misc/Makefile
index 6185217..2fe3555 100644
--- a/misc/Makefile
+++ b/misc/Makefile
@@ -1,4 +1,4 @@
-SSOBJ=ss.o ssfilter.o
+SSOBJ=ss.o ssfilter.o ss_hr_fmt.o ss_json_fmt.o ss_out_fmt.o
 LNSTATOBJ=lnstat.o lnstat_util.o
 
 TARGETS=ss nstat ifstat rtacct arpd lnstat
diff --git a/misc/ss_hr_fmt.c b/misc/ss_hr_fmt.c
new file mode 100644
index 0000000..2f09838
--- /dev/null
+++ b/misc/ss_hr_fmt.c
@@ -0,0 +1,258 @@
+#include <linux/sock_diag.h>
+#include <linux/rtnetlink.h>
+#include "ss_out_fmt.h"
+#include "ss_types.h"
+#include "ss_hr_fmt.h"
+
+static void tcp_stats_hr_fmt(struct tcpstat *s)
+{
+	char b1[64];
+
+	if (s->has_ts_opt)
+		printf(" ts");
+	if (s->has_sack_opt)
+		printf(" sack");
+	if (s->has_ecn_opt)
+		printf(" ecn");
+	if (s->has_ecnseen_opt)
+		printf(" ecnseen");
+	if (s->has_fastopen_opt)
+		printf(" fastopen");
+	if (s->cong_alg)
+		printf(" %s", s->cong_alg);
+	if (s->has_wscale_opt)
+		printf(" wscale:%d,%d", s->snd_wscale, s->rcv_wscale);
+	if (s->rto)
+		printf(" rto:%g", s->rto);
+	if (s->backoff)
+		printf(" backoff:%u", s->backoff);
+	if (s->rtt)
+		printf(" rtt:%g/%g", s->rtt, s->rttvar);
+	if (s->ato)
+		printf(" ato:%g", s->ato);
+
+	if (s->qack)
+		printf(" qack:%d", s->qack);
+	if (s->qack & 1)
+		printf(" bidir");
+
+	if (s->mss)
+		printf(" mss:%d", s->mss);
+	if (s->cwnd)
+		printf(" cwnd:%d", s->cwnd);
+	if (s->ssthresh)
+		printf(" ssthresh:%d", s->ssthresh);
+
+	if (s->dctcp && s->dctcp->enabled) {
+		struct dctcpstat *dctcp = s->dctcp;
+
+		printf(" dctcp:(ce_state:%u,alpha:%u,ab_ecn:%u,ab_tot:%u)",
+		dctcp->ce_state, dctcp->alpha, dctcp->ab_ecn,
+		dctcp->ab_tot);
+	} else if (s->dctcp) {
+		printf(" dctcp:fallback_mode");
+	}
+
+	if (s->send_bps)
+		printf(" send %sbps", sprint_bw(b1, s->send_bps));
+	if (s->lastsnd)
+		printf(" lastsnd:%u", s->lastsnd);
+	if (s->lastrcv)
+		printf(" lastrcv:%u", s->lastrcv);
+	if (s->lastack)
+		printf(" lastack:%u", s->lastack);
+
+	if (s->pacing_rate) {
+		printf(" pacing_rate %sbps", sprint_bw(b1, s->pacing_rate));
+		if (s->pacing_rate_max)
+			printf("/%sbps", sprint_bw(b1, s->pacing_rate_max));
+	}
+
+	if (s->unacked)
+		printf(" unacked:%u", s->unacked);
+	if (s->retrans || s->retrans_total)
+		printf(" retrans:%u/%u", s->retrans, s->retrans_total);
+	if (s->lost)
+		printf(" lost:%u", s->lost);
+	if (s->sacked && s->ss.state != SS_LISTEN)
+		printf(" sacked:%u", s->sacked);
+	if (s->fackets)
+		printf(" fackets:%u", s->fackets);
+	if (s->reordering != 3)
+		printf(" reordering:%d", s->reordering);
+	if (s->rcv_rtt)
+		printf(" rcv_rtt:%g", s->rcv_rtt);
+
+	CHECK_FMT_ADAPT(s->rcv_space, s,
+	hr_handler_must_be_adapted_accordingly_when_json_fmt_is_extended);
+}
+
+static void tcp_timer_hr_fmt(struct tcpstat *s)
+{
+	if (s->timer) {
+		if (s->timer > 4)
+			s->timer = 5;
+		printf(" timer:(%s,%s,%d)",
+		ss_timer_name[s->timer],
+		print_ms_timer(s->timeout), s->retrans);
+	}
+}
+
+static void sock_state_hr_fmt(struct sockstat *s, const char **sstate_name,
+		const char *sock_name, int netid_width, int state_width)
+{
+	if (netid_width)
+		printf("%-*s ", netid_width, sock_name);
+	if (state_width)
+		printf("%-*s ", state_width, sstate_name[s->state]);
+
+	printf("%-6d %-6d ", s->rq, s->wq);
+}
+
+static void sock_details_hr_fmt(struct sockstat *s, int type, unsigned groups,
+			unsigned long long cb)
+{
+	if (type == GENERIC_DETAIL && s->uid)
+		printf(" uid:%u", s->uid);
+
+	if (type == GENERIC_DETAIL) {
+		printf(" ino:%u", s->ino);
+		printf(" sk:%llx", s->sk);
+	}
+
+	if (type == NETLINK_DETAIL)
+		printf(" sk=%llx cb=%llx groups=0x%08x", s->sk, cb, groups);
+
+}
+
+static void sock_addr_hr_fmt(const char *addr, int addr_len,
+		char *delim, int port_len,
+		const char *port, const char *ifname,
+		const char *peer_kind)
+{
+	if (ifname) {
+		printf("%*s%%%s%s%-*s ", addr_len, addr, ifname, delim,
+		port_len, port);
+	} else {
+		printf("%*s%s%-*s ", addr_len, addr, delim, port_len, port);
+	}
+}
+
+static void sock_users_hr_fmt(char *out)
+{
+	printf(" users:(%s)", out);
+}
+
+static void sock_summary_hr_fmt(struct ssummary *s, struct snmpstat *sn,
+			struct slabstat *slabstat)
+{
+	printf("Total: %d (kernel %d)\n", s->socks, slabstat->socks);
+
+	printf("TCP:   %d (estab %d, closed %d, orphaned %d,"
+		"synrecv %d, timewait %d/%d), ports %d\n",
+	s->tcp_total + slabstat->tcp_syns + s->tcp_tws, sn->tcp_estab,
+	s->tcp_total - (s->tcp4_hashed + s->tcp6_hashed - s->tcp_tws),
+	s->tcp_orphans, slabstat->tcp_syns, s->tcp_tws, slabstat->tcp_tws,
+	slabstat->tcp_ports);
+
+	printf("\n");
+	printf("Transport Total     IP        IPv6\n");
+	printf("*	%-9d %-9s %-9s\n", slabstat->socks, "-", "-");
+	printf("RAW	%-9d %-9d %-9d\n", s->raw4 + s->raw6, s->raw4,
+	s->raw6);
+	printf("UDP	%-9d %-9d %-9d\n", s->udp4 + s->udp6, s->udp4,
+	s->udp6);
+	printf("TCP	%-9d %-9d %-9d\n", s->tcp4_hashed + s->tcp6_hashed,
+	s->tcp4_hashed, s->tcp6_hashed);
+	printf("INET	%-9d %-9d %-9d\n",
+	s->raw4 + s->udp4 + s->tcp4_hashed + s->raw6 + s->udp6 +
+	s->tcp6_hashed, s->raw4 + s->udp4 + s->tcp4_hashed,
+	s->raw6 + s->udp6 + s->tcp6_hashed);
+	printf("FRAG	%-9d %-9d %-9d\n", s->frag4 + s->frag6, s->frag4,
+	s->frag6);
+
+	printf("\n");
+}
+
+static void sock_conn_hr_fmt(unsigned char mask)
+{
+	printf(" %c-%c", mask & 1 ? '-' : '<', mask & 2 ? '-' : '>');
+}
+
+static void mem_hr_fmt(const struct inet_diag_meminfo *minfo)
+{
+	printf(" mem:(r%u,w%u,f%u,t%u)",
+	minfo->idiag_rmem,
+	minfo->idiag_wmem, minfo->idiag_fmem, minfo->idiag_tmem);
+}
+
+static void skmem_hr_fmt(const __u32 *skmeminfo,
+		struct rtattr **tb, int attrtype)
+{
+	printf(" skmem:(r%u,rb%u,t%u,tb%u,f%u,w%u,o%u",
+	skmeminfo[SK_MEMINFO_RMEM_ALLOC],
+	skmeminfo[SK_MEMINFO_RCVBUF],
+	skmeminfo[SK_MEMINFO_WMEM_ALLOC],
+	skmeminfo[SK_MEMINFO_SNDBUF],
+	skmeminfo[SK_MEMINFO_FWD_ALLOC],
+	skmeminfo[SK_MEMINFO_WMEM_QUEUED], skmeminfo[SK_MEMINFO_OPTMEM]);
+
+	if (RTA_PAYLOAD(tb[attrtype]) >=
+	(SK_MEMINFO_BACKLOG + 1) * sizeof(__u32))
+		printf(",bl%u", skmeminfo[SK_MEMINFO_BACKLOG]);
+
+	printf(")");
+}
+
+static void bpf_filter_hr_fmt(struct sock_filter *fil, int num)
+{
+	printf("\n\tbpf filter (%d): ", num);
+	while (num) {
+		printf(" 0x%02x %u %u %u,",
+		fil->code, fil->jt, fil->jf, fil->k);
+		num--;
+		fil++;
+	}
+}
+
+static void opt_hr_fmt(char *opt)
+{
+	printf(" opt:\"%s\"", opt);
+}
+
+static void proc_hr_fmt(int serv_width, char *pid_ctx)
+{
+	if (pid_ctx != NULL) {
+		printf("proc_ctx=%-*s ", serv_width, pid_ctx);
+		free(pid_ctx);
+	} else {
+		printf("proc_ctx=%-*s ", serv_width, "unavailable");
+	}
+}
+
+static void packet_show_ring_hr_fmt(struct packet_diag_ring *ring)
+{
+	printf("blk_size:%d", ring->pdr_block_size);
+	printf(",blk_nr:%d", ring->pdr_block_nr);
+	printf(",frm_size:%d", ring->pdr_frame_size);
+	printf(",frm_nr:%d", ring->pdr_frame_nr);
+	printf(",tmo:%d", ring->pdr_retire_tmo);
+	printf(",features:0x%x", ring->pdr_features);
+}
+
+const struct fmt_op_hub hr_output_op = {
+	.tcp_stats_fmt = tcp_stats_hr_fmt,
+	.tcp_timer_fmt = tcp_timer_hr_fmt,
+	.sock_state_fmt = sock_state_hr_fmt,
+	.sock_details_fmt = sock_details_hr_fmt,
+	.sock_addr_fmt = sock_addr_hr_fmt,
+	.sock_users_fmt = sock_users_hr_fmt,
+	.sock_summary_fmt = sock_summary_hr_fmt,
+	.sock_conn_fmt = sock_conn_hr_fmt,
+	.mem_fmt = mem_hr_fmt,
+	.skmem_fmt = skmem_hr_fmt,
+	.bpf_filter_fmt = bpf_filter_hr_fmt,
+	.opt_fmt = opt_hr_fmt,
+	.proc_fmt = proc_hr_fmt,
+	.packet_show_ring_fmt = packet_show_ring_hr_fmt,
+};
diff --git a/misc/ss_hr_fmt.h b/misc/ss_hr_fmt.h
new file mode 100644
index 0000000..969fb17
--- /dev/null
+++ b/misc/ss_hr_fmt.h
@@ -0,0 +1,9 @@
+#ifndef SS_HR_FMT_H
+#define SS_HR_FMT_H
+
+#include <linux/inet_diag.h>
+#include <linux/pkt_sched.h>
+#include <linux/filter.h>
+#include "ss_types.h"
+
+#endif				/* SS_HR_FMT_H */
diff --git a/misc/ss_json_fmt.c b/misc/ss_json_fmt.c
new file mode 100644
index 0000000..a927952
--- /dev/null
+++ b/misc/ss_json_fmt.c
@@ -0,0 +1,408 @@
+#include <linux/sock_diag.h>
+#include <inttypes.h>
+#include <linux/rtnetlink.h>
+#include <json_writer.h>
+#include <stdio.h>
+#include "ss_out_fmt.h"
+#include "ss_types.h"
+#include "ss_json_fmt.h"
+
+#define SHOW_TCP_INFO	(show_tcpinfo && \
+			(ss_current_filter.dbs & (1<<TCP_DB) || \
+			ss_current_filter.dbs & (1<<DCCP_DB)))
+#define SHOW_MEM	(show_mem && \
+			ss_current_filter.dbs & (1<<UDP_DB) || \
+			ss_current_filter.dbs & (1<<TCP_DB))
+
+/* generic auxiliary mechanism for json related dangling delimiter issues*/
+void res_json_fmt_branch(int pred, char bound)
+{
+	if (pred) {
+		if (bound == ' ') {
+			printf(",");
+			goto newl;
+		}
+		printf("%c,", bound);
+	} else {
+		if (bound == ' ')
+			goto newl;
+		printf("%c", bound);
+	}
+ newl:
+	printf("\n");
+}
+
+static void make_userout_valid(char *out)
+{
+	/*replace quote (") by space */
+	char *tmp;
+
+	while ((tmp = strstr(out, "\"")) != NULL)
+		*tmp = ' ';
+}
+
+static void print_serv_or_port(char *serv_o_port)
+{
+	char buf[8];
+
+	if (resolve_services == 0)
+		strcpy(buf, "port");
+	else
+		strcpy(buf, "service");
+
+	jsonw_string_field(json_wr, buf, serv_o_port);
+}
+
+/* hex conversion helper */
+static void jsonw_hex_as_str_outp(json_writer_t *self, uint64_t num)
+{
+	char tmp[17];
+
+	sprintf(tmp, "%"PRIx64, num);
+	jsonw_string(self, tmp);
+}
+
+static void jsonw_hex_field_outp(json_writer_t *self, const char *prop, uint64_t num)
+{
+	jsonw_name(self, prop);
+	jsonw_hex_as_str_outp(self, num);
+}
+
+static void tcp_stats_json_fmt(struct tcpstat *s)
+{
+	char b1[64];
+
+	if (s->has_ts_opt)
+		jsonw_bool_field(json_wr, "ts", true);
+	if (s->has_sack_opt)
+		jsonw_bool_field(json_wr, "sack", true);
+	if (s->has_ecn_opt)
+		jsonw_bool_field(json_wr, "ecn", true);
+	if (s->has_ecnseen_opt)
+		jsonw_bool_field(json_wr, "ecnseen", true);
+	if (s->has_fastopen_opt)
+		jsonw_bool_field(json_wr, "fastopen", true);
+	if (s->cong_alg)
+		jsonw_bool_field(json_wr, "cong_alg", true);
+	if (s->has_wscale_opt) {
+		jsonw_name(json_wr, "wscale");
+		jsonw_start_object(json_wr);
+		jsonw_int_field(json_wr, "snd", s->snd_wscale);
+		jsonw_int_field(json_wr, "rcv", s->rcv_wscale);
+		jsonw_end_object(json_wr);
+	}
+	if (s->rto)
+		jsonw_float_field(json_wr, "rto", s->rto);
+	if (s->backoff)
+		jsonw_uint_field(json_wr, "backoff", s->backoff);
+	if (s->rtt)
+		jsonw_float_field(json_wr, "rtt", s->rtt/s->rttvar);
+	if (s->ato)
+		jsonw_float_field(json_wr, "ato", s->ato);
+	if (s->qack)
+		jsonw_int_field(json_wr, "quack", s->qack);
+	if (s->qack & 1)
+		jsonw_bool_field(json_wr, "bidir", true);
+	if (s->mss)
+		jsonw_int_field(json_wr, "mss", s->mss);
+	if (s->cwnd && s->cwnd != 2)
+		jsonw_int_field(json_wr, "cwnd", s->cwnd);
+	if (s->ssthresh)
+		jsonw_int_field(json_wr, "ssthresh", s->ssthresh);
+	if (s->dctcp && s->dctcp->enabled) {
+		struct dctcpstat *dctcp = s->dctcp;
+
+		jsonw_name(json_wr, "dctcpinfo");
+		jsonw_start_object(json_wr);
+		jsonw_uint_field(json_wr, "ce_state", dctcp->ce_state);
+		jsonw_uint_field(json_wr, "alpha", dctcp->alpha);
+		jsonw_uint_field(json_wr, "ab_ecn", dctcp->ab_ecn);
+		jsonw_uint_field(json_wr, "ab_tot", dctcp->ab_tot);
+		jsonw_end_object(json_wr);
+	} else if (s->dctcp) {
+		jsonw_bool_field(json_wr, "fallback_mode", true);
+	}
+	if (s->send_bps)
+		jsonw_string_field(json_wr, "send_bps",
+			      sprint_bw(b1, s->send_bps));
+	if (s->lastsnd)
+		jsonw_uint_field(json_wr, "lastsnd", s->lastsnd);
+	if (s->lastrcv)
+		jsonw_uint_field(json_wr, "lastrcv", s->lastsnd);
+	if (s->lastack)
+		jsonw_uint_field(json_wr, "lastack", s->lastsnd);
+	if (s->pacing_rate) {
+		jsonw_name(json_wr, "pacing_rate");
+		jsonw_start_object(json_wr);
+		jsonw_string_field(json_wr, "pacing_rate_bps",
+		sprint_bw(b1, s->pacing_rate));
+		if (s->pacing_rate_max)
+			jsonw_string_field(json_wr, "pacing_rate_max_bps",
+				  sprint_bw(b1, s->pacing_rate_max));
+		jsonw_end_object(json_wr);
+	}
+	if (s->unacked)
+		jsonw_uint_field(json_wr, "unacked", s->unacked);
+	if (s->retrans || s->retrans_total) {
+		jsonw_name(json_wr, "retrans_obj");
+		jsonw_start_object(json_wr);
+		jsonw_uint_field(json_wr, "retrans", s->retrans);
+		jsonw_uint_field(json_wr, "retrans_total", s->retrans_total);
+		jsonw_end_object(json_wr);
+	}
+	if (s->lost)
+		jsonw_uint_field(json_wr, "lost", s->lost);
+	if (s->sacked && (s->ss.state != SS_LISTEN))
+		jsonw_uint_field(json_wr, "sacked", s->sacked);
+	if (s->fackets)
+		jsonw_uint_field(json_wr, "fackets", s->fackets);
+	if (s->reordering != 3)
+		jsonw_int_field(json_wr, "reordering", s->reordering);
+	if (s->rcv_rtt)
+		jsonw_float_field(json_wr, "rcv_rtt", s->rcv_rtt);
+
+	/*deal with special case */
+	res_json_fmt_branch(s->ss.state == SS_LISTEN, ' ');
+
+	CHECK_FMT_ADAPT(s->rcv_space, s,
+	json_handler_must_be_adapted_accordingly_when_hr_fmt_is_extended);
+}
+
+static void tcp_timer_json_fmt(struct tcpstat *s)
+{
+
+	if (s->timer)
+		if (s->timer > 4)
+			s->timer = 5;
+	jsonw_name(json_wr, "timer");
+	jsonw_start_object(json_wr);
+	jsonw_string_field(json_wr, "ss_timer_name",
+			ss_timer_name[s->timer]);
+	jsonw_string_field(json_wr, "print_ms_timer",
+			print_ms_timer(s->timeout));
+	jsonw_int_field(json_wr, "retrans", s->retrans);
+	jsonw_end_object(json_wr);
+}
+
+static void sock_state_json_fmt(struct sockstat *s, const char **sstate_name,
+			const char *sock_name, int netid_width,
+			int state_width)
+{
+
+	if (netid_width)
+		jsonw_string_field(json_wr, "Netid", sock_name);
+
+	if (state_width)
+		jsonw_string_field(json_wr, "State", sstate_name[s->state]);
+
+	jsonw_int_field(json_wr, "Recv-Q", s->rq);
+	jsonw_int_field(json_wr, "Send-Q", s->wq);
+}
+
+static void sock_details_json_fmt(struct sockstat *s, int type, unsigned groups,
+			unsigned long long cb)
+{
+	if (type == GENERIC_DETAIL && s->uid)
+		jsonw_uint_field(json_wr, "uid", s->uid);
+	if (type == GENERIC_DETAIL)
+		jsonw_uint_field(json_wr, "ino", s->sk);
+	jsonw_hex_field_outp(json_wr, "sk", s->sk);
+
+	if (type == NETLINK_DETAIL) {
+		jsonw_hex_field_outp(json_wr, "cb", cb);
+		jsonw_hex_field_outp(json_wr, "groups=0x", groups);
+	}
+}
+
+static void sock_addr_json_fmt(const char *addr, int addr_len, char *delim,
+			int port_len, const char *port, const char *ifname,
+			const char *peer_kind)
+{
+	jsonw_name(json_wr, peer_kind);
+	jsonw_start_object(json_wr);
+	jsonw_string_field(json_wr, "addr", addr);
+	if (NULL != ifname)
+		jsonw_string_field(json_wr, "interface", ifname);
+	print_serv_or_port((char *)port);
+	jsonw_end_object(json_wr);
+}
+
+static void sock_users_json_fmt(char *out)
+{
+	make_userout_valid(out);
+
+	jsonw_string_field(json_wr, "users", out);
+}
+
+static void sock_summary_json_fmt(struct ssummary *s, struct snmpstat *sn,
+			struct slabstat *slabstat)
+{
+	jsonw_name(json_wr, "summary");
+	jsonw_start_object(json_wr);
+	jsonw_int_field(json_wr, "total", s->socks);
+	jsonw_int_field(json_wr, "kernel", slabstat->socks);
+
+	jsonw_name(json_wr, "TCP");
+	jsonw_start_object(json_wr);
+	jsonw_int_field(json_wr, "total",
+			s->tcp_total + slabstat->tcp_syns + s->tcp_tws);
+	jsonw_int_field(json_wr, "estab", sn->tcp_estab);
+	jsonw_int_field(json_wr, "closed",
+			s->tcp_total - (s->tcp4_hashed + s->tcp6_hashed - s->tcp_tws));
+	jsonw_int_field(json_wr, "orphaned", s->tcp_orphans);
+	jsonw_int_field(json_wr, "synrecv", slabstat->tcp_syns);
+	jsonw_name(json_wr, "timewait");
+	jsonw_start_object(json_wr);
+	jsonw_int_field(json_wr, "ssummary_tcp_tws", s->tcp_tws);
+	jsonw_int_field(json_wr, "slabstat_tcp_tws", slabstat->tcp_tws);
+	jsonw_end_object(json_wr);
+	jsonw_int_field(json_wr, "ports", slabstat->tcp_ports);
+	jsonw_end_object(json_wr);
+
+	jsonw_name(json_wr, "trans_over_net_prop");
+	jsonw_start_object(json_wr);
+	jsonw_name(json_wr, "*");
+	jsonw_start_object(json_wr);
+	jsonw_int_field(json_wr, "Total", slabstat->socks);
+	jsonw_string_field(json_wr, "IP", "-");
+	jsonw_string_field(json_wr, "IPv6", "-");
+	jsonw_end_object(json_wr);
+	jsonw_end_object(json_wr);
+
+	jsonw_name(json_wr, "RAW");
+	jsonw_start_object(json_wr);
+	jsonw_int_field(json_wr, "Total", s->raw4 + s->raw6);
+	jsonw_int_field(json_wr, "IP", s->raw4);
+	jsonw_int_field(json_wr, "IPv6", s->raw6);
+	jsonw_end_object(json_wr);
+
+	jsonw_name(json_wr, "UDP");
+	jsonw_start_object(json_wr);
+	jsonw_int_field(json_wr, "Total", s->udp4 + s->udp6);
+	jsonw_int_field(json_wr, "IP", s->udp4);
+	jsonw_int_field(json_wr, "IPv6", s->udp6);
+	jsonw_end_object(json_wr);
+
+	jsonw_name(json_wr, "TCP");
+	jsonw_start_object(json_wr);
+	jsonw_int_field(json_wr, "Total", s->tcp4_hashed + s->tcp6_hashed);
+	jsonw_int_field(json_wr, "IP", s->tcp4_hashed);
+	jsonw_int_field(json_wr, "IPv6", s->tcp6_hashed);
+	jsonw_end_object(json_wr);
+
+	jsonw_name(json_wr, "INET");
+	jsonw_start_object(json_wr);
+	jsonw_int_field(json_wr, "Total",
+			s->raw4 + s->udp4 + s->tcp4_hashed +
+			s->raw6 + s->udp6 + s->tcp6_hashed);
+	jsonw_int_field(json_wr, "IP", s->raw4 + s->udp4 + s->tcp4_hashed);
+	jsonw_int_field(json_wr, "IPv6", s->raw6 + s->udp6 + s->tcp6_hashed);
+	jsonw_end_object(json_wr);
+
+	jsonw_name(json_wr, "FRAG");
+	jsonw_start_object(json_wr);
+	jsonw_int_field(json_wr, "Total", s->frag4 + s->frag6);
+	jsonw_int_field(json_wr, "IP", s->frag4);
+	jsonw_int_field(json_wr, "IPv6", s->frag6);
+	jsonw_end_object(json_wr);
+
+	jsonw_end_object(json_wr);
+}
+
+static void sock_conn_json_fmt(unsigned char mask)
+{
+	char tmp[4];
+
+	sprintf(tmp, "%c-%c",  mask & 1 ? '-' : '<',
+	mask & 2 ? '-' : '>');
+	jsonw_string_field(json_wr, "shutdown", tmp);
+}
+
+static void mem_json_fmt(const struct inet_diag_meminfo *minfo)
+{
+	jsonw_name(json_wr, "mem");
+	jsonw_start_object(json_wr);
+	jsonw_uint_field(json_wr, "r", minfo->idiag_rmem);
+	jsonw_uint_field(json_wr, "w", minfo->idiag_wmem);
+	jsonw_uint_field(json_wr, "f", minfo->idiag_fmem);
+	jsonw_uint_field(json_wr, "t", minfo->idiag_fmem);
+	jsonw_end_object(json_wr);
+}
+
+static void skmem_json_fmt(const __u32 *skmeminfo, struct rtattr **tb,
+		int attrtype)
+{
+	jsonw_name(json_wr, "skmem");
+	jsonw_start_object(json_wr);
+	jsonw_uint_field(json_wr, "r", skmeminfo[SK_MEMINFO_RMEM_ALLOC]);
+	jsonw_uint_field(json_wr, "rb", skmeminfo[SK_MEMINFO_RCVBUF]);
+	jsonw_uint_field(json_wr, "t", skmeminfo[SK_MEMINFO_WMEM_ALLOC]);
+	jsonw_uint_field(json_wr, "tb", skmeminfo[SK_MEMINFO_SNDBUF]);
+	jsonw_uint_field(json_wr, "f", skmeminfo[SK_MEMINFO_FWD_ALLOC]);
+	jsonw_uint_field(json_wr, "w", skmeminfo[SK_MEMINFO_WMEM_QUEUED]);
+	jsonw_uint_field(json_wr, "o", skmeminfo[SK_MEMINFO_OPTMEM]);
+	if (RTA_PAYLOAD(tb[attrtype]) >=
+	(SK_MEMINFO_BACKLOG + 1) * sizeof(__u32))
+		jsonw_uint_field(json_wr, "bl", skmeminfo[SK_MEMINFO_BACKLOG]);
+	jsonw_end_object(json_wr);
+}
+
+static void bpf_filter_json_fmt(struct sock_filter *fil, int num)
+{
+	jsonw_name(json_wr, "bpf_filter");
+	jsonw_start_array(json_wr);
+	jsonw_int(json_wr, num);
+	while (num) {
+		jsonw_start_object(json_wr);
+		jsonw_hex_as_str_outp(json_wr, fil->code);
+		jsonw_uint(json_wr, fil->jt);
+		jsonw_uint(json_wr, fil->jf);
+		jsonw_uint(json_wr, fil->k);
+		jsonw_int(json_wr, num--);
+		fil++;
+		jsonw_end_object(json_wr);
+	}
+	jsonw_end_array(json_wr);
+}
+
+static void opt_json_fmt(char *opt)
+{
+	jsonw_string_field(json_wr, "opt", opt);
+}
+
+static void proc_json_fmt(int serv_width, char *pid_ctx)
+{
+	if (pid_ctx != NULL) {
+		jsonw_string_field(json_wr, "proc_ctx", pid_ctx);
+		free(pid_ctx);
+	} else {
+		jsonw_string_field(json_wr, "proc_ctx", "unavailable");
+	}
+}
+
+static void packet_show_ring_json_fmt(struct packet_diag_ring *ring)
+{
+	jsonw_int_field(json_wr, "blk_size", ring->pdr_block_size);
+	jsonw_int_field(json_wr, "blk_nr", ring->pdr_block_nr);
+	jsonw_int_field(json_wr, "frm_size", ring->pdr_frame_size);
+	jsonw_int_field(json_wr, "frm_nr", ring->pdr_frame_nr);
+	jsonw_int_field(json_wr, "tmo", ring->pdr_retire_tmo);
+	jsonw_hex_field_outp(json_wr, "features_0x", ring->pdr_features);
+}
+
+const struct fmt_op_hub json_output_op = {
+	.tcp_stats_fmt = tcp_stats_json_fmt,
+	.tcp_timer_fmt = tcp_timer_json_fmt,
+	.sock_state_fmt = sock_state_json_fmt,
+	.sock_details_fmt = sock_details_json_fmt,
+	.sock_addr_fmt = sock_addr_json_fmt,
+	.sock_users_fmt = sock_users_json_fmt,
+	.sock_summary_fmt = sock_summary_json_fmt,
+	.sock_conn_fmt = sock_conn_json_fmt,
+	.mem_fmt = mem_json_fmt,
+	.skmem_fmt = skmem_json_fmt,
+	.bpf_filter_fmt = bpf_filter_json_fmt,
+	.opt_fmt = opt_json_fmt,
+	.proc_fmt = proc_json_fmt,
+	.packet_show_ring_fmt = packet_show_ring_json_fmt,
+};
diff --git a/misc/ss_json_fmt.h b/misc/ss_json_fmt.h
new file mode 100644
index 0000000..13a6f1d
--- /dev/null
+++ b/misc/ss_json_fmt.h
@@ -0,0 +1,26 @@
+#ifndef SS_JSON_FMT_H
+#define SS_JSON_FMT_H
+
+#include <linux/inet_diag.h>
+#include <linux/pkt_sched.h>
+#include <linux/filter.h>
+#include <json_writer.h>
+#include "ss_types.h"
+
+extern int json_output;
+extern json_writer_t *json_wr;
+extern int resolve_services;
+extern int show_options;
+extern int show_details;
+extern int show_users;
+extern int show_mem;
+extern int show_tcpinfo;
+extern int show_bpf;
+extern int show_proc_ctx;
+extern int show_sock_ctx;
+extern struct filter ss_current_filter;
+
+/*generic auxiliary mechanism for json related dangling delimiter issues*/
+void res_json_fmt_branch(int pred, char bound);
+
+#endif				/* SS_JSON_FMT_H */
diff --git a/misc/ss_out_fmt.c b/misc/ss_out_fmt.c
new file mode 100644
index 0000000..57bc24e
--- /dev/null
+++ b/misc/ss_out_fmt.c
@@ -0,0 +1,127 @@
+#include <stdio.h>
+#include "ss_out_fmt.h"
+const struct fmt_op_hub *fmt_op_hub[] = {
+	/*human readble */
+	&hr_output_op,
+	/*json */
+	&json_output_op
+};
+
+
+
+char *sprint_bw(char *buf, double bw)
+{
+	if (bw > 1000000.)
+		sprintf(buf, "%.1fM", bw / 1000000.);
+	else if (bw > 1000.)
+		sprintf(buf, "%.1fK", bw / 1000.);
+	else
+		sprintf(buf, "%g", bw);
+
+	return buf;
+}
+
+char *print_ms_timer(int timeout)
+{
+	static char buf[64];
+	int secs, msecs, minutes;
+
+	if (timeout < 0)
+		timeout = 0;
+	secs = timeout / 1000;
+	minutes = secs / 60;
+	secs = secs % 60;
+	msecs = timeout % 1000;
+	buf[0] = 0;
+	if (minutes) {
+		msecs = 0;
+		snprintf(buf, sizeof(buf) - 16, "%dmin", minutes);
+		if (minutes > 9)
+			secs = 0;
+	}
+	if (secs) {
+		if (secs > 9)
+			msecs = 0;
+		sprintf(buf + strlen(buf), "%d%s", secs, msecs ? "." : "sec");
+	}
+	if (msecs)
+		sprintf(buf + strlen(buf), "%03dms", msecs);
+	return buf;
+}
+
+void tcp_stats_fmt(struct tcpstat *s)
+{
+	fmt_op_hub[fmt_type]->tcp_stats_fmt(s);
+}
+
+void tcp_timer_fmt(struct tcpstat *s)
+{
+	fmt_op_hub[fmt_type]->tcp_timer_fmt(s);
+}
+
+void sock_state_fmt(struct sockstat *s, const char **sstate_name,
+		const char *sock_name, int netid_width, int state_width)
+{
+	fmt_op_hub[fmt_type]->sock_state_fmt(s, sstate_name, sock_name,
+					netid_width, state_width);
+}
+
+void sock_details_fmt(struct sockstat *s, int type,
+		unsigned groups, unsigned long long cb)
+{
+	fmt_op_hub[fmt_type]->sock_details_fmt(s, type, groups, cb);
+}
+
+void sock_addr_fmt(const char *addr, int addr_len, char *delim,
+		int port_len, const char *port, const char *ifname,
+		const char *peer_kind)
+{
+	fmt_op_hub[fmt_type]->sock_addr_fmt(addr, addr_len,
+					delim, port_len, port, ifname,
+					peer_kind);
+}
+
+void sock_users_fmt(char *out)
+{
+	fmt_op_hub[fmt_type]->sock_users_fmt(out);
+}
+
+void sock_summary_fmt(struct ssummary *s, struct snmpstat *sn,
+		struct slabstat *slabstat)
+{
+	fmt_op_hub[fmt_type]->sock_summary_fmt(s, sn, slabstat);
+}
+
+void sock_conn_fmt(unsigned char mask)
+{
+	fmt_op_hub[fmt_type]->sock_conn_fmt(mask);
+}
+
+void mem_fmt(const struct inet_diag_meminfo *minfo)
+{
+	fmt_op_hub[fmt_type]->mem_fmt(minfo);
+}
+
+void skmem_fmt(const __u32 *skmeminfo, struct rtattr **tb, int attrtype)
+{
+	fmt_op_hub[fmt_type]->skmem_fmt(skmeminfo, tb, attrtype);
+}
+
+void bpf_filter_fmt(struct sock_filter *f, int num)
+{
+	fmt_op_hub[fmt_type]->bpf_filter_fmt(f, num);
+}
+
+void opt_fmt(char *opt)
+{
+	fmt_op_hub[fmt_type]->opt_fmt(opt);
+}
+
+void proc_fmt(int serv_width, char *pid_ctx)
+{
+	fmt_op_hub[fmt_type]->proc_fmt(serv_width, pid_ctx);
+}
+void packet_show_ring_fmt(struct packet_diag_ring *ring)
+{
+	fmt_op_hub[fmt_type]->packet_show_ring_fmt(ring);
+}
diff --git a/misc/ss_out_fmt.h b/misc/ss_out_fmt.h
new file mode 100644
index 0000000..8608cde
--- /dev/null
+++ b/misc/ss_out_fmt.h
@@ -0,0 +1,83 @@
+#ifndef SS_OUT_FMT_H
+#define SS_OUT_FMT_H
+
+#include "ss_hr_fmt.h"
+#include "ss_json_fmt.h"
+#include "ss_types.h"
+#include <utils.h>
+#include <linux/inet_diag.h>
+#include <linux/pkt_sched.h>
+#include <linux/filter.h>
+#include <linux/netdevice.h>
+#include <linux/packet_diag.h>
+
+#define GENERIC_DETAIL 0
+#define NETLINK_DETAIL 1
+
+/* when extending the tcp fmt handler,
+ * you have to pass the last cond of the tcp_stats
+ * struct as sentinel to this macro to ensure compilability
+ * of ss: this enforces a symmetrical extension of the
+ * individual formatting handlers (json, human readable)
+ */
+#define CHECK_FMT_ADAPT(SENTINEL, BEGIN) \
+	BUILD_BUG_ON(!((((void *)&SENTINEL - (void *)BEGIN) + sizeof(SENTINEL)) \
+	/ sizeof(*BEGIN)))
+
+
+extern int json_output;
+extern enum out_fmt_type fmt_type;
+extern const struct fmt_op_hub hr_output_op;
+extern const struct fmt_op_hub json_output_op;
+extern const char *ss_timer_name[];
+
+enum out_fmt_type { FMT_HR, FMT_JSON };
+
+struct fmt_op_hub {
+	void (*tcp_stats_fmt)(struct tcpstat *s);
+	void (*tcp_timer_fmt)(struct tcpstat *s);
+	void (*sock_state_fmt)(struct sockstat *s, const char **sstate_name,
+				const char *sock_name, int netid_width,
+				int state_width);
+	void (*sock_details_fmt)(struct sockstat *s, int type,
+				unsigned groups, unsigned long long cb);
+	void (*sock_addr_fmt)(const char *addr, int addr_len, char *delim,
+			int port_len, const char *port,
+			const char *ifname, const char *peer_kind);
+	void (*sock_users_fmt)(char *out);
+	void (*sock_summary_fmt)(struct ssummary *s, struct snmpstat *sn,
+				struct slabstat *slabstat);
+	void (*sock_conn_fmt)(unsigned char mask);
+	void (*mem_fmt)(const struct inet_diag_meminfo *minfo);
+	void (*skmem_fmt)(const __u32 *skmeminfo, struct rtattr **tb,
+			int attrtype);
+	void (*bpf_filter_fmt)(struct sock_filter *f, int num);
+	void (*opt_fmt)(char *opt);
+	void (*proc_fmt)(int serv_width, char *pid_ctx);
+	void (*packet_show_ring_fmt)(struct packet_diag_ring *ring);
+};
+
+void tcp_stats_fmt(struct tcpstat *s);
+void tcp_timer_fmt(struct tcpstat *s);
+void sock_state_fmt(struct sockstat *s, const char **sstate_name,
+		const char *sock_name, int netid_width, int state_width);
+void sock_details_fmt(struct sockstat *s, int type,
+		unsigned groups, unsigned long long cb);
+void sock_addr_fmt(const char *addr, int addr_len, char *delim, int port_len,
+		const char *port, const char *ifname, const char *peer_kind);
+void sock_users_fmt(char *out);
+void sock_summary_fmt(struct ssummary *s, struct snmpstat *sn,
+		struct slabstat *slabstat);
+void sock_conn_fmt(unsigned char mask);
+void mem_fmt(const struct inet_diag_meminfo *minfo);
+void skmem_fmt(const __u32 *skmeminfo, struct rtattr **tb, int attrtype);
+void bpf_filter_fmt(struct sock_filter *f, int num);
+void opt_fmt(char *opt);
+void proc_fmt(int serv_width, char *pid_ctx);
+void packet_show_ring_fmt(struct packet_diag_ring *ring);
+
+/*unisonly utilized formatting parts*/
+char *sprint_bw(char *buf, double bw);
+char *print_ms_timer(int timeout);
+
+#endif				/* SS_OUT_FMT_H */
-- 
1.9.1

  parent reply	other threads:[~2015-09-10 19:35 UTC|newest]

Thread overview: 33+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-09-10 19:34 iproute2 v7: full ss json support and general output simplification Matthias Tafelmeier
2015-09-10 19:34 ` [PATCH v7 01/10] ss: rooted out ss type declarations for output formatters Matthias Tafelmeier
2015-09-10 19:35 ` Matthias Tafelmeier [this message]
2015-09-23 23:26   ` [PATCH v7 02/10] ss: created formatters for json and hr Stephen Hemminger
2015-09-24 17:43     ` Matthias Tafelmeier
     [not found]     ` <bc9cacb94de24857a7d9e13960bfae56@HQ1WP-EXMB11.corp.brocade.com>
2015-09-24 22:16       ` Stephen Hemminger
2015-09-25  6:01         ` Matthias Tafelmeier
2015-10-28  8:07           ` Matthias Tafelmeier
2015-10-28  8:28             ` Phil Sutter
2015-10-28 11:57               ` Matthias Tafelmeier
2015-10-28 15:05                 ` Phil Sutter
2015-10-28 15:50                   ` Matthias Tafelmeier
     [not found]           ` <b114412a541d4273a9a7a1c0ae4777df@HQ1WP-EXMB11.corp.brocade.com>
2015-11-02 23:06             ` Stephen Hemminger
2015-11-03  6:54               ` Matthias Tafelmeier
2015-10-27 12:21   ` Phil Sutter
2015-10-27 13:47     ` David Miller
2015-10-27 13:35       ` Phil Sutter
2015-09-10 19:35 ` [PATCH v7 03/10] ss: removed obsolet fmt functions Matthias Tafelmeier
2015-09-10 19:35 ` [PATCH v7 04/10] ss: prepare timer for output handler usage Matthias Tafelmeier
2015-09-10 19:35 ` [PATCH v7 05/10] ss: replaced old output with new generic output mechanisms Matthias Tafelmeier
2015-10-27 13:04   ` Phil Sutter
2015-09-10 19:35 ` [PATCH v7 06/10] ss: renaming and export of current_filter Matthias Tafelmeier
2015-10-27 13:10   ` Phil Sutter
2015-09-10 19:35 ` [PATCH v7 07/10] ss: symmetrical subhandler output extension example Matthias Tafelmeier
2015-10-27 13:13   ` Phil Sutter
2015-09-10 19:35 ` [PATCH v7 08/10] ss: symmetrical formatter " Matthias Tafelmeier
2015-10-27 13:17   ` Phil Sutter
2015-09-10 19:35 ` [PATCH v7 09/10] ss: fixed free on local array for valid json output Matthias Tafelmeier
2015-10-27 13:19   ` Phil Sutter
2015-09-10 19:35 ` [PATCH v7 10/10] ss: activate json_writer excluded logic Matthias Tafelmeier
2015-10-27 13:21   ` Phil Sutter
2015-10-28  2:39     ` Stephen Hemminger
2015-10-28  8:17       ` Phil Sutter

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1441913708-15532-3-git-send-email-matthias.tafelmeier@gmx.net \
    --to=matthias.tafelmeier@gmx.net \
    --cc=daniel@iogearbox.net \
    --cc=edumazet@google.com \
    --cc=fw@strlen.de \
    --cc=hagen@jauu.net \
    --cc=netdev@vger.kernel.org \
    --cc=shemminger@osdl.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).