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