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