Commit | Line | Data |
---|---|---|
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 |
28 | const struct evsel_str_handler irq_tp_handlers[] = { |
29 | { "irq:irq_handler_entry", NULL, }, | |
30 | { "irq:irq_handler_exit", NULL, }, | |
31 | }; | |
32 | ||
33 | static 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 | 40 | static struct kwork_class *kwork_class_supported_list[KWORK_CLASS_MAX] = { |
4f8ae962 | 41 | [KWORK_CLASS_IRQ] = &kwork_irq, |
0f70d8e9 YJ |
42 | }; |
43 | ||
44 | static 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 | ||
72 | null_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 | ||
89 | static 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 | ||
136 | int 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 | } |