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