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