1 // SPDX-License-Identifier: GPL-2.0 2 #include "tests.h" 3 #include "debug.h" 4 #include "symbol.h" 5 #include "sort.h" 6 #include "evsel.h" 7 #include "evlist.h" 8 #include "machine.h" 9 #include "map.h" 10 #include "parse-events.h" 11 #include "hists_common.h" 12 #include "util/mmap.h" 13 #include <errno.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 }; 23 24 /* For the numbers, see hists_common.c */ 25 static struct sample fake_common_samples[] = { 26 /* perf [kernel] schedule() */ 27 { .pid = FAKE_PID_PERF1, .ip = FAKE_IP_KERNEL_SCHEDULE, }, 28 /* perf [perf] main() */ 29 { .pid = FAKE_PID_PERF2, .ip = FAKE_IP_PERF_MAIN, }, 30 /* perf [perf] cmd_record() */ 31 { .pid = FAKE_PID_PERF2, .ip = FAKE_IP_PERF_CMD_RECORD, }, 32 /* bash [bash] xmalloc() */ 33 { .pid = FAKE_PID_BASH, .ip = FAKE_IP_BASH_XMALLOC, }, 34 /* bash [libc] malloc() */ 35 { .pid = FAKE_PID_BASH, .ip = FAKE_IP_LIBC_MALLOC, }, 36 }; 37 38 static struct sample fake_samples[][5] = { 39 { 40 /* perf [perf] run_command() */ 41 { .pid = FAKE_PID_PERF1, .ip = FAKE_IP_PERF_RUN_COMMAND, }, 42 /* perf [libc] malloc() */ 43 { .pid = FAKE_PID_PERF1, .ip = FAKE_IP_LIBC_MALLOC, }, 44 /* perf [kernel] page_fault() */ 45 { .pid = FAKE_PID_PERF1, .ip = FAKE_IP_KERNEL_PAGE_FAULT, }, 46 /* perf [kernel] sys_perf_event_open() */ 47 { .pid = FAKE_PID_PERF2, .ip = FAKE_IP_KERNEL_SYS_PERF_EVENT_OPEN, }, 48 /* bash [libc] free() */ 49 { .pid = FAKE_PID_BASH, .ip = FAKE_IP_LIBC_FREE, }, 50 }, 51 { 52 /* perf [libc] free() */ 53 { .pid = FAKE_PID_PERF2, .ip = FAKE_IP_LIBC_FREE, }, 54 /* bash [libc] malloc() */ 55 { .pid = FAKE_PID_BASH, .ip = FAKE_IP_LIBC_MALLOC, }, /* will be merged */ 56 /* bash [bash] xfee() */ 57 { .pid = FAKE_PID_BASH, .ip = FAKE_IP_BASH_XFREE, }, 58 /* bash [libc] realloc() */ 59 { .pid = FAKE_PID_BASH, .ip = FAKE_IP_LIBC_REALLOC, }, 60 /* bash [kernel] page_fault() */ 61 { .pid = FAKE_PID_BASH, .ip = FAKE_IP_KERNEL_PAGE_FAULT, }, 62 }, 63 }; 64 65 static int add_hist_entries(struct evlist *evlist, struct machine *machine) 66 { 67 struct evsel *evsel; 68 struct addr_location al; 69 struct hist_entry *he; 70 struct perf_sample sample = { .period = 1, .weight = 1, }; 71 size_t i = 0, k; 72 73 /* 74 * each evsel will have 10 samples - 5 common and 5 distinct. 75 * However the second evsel also has a collapsed entry for 76 * "bash [libc] malloc" so total 9 entries will be in the tree. 77 */ 78 evlist__for_each_entry(evlist, evsel) { 79 struct hists *hists = evsel__hists(evsel); 80 81 for (k = 0; k < ARRAY_SIZE(fake_common_samples); k++) { 82 sample.cpumode = PERF_RECORD_MISC_USER; 83 sample.pid = fake_common_samples[k].pid; 84 sample.tid = fake_common_samples[k].pid; 85 sample.ip = fake_common_samples[k].ip; 86 87 if (machine__resolve(machine, &al, &sample) < 0) 88 goto out; 89 90 he = hists__add_entry(hists, &al, NULL, 91 NULL, NULL, NULL, &sample, true); 92 if (he == NULL) { 93 addr_location__put(&al); 94 goto out; 95 } 96 97 fake_common_samples[k].thread = al.thread; 98 map__put(fake_common_samples[k].map); 99 fake_common_samples[k].map = al.map; 100 fake_common_samples[k].sym = al.sym; 101 } 102 103 for (k = 0; k < ARRAY_SIZE(fake_samples[i]); k++) { 104 sample.pid = fake_samples[i][k].pid; 105 sample.tid = fake_samples[i][k].pid; 106 sample.ip = fake_samples[i][k].ip; 107 if (machine__resolve(machine, &al, &sample) < 0) 108 goto out; 109 110 he = hists__add_entry(hists, &al, NULL, 111 NULL, NULL, NULL, &sample, true); 112 if (he == NULL) { 113 addr_location__put(&al); 114 goto out; 115 } 116 117 fake_samples[i][k].thread = al.thread; 118 fake_samples[i][k].map = al.map; 119 fake_samples[i][k].sym = al.sym; 120 } 121 i++; 122 } 123 124 return 0; 125 126 out: 127 pr_debug("Not enough memory for adding a hist entry\n"); 128 return -1; 129 } 130 131 static void put_fake_samples(void) 132 { 133 size_t i, j; 134 135 for (i = 0; i < ARRAY_SIZE(fake_common_samples); i++) 136 map__put(fake_common_samples[i].map); 137 for (i = 0; i < ARRAY_SIZE(fake_samples); i++) { 138 for (j = 0; j < ARRAY_SIZE(fake_samples[0]); j++) 139 map__put(fake_samples[i][j].map); 140 } 141 } 142 143 static int find_sample(struct sample *samples, size_t nr_samples, 144 struct thread *t, struct map *m, struct symbol *s) 145 { 146 while (nr_samples--) { 147 if (samples->thread == t && 148 RC_CHK_ACCESS(samples->map) == RC_CHK_ACCESS(m) && 149 samples->sym == s) 150 return 1; 151 samples++; 152 } 153 return 0; 154 } 155 156 static int __validate_match(struct hists *hists) 157 { 158 size_t count = 0; 159 struct rb_root_cached *root; 160 struct rb_node *node; 161 162 /* 163 * Only entries from fake_common_samples should have a pair. 164 */ 165 if (hists__has(hists, need_collapse)) 166 root = &hists->entries_collapsed; 167 else 168 root = hists->entries_in; 169 170 node = rb_first_cached(root); 171 while (node) { 172 struct hist_entry *he; 173 174 he = rb_entry(node, struct hist_entry, rb_node_in); 175 176 if (hist_entry__has_pairs(he)) { 177 if (find_sample(fake_common_samples, 178 ARRAY_SIZE(fake_common_samples), 179 he->thread, he->ms.map, he->ms.sym)) { 180 count++; 181 } else { 182 pr_debug("Can't find the matched entry\n"); 183 return -1; 184 } 185 } 186 187 node = rb_next(node); 188 } 189 190 if (count != ARRAY_SIZE(fake_common_samples)) { 191 pr_debug("Invalid count for matched entries: %zd of %zd\n", 192 count, ARRAY_SIZE(fake_common_samples)); 193 return -1; 194 } 195 196 return 0; 197 } 198 199 static int validate_match(struct hists *leader, struct hists *other) 200 { 201 return __validate_match(leader) || __validate_match(other); 202 } 203 204 static int __validate_link(struct hists *hists, int idx) 205 { 206 size_t count = 0; 207 size_t count_pair = 0; 208 size_t count_dummy = 0; 209 struct rb_root_cached *root; 210 struct rb_node *node; 211 212 /* 213 * Leader hists (idx = 0) will have dummy entries from other, 214 * and some entries will have no pair. However every entry 215 * in other hists should have (dummy) pair. 216 */ 217 if (hists__has(hists, need_collapse)) 218 root = &hists->entries_collapsed; 219 else 220 root = hists->entries_in; 221 222 node = rb_first_cached(root); 223 while (node) { 224 struct hist_entry *he; 225 226 he = rb_entry(node, struct hist_entry, rb_node_in); 227 228 if (hist_entry__has_pairs(he)) { 229 if (!find_sample(fake_common_samples, 230 ARRAY_SIZE(fake_common_samples), 231 he->thread, he->ms.map, he->ms.sym) && 232 !find_sample(fake_samples[idx], 233 ARRAY_SIZE(fake_samples[idx]), 234 he->thread, he->ms.map, he->ms.sym)) { 235 count_dummy++; 236 } 237 count_pair++; 238 } else if (idx) { 239 pr_debug("A entry from the other hists should have pair\n"); 240 return -1; 241 } 242 243 count++; 244 node = rb_next(node); 245 } 246 247 /* 248 * Note that we have a entry collapsed in the other (idx = 1) hists. 249 */ 250 if (idx == 0) { 251 if (count_dummy != ARRAY_SIZE(fake_samples[1]) - 1) { 252 pr_debug("Invalid count of dummy entries: %zd of %zd\n", 253 count_dummy, ARRAY_SIZE(fake_samples[1]) - 1); 254 return -1; 255 } 256 if (count != count_pair + ARRAY_SIZE(fake_samples[0])) { 257 pr_debug("Invalid count of total leader entries: %zd of %zd\n", 258 count, count_pair + ARRAY_SIZE(fake_samples[0])); 259 return -1; 260 } 261 } else { 262 if (count != count_pair) { 263 pr_debug("Invalid count of total other entries: %zd of %zd\n", 264 count, count_pair); 265 return -1; 266 } 267 if (count_dummy > 0) { 268 pr_debug("Other hists should not have dummy entries: %zd\n", 269 count_dummy); 270 return -1; 271 } 272 } 273 274 return 0; 275 } 276 277 static int validate_link(struct hists *leader, struct hists *other) 278 { 279 return __validate_link(leader, 0) || __validate_link(other, 1); 280 } 281 282 static int test__hists_link(struct test_suite *test __maybe_unused, int subtest __maybe_unused) 283 { 284 int err = -1; 285 struct hists *hists, *first_hists; 286 struct machines machines; 287 struct machine *machine = NULL; 288 struct evsel *evsel, *first; 289 struct evlist *evlist = evlist__new(); 290 291 if (evlist == NULL) 292 return -ENOMEM; 293 294 err = parse_event(evlist, "cpu-clock"); 295 if (err) 296 goto out; 297 err = parse_event(evlist, "task-clock"); 298 if (err) 299 goto out; 300 301 err = TEST_FAIL; 302 /* default sort order (comm,dso,sym) will be used */ 303 if (setup_sorting(NULL) < 0) 304 goto out; 305 306 machines__init(&machines); 307 308 /* setup threads/dso/map/symbols also */ 309 machine = setup_fake_machine(&machines); 310 if (!machine) 311 goto out; 312 313 if (verbose > 1) 314 machine__fprintf(machine, stderr); 315 316 /* process sample events */ 317 err = add_hist_entries(evlist, machine); 318 if (err < 0) 319 goto out; 320 321 evlist__for_each_entry(evlist, evsel) { 322 hists = evsel__hists(evsel); 323 hists__collapse_resort(hists, NULL); 324 325 if (verbose > 2) 326 print_hists_in(hists); 327 } 328 329 first = evlist__first(evlist); 330 evsel = evlist__last(evlist); 331 332 first_hists = evsel__hists(first); 333 hists = evsel__hists(evsel); 334 335 /* match common entries */ 336 hists__match(first_hists, hists); 337 err = validate_match(first_hists, hists); 338 if (err) 339 goto out; 340 341 /* link common and/or dummy entries */ 342 hists__link(first_hists, hists); 343 err = validate_link(first_hists, hists); 344 if (err) 345 goto out; 346 347 err = 0; 348 349 out: 350 /* tear down everything */ 351 evlist__delete(evlist); 352 reset_output_field(); 353 machines__exit(&machines); 354 put_fake_samples(); 355 356 return err; 357 } 358 359 DEFINE_SUITE("Match and link multiple hists", hists_link); 360