1 | // SPDX-License-Identifier: GPL-2.0 |
2 | #include "parse-events.h" |
3 | #include "pmu.h" |
4 | #include "tests.h" |
5 | #include <errno.h> |
6 | #include <fcntl.h> |
7 | #include <stdio.h> |
8 | #include <linux/kernel.h> |
9 | #include <linux/limits.h> |
10 | #include <linux/zalloc.h> |
11 | |
12 | /* Simulated format definitions. */ |
13 | static struct test_format { |
14 | const char *name; |
15 | const char *value; |
16 | } test_formats[] = { |
17 | { "krava01" , "config:0-1,62-63\n" , }, |
18 | { "krava02" , "config:10-17\n" , }, |
19 | { "krava03" , "config:5\n" , }, |
20 | { "krava11" , "config1:0,2,4,6,8,20-28\n" , }, |
21 | { "krava12" , "config1:63\n" , }, |
22 | { "krava13" , "config1:45-47\n" , }, |
23 | { "krava21" , "config2:0-3,10-13,20-23,30-33,40-43,50-53,60-63\n" , }, |
24 | { "krava22" , "config2:8,18,48,58\n" , }, |
25 | { "krava23" , "config2:28-29,38\n" , }, |
26 | }; |
27 | |
28 | /* Simulated users input. */ |
29 | static struct parse_events_term test_terms[] = { |
30 | { |
31 | .config = "krava01" , |
32 | .val.num = 15, |
33 | .type_val = PARSE_EVENTS__TERM_TYPE_NUM, |
34 | .type_term = PARSE_EVENTS__TERM_TYPE_USER, |
35 | }, |
36 | { |
37 | .config = "krava02" , |
38 | .val.num = 170, |
39 | .type_val = PARSE_EVENTS__TERM_TYPE_NUM, |
40 | .type_term = PARSE_EVENTS__TERM_TYPE_USER, |
41 | }, |
42 | { |
43 | .config = "krava03" , |
44 | .val.num = 1, |
45 | .type_val = PARSE_EVENTS__TERM_TYPE_NUM, |
46 | .type_term = PARSE_EVENTS__TERM_TYPE_USER, |
47 | }, |
48 | { |
49 | .config = "krava11" , |
50 | .val.num = 27, |
51 | .type_val = PARSE_EVENTS__TERM_TYPE_NUM, |
52 | .type_term = PARSE_EVENTS__TERM_TYPE_USER, |
53 | }, |
54 | { |
55 | .config = "krava12" , |
56 | .val.num = 1, |
57 | .type_val = PARSE_EVENTS__TERM_TYPE_NUM, |
58 | .type_term = PARSE_EVENTS__TERM_TYPE_USER, |
59 | }, |
60 | { |
61 | .config = "krava13" , |
62 | .val.num = 2, |
63 | .type_val = PARSE_EVENTS__TERM_TYPE_NUM, |
64 | .type_term = PARSE_EVENTS__TERM_TYPE_USER, |
65 | }, |
66 | { |
67 | .config = "krava21" , |
68 | .val.num = 119, |
69 | .type_val = PARSE_EVENTS__TERM_TYPE_NUM, |
70 | .type_term = PARSE_EVENTS__TERM_TYPE_USER, |
71 | }, |
72 | { |
73 | .config = "krava22" , |
74 | .val.num = 11, |
75 | .type_val = PARSE_EVENTS__TERM_TYPE_NUM, |
76 | .type_term = PARSE_EVENTS__TERM_TYPE_USER, |
77 | }, |
78 | { |
79 | .config = "krava23" , |
80 | .val.num = 2, |
81 | .type_val = PARSE_EVENTS__TERM_TYPE_NUM, |
82 | .type_term = PARSE_EVENTS__TERM_TYPE_USER, |
83 | }, |
84 | }; |
85 | |
86 | /* |
87 | * Prepare format directory data, exported by kernel |
88 | * at /sys/bus/event_source/devices/<dev>/format. |
89 | */ |
90 | static char *test_format_dir_get(char *dir, size_t sz) |
91 | { |
92 | unsigned int i; |
93 | |
94 | snprintf(buf: dir, size: sz, fmt: "/tmp/perf-pmu-test-format-XXXXXX" ); |
95 | if (!mkdtemp(dir)) |
96 | return NULL; |
97 | |
98 | for (i = 0; i < ARRAY_SIZE(test_formats); i++) { |
99 | char name[PATH_MAX]; |
100 | struct test_format *format = &test_formats[i]; |
101 | FILE *file; |
102 | |
103 | scnprintf(buf: name, PATH_MAX, fmt: "%s/%s" , dir, format->name); |
104 | |
105 | file = fopen(name, "w" ); |
106 | if (!file) |
107 | return NULL; |
108 | |
109 | if (1 != fwrite(format->value, strlen(format->value), 1, file)) |
110 | break; |
111 | |
112 | fclose(file); |
113 | } |
114 | |
115 | return dir; |
116 | } |
117 | |
118 | /* Cleanup format directory. */ |
119 | static int test_format_dir_put(char *dir) |
120 | { |
121 | char buf[PATH_MAX + 20]; |
122 | |
123 | snprintf(buf, size: sizeof(buf), fmt: "rm -f %s/*\n" , dir); |
124 | if (system(buf)) |
125 | return -1; |
126 | |
127 | snprintf(buf, size: sizeof(buf), fmt: "rmdir %s\n" , dir); |
128 | return system(buf); |
129 | } |
130 | |
131 | static void add_test_terms(struct parse_events_terms *terms) |
132 | { |
133 | unsigned int i; |
134 | |
135 | for (i = 0; i < ARRAY_SIZE(test_terms); i++) { |
136 | struct parse_events_term *clone; |
137 | |
138 | parse_events_term__clone(&clone, &test_terms[i]); |
139 | list_add_tail(&clone->list, &terms->terms); |
140 | } |
141 | } |
142 | |
143 | static int test__pmu(struct test_suite *test __maybe_unused, int subtest __maybe_unused) |
144 | { |
145 | char dir[PATH_MAX]; |
146 | char *format; |
147 | struct parse_events_terms terms; |
148 | struct perf_event_attr attr; |
149 | struct perf_pmu *pmu; |
150 | int fd; |
151 | int ret; |
152 | |
153 | parse_events_terms__init(&terms); |
154 | add_test_terms(terms: &terms); |
155 | pmu = zalloc(sizeof(*pmu)); |
156 | if (!pmu) { |
157 | parse_events_terms__exit(&terms); |
158 | return -ENOMEM; |
159 | } |
160 | |
161 | INIT_LIST_HEAD(&pmu->format); |
162 | INIT_LIST_HEAD(&pmu->aliases); |
163 | INIT_LIST_HEAD(&pmu->caps); |
164 | format = test_format_dir_get(dir, sz: sizeof(dir)); |
165 | if (!format) { |
166 | free(pmu); |
167 | parse_events_terms__exit(&terms); |
168 | return -EINVAL; |
169 | } |
170 | |
171 | memset(&attr, 0, sizeof(attr)); |
172 | |
173 | fd = open(format, O_DIRECTORY); |
174 | if (fd < 0) { |
175 | ret = fd; |
176 | goto out; |
177 | } |
178 | |
179 | pmu->name = strdup("perf-pmu-test" ); |
180 | ret = perf_pmu__format_parse(pmu, fd, /*eager_load=*/true); |
181 | if (ret) |
182 | goto out; |
183 | |
184 | ret = perf_pmu__config_terms(pmu, &attr, &terms, /*zero=*/false, /*err=*/NULL); |
185 | if (ret) |
186 | goto out; |
187 | |
188 | ret = -EINVAL; |
189 | if (attr.config != 0xc00000000002a823) |
190 | goto out; |
191 | if (attr.config1 != 0x8000400000000145) |
192 | goto out; |
193 | if (attr.config2 != 0x0400000020041d07) |
194 | goto out; |
195 | |
196 | ret = 0; |
197 | out: |
198 | test_format_dir_put(dir: format); |
199 | perf_pmu__delete(pmu); |
200 | parse_events_terms__exit(&terms); |
201 | return ret; |
202 | } |
203 | |
204 | DEFINE_SUITE("Parse perf pmu format" , pmu); |
205 | |