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