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