xref: /openbmc/linux/tools/perf/tests/hists_filter.c (revision aded0023)
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);
343