Sharing a list of names across API and CLI

Morten Brørup mb at smartsharesystems.com
Sat Aug 22 12:01:49 CEST 2026


> From: Robin Jarry [mailto:rjarry at redhat.com]
> Sent: Saturday, 22 August 2026 11.41
> 
> Hi Morten,
> 
> Morten Brørup, Aug 21, 2026 at 19:22:
> > Seeking guidance...
> >
> > I'm working on a Grout "dump" API+CLI to call the various _dump()
> functions in DPDK.
> >
> > My modules/infra/api/dump.c has the table below.
> >
> > I want the type names in the table to be shared with
> modules/infra/cli/dump.c, so I can avoid copy-paste, and only have one
> place to add new entries.
> >
> > I suppose this can be done using a macro with two variants; one
> variant for use in the cli file, and another variant for use in the api
> file.
> >
> > E.g.
> > #if API
> > #define GR_DUMP_MACRO(name, description, dump_list_fn, dump_fn,
> lookup_fn) \
> > {name, dump_list_fn, dump_fn, lookup_fn}
> > #elif CLI
> > #define
> GR_DUMP_MACRO(name,description,dump_list_fn,dump_fn,lookup_fn) \
> > with_help(description, ec_node_str(EC_NO_ID, name))
> > #endif
> 
> What would the API messages look like?

// Dump an object.
struct gr_dump_obj_req {
#define GR_DUMP_OBJ_ID_SIZE 128
	char type[GR_DUMP_OBJ_ID_SIZE];
	char name[GR_DUMP_OBJ_ID_SIZE];  // All if empty.
};

struct gr_dump_obj_resp {
	uint32_t len; // Limited by GR_API_MAX_MSG_LEN.
	char text[/* len */]; // Text format.
};

GR_REQ(GR_DUMP_OBJ, struct gr_dump_obj_req, struct gr_dump_obj_resp);

> 
> > And then the shared table using these macros:
> > [...]
> > GR_DUMP_MACRO("graph", "DPDK graph.", rte_graph_list_dump,
> (dump_fn_t)graph_dump, (lookup_fn_t)rte_graph_lookup),
> > GR_DUMP_MACRO("memzone", "DPDK memzone.", rte_memzone_dump, NULL,
> NULL),
> > [...]
> >
> > Now, my question is:
> > In which directory should I put the file with the shared table?
> 
> The issue is that grcli concerns have nothing to do with the API socket
> itself. It may not be possible to avoid duplication.

The cli command is something like:
Dump object TYPE all|(name NAME)

Maybe I should just leave TYPE to be "any string" instead of an enum of specific strings.

> 
> > Or should I use a completely different design pattern?
> 
> I must ask: is this intended for debugging/development or do you expect
> to use this in production? What do you want to do with these dumps?

It's for development (debugging and performance analysis); but it might be enabled in some production environments.

> 
> Does this need to go through the API socket and grcli? I am wondering
> if
> such dumps would fit better in the openmetrics endpoints. E.g.:
> 
> https://github.com/DPDK/grout/blob/v0.17.1/modules/infra/api/iface.c#L2
> 18-L239
> https://github.com/DPDK/grout/blob/v0.17.1/modules/infra/api/stats.c#L2
> 63-L306
> 
> These endpoints are self describing, no need to duplicate anything and
> eventually, this data could be scraped by prometheus and stored in
> a time series db for later analysis.
> 
> However, that means you need to expose "structured" information (i.e.
> key=value pairs) which may not fit with the "dump" functions from DPDK
> that seem to only expose text blobs.

They only expose text blobs, not structured information.
So openmetrics is not an option for these.

> 
> If you are concerned with exposing too much information, they could be
> hidden by default and only exposed when a certain variable is set (e.g.
> GROUT_METRICS_DEBUG=1)

Good point.

> 
> What do you think?

Since it's for development purposes only, I will compromise on user friendliness, and simply use "any string", so I don't need to synchronize the list of TYPE strings to the CLI.

Thank you for bringing a broader perspective, Robin.
It's valuable for learning about Grout foundation and design patterns.

> 
> 
> > modules/infra/api/dump.c:
> >
> > 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 dump_obj_helper {
> > 	char *type;
> > 	dump_list_fn_t dump_list_fn;
> > 	dump_fn_t dump_fn;
> > 	lookup_fn_t lookup_fn;
> > } dump_obj_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},
> > 	{"trace", rte_trace_dump, NULL, NULL},
> > 	{NULL, NULL, NULL, NULL}
> > };
> 
> 
> --
> Robin
> 
> # Disposable, use only once.



More information about the grout mailing list