Linux 6.10-rc5
[sfrench/cifs-2.6.git] / tools / perf / tests / hists_filter.c
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
16 struct 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 */
26 static 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
49 static 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
102 out:
103         pr_debug("Not enough memory for adding a hist entry\n");
104         addr_location__exit(&al);
105         return TEST_FAIL;
106 }
107
108 static 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
116 static 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
332 out:
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
342 DEFINE_SUITE("Filter hist entries", hists_filter);