xref: /openbmc/linux/tools/perf/tests/hists_filter.c (revision 9a32dd32)
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 
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 			map__put(fake_samples[i].map);
93 			fake_samples[i].map = al.map;
94 			fake_samples[i].sym = al.sym;
95 		}
96 	}
97 
98 	return 0;
99 
100 out:
101 	pr_debug("Not enough memory for adding a hist entry\n");
102 	return TEST_FAIL;
103 }
104 
105 static void put_fake_samples(void)
106 {
107 	size_t i;
108 
109 	for (i = 0; i < ARRAY_SIZE(fake_samples); i++)
110 		map__put(fake_samples[i].map);
111 }
112 
113 static int test__hists_filter(struct test_suite *test __maybe_unused, int subtest __maybe_unused)
114 {
115 	int err = TEST_FAIL;
116 	struct machines machines;
117 	struct machine *machine;
118 	struct evsel *evsel;
119 	struct evlist *evlist = evlist__new();
120 
121 	TEST_ASSERT_VAL("No memory", evlist);
122 
123 	err = parse_event(evlist, "cpu-clock");
124 	if (err)
125 		goto out;
126 	err = parse_event(evlist, "task-clock");
127 	if (err)
128 		goto out;
129 	err = TEST_FAIL;
130 
131 	/* default sort order (comm,dso,sym) will be used */
132 	if (setup_sorting(NULL) < 0)
133 		goto out;
134 
135 	machines__init(&machines);
136 
137 	/* setup threads/dso/map/symbols also */
138 	machine = setup_fake_machine(&machines);
139 	if (!machine)
140 		goto out;
141 
142 	if (verbose > 1)
143 		machine__fprintf(machine, stderr);
144 
145 	/* process sample events */
146 	err = add_hist_entries(evlist, machine);
147 	if (err < 0)
148 		goto out;
149 
150 	evlist__for_each_entry(evlist, evsel) {
151 		struct hists *hists = evsel__hists(evsel);
152 
153 		hists__collapse_resort(hists, NULL);
154 		evsel__output_resort(evsel, NULL);
155 
156 		if (verbose > 2) {
157 			pr_info("Normal histogram\n");
158 			print_hists_out(hists);
159 		}
160 
161 		TEST_ASSERT_VAL("Invalid nr samples",
162 				hists->stats.nr_samples == 10);
163 		TEST_ASSERT_VAL("Invalid nr hist entries",
164 				hists->nr_entries == 9);
165 		TEST_ASSERT_VAL("Invalid total period",
166 				hists->stats.total_period == 1000);
167 		TEST_ASSERT_VAL("Unmatched nr samples",
168 				hists->stats.nr_samples ==
169 				hists->stats.nr_non_filtered_samples);
170 		TEST_ASSERT_VAL("Unmatched nr hist entries",
171 				hists->nr_entries == hists->nr_non_filtered_entries);
172 		TEST_ASSERT_VAL("Unmatched total period",
173 				hists->stats.total_period ==
174 				hists->stats.total_non_filtered_period);
175 
176 		/* now applying thread filter for 'bash' */
177 		hists->thread_filter = fake_samples[9].thread;
178 		hists__filter_by_thread(hists);
179 
180 		if (verbose > 2) {
181 			pr_info("Histogram for thread filter\n");
182 			print_hists_out(hists);
183 		}
184 
185 		/* normal stats should be invariant */
186 		TEST_ASSERT_VAL("Invalid nr samples",
187 				hists->stats.nr_samples == 10);
188 		TEST_ASSERT_VAL("Invalid nr hist entries",
189 				hists->nr_entries == 9);
190 		TEST_ASSERT_VAL("Invalid total period",
191 				hists->stats.total_period == 1000);
192 
193 		/* but filter stats are changed */
194 		TEST_ASSERT_VAL("Unmatched nr samples for thread filter",
195 				hists->stats.nr_non_filtered_samples == 4);
196 		TEST_ASSERT_VAL("Unmatched nr hist entries for thread filter",
197 				hists->nr_non_filtered_entries == 4);
198 		TEST_ASSERT_VAL("Unmatched total period for thread filter",
199 				hists->stats.total_non_filtered_period == 400);
200 
201 		/* remove thread filter first */
202 		hists->thread_filter = NULL;
203 		hists__filter_by_thread(hists);
204 
205 		/* now applying dso filter for 'kernel' */
206 		hists->dso_filter = map__dso(fake_samples[0].map);
207 		hists__filter_by_dso(hists);
208 
209 		if (verbose > 2) {
210 			pr_info("Histogram for dso filter\n");
211 			print_hists_out(hists);
212 		}
213 
214 		/* normal stats should be invariant */
215 		TEST_ASSERT_VAL("Invalid nr samples",
216 				hists->stats.nr_samples == 10);
217 		TEST_ASSERT_VAL("Invalid nr hist entries",
218 				hists->nr_entries == 9);
219 		TEST_ASSERT_VAL("Invalid total period",
220 				hists->stats.total_period == 1000);
221 
222 		/* but filter stats are changed */
223 		TEST_ASSERT_VAL("Unmatched nr samples for dso filter",
224 				hists->stats.nr_non_filtered_samples == 3);
225 		TEST_ASSERT_VAL("Unmatched nr hist entries for dso filter",
226 				hists->nr_non_filtered_entries == 3);
227 		TEST_ASSERT_VAL("Unmatched total period for dso filter",
228 				hists->stats.total_non_filtered_period == 300);
229 
230 		/* remove dso filter first */
231 		hists->dso_filter = NULL;
232 		hists__filter_by_dso(hists);
233 
234 		/*
235 		 * now applying symbol filter for 'main'.  Also note that
236 		 * there's 3 samples that have 'main' symbol but the 4th
237 		 * entry of fake_samples was collapsed already so it won't
238 		 * be counted as a separate entry but the sample count and
239 		 * total period will be remained.
240 		 */
241 		hists->symbol_filter_str = "main";
242 		hists__filter_by_symbol(hists);
243 
244 		if (verbose > 2) {
245 			pr_info("Histogram for symbol filter\n");
246 			print_hists_out(hists);
247 		}
248 
249 		/* normal stats should be invariant */
250 		TEST_ASSERT_VAL("Invalid nr samples",
251 				hists->stats.nr_samples == 10);
252 		TEST_ASSERT_VAL("Invalid nr hist entries",
253 				hists->nr_entries == 9);
254 		TEST_ASSERT_VAL("Invalid total period",
255 				hists->stats.total_period == 1000);
256 
257 		/* but filter stats are changed */
258 		TEST_ASSERT_VAL("Unmatched nr samples for symbol filter",
259 				hists->stats.nr_non_filtered_samples == 3);
260 		TEST_ASSERT_VAL("Unmatched nr hist entries for symbol filter",
261 				hists->nr_non_filtered_entries == 2);
262 		TEST_ASSERT_VAL("Unmatched total period for symbol filter",
263 				hists->stats.total_non_filtered_period == 300);
264 
265 		/* remove symbol filter first */
266 		hists->symbol_filter_str = NULL;
267 		hists__filter_by_symbol(hists);
268 
269 		/* now applying socket filters */
270 		hists->socket_filter = 2;
271 		hists__filter_by_socket(hists);
272 
273 		if (verbose > 2) {
274 			pr_info("Histogram for socket filters\n");
275 			print_hists_out(hists);
276 		}
277 
278 		/* normal stats should be invariant */
279 		TEST_ASSERT_VAL("Invalid nr samples",
280 				hists->stats.nr_samples == 10);
281 		TEST_ASSERT_VAL("Invalid nr hist entries",
282 				hists->nr_entries == 9);
283 		TEST_ASSERT_VAL("Invalid total period",
284 				hists->stats.total_period == 1000);
285 
286 		/* but filter stats are changed */
287 		TEST_ASSERT_VAL("Unmatched nr samples for socket filter",
288 				hists->stats.nr_non_filtered_samples == 2);
289 		TEST_ASSERT_VAL("Unmatched nr hist entries for socket filter",
290 				hists->nr_non_filtered_entries == 2);
291 		TEST_ASSERT_VAL("Unmatched total period for socket filter",
292 				hists->stats.total_non_filtered_period == 200);
293 
294 		/* remove socket filter first */
295 		hists->socket_filter = -1;
296 		hists__filter_by_socket(hists);
297 
298 		/* now applying all filters at once. */
299 		hists->thread_filter = fake_samples[1].thread;
300 		hists->dso_filter = map__dso(fake_samples[1].map);
301 		hists__filter_by_thread(hists);
302 		hists__filter_by_dso(hists);
303 
304 		if (verbose > 2) {
305 			pr_info("Histogram for all filters\n");
306 			print_hists_out(hists);
307 		}
308 
309 		/* normal stats should be invariant */
310 		TEST_ASSERT_VAL("Invalid nr samples",
311 				hists->stats.nr_samples == 10);
312 		TEST_ASSERT_VAL("Invalid nr hist entries",
313 				hists->nr_entries == 9);
314 		TEST_ASSERT_VAL("Invalid total period",
315 				hists->stats.total_period == 1000);
316 
317 		/* but filter stats are changed */
318 		TEST_ASSERT_VAL("Unmatched nr samples for all filter",
319 				hists->stats.nr_non_filtered_samples == 2);
320 		TEST_ASSERT_VAL("Unmatched nr hist entries for all filter",
321 				hists->nr_non_filtered_entries == 1);
322 		TEST_ASSERT_VAL("Unmatched total period for all filter",
323 				hists->stats.total_non_filtered_period == 200);
324 	}
325 
326 
327 	err = TEST_OK;
328 
329 out:
330 	/* tear down everything */
331 	evlist__delete(evlist);
332 	reset_output_field();
333 	machines__exit(&machines);
334 	put_fake_samples();
335 
336 	return err;
337 }
338 
339 DEFINE_SUITE("Filter hist entries", hists_filter);
340