tc: q_netem: JSON-ify the output

Add JSON output support to q_netem.

The normal output is untouched.

In JSON output always use seconds as the base of time units,
and non-percentage numbers (0.01 instead of 1%). Try to always
report the fields, even if they are zero.
All this should make the output more machine-friendly.

v2: less macroes

Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com>
Reviewed-by: Quentin Monnet <quentin.monnet@netronome.com>
Signed-off-by: David Ahern <dsahern@gmail.com>
This commit is contained in:
Jakub Kicinski 2019-06-17 17:49:29 -07:00 committed by David Ahern
parent ca697cee4c
commit b3cf1167e7
1 changed files with 114 additions and 66 deletions

View File

@ -58,6 +58,43 @@ static void explain1(const char *arg)
*/ */
#define MAX_DIST (16*1024) #define MAX_DIST (16*1024)
/* Print values only if they are non-zero */
static void __print_int_opt(const char *label_json, const char *label_fp,
int val)
{
print_int(PRINT_ANY, label_json, val ? label_fp : "", val);
}
#define PRINT_INT_OPT(label, val) \
__print_int_opt(label, " " label " %d", (val))
/* Time print prints normally with varying units, but for JSON prints
* in seconds (1ms vs 0.001).
*/
static void __print_time64(const char *label_json, const char *label_fp,
__u64 val)
{
SPRINT_BUF(b1);
print_string(PRINT_FP, NULL, label_fp, sprint_time64(val, b1));
print_float(PRINT_JSON, label_json, NULL, val / 1000000000.);
}
#define __PRINT_TIME64(label_json, label_fp, val) \
__print_time64(label_json, label_fp " %s", (val))
#define PRINT_TIME64(label, val) __PRINT_TIME64(label, " " label, (val))
/* Percent print prints normally in percentage points, but for JSON prints
* an absolute value (1% vs 0.01).
*/
static void __print_percent(const char *label_json, const char *label_fp,
__u32 per)
{
print_float(PRINT_FP, NULL, label_fp, (100. * per) / UINT32_MAX);
print_float(PRINT_JSON, label_json, NULL, (1. * per) / UINT32_MAX);
}
#define __PRINT_PERCENT(label_json, label_fp, per) \
__print_percent(label_json, label_fp " %g%%", (per))
#define PRINT_PERCENT(label, per) __PRINT_PERCENT(label, " " label, (per))
/* scaled value used to percent of maximum. */ /* scaled value used to percent of maximum. */
static void set_percent(__u32 *percent, double per) static void set_percent(__u32 *percent, double per)
{ {
@ -75,15 +112,14 @@ static int get_percent(__u32 *percent, const char *str)
return 0; return 0;
} }
static void print_percent(char *buf, int len, __u32 per) static void print_corr(bool present, __u32 value)
{ {
snprintf(buf, len, "%g%%", (100. * per) / UINT32_MAX); if (!is_json_context()) {
} if (present)
__PRINT_PERCENT("", "", value);
static char *sprint_percent(__u32 per, char *buf) } else {
{ PRINT_PERCENT("correlation", value);
print_percent(buf, SPRINT_BSIZE-1, per); }
return buf;
} }
/* /*
@ -687,97 +723,109 @@ static int netem_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
} }
} }
fprintf(f, "limit %d", qopt.limit); print_uint(PRINT_ANY, "limit", "limit %d", qopt.limit);
if (qopt.latency) { if (qopt.latency) {
fprintf(f, " delay %s", sprint_ticks(qopt.latency, b1)); open_json_object("delay");
if (!is_json_context()) {
print_string(PRINT_FP, NULL, " delay %s",
sprint_ticks(qopt.latency, b1));
if (qopt.jitter) { if (qopt.jitter)
fprintf(f, " %s", sprint_ticks(qopt.jitter, b1)); print_string(PRINT_FP, NULL, " %s",
if (cor && cor->delay_corr) sprint_ticks(qopt.jitter, b1));
fprintf(f, " %s", sprint_percent(cor->delay_corr, b1)); } else {
print_float(PRINT_JSON, "delay", NULL,
tc_core_tick2time(qopt.latency) /
1000000.);
print_float(PRINT_JSON, "jitter", NULL,
tc_core_tick2time(qopt.jitter) /
1000000.);
} }
print_corr(qopt.jitter && cor && cor->delay_corr,
cor ? cor->delay_corr : 0);
close_json_object();
} }
if (qopt.loss) { if (qopt.loss) {
fprintf(f, " loss %s", sprint_percent(qopt.loss, b1)); open_json_object("loss-random");
if (cor && cor->loss_corr) PRINT_PERCENT("loss", qopt.loss);
fprintf(f, " %s", sprint_percent(cor->loss_corr, b1)); print_corr(cor && cor->loss_corr, cor ? cor->loss_corr : 0);
close_json_object();
} }
if (gimodel) { if (gimodel) {
fprintf(f, " loss state p13 %s", sprint_percent(gimodel->p13, b1)); open_json_object("loss-state");
fprintf(f, " p31 %s", sprint_percent(gimodel->p31, b1)); __PRINT_PERCENT("p13", " loss state p13", gimodel->p13);
fprintf(f, " p32 %s", sprint_percent(gimodel->p32, b1)); PRINT_PERCENT("p31", gimodel->p31);
fprintf(f, " p23 %s", sprint_percent(gimodel->p23, b1)); PRINT_PERCENT("p32", gimodel->p32);
fprintf(f, " p14 %s", sprint_percent(gimodel->p14, b1)); PRINT_PERCENT("p23", gimodel->p23);
PRINT_PERCENT("p14", gimodel->p14);
close_json_object();
} }
if (gemodel) { if (gemodel) {
fprintf(f, " loss gemodel p %s", open_json_object("loss-gemodel");
sprint_percent(gemodel->p, b1)); __PRINT_PERCENT("p", " loss gemodel p", gemodel->p);
fprintf(f, " r %s", sprint_percent(gemodel->r, b1)); PRINT_PERCENT("r", gemodel->r);
fprintf(f, " 1-h %s", sprint_percent(UINT32_MAX - PRINT_PERCENT("1-h", UINT32_MAX - gemodel->h);
gemodel->h, b1)); PRINT_PERCENT("1-k", gemodel->k1);
fprintf(f, " 1-k %s", sprint_percent(gemodel->k1, b1)); close_json_object();
} }
if (qopt.duplicate) { if (qopt.duplicate) {
fprintf(f, " duplicate %s", open_json_object("duplicate");
sprint_percent(qopt.duplicate, b1)); PRINT_PERCENT("duplicate", qopt.duplicate);
if (cor && cor->dup_corr) print_corr(cor && cor->dup_corr, cor ? cor->dup_corr : 0);
fprintf(f, " %s", sprint_percent(cor->dup_corr, b1)); close_json_object();
} }
if (reorder && reorder->probability) { if (reorder && reorder->probability) {
fprintf(f, " reorder %s", open_json_object("reorder");
sprint_percent(reorder->probability, b1)); PRINT_PERCENT("reorder", reorder->probability);
if (reorder->correlation) print_corr(reorder->correlation, reorder->correlation);
fprintf(f, " %s", close_json_object();
sprint_percent(reorder->correlation, b1));
} }
if (corrupt && corrupt->probability) { if (corrupt && corrupt->probability) {
fprintf(f, " corrupt %s", open_json_object("corrupt");
sprint_percent(corrupt->probability, b1)); PRINT_PERCENT("corrupt", corrupt->probability);
if (corrupt->correlation) print_corr(corrupt->correlation, corrupt->correlation);
fprintf(f, " %s", close_json_object();
sprint_percent(corrupt->correlation, b1));
} }
if (rate && rate->rate) { if (rate && rate->rate) {
if (rate64) open_json_object("rate");
fprintf(f, " rate %s", sprint_rate(rate64, b1)); rate64 = rate64 ? : rate->rate;
else print_string(PRINT_FP, NULL, " rate %s",
fprintf(f, " rate %s", sprint_rate(rate->rate, b1)); sprint_rate(rate64, b1));
if (rate->packet_overhead) print_lluint(PRINT_JSON, "rate", NULL, rate64);
fprintf(f, " packetoverhead %d", rate->packet_overhead); PRINT_INT_OPT("packetoverhead", rate->packet_overhead);
if (rate->cell_size) print_uint(PRINT_ANY, "cellsize",
fprintf(f, " cellsize %u", rate->cell_size); rate->cell_size ? " cellsize %u" : "",
if (rate->cell_overhead) rate->cell_size);
fprintf(f, " celloverhead %d", rate->cell_overhead); PRINT_INT_OPT("celloverhead", rate->cell_overhead);
close_json_object();
} }
if (slot) { if (slot) {
open_json_object("slot");
if (slot->dist_jitter > 0) { if (slot->dist_jitter > 0) {
fprintf(f, " slot distribution %s", sprint_time64(slot->dist_delay, b1)); __PRINT_TIME64("distribution", " slot distribution",
fprintf(f, " %s", sprint_time64(slot->dist_jitter, b1)); slot->dist_delay);
__PRINT_TIME64("jitter", "", slot->dist_jitter);
} else { } else {
fprintf(f, " slot %s", sprint_time64(slot->min_delay, b1)); __PRINT_TIME64("min-delay", " slot", slot->min_delay);
fprintf(f, " %s", sprint_time64(slot->max_delay, b1)); __PRINT_TIME64("max-delay", "", slot->max_delay);
} }
if (slot->max_packets) PRINT_INT_OPT("packets", slot->max_packets);
fprintf(f, " packets %d", slot->max_packets); PRINT_INT_OPT("bytes", slot->max_bytes);
if (slot->max_bytes) close_json_object();
fprintf(f, " bytes %d", slot->max_bytes);
} }
if (ecn) print_bool(PRINT_ANY, "ecn", ecn ? " ecn " : "", ecn);
fprintf(f, " ecn "); print_luint(PRINT_ANY, "gap", qopt.gap ? " gap %lu" : "",
(unsigned long)qopt.gap);
if (qopt.gap)
fprintf(f, " gap %lu", (unsigned long)qopt.gap);
return 0; return 0;
} }