xref: /linux/tools/perf/util/bpf-trace-summary.c (revision 0939bd2fcf337243133b0271335a2838857c319f)
11bec43f5SNamhyung Kim /* SPDX-License-Identifier: GPL-2.0 */
21bec43f5SNamhyung Kim #include <inttypes.h>
31bec43f5SNamhyung Kim #include <math.h>
41bec43f5SNamhyung Kim #include <stdio.h>
51bec43f5SNamhyung Kim #include <stdlib.h>
61bec43f5SNamhyung Kim 
71bec43f5SNamhyung Kim #include "dwarf-regs.h" /* for EM_HOST */
81bec43f5SNamhyung Kim #include "syscalltbl.h"
9*ef60b8f5SNamhyung Kim #include "util/cgroup.h"
101bec43f5SNamhyung Kim #include "util/hashmap.h"
111bec43f5SNamhyung Kim #include "util/trace.h"
121bec43f5SNamhyung Kim #include "util/util.h"
131bec43f5SNamhyung Kim #include <bpf/bpf.h>
14*ef60b8f5SNamhyung Kim #include <linux/rbtree.h>
151bec43f5SNamhyung Kim #include <linux/time64.h>
161bec43f5SNamhyung Kim #include <tools/libc_compat.h> /* reallocarray */
171bec43f5SNamhyung Kim 
181bec43f5SNamhyung Kim #include "bpf_skel/syscall_summary.h"
191bec43f5SNamhyung Kim #include "bpf_skel/syscall_summary.skel.h"
201bec43f5SNamhyung Kim 
211bec43f5SNamhyung Kim 
221bec43f5SNamhyung Kim static struct syscall_summary_bpf *skel;
23*ef60b8f5SNamhyung Kim static struct rb_root cgroups = RB_ROOT;
241bec43f5SNamhyung Kim 
251bec43f5SNamhyung Kim int trace_prepare_bpf_summary(enum trace_summary_mode mode)
261bec43f5SNamhyung Kim {
271bec43f5SNamhyung Kim 	skel = syscall_summary_bpf__open();
281bec43f5SNamhyung Kim 	if (skel == NULL) {
291bec43f5SNamhyung Kim 		fprintf(stderr, "failed to open syscall summary bpf skeleton\n");
301bec43f5SNamhyung Kim 		return -1;
311bec43f5SNamhyung Kim 	}
321bec43f5SNamhyung Kim 
331bec43f5SNamhyung Kim 	if (mode == SUMMARY__BY_THREAD)
341bec43f5SNamhyung Kim 		skel->rodata->aggr_mode = SYSCALL_AGGR_THREAD;
35*ef60b8f5SNamhyung Kim 	else if (mode == SUMMARY__BY_CGROUP)
36*ef60b8f5SNamhyung Kim 		skel->rodata->aggr_mode = SYSCALL_AGGR_CGROUP;
371bec43f5SNamhyung Kim 	else
381bec43f5SNamhyung Kim 		skel->rodata->aggr_mode = SYSCALL_AGGR_CPU;
391bec43f5SNamhyung Kim 
40*ef60b8f5SNamhyung Kim 	if (cgroup_is_v2("perf_event") > 0)
41*ef60b8f5SNamhyung Kim 		skel->rodata->use_cgroup_v2 = 1;
42*ef60b8f5SNamhyung Kim 
431bec43f5SNamhyung Kim 	if (syscall_summary_bpf__load(skel) < 0) {
441bec43f5SNamhyung Kim 		fprintf(stderr, "failed to load syscall summary bpf skeleton\n");
451bec43f5SNamhyung Kim 		return -1;
461bec43f5SNamhyung Kim 	}
471bec43f5SNamhyung Kim 
481bec43f5SNamhyung Kim 	if (syscall_summary_bpf__attach(skel) < 0) {
491bec43f5SNamhyung Kim 		fprintf(stderr, "failed to attach syscall summary bpf skeleton\n");
501bec43f5SNamhyung Kim 		return -1;
511bec43f5SNamhyung Kim 	}
521bec43f5SNamhyung Kim 
53*ef60b8f5SNamhyung Kim 	if (mode == SUMMARY__BY_CGROUP)
54*ef60b8f5SNamhyung Kim 		read_all_cgroups(&cgroups);
55*ef60b8f5SNamhyung Kim 
561bec43f5SNamhyung Kim 	return 0;
571bec43f5SNamhyung Kim }
581bec43f5SNamhyung Kim 
591bec43f5SNamhyung Kim void trace_start_bpf_summary(void)
601bec43f5SNamhyung Kim {
611bec43f5SNamhyung Kim 	skel->bss->enabled = 1;
621bec43f5SNamhyung Kim }
631bec43f5SNamhyung Kim 
641bec43f5SNamhyung Kim void trace_end_bpf_summary(void)
651bec43f5SNamhyung Kim {
661bec43f5SNamhyung Kim 	skel->bss->enabled = 0;
671bec43f5SNamhyung Kim }
681bec43f5SNamhyung Kim 
691bec43f5SNamhyung Kim struct syscall_node {
701bec43f5SNamhyung Kim 	int syscall_nr;
711bec43f5SNamhyung Kim 	struct syscall_stats stats;
721bec43f5SNamhyung Kim };
731bec43f5SNamhyung Kim 
741bec43f5SNamhyung Kim static double rel_stddev(struct syscall_stats *stat)
751bec43f5SNamhyung Kim {
761bec43f5SNamhyung Kim 	double variance, average;
771bec43f5SNamhyung Kim 
781bec43f5SNamhyung Kim 	if (stat->count < 2)
791bec43f5SNamhyung Kim 		return 0;
801bec43f5SNamhyung Kim 
811bec43f5SNamhyung Kim 	average = (double)stat->total_time / stat->count;
821bec43f5SNamhyung Kim 
831bec43f5SNamhyung Kim 	variance = stat->squared_sum;
841bec43f5SNamhyung Kim 	variance -= (stat->total_time * stat->total_time) / stat->count;
851bec43f5SNamhyung Kim 	variance /= stat->count - 1;
861bec43f5SNamhyung Kim 
871bec43f5SNamhyung Kim 	return 100 * sqrt(variance / stat->count) / average;
881bec43f5SNamhyung Kim }
891bec43f5SNamhyung Kim 
901bec43f5SNamhyung Kim /*
911bec43f5SNamhyung Kim  * The syscall_data is to maintain syscall stats ordered by total time.
921bec43f5SNamhyung Kim  * It supports different summary modes like per-thread or global.
931bec43f5SNamhyung Kim  *
941bec43f5SNamhyung Kim  * For per-thread stats, it uses two-level data strurcture -
951bec43f5SNamhyung Kim  * syscall_data is keyed by TID and has an array of nodes which
961bec43f5SNamhyung Kim  * represents each syscall for the thread.
971bec43f5SNamhyung Kim  *
981bec43f5SNamhyung Kim  * For global stats, it's still two-level technically but we don't need
991bec43f5SNamhyung Kim  * per-cpu analysis so it's keyed by the syscall number to combine stats
1001bec43f5SNamhyung Kim  * from different CPUs.  And syscall_data always has a syscall_node so
1011bec43f5SNamhyung Kim  * it can effectively work as flat hierarchy.
102*ef60b8f5SNamhyung Kim  *
103*ef60b8f5SNamhyung Kim  * For per-cgroup stats, it uses two-level data structure like thread
104*ef60b8f5SNamhyung Kim  * syscall_data is keyed by CGROUP and has an array of node which
105*ef60b8f5SNamhyung Kim  * represents each syscall for the cgroup.
1061bec43f5SNamhyung Kim  */
1071bec43f5SNamhyung Kim struct syscall_data {
108*ef60b8f5SNamhyung Kim 	u64 key; /* tid if AGGR_THREAD, syscall-nr if AGGR_CPU, cgroup if AGGR_CGROUP */
1091bec43f5SNamhyung Kim 	int nr_events;
1101bec43f5SNamhyung Kim 	int nr_nodes;
1111bec43f5SNamhyung Kim 	u64 total_time;
1121bec43f5SNamhyung Kim 	struct syscall_node *nodes;
1131bec43f5SNamhyung Kim };
1141bec43f5SNamhyung Kim 
1151bec43f5SNamhyung Kim static int datacmp(const void *a, const void *b)
1161bec43f5SNamhyung Kim {
1171bec43f5SNamhyung Kim 	const struct syscall_data * const *sa = a;
1181bec43f5SNamhyung Kim 	const struct syscall_data * const *sb = b;
1191bec43f5SNamhyung Kim 
1201bec43f5SNamhyung Kim 	return (*sa)->total_time > (*sb)->total_time ? -1 : 1;
1211bec43f5SNamhyung Kim }
1221bec43f5SNamhyung Kim 
1231bec43f5SNamhyung Kim static int nodecmp(const void *a, const void *b)
1241bec43f5SNamhyung Kim {
1251bec43f5SNamhyung Kim 	const struct syscall_node *na = a;
1261bec43f5SNamhyung Kim 	const struct syscall_node *nb = b;
1271bec43f5SNamhyung Kim 
1281bec43f5SNamhyung Kim 	return na->stats.total_time > nb->stats.total_time ? -1 : 1;
1291bec43f5SNamhyung Kim }
1301bec43f5SNamhyung Kim 
1311bec43f5SNamhyung Kim static size_t sc_node_hash(long key, void *ctx __maybe_unused)
1321bec43f5SNamhyung Kim {
1331bec43f5SNamhyung Kim 	return key;
1341bec43f5SNamhyung Kim }
1351bec43f5SNamhyung Kim 
1361bec43f5SNamhyung Kim static bool sc_node_equal(long key1, long key2, void *ctx __maybe_unused)
1371bec43f5SNamhyung Kim {
1381bec43f5SNamhyung Kim 	return key1 == key2;
1391bec43f5SNamhyung Kim }
1401bec43f5SNamhyung Kim 
1411bec43f5SNamhyung Kim static int print_common_stats(struct syscall_data *data, FILE *fp)
1421bec43f5SNamhyung Kim {
1431bec43f5SNamhyung Kim 	int printed = 0;
1441bec43f5SNamhyung Kim 
1451bec43f5SNamhyung Kim 	for (int i = 0; i < data->nr_nodes; i++) {
1461bec43f5SNamhyung Kim 		struct syscall_node *node = &data->nodes[i];
1471bec43f5SNamhyung Kim 		struct syscall_stats *stat = &node->stats;
1481bec43f5SNamhyung Kim 		double total = (double)(stat->total_time) / NSEC_PER_MSEC;
1491bec43f5SNamhyung Kim 		double min = (double)(stat->min_time) / NSEC_PER_MSEC;
1501bec43f5SNamhyung Kim 		double max = (double)(stat->max_time) / NSEC_PER_MSEC;
1511bec43f5SNamhyung Kim 		double avg = total / stat->count;
1521bec43f5SNamhyung Kim 		const char *name;
1531bec43f5SNamhyung Kim 
1541bec43f5SNamhyung Kim 		/* TODO: support other ABIs */
1551bec43f5SNamhyung Kim 		name = syscalltbl__name(EM_HOST, node->syscall_nr);
1561bec43f5SNamhyung Kim 		if (name)
1571bec43f5SNamhyung Kim 			printed += fprintf(fp, "   %-15s", name);
1581bec43f5SNamhyung Kim 		else
1591bec43f5SNamhyung Kim 			printed += fprintf(fp, "   syscall:%-7d", node->syscall_nr);
1601bec43f5SNamhyung Kim 
1611bec43f5SNamhyung Kim 		printed += fprintf(fp, " %8u %6u %9.3f %9.3f %9.3f %9.3f %9.2f%%\n",
1621bec43f5SNamhyung Kim 				   stat->count, stat->error, total, min, avg, max,
1631bec43f5SNamhyung Kim 				   rel_stddev(stat));
1641bec43f5SNamhyung Kim 	}
1651bec43f5SNamhyung Kim 	return printed;
1661bec43f5SNamhyung Kim }
1671bec43f5SNamhyung Kim 
1681bec43f5SNamhyung Kim static int update_thread_stats(struct hashmap *hash, struct syscall_key *map_key,
1691bec43f5SNamhyung Kim 			       struct syscall_stats *map_data)
1701bec43f5SNamhyung Kim {
1711bec43f5SNamhyung Kim 	struct syscall_data *data;
1721bec43f5SNamhyung Kim 	struct syscall_node *nodes;
1731bec43f5SNamhyung Kim 
1741bec43f5SNamhyung Kim 	if (!hashmap__find(hash, map_key->cpu_or_tid, &data)) {
1751bec43f5SNamhyung Kim 		data = zalloc(sizeof(*data));
1761bec43f5SNamhyung Kim 		if (data == NULL)
1771bec43f5SNamhyung Kim 			return -ENOMEM;
1781bec43f5SNamhyung Kim 
1791bec43f5SNamhyung Kim 		data->key = map_key->cpu_or_tid;
1801bec43f5SNamhyung Kim 		if (hashmap__add(hash, data->key, data) < 0) {
1811bec43f5SNamhyung Kim 			free(data);
1821bec43f5SNamhyung Kim 			return -ENOMEM;
1831bec43f5SNamhyung Kim 		}
1841bec43f5SNamhyung Kim 	}
1851bec43f5SNamhyung Kim 
1861bec43f5SNamhyung Kim 	/* update thread total stats */
1871bec43f5SNamhyung Kim 	data->nr_events += map_data->count;
1881bec43f5SNamhyung Kim 	data->total_time += map_data->total_time;
1891bec43f5SNamhyung Kim 
1901bec43f5SNamhyung Kim 	nodes = reallocarray(data->nodes, data->nr_nodes + 1, sizeof(*nodes));
1911bec43f5SNamhyung Kim 	if (nodes == NULL)
1921bec43f5SNamhyung Kim 		return -ENOMEM;
1931bec43f5SNamhyung Kim 
1941bec43f5SNamhyung Kim 	data->nodes = nodes;
1951bec43f5SNamhyung Kim 	nodes = &data->nodes[data->nr_nodes++];
1961bec43f5SNamhyung Kim 	nodes->syscall_nr = map_key->nr;
1971bec43f5SNamhyung Kim 
1981bec43f5SNamhyung Kim 	/* each thread has an entry for each syscall, just use the stat */
1991bec43f5SNamhyung Kim 	memcpy(&nodes->stats, map_data, sizeof(*map_data));
2001bec43f5SNamhyung Kim 	return 0;
2011bec43f5SNamhyung Kim }
2021bec43f5SNamhyung Kim 
2031bec43f5SNamhyung Kim static int print_thread_stat(struct syscall_data *data, FILE *fp)
2041bec43f5SNamhyung Kim {
2051bec43f5SNamhyung Kim 	int printed = 0;
2061bec43f5SNamhyung Kim 
2071bec43f5SNamhyung Kim 	qsort(data->nodes, data->nr_nodes, sizeof(*data->nodes), nodecmp);
2081bec43f5SNamhyung Kim 
209*ef60b8f5SNamhyung Kim 	printed += fprintf(fp, " thread (%d), ", (int)data->key);
2101bec43f5SNamhyung Kim 	printed += fprintf(fp, "%d events\n\n", data->nr_events);
2111bec43f5SNamhyung Kim 
2121bec43f5SNamhyung Kim 	printed += fprintf(fp, "   syscall            calls  errors  total       min       avg       max       stddev\n");
2131bec43f5SNamhyung Kim 	printed += fprintf(fp, "                                     (msec)    (msec)    (msec)    (msec)        (%%)\n");
2141bec43f5SNamhyung Kim 	printed += fprintf(fp, "   --------------- --------  ------ -------- --------- --------- ---------     ------\n");
2151bec43f5SNamhyung Kim 
2161bec43f5SNamhyung Kim 	printed += print_common_stats(data, fp);
2171bec43f5SNamhyung Kim 	printed += fprintf(fp, "\n\n");
2181bec43f5SNamhyung Kim 
2191bec43f5SNamhyung Kim 	return printed;
2201bec43f5SNamhyung Kim }
2211bec43f5SNamhyung Kim 
2221bec43f5SNamhyung Kim static int print_thread_stats(struct syscall_data **data, int nr_data, FILE *fp)
2231bec43f5SNamhyung Kim {
2241bec43f5SNamhyung Kim 	int printed = 0;
2251bec43f5SNamhyung Kim 
2261bec43f5SNamhyung Kim 	for (int i = 0; i < nr_data; i++)
2271bec43f5SNamhyung Kim 		printed += print_thread_stat(data[i], fp);
2281bec43f5SNamhyung Kim 
2291bec43f5SNamhyung Kim 	return printed;
2301bec43f5SNamhyung Kim }
2311bec43f5SNamhyung Kim 
2321bec43f5SNamhyung Kim static int update_total_stats(struct hashmap *hash, struct syscall_key *map_key,
2331bec43f5SNamhyung Kim 			      struct syscall_stats *map_data)
2341bec43f5SNamhyung Kim {
2351bec43f5SNamhyung Kim 	struct syscall_data *data;
2361bec43f5SNamhyung Kim 	struct syscall_stats *stat;
2371bec43f5SNamhyung Kim 
2381bec43f5SNamhyung Kim 	if (!hashmap__find(hash, map_key->nr, &data)) {
2391bec43f5SNamhyung Kim 		data = zalloc(sizeof(*data));
2401bec43f5SNamhyung Kim 		if (data == NULL)
2411bec43f5SNamhyung Kim 			return -ENOMEM;
2421bec43f5SNamhyung Kim 
2431bec43f5SNamhyung Kim 		data->nodes = zalloc(sizeof(*data->nodes));
2441bec43f5SNamhyung Kim 		if (data->nodes == NULL) {
2451bec43f5SNamhyung Kim 			free(data);
2461bec43f5SNamhyung Kim 			return -ENOMEM;
2471bec43f5SNamhyung Kim 		}
2481bec43f5SNamhyung Kim 
2491bec43f5SNamhyung Kim 		data->nr_nodes = 1;
2501bec43f5SNamhyung Kim 		data->key = map_key->nr;
2511bec43f5SNamhyung Kim 		data->nodes->syscall_nr = data->key;
2521bec43f5SNamhyung Kim 
2531bec43f5SNamhyung Kim 		if (hashmap__add(hash, data->key, data) < 0) {
2541bec43f5SNamhyung Kim 			free(data->nodes);
2551bec43f5SNamhyung Kim 			free(data);
2561bec43f5SNamhyung Kim 			return -ENOMEM;
2571bec43f5SNamhyung Kim 		}
2581bec43f5SNamhyung Kim 	}
2591bec43f5SNamhyung Kim 
2601bec43f5SNamhyung Kim 	/* update total stats for this syscall */
2611bec43f5SNamhyung Kim 	data->nr_events += map_data->count;
2621bec43f5SNamhyung Kim 	data->total_time += map_data->total_time;
2631bec43f5SNamhyung Kim 
2641bec43f5SNamhyung Kim 	/* This is sum of the same syscall from different CPUs */
2651bec43f5SNamhyung Kim 	stat = &data->nodes->stats;
2661bec43f5SNamhyung Kim 
2671bec43f5SNamhyung Kim 	stat->total_time += map_data->total_time;
2681bec43f5SNamhyung Kim 	stat->squared_sum += map_data->squared_sum;
2691bec43f5SNamhyung Kim 	stat->count += map_data->count;
2701bec43f5SNamhyung Kim 	stat->error += map_data->error;
2711bec43f5SNamhyung Kim 
2721bec43f5SNamhyung Kim 	if (stat->max_time < map_data->max_time)
2731bec43f5SNamhyung Kim 		stat->max_time = map_data->max_time;
2741bec43f5SNamhyung Kim 	if (stat->min_time > map_data->min_time || stat->min_time == 0)
2751bec43f5SNamhyung Kim 		stat->min_time = map_data->min_time;
2761bec43f5SNamhyung Kim 
2771bec43f5SNamhyung Kim 	return 0;
2781bec43f5SNamhyung Kim }
2791bec43f5SNamhyung Kim 
2801bec43f5SNamhyung Kim static int print_total_stats(struct syscall_data **data, int nr_data, FILE *fp)
2811bec43f5SNamhyung Kim {
2821bec43f5SNamhyung Kim 	int printed = 0;
2831bec43f5SNamhyung Kim 	int nr_events = 0;
2841bec43f5SNamhyung Kim 
2851bec43f5SNamhyung Kim 	for (int i = 0; i < nr_data; i++)
2861bec43f5SNamhyung Kim 		nr_events += data[i]->nr_events;
2871bec43f5SNamhyung Kim 
2881bec43f5SNamhyung Kim 	printed += fprintf(fp, " total, %d events\n\n", nr_events);
2891bec43f5SNamhyung Kim 
2901bec43f5SNamhyung Kim 	printed += fprintf(fp, "   syscall            calls  errors  total       min       avg       max       stddev\n");
2911bec43f5SNamhyung Kim 	printed += fprintf(fp, "                                     (msec)    (msec)    (msec)    (msec)        (%%)\n");
2921bec43f5SNamhyung Kim 	printed += fprintf(fp, "   --------------- --------  ------ -------- --------- --------- ---------     ------\n");
2931bec43f5SNamhyung Kim 
2941bec43f5SNamhyung Kim 	for (int i = 0; i < nr_data; i++)
2951bec43f5SNamhyung Kim 		printed += print_common_stats(data[i], fp);
2961bec43f5SNamhyung Kim 
2971bec43f5SNamhyung Kim 	printed += fprintf(fp, "\n\n");
2981bec43f5SNamhyung Kim 	return printed;
2991bec43f5SNamhyung Kim }
3001bec43f5SNamhyung Kim 
301*ef60b8f5SNamhyung Kim static int update_cgroup_stats(struct hashmap *hash, struct syscall_key *map_key,
302*ef60b8f5SNamhyung Kim 			       struct syscall_stats *map_data)
303*ef60b8f5SNamhyung Kim {
304*ef60b8f5SNamhyung Kim 	struct syscall_data *data;
305*ef60b8f5SNamhyung Kim 	struct syscall_node *nodes;
306*ef60b8f5SNamhyung Kim 
307*ef60b8f5SNamhyung Kim 	if (!hashmap__find(hash, map_key->cgroup, &data)) {
308*ef60b8f5SNamhyung Kim 		data = zalloc(sizeof(*data));
309*ef60b8f5SNamhyung Kim 		if (data == NULL)
310*ef60b8f5SNamhyung Kim 			return -ENOMEM;
311*ef60b8f5SNamhyung Kim 
312*ef60b8f5SNamhyung Kim 		data->key = map_key->cgroup;
313*ef60b8f5SNamhyung Kim 		if (hashmap__add(hash, data->key, data) < 0) {
314*ef60b8f5SNamhyung Kim 			free(data);
315*ef60b8f5SNamhyung Kim 			return -ENOMEM;
316*ef60b8f5SNamhyung Kim 		}
317*ef60b8f5SNamhyung Kim 	}
318*ef60b8f5SNamhyung Kim 
319*ef60b8f5SNamhyung Kim 	/* update thread total stats */
320*ef60b8f5SNamhyung Kim 	data->nr_events += map_data->count;
321*ef60b8f5SNamhyung Kim 	data->total_time += map_data->total_time;
322*ef60b8f5SNamhyung Kim 
323*ef60b8f5SNamhyung Kim 	nodes = reallocarray(data->nodes, data->nr_nodes + 1, sizeof(*nodes));
324*ef60b8f5SNamhyung Kim 	if (nodes == NULL)
325*ef60b8f5SNamhyung Kim 		return -ENOMEM;
326*ef60b8f5SNamhyung Kim 
327*ef60b8f5SNamhyung Kim 	data->nodes = nodes;
328*ef60b8f5SNamhyung Kim 	nodes = &data->nodes[data->nr_nodes++];
329*ef60b8f5SNamhyung Kim 	nodes->syscall_nr = map_key->nr;
330*ef60b8f5SNamhyung Kim 
331*ef60b8f5SNamhyung Kim 	/* each thread has an entry for each syscall, just use the stat */
332*ef60b8f5SNamhyung Kim 	memcpy(&nodes->stats, map_data, sizeof(*map_data));
333*ef60b8f5SNamhyung Kim 	return 0;
334*ef60b8f5SNamhyung Kim }
335*ef60b8f5SNamhyung Kim 
336*ef60b8f5SNamhyung Kim static int print_cgroup_stat(struct syscall_data *data, FILE *fp)
337*ef60b8f5SNamhyung Kim {
338*ef60b8f5SNamhyung Kim 	int printed = 0;
339*ef60b8f5SNamhyung Kim 	struct cgroup *cgrp = __cgroup__find(&cgroups, data->key);
340*ef60b8f5SNamhyung Kim 
341*ef60b8f5SNamhyung Kim 	qsort(data->nodes, data->nr_nodes, sizeof(*data->nodes), nodecmp);
342*ef60b8f5SNamhyung Kim 
343*ef60b8f5SNamhyung Kim 	if (cgrp)
344*ef60b8f5SNamhyung Kim 		printed += fprintf(fp, " cgroup %s,", cgrp->name);
345*ef60b8f5SNamhyung Kim 	else
346*ef60b8f5SNamhyung Kim 		printed += fprintf(fp, " cgroup id:%lu,", (unsigned long)data->key);
347*ef60b8f5SNamhyung Kim 
348*ef60b8f5SNamhyung Kim 	printed += fprintf(fp, " %d events\n\n", data->nr_events);
349*ef60b8f5SNamhyung Kim 
350*ef60b8f5SNamhyung Kim 	printed += fprintf(fp, "   syscall            calls  errors  total       min       avg       max       stddev\n");
351*ef60b8f5SNamhyung Kim 	printed += fprintf(fp, "                                     (msec)    (msec)    (msec)    (msec)        (%%)\n");
352*ef60b8f5SNamhyung Kim 	printed += fprintf(fp, "   --------------- --------  ------ -------- --------- --------- ---------     ------\n");
353*ef60b8f5SNamhyung Kim 
354*ef60b8f5SNamhyung Kim 	printed += print_common_stats(data, fp);
355*ef60b8f5SNamhyung Kim 	printed += fprintf(fp, "\n\n");
356*ef60b8f5SNamhyung Kim 
357*ef60b8f5SNamhyung Kim 	return printed;
358*ef60b8f5SNamhyung Kim }
359*ef60b8f5SNamhyung Kim 
360*ef60b8f5SNamhyung Kim static int print_cgroup_stats(struct syscall_data **data, int nr_data, FILE *fp)
361*ef60b8f5SNamhyung Kim {
362*ef60b8f5SNamhyung Kim 	int printed = 0;
363*ef60b8f5SNamhyung Kim 
364*ef60b8f5SNamhyung Kim 	for (int i = 0; i < nr_data; i++)
365*ef60b8f5SNamhyung Kim 		printed += print_cgroup_stat(data[i], fp);
366*ef60b8f5SNamhyung Kim 
367*ef60b8f5SNamhyung Kim 	return printed;
368*ef60b8f5SNamhyung Kim }
369*ef60b8f5SNamhyung Kim 
3701bec43f5SNamhyung Kim int trace_print_bpf_summary(FILE *fp)
3711bec43f5SNamhyung Kim {
3721bec43f5SNamhyung Kim 	struct bpf_map *map = skel->maps.syscall_stats_map;
3731bec43f5SNamhyung Kim 	struct syscall_key *prev_key, key;
3741bec43f5SNamhyung Kim 	struct syscall_data **data = NULL;
3751bec43f5SNamhyung Kim 	struct hashmap schash;
3761bec43f5SNamhyung Kim 	struct hashmap_entry *entry;
3771bec43f5SNamhyung Kim 	int nr_data = 0;
3781bec43f5SNamhyung Kim 	int printed = 0;
3791bec43f5SNamhyung Kim 	int i;
3801bec43f5SNamhyung Kim 	size_t bkt;
3811bec43f5SNamhyung Kim 
3821bec43f5SNamhyung Kim 	hashmap__init(&schash, sc_node_hash, sc_node_equal, /*ctx=*/NULL);
3831bec43f5SNamhyung Kim 
3841bec43f5SNamhyung Kim 	printed = fprintf(fp, "\n Summary of events:\n\n");
3851bec43f5SNamhyung Kim 
3861bec43f5SNamhyung Kim 	/* get stats from the bpf map */
3871bec43f5SNamhyung Kim 	prev_key = NULL;
3881bec43f5SNamhyung Kim 	while (!bpf_map__get_next_key(map, prev_key, &key, sizeof(key))) {
3891bec43f5SNamhyung Kim 		struct syscall_stats stat;
3901bec43f5SNamhyung Kim 
3911bec43f5SNamhyung Kim 		if (!bpf_map__lookup_elem(map, &key, sizeof(key), &stat, sizeof(stat), 0)) {
392*ef60b8f5SNamhyung Kim 			switch (skel->rodata->aggr_mode) {
393*ef60b8f5SNamhyung Kim 			case SYSCALL_AGGR_THREAD:
3941bec43f5SNamhyung Kim 				update_thread_stats(&schash, &key, &stat);
395*ef60b8f5SNamhyung Kim 				break;
396*ef60b8f5SNamhyung Kim 			case SYSCALL_AGGR_CPU:
3971bec43f5SNamhyung Kim 				update_total_stats(&schash, &key, &stat);
398*ef60b8f5SNamhyung Kim 				break;
399*ef60b8f5SNamhyung Kim 			case SYSCALL_AGGR_CGROUP:
400*ef60b8f5SNamhyung Kim 				update_cgroup_stats(&schash, &key, &stat);
401*ef60b8f5SNamhyung Kim 				break;
402*ef60b8f5SNamhyung Kim 			default:
403*ef60b8f5SNamhyung Kim 				break;
404*ef60b8f5SNamhyung Kim 			}
4051bec43f5SNamhyung Kim 		}
4061bec43f5SNamhyung Kim 
4071bec43f5SNamhyung Kim 		prev_key = &key;
4081bec43f5SNamhyung Kim 	}
4091bec43f5SNamhyung Kim 
4101bec43f5SNamhyung Kim 	nr_data = hashmap__size(&schash);
4111bec43f5SNamhyung Kim 	data = calloc(nr_data, sizeof(*data));
4121bec43f5SNamhyung Kim 	if (data == NULL)
4131bec43f5SNamhyung Kim 		goto out;
4141bec43f5SNamhyung Kim 
4151bec43f5SNamhyung Kim 	i = 0;
4161bec43f5SNamhyung Kim 	hashmap__for_each_entry(&schash, entry, bkt)
4171bec43f5SNamhyung Kim 		data[i++] = entry->pvalue;
4181bec43f5SNamhyung Kim 
4191bec43f5SNamhyung Kim 	qsort(data, nr_data, sizeof(*data), datacmp);
4201bec43f5SNamhyung Kim 
421*ef60b8f5SNamhyung Kim 	switch (skel->rodata->aggr_mode) {
422*ef60b8f5SNamhyung Kim 	case SYSCALL_AGGR_THREAD:
4231bec43f5SNamhyung Kim 		printed += print_thread_stats(data, nr_data, fp);
424*ef60b8f5SNamhyung Kim 		break;
425*ef60b8f5SNamhyung Kim 	case SYSCALL_AGGR_CPU:
4261bec43f5SNamhyung Kim 		printed += print_total_stats(data, nr_data, fp);
427*ef60b8f5SNamhyung Kim 		break;
428*ef60b8f5SNamhyung Kim 	case SYSCALL_AGGR_CGROUP:
429*ef60b8f5SNamhyung Kim 		printed += print_cgroup_stats(data, nr_data, fp);
430*ef60b8f5SNamhyung Kim 		break;
431*ef60b8f5SNamhyung Kim 	default:
432*ef60b8f5SNamhyung Kim 		break;
433*ef60b8f5SNamhyung Kim 	}
4341bec43f5SNamhyung Kim 
4351bec43f5SNamhyung Kim 	for (i = 0; i < nr_data && data; i++) {
4361bec43f5SNamhyung Kim 		free(data[i]->nodes);
4371bec43f5SNamhyung Kim 		free(data[i]);
4381bec43f5SNamhyung Kim 	}
4391bec43f5SNamhyung Kim 	free(data);
4401bec43f5SNamhyung Kim 
4411bec43f5SNamhyung Kim out:
4421bec43f5SNamhyung Kim 	hashmap__clear(&schash);
4431bec43f5SNamhyung Kim 	return printed;
4441bec43f5SNamhyung Kim }
4451bec43f5SNamhyung Kim 
4461bec43f5SNamhyung Kim void trace_cleanup_bpf_summary(void)
4471bec43f5SNamhyung Kim {
448*ef60b8f5SNamhyung Kim 	if (!RB_EMPTY_ROOT(&cgroups)) {
449*ef60b8f5SNamhyung Kim 		struct cgroup *cgrp, *tmp;
450*ef60b8f5SNamhyung Kim 
451*ef60b8f5SNamhyung Kim 		rbtree_postorder_for_each_entry_safe(cgrp, tmp, &cgroups, node)
452*ef60b8f5SNamhyung Kim 			cgroup__put(cgrp);
453*ef60b8f5SNamhyung Kim 
454*ef60b8f5SNamhyung Kim 		cgroups = RB_ROOT;
455*ef60b8f5SNamhyung Kim 	}
456*ef60b8f5SNamhyung Kim 
4571bec43f5SNamhyung Kim 	syscall_summary_bpf__destroy(skel);
4581bec43f5SNamhyung Kim }
459