• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 };
20 
21 /* For the numbers, see hists_common.c */
22 static 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 
add_hist_entries(struct perf_evlist * evlist,struct machine * machine __maybe_unused)45 static int add_hist_entries(struct perf_evlist *evlist,
46 			    struct machine *machine __maybe_unused)
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 
96 out:
97 	pr_debug("Not enough memory for adding a hist entry\n");
98 	return TEST_FAIL;
99 }
100 
test__hists_filter(void)101 int 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);
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 
283 out:
284 	/* tear down everything */
285 	perf_evlist__delete(evlist);
286 	reset_output_field();
287 	machines__exit(&machines);
288 
289 	return err;
290 }
291