[PATCH] infra: add diagnostics (for developers)
Morten Brørup
mb at smartsharesystems.com
Sun Aug 23 15:17:29 CEST 2026
Add API and CLI command to dump DPDK objects.
Intended for developers only, so the object types are not enumerated.
Signed-off-by: Morten Brørup <mb at smartsharesystems.com>
---
api/gr_api.h | 6 ++
modules/infra/api/diagnos.c | 125 ++++++++++++++++++++++++++++++++++
modules/infra/api/gr_infra.h | 12 ++++
modules/infra/api/meson.build | 1 +
modules/infra/cli/diagnos.c | 66 ++++++++++++++++++
modules/infra/cli/meson.build | 1 +
6 files changed, 211 insertions(+)
create mode 100644 modules/infra/api/diagnos.c
create mode 100644 modules/infra/cli/diagnos.c
diff --git a/api/gr_api.h b/api/gr_api.h
index 4ddca3f9..495f77eb 100644
--- a/api/gr_api.h
+++ b/api/gr_api.h
@@ -165,6 +165,12 @@ const char *gr_api_message_name(uint32_t type);
struct gr_empty { };
+// Unstructured text, not zero-terminated.
+struct gr_text {
+ uint32_t len; // Limited by GR_API_MAX_MSG_LEN.
+ char text[/* len */]; // Text format.
+};
+
#define GR_MAIN_MODULE 0xcafe
enum gr_main_requests : uint32_t {
diff --git a/modules/infra/api/diagnos.c b/modules/infra/api/diagnos.c
new file mode 100644
index 00000000..16fd55ad
--- /dev/null
+++ b/modules/infra/api/diagnos.c
@@ -0,0 +1,125 @@
+// SPDX-License-Identifier: BSD-3-Clause
+// Copyright (c) 2026 SmartShare Systems
+
+#include "module.h"
+
+#include <gr_api.h>
+#include <gr_infra.h>
+
+#include <rte_bus.h>
+#include <rte_bus_pci.h>
+#include <rte_graph.h>
+#include <rte_lcore.h>
+#include <rte_log.h>
+#include <rte_malloc.h>
+#include <rte_mbuf_dyn.h>
+#include <rte_memory.h>
+#include <rte_mempool.h>
+#include <rte_memzone.h>
+#include <rte_ring.h>
+#include <rte_tailq.h>
+//#include <rte_timer.h>
+#include <rte_trace.h>
+
+static void
+graph_dump(FILE *f, struct rte_graph *graph)
+{
+ rte_graph_obj_dump(f, graph, true);
+}
+
+static void
+malloc_dump_stats(FILE *f)
+{
+ rte_malloc_dump_stats(f, NULL);
+}
+
+static void
+node_dump(FILE *f, uintptr_t id)
+{
+ rte_node_dump(f, (rte_node_t)id);
+}
+
+typedef void (*dump_list_fn_t)(FILE *f);
+typedef void (*dump_fn_t)(FILE *f, void *obj);
+typedef void *(*lookup_fn_t)(const char *name);
+
+struct obj_dump_helper {
+ char *type;
+ dump_list_fn_t dump_list_fn;
+ dump_fn_t dump_fn;
+ lookup_fn_t lookup_fn;
+} obj_dump_helpers[] =
+{
+ // DPDK libs first, in alphabetical order.
+ {"bus", rte_bus_dump, NULL, (lookup_fn_t)rte_bus_find_by_name},
+ {"graph", rte_graph_list_dump, (dump_fn_t)graph_dump, (lookup_fn_t)rte_graph_lookup},
+ {"lcore", rte_lcore_dump, NULL, NULL},
+ {"log", rte_log_dump, NULL, NULL},
+ {"malloc_heaps", rte_malloc_dump_heaps, NULL, NULL},
+ {"malloc_stats", malloc_dump_stats, NULL, NULL},
+ {"mbuf_dyn", rte_mbuf_dyn_dump, NULL, NULL},
+ {"mempool", rte_mempool_list_dump, (dump_fn_t)rte_mempool_dump, (lookup_fn_t)rte_mempool_lookup},
+ {"memzone", rte_memzone_dump, NULL, NULL},
+ {"node", rte_node_list_dump, (dump_fn_t)node_dump, (lookup_fn_t)rte_node_from_name},
+ {"pci", rte_pci_dump, NULL, NULL},
+ {"physmem_layout", rte_dump_physmem_layout, NULL, NULL},
+ {"ring", rte_ring_list_dump, (dump_fn_t)rte_ring_dump, (lookup_fn_t)rte_ring_lookup},
+ {"tailq", rte_dump_tailq, NULL, NULL},
+// {"timer_stats", (dump_list_fn_t)rte_timer_dump_stats, NULL, NULL},
+ {"trace", rte_trace_dump, NULL, NULL},
+ {NULL, NULL, NULL, NULL}
+};
+
+
+static struct api_out obj_dump(const void *request, struct api_ctx *ctx) {
+ const struct gr_diagnos_obj_dump_req *req = request;
+ struct gr_text *resp;
+ struct obj_dump_helper *helper;
+ void *obj = NULL;
+ char *buf = NULL;
+ size_t len = 0, pos;
+ FILE *f;
+
+ if (strlen(req->type) == 0)
+ return api_out(EINVAL, 0, NULL);
+
+ // Lookup object type.
+ for (helper = obj_dump_helpers; helper->type != NULL; helper++)
+ if (strcmp(req->type, helper->type) == 0)
+ break;
+ if (helper->type == NULL)
+ return api_out(EINVAL, 0, NULL); // Object type not supported.
+
+ // If name provided, lookup object.
+ if (*req->name != 0) {
+ if (helper->lookup_fn == NULL || helper->dump_fn == NULL)
+ return api_out(EINVAL, 0, NULL); // Name lookup/dump not supported.
+ if ((obj = helper->lookup_fn(req->name)) == NULL)
+ return api_out(ENOENT, 0, NULL); // Not found.
+ }
+
+ if ((resp = malloc(GR_API_MAX_MSG_LEN)) == NULL)
+ return api_out(ENOMEM, 0, NULL);
+
+ f = open_memstream(&buf, &len);
+ if (obj != NULL)
+ helper->dump_fn(f, obj);
+ else
+ helper->dump_list_fn(f);
+ fclose(f);
+
+ for (pos = 0; pos < len; pos += resp->len) {
+ resp->len = RTE_MIN(GR_API_MAX_MSG_LEN - sizeof(*resp), len - pos);
+ memcpy(resp->text, buf + pos, resp->len);
+ api_send(ctx, sizeof(*resp) + resp->len, resp);
+ }
+
+ free(buf);
+ free(resp);
+
+ return api_out(0, 0, NULL);
+}
+
+RTE_INIT(diagnos_init) {
+ api_handler(GR_DIAGNOS_OBJ_DUMP, obj_dump);
+}
diff --git a/modules/infra/api/gr_infra.h b/modules/infra/api/gr_infra.h
index 72c1f79e..ef21acd4 100644
--- a/modules/infra/api/gr_infra.h
+++ b/modules/infra/api/gr_infra.h
@@ -246,6 +246,7 @@ enum gr_infra_requests : uint32_t {
GR_PACKET_TRACE_CLEAR,
GR_PACKET_TRACE_DUMP,
GR_PACKET_TRACE_SET,
+ GR_DIAGNOS_OBJ_DUMP,
GR_AFFINITY_CPU_GET,
GR_AFFINITY_CPU_SET,
GR_IFACE_MAC_ADD,
@@ -482,6 +483,17 @@ struct gr_packet_trace_set_req {
GR_REQ(GR_PACKET_TRACE_SET, struct gr_packet_trace_set_req, struct gr_empty);
+// diagnotics (for developers) /////////////////////////////////////////////////
+
+// Dump an object.
+struct gr_diagnos_obj_dump_req {
+#define GR_DIAGNOS_OBJ_DUMP_ID_SIZE 128
+ char type[GR_DIAGNOS_OBJ_DUMP_ID_SIZE];
+ char name[GR_DIAGNOS_OBJ_DUMP_ID_SIZE]; // All if empty.
+};
+
+GR_REQ_STREAM(GR_DIAGNOS_OBJ_DUMP, struct gr_diagnos_obj_dump_req, struct gr_text);
+
// cpu affinities //////////////////////////////////////////////////////////////
// Get the current CPU affinity masks.
diff --git a/modules/infra/api/meson.build b/modules/infra/api/meson.build
index b67981a3..f12abc25 100644
--- a/modules/infra/api/meson.build
+++ b/modules/infra/api/meson.build
@@ -3,6 +3,7 @@
src += files(
'affinity.c',
+ 'diagnos.c',
'iface.c',
'nexthop.c',
'stats.c',
diff --git a/modules/infra/cli/diagnos.c b/modules/infra/cli/diagnos.c
new file mode 100644
index 00000000..a20e943e
--- /dev/null
+++ b/modules/infra/cli/diagnos.c
@@ -0,0 +1,66 @@
+// SPDX-License-Identifier: BSD-3-Clause
+// Copyright (c) 2026 SmartShare Systems
+
+#include "cli.h"
+
+#include <gr_api.h>
+#include <gr_infra.h>
+
+#include <ecoli.h>
+
+#include <stdio.h>
+
+static cmd_status_t obj_dump(struct gr_api_client *c, const struct ec_pnode *p) {
+ const struct gr_text *resp = NULL;
+ struct gr_diagnos_obj_dump_req req;
+ const char *type = NULL;
+ const char *name = NULL;
+ int ret;
+
+ if ((type = arg_str(p, "TYPE")) == NULL ||
+ strlcpy(req.type, type, sizeof(req.type)) >= sizeof(req.type))
+ return CMD_ERROR;
+
+ *req.name = 0;
+ if ((name = arg_str(p, "NAME")) != NULL &&
+ strlcpy(req.name, name, sizeof(req.name)) >= sizeof(req.name))
+ return CMD_ERROR;
+
+ gr_api_client_stream_foreach(
+ resp, ret, c, GR_DIAGNOS_OBJ_DUMP, sizeof(req), &req
+ )
+ fwrite(resp->text, 1, resp->len, stdout);
+
+ if (ret < 0)
+ return CMD_ERROR;
+
+ return CMD_SUCCESS;
+}
+
+#define DIAGNOS_CTX(root) CLI_CONTEXT(root, CTX_ARG("diagnos", "Diagnostics (for developers)."))
+
+static int ctx_init(struct ec_node *root) {
+ int ret;
+
+ ret = CLI_COMMAND(
+ DIAGNOS_CTX(root),
+ "object dump TYPE [NAME]",
+ obj_dump,
+ "Dump object.",
+ with_help("Object type.", ec_node("any", "TYPE")),
+ with_help("Specific object.", ec_node("any", "NAME"))
+ );
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static struct cli_context ctx = {
+ .name = "diagnos",
+ .init = ctx_init,
+};
+
+static void __attribute__((constructor, used)) init(void) {
+ cli_context_register(&ctx);
+}
diff --git a/modules/infra/cli/meson.build b/modules/infra/cli/meson.build
index 222f7219..9763bcb9 100644
--- a/modules/infra/cli/meson.build
+++ b/modules/infra/cli/meson.build
@@ -5,6 +5,7 @@ cli_src += files(
'address.c',
'affinity.c',
'bond.c',
+ 'diagnos.c',
'events.c',
'graph.c',
'icmp.c',
--
2.43.0
More information about the grout
mailing list