diff options
Diffstat (limited to 'tools/perf/tests/sample-parsing.c')
-rw-r--r-- | tools/perf/tests/sample-parsing.c | 316 |
1 files changed, 316 insertions, 0 deletions
diff --git a/tools/perf/tests/sample-parsing.c b/tools/perf/tests/sample-parsing.c new file mode 100644 index 000000000000..77f598dbd97a --- /dev/null +++ b/tools/perf/tests/sample-parsing.c | |||
@@ -0,0 +1,316 @@ | |||
1 | #include <stdbool.h> | ||
2 | #include <inttypes.h> | ||
3 | |||
4 | #include "util.h" | ||
5 | #include "event.h" | ||
6 | #include "evsel.h" | ||
7 | |||
8 | #include "tests.h" | ||
9 | |||
10 | #define COMP(m) do { \ | ||
11 | if (s1->m != s2->m) { \ | ||
12 | pr_debug("Samples differ at '"#m"'\n"); \ | ||
13 | return false; \ | ||
14 | } \ | ||
15 | } while (0) | ||
16 | |||
17 | #define MCOMP(m) do { \ | ||
18 | if (memcmp(&s1->m, &s2->m, sizeof(s1->m))) { \ | ||
19 | pr_debug("Samples differ at '"#m"'\n"); \ | ||
20 | return false; \ | ||
21 | } \ | ||
22 | } while (0) | ||
23 | |||
24 | static bool samples_same(const struct perf_sample *s1, | ||
25 | const struct perf_sample *s2, u64 type, u64 regs_user, | ||
26 | u64 read_format) | ||
27 | { | ||
28 | size_t i; | ||
29 | |||
30 | if (type & PERF_SAMPLE_IDENTIFIER) | ||
31 | COMP(id); | ||
32 | |||
33 | if (type & PERF_SAMPLE_IP) | ||
34 | COMP(ip); | ||
35 | |||
36 | if (type & PERF_SAMPLE_TID) { | ||
37 | COMP(pid); | ||
38 | COMP(tid); | ||
39 | } | ||
40 | |||
41 | if (type & PERF_SAMPLE_TIME) | ||
42 | COMP(time); | ||
43 | |||
44 | if (type & PERF_SAMPLE_ADDR) | ||
45 | COMP(addr); | ||
46 | |||
47 | if (type & PERF_SAMPLE_ID) | ||
48 | COMP(id); | ||
49 | |||
50 | if (type & PERF_SAMPLE_STREAM_ID) | ||
51 | COMP(stream_id); | ||
52 | |||
53 | if (type & PERF_SAMPLE_CPU) | ||
54 | COMP(cpu); | ||
55 | |||
56 | if (type & PERF_SAMPLE_PERIOD) | ||
57 | COMP(period); | ||
58 | |||
59 | if (type & PERF_SAMPLE_READ) { | ||
60 | if (read_format & PERF_FORMAT_GROUP) | ||
61 | COMP(read.group.nr); | ||
62 | else | ||
63 | COMP(read.one.value); | ||
64 | if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) | ||
65 | COMP(read.time_enabled); | ||
66 | if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING) | ||
67 | COMP(read.time_running); | ||
68 | /* PERF_FORMAT_ID is forced for PERF_SAMPLE_READ */ | ||
69 | if (read_format & PERF_FORMAT_GROUP) { | ||
70 | for (i = 0; i < s1->read.group.nr; i++) | ||
71 | MCOMP(read.group.values[i]); | ||
72 | } else { | ||
73 | COMP(read.one.id); | ||
74 | } | ||
75 | } | ||
76 | |||
77 | if (type & PERF_SAMPLE_CALLCHAIN) { | ||
78 | COMP(callchain->nr); | ||
79 | for (i = 0; i < s1->callchain->nr; i++) | ||
80 | COMP(callchain->ips[i]); | ||
81 | } | ||
82 | |||
83 | if (type & PERF_SAMPLE_RAW) { | ||
84 | COMP(raw_size); | ||
85 | if (memcmp(s1->raw_data, s2->raw_data, s1->raw_size)) { | ||
86 | pr_debug("Samples differ at 'raw_data'\n"); | ||
87 | return false; | ||
88 | } | ||
89 | } | ||
90 | |||
91 | if (type & PERF_SAMPLE_BRANCH_STACK) { | ||
92 | COMP(branch_stack->nr); | ||
93 | for (i = 0; i < s1->branch_stack->nr; i++) | ||
94 | MCOMP(branch_stack->entries[i]); | ||
95 | } | ||
96 | |||
97 | if (type & PERF_SAMPLE_REGS_USER) { | ||
98 | size_t sz = hweight_long(regs_user) * sizeof(u64); | ||
99 | |||
100 | COMP(user_regs.abi); | ||
101 | if (s1->user_regs.abi && | ||
102 | (!s1->user_regs.regs || !s2->user_regs.regs || | ||
103 | memcmp(s1->user_regs.regs, s2->user_regs.regs, sz))) { | ||
104 | pr_debug("Samples differ at 'user_regs'\n"); | ||
105 | return false; | ||
106 | } | ||
107 | } | ||
108 | |||
109 | if (type & PERF_SAMPLE_STACK_USER) { | ||
110 | COMP(user_stack.size); | ||
111 | if (memcmp(s1->user_stack.data, s1->user_stack.data, | ||
112 | s1->user_stack.size)) { | ||
113 | pr_debug("Samples differ at 'user_stack'\n"); | ||
114 | return false; | ||
115 | } | ||
116 | } | ||
117 | |||
118 | if (type & PERF_SAMPLE_WEIGHT) | ||
119 | COMP(weight); | ||
120 | |||
121 | if (type & PERF_SAMPLE_DATA_SRC) | ||
122 | COMP(data_src); | ||
123 | |||
124 | return true; | ||
125 | } | ||
126 | |||
127 | static int do_test(u64 sample_type, u64 sample_regs_user, u64 read_format) | ||
128 | { | ||
129 | struct perf_evsel evsel = { | ||
130 | .needs_swap = false, | ||
131 | .attr = { | ||
132 | .sample_type = sample_type, | ||
133 | .sample_regs_user = sample_regs_user, | ||
134 | .read_format = read_format, | ||
135 | }, | ||
136 | }; | ||
137 | union perf_event *event; | ||
138 | union { | ||
139 | struct ip_callchain callchain; | ||
140 | u64 data[64]; | ||
141 | } callchain = { | ||
142 | /* 3 ips */ | ||
143 | .data = {3, 201, 202, 203}, | ||
144 | }; | ||
145 | union { | ||
146 | struct branch_stack branch_stack; | ||
147 | u64 data[64]; | ||
148 | } branch_stack = { | ||
149 | /* 1 branch_entry */ | ||
150 | .data = {1, 211, 212, 213}, | ||
151 | }; | ||
152 | u64 user_regs[64]; | ||
153 | const u64 raw_data[] = {0x123456780a0b0c0dULL, 0x1102030405060708ULL}; | ||
154 | const u64 data[] = {0x2211443366558877ULL, 0, 0xaabbccddeeff4321ULL}; | ||
155 | struct perf_sample sample = { | ||
156 | .ip = 101, | ||
157 | .pid = 102, | ||
158 | .tid = 103, | ||
159 | .time = 104, | ||
160 | .addr = 105, | ||
161 | .id = 106, | ||
162 | .stream_id = 107, | ||
163 | .period = 108, | ||
164 | .weight = 109, | ||
165 | .cpu = 110, | ||
166 | .raw_size = sizeof(raw_data), | ||
167 | .data_src = 111, | ||
168 | .raw_data = (void *)raw_data, | ||
169 | .callchain = &callchain.callchain, | ||
170 | .branch_stack = &branch_stack.branch_stack, | ||
171 | .user_regs = { | ||
172 | .abi = PERF_SAMPLE_REGS_ABI_64, | ||
173 | .regs = user_regs, | ||
174 | }, | ||
175 | .user_stack = { | ||
176 | .size = sizeof(data), | ||
177 | .data = (void *)data, | ||
178 | }, | ||
179 | .read = { | ||
180 | .time_enabled = 0x030a59d664fca7deULL, | ||
181 | .time_running = 0x011b6ae553eb98edULL, | ||
182 | }, | ||
183 | }; | ||
184 | struct sample_read_value values[] = {{1, 5}, {9, 3}, {2, 7}, {6, 4},}; | ||
185 | struct perf_sample sample_out; | ||
186 | size_t i, sz, bufsz; | ||
187 | int err, ret = -1; | ||
188 | |||
189 | for (i = 0; i < sizeof(user_regs); i++) | ||
190 | *(i + (u8 *)user_regs) = i & 0xfe; | ||
191 | |||
192 | if (read_format & PERF_FORMAT_GROUP) { | ||
193 | sample.read.group.nr = 4; | ||
194 | sample.read.group.values = values; | ||
195 | } else { | ||
196 | sample.read.one.value = 0x08789faeb786aa87ULL; | ||
197 | sample.read.one.id = 99; | ||
198 | } | ||
199 | |||
200 | sz = perf_event__sample_event_size(&sample, sample_type, | ||
201 | sample_regs_user, read_format); | ||
202 | bufsz = sz + 4096; /* Add a bit for overrun checking */ | ||
203 | event = malloc(bufsz); | ||
204 | if (!event) { | ||
205 | pr_debug("malloc failed\n"); | ||
206 | return -1; | ||
207 | } | ||
208 | |||
209 | memset(event, 0xff, bufsz); | ||
210 | event->header.type = PERF_RECORD_SAMPLE; | ||
211 | event->header.misc = 0; | ||
212 | event->header.size = sz; | ||
213 | |||
214 | err = perf_event__synthesize_sample(event, sample_type, | ||
215 | sample_regs_user, read_format, | ||
216 | &sample, false); | ||
217 | if (err) { | ||
218 | pr_debug("%s failed for sample_type %#"PRIx64", error %d\n", | ||
219 | "perf_event__synthesize_sample", sample_type, err); | ||
220 | goto out_free; | ||
221 | } | ||
222 | |||
223 | /* The data does not contain 0xff so we use that to check the size */ | ||
224 | for (i = bufsz; i > 0; i--) { | ||
225 | if (*(i - 1 + (u8 *)event) != 0xff) | ||
226 | break; | ||
227 | } | ||
228 | if (i != sz) { | ||
229 | pr_debug("Event size mismatch: actual %zu vs expected %zu\n", | ||
230 | i, sz); | ||
231 | goto out_free; | ||
232 | } | ||
233 | |||
234 | evsel.sample_size = __perf_evsel__sample_size(sample_type); | ||
235 | |||
236 | err = perf_evsel__parse_sample(&evsel, event, &sample_out); | ||
237 | if (err) { | ||
238 | pr_debug("%s failed for sample_type %#"PRIx64", error %d\n", | ||
239 | "perf_evsel__parse_sample", sample_type, err); | ||
240 | goto out_free; | ||
241 | } | ||
242 | |||
243 | if (!samples_same(&sample, &sample_out, sample_type, | ||
244 | sample_regs_user, read_format)) { | ||
245 | pr_debug("parsing failed for sample_type %#"PRIx64"\n", | ||
246 | sample_type); | ||
247 | goto out_free; | ||
248 | } | ||
249 | |||
250 | ret = 0; | ||
251 | out_free: | ||
252 | free(event); | ||
253 | if (ret && read_format) | ||
254 | pr_debug("read_format %#"PRIx64"\n", read_format); | ||
255 | return ret; | ||
256 | } | ||
257 | |||
258 | /** | ||
259 | * test__sample_parsing - test sample parsing. | ||
260 | * | ||
261 | * This function implements a test that synthesizes a sample event, parses it | ||
262 | * and then checks that the parsed sample matches the original sample. The test | ||
263 | * checks sample format bits separately and together. If the test passes %0 is | ||
264 | * returned, otherwise %-1 is returned. | ||
265 | */ | ||
266 | int test__sample_parsing(void) | ||
267 | { | ||
268 | const u64 rf[] = {4, 5, 6, 7, 12, 13, 14, 15}; | ||
269 | u64 sample_type; | ||
270 | u64 sample_regs_user; | ||
271 | size_t i; | ||
272 | int err; | ||
273 | |||
274 | /* | ||
275 | * Fail the test if it has not been updated when new sample format bits | ||
276 | * were added. | ||
277 | */ | ||
278 | if (PERF_SAMPLE_MAX > PERF_SAMPLE_IDENTIFIER << 1) { | ||
279 | pr_debug("sample format has changed - test needs updating\n"); | ||
280 | return -1; | ||
281 | } | ||
282 | |||
283 | /* Test each sample format bit separately */ | ||
284 | for (sample_type = 1; sample_type != PERF_SAMPLE_MAX; | ||
285 | sample_type <<= 1) { | ||
286 | /* Test read_format variations */ | ||
287 | if (sample_type == PERF_SAMPLE_READ) { | ||
288 | for (i = 0; i < ARRAY_SIZE(rf); i++) { | ||
289 | err = do_test(sample_type, 0, rf[i]); | ||
290 | if (err) | ||
291 | return err; | ||
292 | } | ||
293 | continue; | ||
294 | } | ||
295 | |||
296 | if (sample_type == PERF_SAMPLE_REGS_USER) | ||
297 | sample_regs_user = 0x3fff; | ||
298 | else | ||
299 | sample_regs_user = 0; | ||
300 | |||
301 | err = do_test(sample_type, sample_regs_user, 0); | ||
302 | if (err) | ||
303 | return err; | ||
304 | } | ||
305 | |||
306 | /* Test all sample format bits together */ | ||
307 | sample_type = PERF_SAMPLE_MAX - 1; | ||
308 | sample_regs_user = 0x3fff; | ||
309 | for (i = 0; i < ARRAY_SIZE(rf); i++) { | ||
310 | err = do_test(sample_type, sample_regs_user, rf[i]); | ||
311 | if (err) | ||
312 | return err; | ||
313 | } | ||
314 | |||
315 | return 0; | ||
316 | } | ||