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