xref: /openbmc/linux/tools/perf/builtin-kwork.c (revision e6439321)
10f70d8e9SYang Jihong // SPDX-License-Identifier: GPL-2.0
20f70d8e9SYang Jihong /*
30f70d8e9SYang Jihong  * builtin-kwork.c
40f70d8e9SYang Jihong  *
50f70d8e9SYang Jihong  * Copyright (c) 2022  Huawei Inc,  Yang Jihong <yangjihong1@huawei.com>
60f70d8e9SYang Jihong  */
70f70d8e9SYang Jihong 
80f70d8e9SYang Jihong #include "builtin.h"
90f70d8e9SYang Jihong 
100f70d8e9SYang Jihong #include "util/data.h"
110f70d8e9SYang Jihong #include "util/kwork.h"
120f70d8e9SYang Jihong #include "util/debug.h"
130f70d8e9SYang Jihong #include "util/symbol.h"
140f70d8e9SYang Jihong #include "util/thread.h"
150f70d8e9SYang Jihong #include "util/string2.h"
160f70d8e9SYang Jihong #include "util/callchain.h"
170f70d8e9SYang Jihong #include "util/evsel_fprintf.h"
180f70d8e9SYang Jihong 
190f70d8e9SYang Jihong #include <subcmd/pager.h>
200f70d8e9SYang Jihong #include <subcmd/parse-options.h>
210f70d8e9SYang Jihong 
220f70d8e9SYang Jihong #include <errno.h>
230f70d8e9SYang Jihong #include <inttypes.h>
240f70d8e9SYang Jihong #include <linux/err.h>
250f70d8e9SYang Jihong #include <linux/time64.h>
260f70d8e9SYang Jihong #include <linux/zalloc.h>
270f70d8e9SYang Jihong 
284f8ae962SYang Jihong const struct evsel_str_handler irq_tp_handlers[] = {
294f8ae962SYang Jihong 	{ "irq:irq_handler_entry", NULL, },
304f8ae962SYang Jihong 	{ "irq:irq_handler_exit",  NULL, },
314f8ae962SYang Jihong };
324f8ae962SYang Jihong 
334f8ae962SYang Jihong static struct kwork_class kwork_irq = {
344f8ae962SYang Jihong 	.name           = "irq",
354f8ae962SYang Jihong 	.type           = KWORK_CLASS_IRQ,
364f8ae962SYang Jihong 	.nr_tracepoints = 2,
374f8ae962SYang Jihong 	.tp_handlers    = irq_tp_handlers,
384f8ae962SYang Jihong };
394f8ae962SYang Jihong 
40*e6439321SYang Jihong const struct evsel_str_handler softirq_tp_handlers[] = {
41*e6439321SYang Jihong 	{ "irq:softirq_raise", NULL, },
42*e6439321SYang Jihong 	{ "irq:softirq_entry", NULL, },
43*e6439321SYang Jihong 	{ "irq:softirq_exit",  NULL, },
44*e6439321SYang Jihong };
45*e6439321SYang Jihong 
46*e6439321SYang Jihong static struct kwork_class kwork_softirq = {
47*e6439321SYang Jihong 	.name           = "softirq",
48*e6439321SYang Jihong 	.type           = KWORK_CLASS_SOFTIRQ,
49*e6439321SYang Jihong 	.nr_tracepoints = 3,
50*e6439321SYang Jihong 	.tp_handlers    = softirq_tp_handlers,
51*e6439321SYang Jihong };
52*e6439321SYang Jihong 
530f70d8e9SYang Jihong static struct kwork_class *kwork_class_supported_list[KWORK_CLASS_MAX] = {
544f8ae962SYang Jihong 	[KWORK_CLASS_IRQ]       = &kwork_irq,
55*e6439321SYang Jihong 	[KWORK_CLASS_SOFTIRQ]   = &kwork_softirq,
560f70d8e9SYang Jihong };
570f70d8e9SYang Jihong 
580f70d8e9SYang Jihong static void setup_event_list(struct perf_kwork *kwork,
590f70d8e9SYang Jihong 			     const struct option *options,
600f70d8e9SYang Jihong 			     const char * const usage_msg[])
610f70d8e9SYang Jihong {
620f70d8e9SYang Jihong 	int i;
630f70d8e9SYang Jihong 	struct kwork_class *class;
640f70d8e9SYang Jihong 	char *tmp, *tok, *str;
650f70d8e9SYang Jihong 
660f70d8e9SYang Jihong 	if (kwork->event_list_str == NULL)
670f70d8e9SYang Jihong 		goto null_event_list_str;
680f70d8e9SYang Jihong 
690f70d8e9SYang Jihong 	str = strdup(kwork->event_list_str);
700f70d8e9SYang Jihong 	for (tok = strtok_r(str, ", ", &tmp);
710f70d8e9SYang Jihong 	     tok; tok = strtok_r(NULL, ", ", &tmp)) {
720f70d8e9SYang Jihong 		for (i = 0; i < KWORK_CLASS_MAX; i++) {
730f70d8e9SYang Jihong 			class = kwork_class_supported_list[i];
740f70d8e9SYang Jihong 			if (strcmp(tok, class->name) == 0) {
750f70d8e9SYang Jihong 				list_add_tail(&class->list, &kwork->class_list);
760f70d8e9SYang Jihong 				break;
770f70d8e9SYang Jihong 			}
780f70d8e9SYang Jihong 		}
790f70d8e9SYang Jihong 		if (i == KWORK_CLASS_MAX) {
800f70d8e9SYang Jihong 			usage_with_options_msg(usage_msg, options,
810f70d8e9SYang Jihong 					       "Unknown --event key: `%s'", tok);
820f70d8e9SYang Jihong 		}
830f70d8e9SYang Jihong 	}
840f70d8e9SYang Jihong 	free(str);
850f70d8e9SYang Jihong 
860f70d8e9SYang Jihong null_event_list_str:
870f70d8e9SYang Jihong 	/*
880f70d8e9SYang Jihong 	 * config all kwork events if not specified
890f70d8e9SYang Jihong 	 */
900f70d8e9SYang Jihong 	if (list_empty(&kwork->class_list)) {
910f70d8e9SYang Jihong 		for (i = 0; i < KWORK_CLASS_MAX; i++) {
920f70d8e9SYang Jihong 			list_add_tail(&kwork_class_supported_list[i]->list,
930f70d8e9SYang Jihong 				      &kwork->class_list);
940f70d8e9SYang Jihong 		}
950f70d8e9SYang Jihong 	}
960f70d8e9SYang Jihong 
970f70d8e9SYang Jihong 	pr_debug("Config event list:");
980f70d8e9SYang Jihong 	list_for_each_entry(class, &kwork->class_list, list)
990f70d8e9SYang Jihong 		pr_debug(" %s", class->name);
1000f70d8e9SYang Jihong 	pr_debug("\n");
1010f70d8e9SYang Jihong }
1020f70d8e9SYang Jihong 
1030f70d8e9SYang Jihong static int perf_kwork__record(struct perf_kwork *kwork,
1040f70d8e9SYang Jihong 			      int argc, const char **argv)
1050f70d8e9SYang Jihong {
1060f70d8e9SYang Jihong 	const char **rec_argv;
1070f70d8e9SYang Jihong 	unsigned int rec_argc, i, j;
1080f70d8e9SYang Jihong 	struct kwork_class *class;
1090f70d8e9SYang Jihong 
1100f70d8e9SYang Jihong 	const char *const record_args[] = {
1110f70d8e9SYang Jihong 		"record",
1120f70d8e9SYang Jihong 		"-a",
1130f70d8e9SYang Jihong 		"-R",
1140f70d8e9SYang Jihong 		"-m", "1024",
1150f70d8e9SYang Jihong 		"-c", "1",
1160f70d8e9SYang Jihong 	};
1170f70d8e9SYang Jihong 
1180f70d8e9SYang Jihong 	rec_argc = ARRAY_SIZE(record_args) + argc - 1;
1190f70d8e9SYang Jihong 
1200f70d8e9SYang Jihong 	list_for_each_entry(class, &kwork->class_list, list)
1210f70d8e9SYang Jihong 		rec_argc += 2 * class->nr_tracepoints;
1220f70d8e9SYang Jihong 
1230f70d8e9SYang Jihong 	rec_argv = calloc(rec_argc + 1, sizeof(char *));
1240f70d8e9SYang Jihong 	if (rec_argv == NULL)
1250f70d8e9SYang Jihong 		return -ENOMEM;
1260f70d8e9SYang Jihong 
1270f70d8e9SYang Jihong 	for (i = 0; i < ARRAY_SIZE(record_args); i++)
1280f70d8e9SYang Jihong 		rec_argv[i] = strdup(record_args[i]);
1290f70d8e9SYang Jihong 
1300f70d8e9SYang Jihong 	list_for_each_entry(class, &kwork->class_list, list) {
1310f70d8e9SYang Jihong 		for (j = 0; j < class->nr_tracepoints; j++) {
1320f70d8e9SYang Jihong 			rec_argv[i++] = strdup("-e");
1330f70d8e9SYang Jihong 			rec_argv[i++] = strdup(class->tp_handlers[j].name);
1340f70d8e9SYang Jihong 		}
1350f70d8e9SYang Jihong 	}
1360f70d8e9SYang Jihong 
1370f70d8e9SYang Jihong 	for (j = 1; j < (unsigned int)argc; j++, i++)
1380f70d8e9SYang Jihong 		rec_argv[i] = argv[j];
1390f70d8e9SYang Jihong 
1400f70d8e9SYang Jihong 	BUG_ON(i != rec_argc);
1410f70d8e9SYang Jihong 
1420f70d8e9SYang Jihong 	pr_debug("record comm: ");
1430f70d8e9SYang Jihong 	for (j = 0; j < rec_argc; j++)
1440f70d8e9SYang Jihong 		pr_debug("%s ", rec_argv[j]);
1450f70d8e9SYang Jihong 	pr_debug("\n");
1460f70d8e9SYang Jihong 
1470f70d8e9SYang Jihong 	return cmd_record(i, rec_argv);
1480f70d8e9SYang Jihong }
1490f70d8e9SYang Jihong 
1500f70d8e9SYang Jihong int cmd_kwork(int argc, const char **argv)
1510f70d8e9SYang Jihong {
1520f70d8e9SYang Jihong 	static struct perf_kwork kwork = {
1530f70d8e9SYang Jihong 		.class_list          = LIST_HEAD_INIT(kwork.class_list),
1540f70d8e9SYang Jihong 		.force               = false,
1550f70d8e9SYang Jihong 		.event_list_str      = NULL,
1560f70d8e9SYang Jihong 	};
1570f70d8e9SYang Jihong 	const struct option kwork_options[] = {
1580f70d8e9SYang Jihong 	OPT_INCR('v', "verbose", &verbose,
1590f70d8e9SYang Jihong 		 "be more verbose (show symbol address, etc)"),
1600f70d8e9SYang Jihong 	OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
1610f70d8e9SYang Jihong 		    "dump raw trace in ASCII"),
1620f70d8e9SYang Jihong 	OPT_STRING('k', "kwork", &kwork.event_list_str, "kwork",
163*e6439321SYang Jihong 		   "list of kwork to profile (irq, softirq, etc)"),
1640f70d8e9SYang Jihong 	OPT_BOOLEAN('f', "force", &kwork.force, "don't complain, do it"),
1650f70d8e9SYang Jihong 	OPT_END()
1660f70d8e9SYang Jihong 	};
1670f70d8e9SYang Jihong 	const char *kwork_usage[] = {
1680f70d8e9SYang Jihong 		NULL,
1690f70d8e9SYang Jihong 		NULL
1700f70d8e9SYang Jihong 	};
1710f70d8e9SYang Jihong 	const char *const kwork_subcommands[] = {
1720f70d8e9SYang Jihong 		"record", NULL
1730f70d8e9SYang Jihong 	};
1740f70d8e9SYang Jihong 
1750f70d8e9SYang Jihong 	argc = parse_options_subcommand(argc, argv, kwork_options,
1760f70d8e9SYang Jihong 					kwork_subcommands, kwork_usage,
1770f70d8e9SYang Jihong 					PARSE_OPT_STOP_AT_NON_OPTION);
1780f70d8e9SYang Jihong 	if (!argc)
1790f70d8e9SYang Jihong 		usage_with_options(kwork_usage, kwork_options);
1800f70d8e9SYang Jihong 
1810f70d8e9SYang Jihong 	setup_event_list(&kwork, kwork_options, kwork_usage);
1820f70d8e9SYang Jihong 
1830f70d8e9SYang Jihong 	if (strlen(argv[0]) > 2 && strstarts("record", argv[0]))
1840f70d8e9SYang Jihong 		return perf_kwork__record(&kwork, argc, argv);
1850f70d8e9SYang Jihong 	else
1860f70d8e9SYang Jihong 		usage_with_options(kwork_usage, kwork_options);
1870f70d8e9SYang Jihong 
1880f70d8e9SYang Jihong 	return 0;
1890f70d8e9SYang Jihong }
190