When --format json is passed to ovs-appctl, pmd-perf-show returns a
JSON object keyed by thread identifier ("pmd-cNN" for PMD threads,
"main" for the main thread). Each entry contains:
- "core" and "numa" fields (numeric or null)
- "measurement-duration-s" (seconds as real)
- "iterations" (object with cycle/iteration stats, or null for main)
- "rx-packets" (count, kpps, cycles-per-packet)
- "datapath-passes" (count, per-packet ratio, all hit types, upcalls)
- "tx-packets" (count, kpps, batches, packets-per-batch)
Values that depend on TSC cycle accounting (cycles-per-packet,
us-per-upcall) are null for the main thread.
The JSON formatting function is placed in dpif-netdev-perf.c alongside
the text formatter, since both need access to the file-local tsc_hz.
Signed-off-by: Timothy Redaelli <[email protected]>
---
lib/dpif-netdev-perf.c | 129 +++++++++++++++++++++++++++++++++++++++++
lib/dpif-netdev-perf.h | 5 ++
lib/dpif-netdev.c | 65 +++++++++++++++++----
tests/pmd.at | 78 +++++++++++++++++++++++++
4 files changed, 266 insertions(+), 11 deletions(-)
diff --git a/lib/dpif-netdev-perf.c b/lib/dpif-netdev-perf.c
index 4b3b35852..536d15a07 100644
--- a/lib/dpif-netdev-perf.c
+++ b/lib/dpif-netdev-perf.c
@@ -315,6 +315,135 @@ pmd_perf_format_overall_stats(struct ds *str, struct
pmd_perf_stats *s,
}
}
+void
+pmd_perf_format_overall_stats_json(struct json *json,
+ struct pmd_perf_stats *s,
+ double duration, bool show_iterations)
+{
+ uint64_t stats[PMD_N_STATS];
+
+ if (duration == 0) {
+ json_object_put(json, "iterations", json_null_create());
+ json_object_put(json, "rx-packets", json_null_create());
+ json_object_put(json, "datapath-passes", json_null_create());
+ json_object_put(json, "tx-packets", json_null_create());
+ return;
+ }
+
+ double us_per_cycle = 1000000.0 / tsc_hz;
+
+ pmd_perf_read_counters(s, stats);
+ uint64_t tot_cycles = stats[PMD_CYCLES_ITER_IDLE] +
+ stats[PMD_CYCLES_ITER_BUSY];
+ uint64_t rx_packets = stats[PMD_STAT_RECV];
+ uint64_t tx_packets = stats[PMD_STAT_SENT_PKTS];
+ uint64_t tx_batches = stats[PMD_STAT_SENT_BATCHES];
+ uint64_t passes = stats[PMD_STAT_RECV] +
+ stats[PMD_STAT_RECIRC];
+ uint64_t upcalls = stats[PMD_STAT_MISS];
+ uint64_t upcall_cycles = stats[PMD_CYCLES_UPCALL];
+ uint64_t tot_iter = histogram_samples(&s->pkts);
+ uint64_t idle_iter = s->pkts.bin[0];
+ uint64_t busy_iter = tot_iter >= idle_iter ? tot_iter - idle_iter : 0;
+ uint64_t sleep_iter = stats[PMD_SLEEP_ITER];
+ uint64_t tot_sleep_cycles = stats[PMD_CYCLES_SLEEP];
+
+ /* Iterations. */
+ if (show_iterations) {
+ struct json *iter = json_object_create();
+ json_object_put(iter, "total", json_integer_create(tot_iter));
+ json_object_put(iter, "us-per-iteration",
+ json_real_create(tot_iter
+ ? (tot_cycles + tot_sleep_cycles)
+ * us_per_cycle / tot_iter
+ : 0));
+ json_object_put(iter, "used-tsc-cycles",
+ json_integer_create(tot_cycles));
+ json_object_put(iter, "used-tsc-pct",
+ json_real_create(
+ 100.0 * (tot_cycles / duration) / tsc_hz));
+ json_object_put(iter, "idle", json_integer_create(idle_iter));
+ json_object_put(iter, "idle-pct",
+ json_real_create(tot_cycles
+ ? 100.0 * stats[PMD_CYCLES_ITER_IDLE] / tot_cycles
+ : 0));
+ json_object_put(iter, "busy", json_integer_create(busy_iter));
+ json_object_put(iter, "busy-pct",
+ json_real_create(tot_cycles
+ ? 100.0 * stats[PMD_CYCLES_ITER_BUSY] / tot_cycles
+ : 0));
+ json_object_put(iter, "sleep", json_integer_create(sleep_iter));
+ json_object_put(iter, "sleep-pct",
+ json_real_create(tot_iter
+ ? 100.0 * sleep_iter / tot_iter : 0));
+ json_object_put(iter, "sleep-time-us",
+ json_real_create(tot_sleep_cycles * us_per_cycle));
+ json_object_put(iter, "sleep-us-per-iteration",
+ json_real_create(sleep_iter
+ ? (tot_sleep_cycles * us_per_cycle) / sleep_iter
+ : 0));
+ json_object_put(json, "iterations", iter);
+ } else {
+ json_object_put(json, "iterations", json_null_create());
+ }
+
+ /* Rx packets. */
+ struct json *rx = json_object_create();
+ json_object_put(rx, "count", json_integer_create(rx_packets));
+ json_object_put(rx, "kpps",
+ json_real_create(rx_packets
+ ? (rx_packets / duration) / 1000 : 0));
+ json_object_put(rx, "cycles-per-packet",
+ tot_iter && rx_packets
+ ? json_real_create(
+ 1.0 * stats[PMD_CYCLES_ITER_BUSY] / rx_packets)
+ : json_null_create());
+ json_object_put(json, "rx-packets", rx);
+
+ /* Datapath passes. */
+ struct json *dp = json_object_create();
+ json_object_put(dp, "count", json_integer_create(passes));
+ json_object_put(dp, "passes-per-packet",
+ json_real_create(rx_packets
+ ? 1.0 * passes / rx_packets : 0));
+ json_object_put(dp, "phwol-hits",
+ json_integer_create(stats[PMD_STAT_PHWOL_HIT]));
+ json_object_put(dp, "simple-match-hits",
+ json_integer_create(stats[PMD_STAT_SIMPLE_HIT]));
+ json_object_put(dp, "emc-hits",
+ json_integer_create(stats[PMD_STAT_EXACT_HIT]));
+ json_object_put(dp, "smc-hits",
+ json_integer_create(stats[PMD_STAT_SMC_HIT]));
+ json_object_put(dp, "megaflow-hits",
+ json_integer_create(stats[PMD_STAT_MASKED_HIT]));
+ json_object_put(dp, "subtable-lookups-per-megaflow-hit",
+ json_real_create(stats[PMD_STAT_MASKED_HIT]
+ ? 1.0 * stats[PMD_STAT_MASKED_LOOKUP]
+ / stats[PMD_STAT_MASKED_HIT]
+ : 0));
+ json_object_put(dp, "upcalls", json_integer_create(upcalls));
+ json_object_put(dp, "us-per-upcall",
+ tot_iter
+ ? json_real_create(upcalls
+ ? (upcall_cycles * us_per_cycle) / upcalls : 0)
+ : json_null_create());
+ json_object_put(dp, "lost-upcalls",
+ json_integer_create(stats[PMD_STAT_LOST]));
+ json_object_put(json, "datapath-passes", dp);
+
+ /* Tx packets. */
+ struct json *tx = json_object_create();
+ json_object_put(tx, "count", json_integer_create(tx_packets));
+ json_object_put(tx, "kpps",
+ json_real_create(tx_packets
+ ? (tx_packets / duration) / 1000 : 0));
+ json_object_put(tx, "batches", json_integer_create(tx_batches));
+ json_object_put(tx, "packets-per-batch",
+ json_real_create(tx_batches
+ ? 1.0 * tx_packets / tx_batches : 0));
+ json_object_put(json, "tx-packets", tx);
+}
+
void
pmd_perf_format_histograms(struct ds *str, struct pmd_perf_stats *s)
{
diff --git a/lib/dpif-netdev-perf.h b/lib/dpif-netdev-perf.h
index cf4b6e103..9b0cb789a 100644
--- a/lib/dpif-netdev-perf.h
+++ b/lib/dpif-netdev-perf.h
@@ -29,6 +29,7 @@
#include <rte_cycles.h>
#endif
+#include "openvswitch/json.h"
#include "openvswitch/vlog.h"
#include "ovs-atomic.h"
#include "timeval.h"
@@ -423,6 +424,10 @@ struct pmd_perf_params {
void pmd_perf_format_overall_stats(struct ds *str, struct pmd_perf_stats *s,
double duration, bool format_iterations);
+void pmd_perf_format_overall_stats_json(struct json *json,
+ struct pmd_perf_stats *s,
+ double duration,
+ bool show_iterations);
void pmd_perf_format_histograms(struct ds *str, struct pmd_perf_stats *s);
void pmd_perf_format_iteration_history(struct ds *str,
struct pmd_perf_stats *s,
diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c
index b9233ba99..e3b782354 100644
--- a/lib/dpif-netdev.c
+++ b/lib/dpif-netdev.c
@@ -641,6 +641,32 @@ format_pmd_thread(struct ds *reply, struct
dp_netdev_pmd_thread *pmd)
ds_put_cstr(reply, ":\n");
}
+static struct json *
+pmd_info_show_perf_json(struct dp_netdev_pmd_thread *pmd)
+{
+ long long now = time_msec();
+ double duration = (now - pmd->perf_stats.start_ms) / 1000.0;
+ bool show_iterations = (pmd->core_id != NON_PMD_CORE_ID);
+
+ struct json *json = json_object_create();
+ json_object_put(json, "core",
+ (pmd->core_id != OVS_CORE_UNSPEC
+ && pmd->core_id != NON_PMD_CORE_ID)
+ ? json_integer_create(pmd->core_id)
+ : json_null_create());
+ json_object_put(json, "numa",
+ pmd->numa_id != OVS_NUMA_UNSPEC
+ ? json_integer_create(pmd->numa_id)
+ : json_null_create());
+ json_object_put(json, "measurement-duration-s",
+ json_real_create(duration));
+
+ pmd_perf_format_overall_stats_json(json, &pmd->perf_stats,
+ duration, show_iterations);
+
+ return json;
+}
+
static void
pmd_info_show_perf(struct ds *reply,
struct dp_netdev_pmd_thread *pmd,
@@ -942,6 +968,7 @@ dpif_netdev_pmd_info(struct unixctl_conn *conn, int argc,
const char *argv[],
/ INTERVAL_USEC_TO_SEC;
bool show_header = true;
uint64_t max_sleep;
+ struct json *json_result = NULL;
ovs_mutex_lock(&dp_netdev_mutex);
@@ -981,15 +1008,17 @@ dpif_netdev_pmd_info(struct unixctl_conn *conn, int
argc, const char *argv[],
sorted_poll_thread_list(dp, &pmd_list, &n);
- if (type == PMD_INFO_SLEEP_SHOW
- && unixctl_command_get_output_format(conn)
- == UNIXCTL_OUTPUT_FMT_JSON) {
- struct json *json_result = pmd_info_sleep_show_json(dp, pmd_list, n);
- free(pmd_list);
- ovs_mutex_unlock(&dp_netdev_mutex);
- unixctl_command_reply_json(conn, json_result);
- ds_destroy(&reply);
- return;
+ if (unixctl_command_get_output_format(conn) == UNIXCTL_OUTPUT_FMT_JSON) {
+ if (type == PMD_INFO_PERF_SHOW) {
+ json_result = json_object_create();
+ } else if (type == PMD_INFO_SLEEP_SHOW) {
+ json_result = pmd_info_sleep_show_json(dp, pmd_list, n);
+ free(pmd_list);
+ ovs_mutex_unlock(&dp_netdev_mutex);
+ unixctl_command_reply_json(conn, json_result);
+ ds_destroy(&reply);
+ return;
+ }
}
for (size_t i = 0; i < n; i++) {
@@ -1016,7 +1045,17 @@ dpif_netdev_pmd_info(struct unixctl_conn *conn, int
argc, const char *argv[],
} else if (type == PMD_INFO_CLEAR_STATS) {
pmd_perf_stats_clear(&pmd->perf_stats);
} else if (type == PMD_INFO_PERF_SHOW) {
- pmd_info_show_perf(&reply, pmd, (struct pmd_perf_params *)aux);
+ if (json_result) {
+ bool is_main = pmd->core_id == NON_PMD_CORE_ID;
+ char *key = is_main
+ ? xstrdup("main")
+ : xasprintf("pmd-c%02u", pmd->core_id);
+ json_object_put_nocopy(json_result, key,
+ pmd_info_show_perf_json(pmd));
+ } else {
+ pmd_info_show_perf(&reply, pmd,
+ (struct pmd_perf_params *) aux);
+ }
} else if (type == PMD_INFO_SLEEP_SHOW) {
if (show_header) {
ds_put_format(&reply, "Default max sleep: %4"PRIu64" us\n",
@@ -1032,7 +1071,11 @@ dpif_netdev_pmd_info(struct unixctl_conn *conn, int
argc, const char *argv[],
ovs_mutex_unlock(&dp_netdev_mutex);
- unixctl_command_reply(conn, ds_cstr(&reply));
+ if (json_result) {
+ unixctl_command_reply_json(conn, json_result);
+ } else {
+ unixctl_command_reply(conn, ds_cstr(&reply));
+ }
ds_destroy(&reply);
}
diff --git a/tests/pmd.at b/tests/pmd.at
index 79ee2d470..9a2865b8f 100644
--- a/tests/pmd.at
+++ b/tests/pmd.at
@@ -484,6 +484,84 @@ pmd thread numa_id <cleared> core_id <cleared>:
Tx batches: 20 (1.00 pkts/batch)
])
+dnl Check JSON output.
+dnl Mask dynamic values: core id, measurement duration, all iteration fields,
+dnl kpps, cycles-per-packet, and us-per-upcall.
+AT_CHECK([ovs-appctl --format json --pretty dpif-netdev/pmd-perf-show | dnl
+ sed 's/"pmd-c[[0-9]][[0-9]]"/"pmd-c<cleared>"/g' | dnl
+ sed 's/"core": [[0-9]][[0-9]]*/"core": <cleared>/g' | dnl
+ sed 's/"measurement-duration-s":
[[0-9.]][[0-9.]]*/"measurement-duration-s": <cleared>/g' | dnl
+ sed 's/"kpps": [[0-9.]][[0-9.]]*/"kpps": <cleared>/g' | dnl
+ sed 's/"cycles-per-packet": [[0-9.]][[0-9.]]*/"cycles-per-packet":
<cleared>/g' | dnl
+ sed 's/"us-per-upcall": [[0-9.]][[0-9.]]*/"us-per-upcall":
<cleared>/g' | dnl
+ sed '/"iterations": {/,/}/{ s/: [[0-9.e+-]][[0-9.e+-]]*/:
<cleared>/g; }'], [0], [dnl
+{
+ "main": {
+ "core": null,
+ "datapath-passes": {
+ "count": 0,
+ "emc-hits": 0,
+ "lost-upcalls": 0,
+ "megaflow-hits": 0,
+ "passes-per-packet": 0,
+ "phwol-hits": 0,
+ "simple-match-hits": 0,
+ "smc-hits": 0,
+ "subtable-lookups-per-megaflow-hit": 0,
+ "upcalls": 0,
+ "us-per-upcall": null},
+ "iterations": null,
+ "measurement-duration-s": <cleared>,
+ "numa": null,
+ "rx-packets": {
+ "count": 0,
+ "cycles-per-packet": null,
+ "kpps": <cleared>},
+ "tx-packets": {
+ "batches": 0,
+ "count": 0,
+ "kpps": <cleared>,
+ "packets-per-batch": 0}},
+ "pmd-c<cleared>": {
+ "core": <cleared>,
+ "datapath-passes": {
+ "count": 20,
+ "emc-hits": 19,
+ "lost-upcalls": 0,
+ "megaflow-hits": 0,
+ "passes-per-packet": 1,
+ "phwol-hits": 0,
+ "simple-match-hits": 0,
+ "smc-hits": 0,
+ "subtable-lookups-per-megaflow-hit": 0,
+ "upcalls": 1,
+ "us-per-upcall": <cleared>},
+ "iterations": {
+ "busy": <cleared>,
+ "busy-pct": <cleared>,
+ "idle": <cleared>,
+ "idle-pct": <cleared>,
+ "sleep": <cleared>,
+ "sleep-pct": <cleared>,
+ "sleep-time-us": <cleared>,
+ "sleep-us-per-iteration": <cleared>,
+ "total": <cleared>,
+ "us-per-iteration": <cleared>,
+ "used-tsc-cycles": <cleared>,
+ "used-tsc-pct": <cleared>},
+ "measurement-duration-s": <cleared>,
+ "numa": 0,
+ "rx-packets": {
+ "count": 20,
+ "cycles-per-packet": <cleared>,
+ "kpps": <cleared>},
+ "tx-packets": {
+ "batches": 20,
+ "count": 20,
+ "kpps": <cleared>,
+ "packets-per-batch": 1}}}
+])
+
OVS_VSWITCHD_STOP
AT_CLEANUP
--
2.54.0
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev