1// SPDX-License-Identifier: GPL-2.0
2#include "util/debug.h"
3#include "util/map.h"
4#include "util/symbol.h"
5#include "util/sort.h"
6#include "util/evsel.h"
7#include "util/event.h"
8#include "util/evlist.h"
9#include "util/machine.h"
10#include "util/parse-events.h"
11#include "util/thread.h"
12#include "tests/tests.h"
13#include "tests/hists_common.h"
14#include <linux/kernel.h>
15
16struct sample {
17 u32 pid;
18 u64 ip;
19 struct thread *thread;
20 struct map *map;
21 struct symbol *sym;
22 int socket;
23};
24
25/* For the numbers, see hists_common.c */
26static struct sample fake_samples[] = {
27 /* perf [kernel] schedule() */
28 { .pid = FAKE_PID_PERF1, .ip = FAKE_IP_KERNEL_SCHEDULE, .socket = 0 },
29 /* perf [perf] main() */
30 { .pid = FAKE_PID_PERF1, .ip = FAKE_IP_PERF_MAIN, .socket = 0 },
31 /* perf [libc] malloc() */
32 { .pid = FAKE_PID_PERF1, .ip = FAKE_IP_LIBC_MALLOC, .socket = 0 },
33 /* perf [perf] main() */
34 { .pid = FAKE_PID_PERF2, .ip = FAKE_IP_PERF_MAIN, .socket = 0 }, /* will be merged */
35 /* perf [perf] cmd_record() */
36 { .pid = FAKE_PID_PERF2, .ip = FAKE_IP_PERF_CMD_RECORD, .socket = 1 },
37 /* perf [kernel] page_fault() */
38 { .pid = FAKE_PID_PERF2, .ip = FAKE_IP_KERNEL_PAGE_FAULT, .socket = 1 },
39 /* bash [bash] main() */
40 { .pid = FAKE_PID_BASH, .ip = FAKE_IP_BASH_MAIN, .socket = 2 },
41 /* bash [bash] xmalloc() */
42 { .pid = FAKE_PID_BASH, .ip = FAKE_IP_BASH_XMALLOC, .socket = 2 },
43 /* bash [libc] malloc() */
44 { .pid = FAKE_PID_BASH, .ip = FAKE_IP_LIBC_MALLOC, .socket = 3 },
45 /* bash [kernel] page_fault() */
46 { .pid = FAKE_PID_BASH, .ip = FAKE_IP_KERNEL_PAGE_FAULT, .socket = 3 },
47};
48
49static int add_hist_entries(struct evlist *evlist,
50 struct machine *machine)
51{
52 struct evsel *evsel;
53 struct addr_location al;
54 struct perf_sample sample = { .period = 100, };
55 size_t i;
56
57 addr_location__init(&al);
58 /*
59 * each evsel will have 10 samples but the 4th sample
60 * (perf [perf] main) will be collapsed to an existing entry
61 * so total 9 entries will be in the tree.
62 */
63 evlist__for_each_entry(evlist, evsel) {
64 for (i = 0; i < ARRAY_SIZE(fake_samples); i++) {
65 struct hist_entry_iter iter = {
66 .evsel = evsel,
67 .sample = &sample,
68 .ops = &hist_iter_normal,
69 .hide_unresolved = false,
70 };
71 struct hists *hists = evsel__hists(evsel);
72
73 /* make sure it has no filter at first */
74 hists->thread_filter = NULL;
75 hists->dso_filter = NULL;
76 hists->symbol_filter_str = NULL;
77
78 sample.cpumode = PERF_RECORD_MISC_USER;
79 sample.pid = fake_samples[i].pid;
80 sample.tid = fake_samples[i].pid;
81 sample.ip = fake_samples[i].ip;
82
83 if (machine__resolve(machine, &al, &sample) < 0)
84 goto out;
85
86 al.socket = fake_samples[i].socket;
87 if (hist_entry_iter__add(&iter, &al,
88 sysctl_perf_event_max_stack, NULL) < 0) {
89 goto out;
90 }
91
92 thread__put(fake_samples[i].thread);
93 fake_samples[i].thread = thread__get(al.thread);
94 map__put(fake_samples[i].map);
95 fake_samples[i].map = map__get(al.map);
96 fake_samples[i].sym = al.sym;
97 }
98 }
99 addr_location__exit(&al);
100 return 0;
101
102out:
103 pr_debug("Not enough memory for adding a hist entry\n");
104 addr_location__exit(&al);
105 return TEST_FAIL;
106}
107
108static void put_fake_samples(void)
109{
110 size_t i;
111
112 for (i = 0; i < ARRAY_SIZE(fake_samples); i++)
113 map__put(fake_samples[i].map);
114}
115
116static int test__hists_filter(struct test_suite *test __maybe_unused, int subtest __maybe_unused)
117{
118 int err = TEST_FAIL;
119 struct machines machines;
120 struct machine *machine;
121 struct evsel *evsel;
122 struct evlist *evlist = evlist__new();
123
124 TEST_ASSERT_VAL("No memory", evlist);
125
126 err = parse_event(evlist, "cpu-clock");
127 if (err)
128 goto out;
129 err = parse_event(evlist, "task-clock");
130 if (err)
131 goto out;
132 err = TEST_FAIL;
133
134 /* default sort order (comm,dso,sym) will be used */
135 if (setup_sorting(NULL) < 0)
136 goto out;
137
138 machines__init(&machines);
139
140 /* setup threads/dso/map/symbols also */
141 machine = setup_fake_machine(&machines);
142 if (!machine)
143 goto out;
144
145 if (verbose > 1)
146 machine__fprintf(machine, stderr);
147
148 /* process sample events */
149 err = add_hist_entries(evlist, machine);
150 if (err < 0)
151 goto out;
152
153 evlist__for_each_entry(evlist, evsel) {
154 struct hists *hists = evsel__hists(evsel);
155
156 hists__collapse_resort(hists, NULL);
157 evsel__output_resort(evsel, NULL);
158
159 if (verbose > 2) {
160 pr_info("Normal histogram\n");
161 print_hists_out(hists);
162 }
163
164 TEST_ASSERT_VAL("Invalid nr samples",
165 hists->stats.nr_samples == 10);
166 TEST_ASSERT_VAL("Invalid nr hist entries",
167 hists->nr_entries == 9);
168 TEST_ASSERT_VAL("Invalid total period",
169 hists->stats.total_period == 1000);
170 TEST_ASSERT_VAL("Unmatched nr samples",
171 hists->stats.nr_samples ==
172 hists->stats.nr_non_filtered_samples);
173 TEST_ASSERT_VAL("Unmatched nr hist entries",
174 hists->nr_entries == hists->nr_non_filtered_entries);
175 TEST_ASSERT_VAL("Unmatched total period",
176 hists->stats.total_period ==
177 hists->stats.total_non_filtered_period);
178
179 /* now applying thread filter for 'bash' */
180 hists->thread_filter = fake_samples[9].thread;
181 hists__filter_by_thread(hists);
182
183 if (verbose > 2) {
184 pr_info("Histogram for thread filter\n");
185 print_hists_out(hists);
186 }
187
188 /* normal stats should be invariant */
189 TEST_ASSERT_VAL("Invalid nr samples",
190 hists->stats.nr_samples == 10);
191 TEST_ASSERT_VAL("Invalid nr hist entries",
192 hists->nr_entries == 9);
193 TEST_ASSERT_VAL("Invalid total period",
194 hists->stats.total_period == 1000);
195
196 /* but filter stats are changed */
197 TEST_ASSERT_VAL("Unmatched nr samples for thread filter",
198 hists->stats.nr_non_filtered_samples == 4);
199 TEST_ASSERT_VAL("Unmatched nr hist entries for thread filter",
200 hists->nr_non_filtered_entries == 4);
201 TEST_ASSERT_VAL("Unmatched total period for thread filter",
202 hists->stats.total_non_filtered_period == 400);
203
204 /* remove thread filter first */
205 hists->thread_filter = NULL;
206 hists__filter_by_thread(hists);
207
208 /* now applying dso filter for 'kernel' */
209 hists->dso_filter = map__dso(fake_samples[0].map);
210 hists__filter_by_dso(hists);
211
212 if (verbose > 2) {
213 pr_info("Histogram for dso filter\n");
214 print_hists_out(hists);
215 }
216
217 /* normal stats should be invariant */
218 TEST_ASSERT_VAL("Invalid nr samples",
219 hists->stats.nr_samples == 10);
220 TEST_ASSERT_VAL("Invalid nr hist entries",
221 hists->nr_entries == 9);
222 TEST_ASSERT_VAL("Invalid total period",
223 hists->stats.total_period == 1000);
224
225 /* but filter stats are changed */
226 TEST_ASSERT_VAL("Unmatched nr samples for dso filter",
227 hists->stats.nr_non_filtered_samples == 3);
228 TEST_ASSERT_VAL("Unmatched nr hist entries for dso filter",
229 hists->nr_non_filtered_entries == 3);
230 TEST_ASSERT_VAL("Unmatched total period for dso filter",
231 hists->stats.total_non_filtered_period == 300);
232
233 /* remove dso filter first */
234 hists->dso_filter = NULL;
235 hists__filter_by_dso(hists);
236
237 /*
238 * now applying symbol filter for 'main'. Also note that
239 * there's 3 samples that have 'main' symbol but the 4th
240 * entry of fake_samples was collapsed already so it won't
241 * be counted as a separate entry but the sample count and
242 * total period will be remained.
243 */
244 hists->symbol_filter_str = "main";
245 hists__filter_by_symbol(hists);
246
247 if (verbose > 2) {
248 pr_info("Histogram for symbol filter\n");
249 print_hists_out(hists);
250 }
251
252 /* normal stats should be invariant */
253 TEST_ASSERT_VAL("Invalid nr samples",
254 hists->stats.nr_samples == 10);
255 TEST_ASSERT_VAL("Invalid nr hist entries",
256 hists->nr_entries == 9);
257 TEST_ASSERT_VAL("Invalid total period",
258 hists->stats.total_period == 1000);
259
260 /* but filter stats are changed */
261 TEST_ASSERT_VAL("Unmatched nr samples for symbol filter",
262 hists->stats.nr_non_filtered_samples == 3);
263 TEST_ASSERT_VAL("Unmatched nr hist entries for symbol filter",
264 hists->nr_non_filtered_entries == 2);
265 TEST_ASSERT_VAL("Unmatched total period for symbol filter",
266 hists->stats.total_non_filtered_period == 300);
267
268 /* remove symbol filter first */
269 hists->symbol_filter_str = NULL;
270 hists__filter_by_symbol(hists);
271
272 /* now applying socket filters */
273 hists->socket_filter = 2;
274 hists__filter_by_socket(hists);
275
276 if (verbose > 2) {
277 pr_info("Histogram for socket filters\n");
278 print_hists_out(hists);
279 }
280
281 /* normal stats should be invariant */
282 TEST_ASSERT_VAL("Invalid nr samples",
283 hists->stats.nr_samples == 10);
284 TEST_ASSERT_VAL("Invalid nr hist entries",
285 hists->nr_entries == 9);
286 TEST_ASSERT_VAL("Invalid total period",
287 hists->stats.total_period == 1000);
288
289 /* but filter stats are changed */
290 TEST_ASSERT_VAL("Unmatched nr samples for socket filter",
291 hists->stats.nr_non_filtered_samples == 2);
292 TEST_ASSERT_VAL("Unmatched nr hist entries for socket filter",
293 hists->nr_non_filtered_entries == 2);
294 TEST_ASSERT_VAL("Unmatched total period for socket filter",
295 hists->stats.total_non_filtered_period == 200);
296
297 /* remove socket filter first */
298 hists->socket_filter = -1;
299 hists__filter_by_socket(hists);
300
301 /* now applying all filters at once. */
302 hists->thread_filter = fake_samples[1].thread;
303 hists->dso_filter = map__dso(fake_samples[1].map);
304 hists__filter_by_thread(hists);
305 hists__filter_by_dso(hists);
306
307 if (verbose > 2) {
308 pr_info("Histogram for all filters\n");
309 print_hists_out(hists);
310 }
311
312 /* normal stats should be invariant */
313 TEST_ASSERT_VAL("Invalid nr samples",
314 hists->stats.nr_samples == 10);
315 TEST_ASSERT_VAL("Invalid nr hist entries",
316 hists->nr_entries == 9);
317 TEST_ASSERT_VAL("Invalid total period",
318 hists->stats.total_period == 1000);
319
320 /* but filter stats are changed */
321 TEST_ASSERT_VAL("Unmatched nr samples for all filter",
322 hists->stats.nr_non_filtered_samples == 2);
323 TEST_ASSERT_VAL("Unmatched nr hist entries for all filter",
324 hists->nr_non_filtered_entries == 1);
325 TEST_ASSERT_VAL("Unmatched total period for all filter",
326 hists->stats.total_non_filtered_period == 200);
327 }
328
329
330 err = TEST_OK;
331
332out:
333 /* tear down everything */
334 evlist__delete(evlist);
335 reset_output_field();
336 machines__exit(&machines);
337 put_fake_samples();
338
339 return err;
340}
341
342DEFINE_SUITE("Filter hist entries", hists_filter);
343

source code of linux/tools/perf/tests/hists_filter.c