xref: /dpdk/lib/graph/graph_stats.c (revision a2258ea1beaac8f162fd38911a2d6691366dcd66)
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(C) 2020 Marvell International Ltd.
3  */
4 
5 #include <fnmatch.h>
6 #include <stdbool.h>
7 
8 #include <rte_common.h>
9 #include <rte_errno.h>
10 #include <rte_malloc.h>
11 
12 #include "graph_private.h"
13 
14 /* Capture all graphs of cluster */
15 struct cluster {
16 	rte_graph_t nb_graphs;
17 	rte_graph_t size;
18 
19 	struct graph **graphs;
20 };
21 
22 /* Capture same node ID across cluster  */
23 struct cluster_node {
24 	struct rte_graph_cluster_node_stats stat;
25 	rte_node_t nb_nodes;
26 
27 	struct rte_node *nodes[];
28 };
29 
30 struct rte_graph_cluster_stats {
31 	/* Header */
32 	rte_graph_cluster_stats_cb_t fn;
33 	uint32_t cluster_node_size; /* Size of struct cluster_node */
34 	rte_node_t max_nodes;
35 	int socket_id;
36 	void *cookie;
37 	size_t sz;
38 
39 	struct cluster_node clusters[];
40 } __rte_cache_aligned;
41 
42 #define boarder()                                                              \
43 	fprintf(f, "+-------------------------------+---------------+--------" \
44 		   "-------+---------------+---------------+---------------+-" \
45 		   "----------+\n")
46 
47 static inline void
48 print_banner(FILE *f)
49 {
50 	boarder();
51 	fprintf(f, "%-32s%-16s%-16s%-16s%-16s%-16s%-16s\n", "|Node", "|calls",
52 		"|objs", "|realloc_count", "|objs/call", "|objs/sec(10E6)",
53 		"|cycles/call|");
54 	boarder();
55 }
56 
57 static inline void
58 print_node(FILE *f, const struct rte_graph_cluster_node_stats *stat)
59 {
60 	double objs_per_call, objs_per_sec, cycles_per_call, ts_per_hz;
61 	const uint64_t prev_calls = stat->prev_calls;
62 	const uint64_t prev_objs = stat->prev_objs;
63 	const uint64_t cycles = stat->cycles;
64 	const uint64_t calls = stat->calls;
65 	const uint64_t objs = stat->objs;
66 	uint64_t call_delta;
67 
68 	call_delta = calls - prev_calls;
69 	objs_per_call =
70 		call_delta ? (double)((objs - prev_objs) / call_delta) : 0;
71 	cycles_per_call =
72 		call_delta ? (double)((cycles - stat->prev_cycles) / call_delta)
73 			   : 0;
74 	ts_per_hz = (double)((stat->ts - stat->prev_ts) / stat->hz);
75 	objs_per_sec = ts_per_hz ? (objs - prev_objs) / ts_per_hz : 0;
76 	objs_per_sec /= 1000000;
77 
78 	fprintf(f,
79 		"|%-31s|%-15" PRIu64 "|%-15" PRIu64 "|%-15" PRIu64
80 		"|%-15.3f|%-15.6f|%-11.4f|\n",
81 		stat->name, calls, objs, stat->realloc_count, objs_per_call,
82 		objs_per_sec, cycles_per_call);
83 }
84 
85 static int
86 graph_cluster_stats_cb(bool is_first, bool is_last, void *cookie,
87 		       const struct rte_graph_cluster_node_stats *stat)
88 {
89 	FILE *f = cookie;
90 
91 	if (unlikely(is_first))
92 		print_banner(f);
93 	if (stat->objs)
94 		print_node(f, stat);
95 	if (unlikely(is_last))
96 		boarder();
97 
98 	return 0;
99 };
100 
101 static struct rte_graph_cluster_stats *
102 stats_mem_init(struct cluster *cluster,
103 	       const struct rte_graph_cluster_stats_param *prm)
104 {
105 	size_t sz = sizeof(struct rte_graph_cluster_stats);
106 	struct rte_graph_cluster_stats *stats;
107 	rte_graph_cluster_stats_cb_t fn;
108 	int socket_id = prm->socket_id;
109 	uint32_t cluster_node_size;
110 
111 	/* Fix up callback */
112 	fn = prm->fn;
113 	if (fn == NULL)
114 		fn = graph_cluster_stats_cb;
115 
116 	cluster_node_size = sizeof(struct cluster_node);
117 	/* For a given cluster, max nodes will be the max number of graphs */
118 	cluster_node_size += cluster->nb_graphs * sizeof(struct rte_node *);
119 	cluster_node_size = RTE_ALIGN(cluster_node_size, RTE_CACHE_LINE_SIZE);
120 
121 	stats = realloc(NULL, sz);
122 	memset(stats, 0, sz);
123 	if (stats) {
124 		stats->fn = fn;
125 		stats->cluster_node_size = cluster_node_size;
126 		stats->max_nodes = 0;
127 		stats->socket_id = socket_id;
128 		stats->cookie = prm->cookie;
129 		stats->sz = sz;
130 	}
131 
132 	return stats;
133 }
134 
135 static int
136 stats_mem_populate(struct rte_graph_cluster_stats **stats_in,
137 		   struct rte_graph *graph, struct graph_node *graph_node)
138 {
139 	struct rte_graph_cluster_stats *stats = *stats_in;
140 	rte_node_t id = graph_node->node->id;
141 	struct cluster_node *cluster;
142 	struct rte_node *node;
143 	rte_node_t count;
144 
145 	cluster = stats->clusters;
146 
147 	/* Iterate over cluster node array to find node ID match */
148 	for (count = 0; count < stats->max_nodes; count++) {
149 		/* Found an existing node in the reel */
150 		if (cluster->stat.id == id) {
151 			node = graph_node_id_to_ptr(graph, id);
152 			if (node == NULL)
153 				SET_ERR_JMP(
154 					ENOENT, err,
155 					"Failed to find node %s in graph %s",
156 					graph_node->node->name, graph->name);
157 
158 			cluster->nodes[cluster->nb_nodes++] = node;
159 			return 0;
160 		}
161 		cluster = RTE_PTR_ADD(cluster, stats->cluster_node_size);
162 	}
163 
164 	/* Hey, it is a new node, allocate space for it in the reel */
165 	stats = realloc(stats, stats->sz + stats->cluster_node_size);
166 	if (stats == NULL)
167 		SET_ERR_JMP(ENOMEM, err, "Realloc failed");
168 
169 	/* Clear the new struct cluster_node area */
170 	cluster = RTE_PTR_ADD(stats, stats->sz),
171 	memset(cluster, 0, stats->cluster_node_size);
172 	memcpy(cluster->stat.name, graph_node->node->name, RTE_NODE_NAMESIZE);
173 	cluster->stat.id = graph_node->node->id;
174 	cluster->stat.hz = rte_get_timer_hz();
175 	node = graph_node_id_to_ptr(graph, id);
176 	if (node == NULL)
177 		SET_ERR_JMP(ENOENT, err, "Failed to find node %s in graph %s",
178 			    graph_node->node->name, graph->name);
179 	cluster->nodes[cluster->nb_nodes++] = node;
180 
181 	stats->sz += stats->cluster_node_size;
182 	stats->max_nodes++;
183 	*stats_in = stats;
184 
185 	return 0;
186 err:
187 	return -rte_errno;
188 }
189 
190 static void
191 stats_mem_fini(struct rte_graph_cluster_stats *stats)
192 {
193 	free(stats);
194 }
195 
196 static void
197 cluster_init(struct cluster *cluster)
198 {
199 	memset(cluster, 0, sizeof(*cluster));
200 }
201 
202 static int
203 cluster_add(struct cluster *cluster, struct graph *graph)
204 {
205 	rte_graph_t count;
206 	size_t sz;
207 
208 	/* Skip the if graph is already added to cluster */
209 	for (count = 0; count < cluster->nb_graphs; count++)
210 		if (cluster->graphs[count] == graph)
211 			return 0;
212 
213 	/* Expand the cluster if required to store graph objects */
214 	if (cluster->nb_graphs + 1 > cluster->size) {
215 		cluster->size = RTE_MAX(1, cluster->size * 2);
216 		sz = sizeof(struct graph *) * cluster->size;
217 		cluster->graphs = realloc(cluster->graphs, sz);
218 		if (cluster->graphs == NULL)
219 			SET_ERR_JMP(ENOMEM, free, "Failed to realloc");
220 	}
221 
222 	/* Add graph to cluster */
223 	cluster->graphs[cluster->nb_graphs++] = graph;
224 	return 0;
225 
226 free:
227 	return -rte_errno;
228 }
229 
230 static void
231 cluster_fini(struct cluster *cluster)
232 {
233 	if (cluster->graphs)
234 		free(cluster->graphs);
235 }
236 
237 static int
238 expand_pattern_to_cluster(struct cluster *cluster, const char *pattern)
239 {
240 	struct graph_head *graph_head = graph_list_head_get();
241 	struct graph *graph;
242 	bool found = false;
243 
244 	/* Check for pattern match */
245 	STAILQ_FOREACH(graph, graph_head, next) {
246 		if (fnmatch(pattern, graph->name, 0) == 0) {
247 			if (cluster_add(cluster, graph))
248 				goto fail;
249 			found = true;
250 		}
251 	}
252 	if (found == false)
253 		SET_ERR_JMP(EFAULT, fail, "Pattern %s graph not found",
254 			    pattern);
255 
256 	return 0;
257 fail:
258 	return -rte_errno;
259 }
260 
261 struct rte_graph_cluster_stats *
262 rte_graph_cluster_stats_create(const struct rte_graph_cluster_stats_param *prm)
263 {
264 	struct rte_graph_cluster_stats *stats, *rc = NULL;
265 	struct graph_node *graph_node;
266 	struct cluster cluster;
267 	struct graph *graph;
268 	const char *pattern;
269 	rte_graph_t i;
270 
271 	/* Sanity checks */
272 	if (!rte_graph_has_stats_feature())
273 		SET_ERR_JMP(EINVAL, fail, "Stats feature is not enabled");
274 
275 	if (prm == NULL)
276 		SET_ERR_JMP(EINVAL, fail, "Invalid param");
277 
278 	if (prm->graph_patterns == NULL || prm->nb_graph_patterns == 0)
279 		SET_ERR_JMP(EINVAL, fail, "Invalid graph param");
280 
281 	cluster_init(&cluster);
282 
283 	graph_spinlock_lock();
284 	/* Expand graph pattern and add the graph to the cluster */
285 	for (i = 0; i < prm->nb_graph_patterns; i++) {
286 		pattern = prm->graph_patterns[i];
287 		if (expand_pattern_to_cluster(&cluster, pattern))
288 			goto bad_pattern;
289 	}
290 
291 	/* Alloc the stats memory */
292 	stats = stats_mem_init(&cluster, prm);
293 	if (stats == NULL)
294 		SET_ERR_JMP(ENOMEM, bad_pattern, "Failed alloc stats memory");
295 
296 	/* Iterate over M(Graph) x N (Nodes in graph) */
297 	for (i = 0; i < cluster.nb_graphs; i++) {
298 		graph = cluster.graphs[i];
299 		STAILQ_FOREACH(graph_node, &graph->node_list, next) {
300 			struct rte_graph *graph_fp = graph->graph;
301 			if (stats_mem_populate(&stats, graph_fp, graph_node))
302 				goto realloc_fail;
303 		}
304 	}
305 
306 	/* Finally copy to hugepage memory to avoid pressure on rte_realloc */
307 	rc = rte_malloc_socket(NULL, stats->sz, 0, stats->socket_id);
308 	if (rc)
309 		rte_memcpy(rc, stats, stats->sz);
310 	else
311 		SET_ERR_JMP(ENOMEM, realloc_fail, "rte_malloc failed");
312 
313 realloc_fail:
314 	stats_mem_fini(stats);
315 bad_pattern:
316 	graph_spinlock_unlock();
317 	cluster_fini(&cluster);
318 fail:
319 	return rc;
320 }
321 
322 void
323 rte_graph_cluster_stats_destroy(struct rte_graph_cluster_stats *stat)
324 {
325 	return rte_free(stat);
326 }
327 
328 static inline void
329 cluster_node_arregate_stats(struct cluster_node *cluster)
330 {
331 	uint64_t calls = 0, cycles = 0, objs = 0, realloc_count = 0;
332 	struct rte_graph_cluster_node_stats *stat = &cluster->stat;
333 	struct rte_node *node;
334 	rte_node_t count;
335 
336 	for (count = 0; count < cluster->nb_nodes; count++) {
337 		node = cluster->nodes[count];
338 
339 		calls += node->total_calls;
340 		objs += node->total_objs;
341 		cycles += node->total_cycles;
342 		realloc_count += node->realloc_count;
343 	}
344 
345 	stat->calls = calls;
346 	stat->objs = objs;
347 	stat->cycles = cycles;
348 	stat->ts = rte_get_timer_cycles();
349 	stat->realloc_count = realloc_count;
350 }
351 
352 static inline void
353 cluster_node_store_prev_stats(struct cluster_node *cluster)
354 {
355 	struct rte_graph_cluster_node_stats *stat = &cluster->stat;
356 
357 	stat->prev_ts = stat->ts;
358 	stat->prev_calls = stat->calls;
359 	stat->prev_objs = stat->objs;
360 	stat->prev_cycles = stat->cycles;
361 }
362 
363 void
364 rte_graph_cluster_stats_get(struct rte_graph_cluster_stats *stat, bool skip_cb)
365 {
366 	struct cluster_node *cluster;
367 	rte_node_t count;
368 	int rc = 0;
369 
370 	cluster = stat->clusters;
371 
372 	for (count = 0; count < stat->max_nodes; count++) {
373 		cluster_node_arregate_stats(cluster);
374 		if (!skip_cb)
375 			rc = stat->fn(!count, (count == stat->max_nodes - 1),
376 				      stat->cookie, &cluster->stat);
377 		cluster_node_store_prev_stats(cluster);
378 		if (rc)
379 			break;
380 		cluster = RTE_PTR_ADD(cluster, stat->cluster_node_size);
381 	}
382 }
383 
384 void
385 rte_graph_cluster_stats_reset(struct rte_graph_cluster_stats *stat)
386 {
387 	struct cluster_node *cluster;
388 	rte_node_t count;
389 
390 	cluster = stat->clusters;
391 
392 	for (count = 0; count < stat->max_nodes; count++) {
393 		struct rte_graph_cluster_node_stats *node = &cluster->stat;
394 
395 		node->ts = 0;
396 		node->calls = 0;
397 		node->objs = 0;
398 		node->cycles = 0;
399 		node->prev_ts = 0;
400 		node->prev_calls = 0;
401 		node->prev_objs = 0;
402 		node->prev_cycles = 0;
403 		node->realloc_count = 0;
404 		cluster = RTE_PTR_ADD(cluster, stat->cluster_node_size);
405 	}
406 }
407