perf kwork: Add irq kwork record support
[linux-block.git] / tools / perf / builtin-kwork.c
CommitLineData
0f70d8e9
YJ
1// SPDX-License-Identifier: GPL-2.0
2/*
3 * builtin-kwork.c
4 *
5 * Copyright (c) 2022 Huawei Inc, Yang Jihong <yangjihong1@huawei.com>
6 */
7
8#include "builtin.h"
9
10#include "util/data.h"
11#include "util/kwork.h"
12#include "util/debug.h"
13#include "util/symbol.h"
14#include "util/thread.h"
15#include "util/string2.h"
16#include "util/callchain.h"
17#include "util/evsel_fprintf.h"
18
19#include <subcmd/pager.h>
20#include <subcmd/parse-options.h>
21
22#include <errno.h>
23#include <inttypes.h>
24#include <linux/err.h>
25#include <linux/time64.h>
26#include <linux/zalloc.h>
27
4f8ae962
YJ
28const struct evsel_str_handler irq_tp_handlers[] = {
29 { "irq:irq_handler_entry", NULL, },
30 { "irq:irq_handler_exit", NULL, },
31};
32
33static struct kwork_class kwork_irq = {
34 .name = "irq",
35 .type = KWORK_CLASS_IRQ,
36 .nr_tracepoints = 2,
37 .tp_handlers = irq_tp_handlers,
38};
39
0f70d8e9 40static struct kwork_class *kwork_class_supported_list[KWORK_CLASS_MAX] = {
4f8ae962 41 [KWORK_CLASS_IRQ] = &kwork_irq,
0f70d8e9
YJ
42};
43
44static void setup_event_list(struct perf_kwork *kwork,
45 const struct option *options,
46 const char * const usage_msg[])
47{
48 int i;
49 struct kwork_class *class;
50 char *tmp, *tok, *str;
51
52 if (kwork->event_list_str == NULL)
53 goto null_event_list_str;
54
55 str = strdup(kwork->event_list_str);
56 for (tok = strtok_r(str, ", ", &tmp);
57 tok; tok = strtok_r(NULL, ", ", &tmp)) {
58 for (i = 0; i < KWORK_CLASS_MAX; i++) {
59 class = kwork_class_supported_list[i];
60 if (strcmp(tok, class->name) == 0) {
61 list_add_tail(&class->list, &kwork->class_list);
62 break;
63 }
64 }
65 if (i == KWORK_CLASS_MAX) {
66 usage_with_options_msg(usage_msg, options,
67 "Unknown --event key: `%s'", tok);
68 }
69 }
70 free(str);
71
72null_event_list_str:
73 /*
74 * config all kwork events if not specified
75 */
76 if (list_empty(&kwork->class_list)) {
77 for (i = 0; i < KWORK_CLASS_MAX; i++) {
78 list_add_tail(&kwork_class_supported_list[i]->list,
79 &kwork->class_list);
80 }
81 }
82
83 pr_debug("Config event list:");
84 list_for_each_entry(class, &kwork->class_list, list)
85 pr_debug(" %s", class->name);
86 pr_debug("\n");
87}
88
89static int perf_kwork__record(struct perf_kwork *kwork,
90 int argc, const char **argv)
91{
92 const char **rec_argv;
93 unsigned int rec_argc, i, j;
94 struct kwork_class *class;
95
96 const char *const record_args[] = {
97 "record",
98 "-a",
99 "-R",
100 "-m", "1024",
101 "-c", "1",
102 };
103
104 rec_argc = ARRAY_SIZE(record_args) + argc - 1;
105
106 list_for_each_entry(class, &kwork->class_list, list)
107 rec_argc += 2 * class->nr_tracepoints;
108
109 rec_argv = calloc(rec_argc + 1, sizeof(char *));
110 if (rec_argv == NULL)
111 return -ENOMEM;
112
113 for (i = 0; i < ARRAY_SIZE(record_args); i++)
114 rec_argv[i] = strdup(record_args[i]);
115
116 list_for_each_entry(class, &kwork->class_list, list) {
117 for (j = 0; j < class->nr_tracepoints; j++) {
118 rec_argv[i++] = strdup("-e");
119 rec_argv[i++] = strdup(class->tp_handlers[j].name);
120 }
121 }
122
123 for (j = 1; j < (unsigned int)argc; j++, i++)
124 rec_argv[i] = argv[j];
125
126 BUG_ON(i != rec_argc);
127
128 pr_debug("record comm: ");
129 for (j = 0; j < rec_argc; j++)
130 pr_debug("%s ", rec_argv[j]);
131 pr_debug("\n");
132
133 return cmd_record(i, rec_argv);
134}
135
136int cmd_kwork(int argc, const char **argv)
137{
138 static struct perf_kwork kwork = {
139 .class_list = LIST_HEAD_INIT(kwork.class_list),
140 .force = false,
141 .event_list_str = NULL,
142 };
143 const struct option kwork_options[] = {
144 OPT_INCR('v', "verbose", &verbose,
145 "be more verbose (show symbol address, etc)"),
146 OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
147 "dump raw trace in ASCII"),
148 OPT_STRING('k', "kwork", &kwork.event_list_str, "kwork",
4f8ae962 149 "list of kwork to profile (irq, etc)"),
0f70d8e9
YJ
150 OPT_BOOLEAN('f', "force", &kwork.force, "don't complain, do it"),
151 OPT_END()
152 };
153 const char *kwork_usage[] = {
154 NULL,
155 NULL
156 };
157 const char *const kwork_subcommands[] = {
158 "record", NULL
159 };
160
161 argc = parse_options_subcommand(argc, argv, kwork_options,
162 kwork_subcommands, kwork_usage,
163 PARSE_OPT_STOP_AT_NON_OPTION);
164 if (!argc)
165 usage_with_options(kwork_usage, kwork_options);
166
167 setup_event_list(&kwork, kwork_options, kwork_usage);
168
169 if (strlen(argv[0]) > 2 && strstarts("record", argv[0]))
170 return perf_kwork__record(&kwork, argc, argv);
171 else
172 usage_with_options(kwork_usage, kwork_options);
173
174 return 0;
175}