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 cpu; 15 u32 pid; 16 u64 ip; 17 struct thread *thread; 18 struct map *map; 19 struct symbol *sym; 20 }; 21 22 /* For the numbers, see hists_common.c */ 23 static struct sample fake_samples[] = { 24 /* perf [kernel] schedule() */ 25 { .cpu = 0, .pid = FAKE_PID_PERF1, .ip = FAKE_IP_KERNEL_SCHEDULE, }, 26 /* perf [perf] main() */ 27 { .cpu = 1, .pid = FAKE_PID_PERF1, .ip = FAKE_IP_PERF_MAIN, }, 28 /* perf [perf] cmd_record() */ 29 { .cpu = 1, .pid = FAKE_PID_PERF1, .ip = FAKE_IP_PERF_CMD_RECORD, }, 30 /* perf [libc] malloc() */ 31 { .cpu = 1, .pid = FAKE_PID_PERF1, .ip = FAKE_IP_LIBC_MALLOC, }, 32 /* perf [libc] free() */ 33 { .cpu = 2, .pid = FAKE_PID_PERF1, .ip = FAKE_IP_LIBC_FREE, }, 34 /* perf [perf] main() */ 35 { .cpu = 2, .pid = FAKE_PID_PERF2, .ip = FAKE_IP_PERF_MAIN, }, 36 /* perf [kernel] page_fault() */ 37 { .cpu = 2, .pid = FAKE_PID_PERF2, .ip = FAKE_IP_KERNEL_PAGE_FAULT, }, 38 /* bash [bash] main() */ 39 { .cpu = 3, .pid = FAKE_PID_BASH, .ip = FAKE_IP_BASH_MAIN, }, 40 /* bash [bash] xmalloc() */ 41 { .cpu = 0, .pid = FAKE_PID_BASH, .ip = FAKE_IP_BASH_XMALLOC, }, 42 /* bash [kernel] page_fault() */ 43 { .cpu = 1, .pid = FAKE_PID_BASH, .ip = FAKE_IP_KERNEL_PAGE_FAULT, }, 44 }; 45 46 static int add_hist_entries(struct hists *hists, struct machine *machine) 47 { 48 struct addr_location al; 49 struct perf_evsel *evsel = hists_to_evsel(hists); 50 struct perf_sample sample = { .period = 100, }; 51 size_t i; 52 53 for (i = 0; i < ARRAY_SIZE(fake_samples); i++) { 54 const union perf_event event = { 55 .header = { 56 .misc = PERF_RECORD_MISC_USER, 57 }, 58 }; 59 struct hist_entry_iter iter = { 60 .ops = &hist_iter_normal, 61 .hide_unresolved = false, 62 }; 63 64 sample.cpu = fake_samples[i].cpu; 65 sample.pid = fake_samples[i].pid; 66 sample.tid = fake_samples[i].pid; 67 sample.ip = fake_samples[i].ip; 68 69 if (perf_event__preprocess_sample(&event, machine, &al, 70 &sample) < 0) 71 goto out; 72 73 if (hist_entry_iter__add(&iter, &al, evsel, &sample, 74 PERF_MAX_STACK_DEPTH, NULL) < 0) 75 goto out; 76 77 fake_samples[i].thread = al.thread; 78 fake_samples[i].map = al.map; 79 fake_samples[i].sym = al.sym; 80 } 81 82 return TEST_OK; 83 84 out: 85 pr_debug("Not enough memory for adding a hist entry\n"); 86 return TEST_FAIL; 87 } 88 89 static void del_hist_entries(struct hists *hists) 90 { 91 struct hist_entry *he; 92 struct rb_root *root_in; 93 struct rb_root *root_out; 94 struct rb_node *node; 95 96 if (sort__need_collapse) 97 root_in = &hists->entries_collapsed; 98 else 99 root_in = hists->entries_in; 100 101 root_out = &hists->entries; 102 103 while (!RB_EMPTY_ROOT(root_out)) { 104 node = rb_first(root_out); 105 106 he = rb_entry(node, struct hist_entry, rb_node); 107 rb_erase(node, root_out); 108 rb_erase(&he->rb_node_in, root_in); 109 hist_entry__delete(he); 110 } 111 } 112 113 typedef int (*test_fn_t)(struct perf_evsel *, struct machine *); 114 115 #define COMM(he) (thread__comm_str(he->thread)) 116 #define DSO(he) (he->ms.map->dso->short_name) 117 #define SYM(he) (he->ms.sym->name) 118 #define CPU(he) (he->cpu) 119 #define PID(he) (he->thread->tid) 120 121 /* default sort keys (no field) */ 122 static int test1(struct perf_evsel *evsel, struct machine *machine) 123 { 124 int err; 125 struct hists *hists = evsel__hists(evsel); 126 struct hist_entry *he; 127 struct rb_root *root; 128 struct rb_node *node; 129 130 field_order = NULL; 131 sort_order = NULL; /* equivalent to sort_order = "comm,dso,sym" */ 132 133 setup_sorting(); 134 135 /* 136 * expected output: 137 * 138 * Overhead Command Shared Object Symbol 139 * ======== ======= ============= ============== 140 * 20.00% perf perf [.] main 141 * 10.00% bash [kernel] [k] page_fault 142 * 10.00% bash bash [.] main 143 * 10.00% bash bash [.] xmalloc 144 * 10.00% perf [kernel] [k] page_fault 145 * 10.00% perf [kernel] [k] schedule 146 * 10.00% perf libc [.] free 147 * 10.00% perf libc [.] malloc 148 * 10.00% perf perf [.] cmd_record 149 */ 150 err = add_hist_entries(hists, machine); 151 if (err < 0) 152 goto out; 153 154 hists__collapse_resort(hists, NULL); 155 hists__output_resort(hists, NULL); 156 157 if (verbose > 2) { 158 pr_info("[fields = %s, sort = %s]\n", field_order, sort_order); 159 print_hists_out(hists); 160 } 161 162 root = &hists->entries; 163 node = rb_first(root); 164 he = rb_entry(node, struct hist_entry, rb_node); 165 TEST_ASSERT_VAL("Invalid hist entry", 166 !strcmp(COMM(he), "perf") && !strcmp(DSO(he), "perf") && 167 !strcmp(SYM(he), "main") && he->stat.period == 200); 168 169 node = rb_next(node); 170 he = rb_entry(node, struct hist_entry, rb_node); 171 TEST_ASSERT_VAL("Invalid hist entry", 172 !strcmp(COMM(he), "bash") && !strcmp(DSO(he), "[kernel]") && 173 !strcmp(SYM(he), "page_fault") && he->stat.period == 100); 174 175 node = rb_next(node); 176 he = rb_entry(node, struct hist_entry, rb_node); 177 TEST_ASSERT_VAL("Invalid hist entry", 178 !strcmp(COMM(he), "bash") && !strcmp(DSO(he), "bash") && 179 !strcmp(SYM(he), "main") && he->stat.period == 100); 180 181 node = rb_next(node); 182 he = rb_entry(node, struct hist_entry, rb_node); 183 TEST_ASSERT_VAL("Invalid hist entry", 184 !strcmp(COMM(he), "bash") && !strcmp(DSO(he), "bash") && 185 !strcmp(SYM(he), "xmalloc") && he->stat.period == 100); 186 187 node = rb_next(node); 188 he = rb_entry(node, struct hist_entry, rb_node); 189 TEST_ASSERT_VAL("Invalid hist entry", 190 !strcmp(COMM(he), "perf") && !strcmp(DSO(he), "[kernel]") && 191 !strcmp(SYM(he), "page_fault") && he->stat.period == 100); 192 193 node = rb_next(node); 194 he = rb_entry(node, struct hist_entry, rb_node); 195 TEST_ASSERT_VAL("Invalid hist entry", 196 !strcmp(COMM(he), "perf") && !strcmp(DSO(he), "[kernel]") && 197 !strcmp(SYM(he), "schedule") && he->stat.period == 100); 198 199 node = rb_next(node); 200 he = rb_entry(node, struct hist_entry, rb_node); 201 TEST_ASSERT_VAL("Invalid hist entry", 202 !strcmp(COMM(he), "perf") && !strcmp(DSO(he), "libc") && 203 !strcmp(SYM(he), "free") && he->stat.period == 100); 204 205 node = rb_next(node); 206 he = rb_entry(node, struct hist_entry, rb_node); 207 TEST_ASSERT_VAL("Invalid hist entry", 208 !strcmp(COMM(he), "perf") && !strcmp(DSO(he), "libc") && 209 !strcmp(SYM(he), "malloc") && he->stat.period == 100); 210 211 node = rb_next(node); 212 he = rb_entry(node, struct hist_entry, rb_node); 213 TEST_ASSERT_VAL("Invalid hist entry", 214 !strcmp(COMM(he), "perf") && !strcmp(DSO(he), "perf") && 215 !strcmp(SYM(he), "cmd_record") && he->stat.period == 100); 216 217 out: 218 del_hist_entries(hists); 219 reset_output_field(); 220 return err; 221 } 222 223 /* mixed fields and sort keys */ 224 static int test2(struct perf_evsel *evsel, struct machine *machine) 225 { 226 int err; 227 struct hists *hists = evsel__hists(evsel); 228 struct hist_entry *he; 229 struct rb_root *root; 230 struct rb_node *node; 231 232 field_order = "overhead,cpu"; 233 sort_order = "pid"; 234 235 setup_sorting(); 236 237 /* 238 * expected output: 239 * 240 * Overhead CPU Command: Pid 241 * ======== === ============= 242 * 30.00% 1 perf : 100 243 * 10.00% 0 perf : 100 244 * 10.00% 2 perf : 100 245 * 20.00% 2 perf : 200 246 * 10.00% 0 bash : 300 247 * 10.00% 1 bash : 300 248 * 10.00% 3 bash : 300 249 */ 250 err = add_hist_entries(hists, machine); 251 if (err < 0) 252 goto out; 253 254 hists__collapse_resort(hists, NULL); 255 hists__output_resort(hists, NULL); 256 257 if (verbose > 2) { 258 pr_info("[fields = %s, sort = %s]\n", field_order, sort_order); 259 print_hists_out(hists); 260 } 261 262 root = &hists->entries; 263 node = rb_first(root); 264 he = rb_entry(node, struct hist_entry, rb_node); 265 TEST_ASSERT_VAL("Invalid hist entry", 266 CPU(he) == 1 && PID(he) == 100 && he->stat.period == 300); 267 268 node = rb_next(node); 269 he = rb_entry(node, struct hist_entry, rb_node); 270 TEST_ASSERT_VAL("Invalid hist entry", 271 CPU(he) == 0 && PID(he) == 100 && he->stat.period == 100); 272 273 out: 274 del_hist_entries(hists); 275 reset_output_field(); 276 return err; 277 } 278 279 /* fields only (no sort key) */ 280 static int test3(struct perf_evsel *evsel, struct machine *machine) 281 { 282 int err; 283 struct hists *hists = evsel__hists(evsel); 284 struct hist_entry *he; 285 struct rb_root *root; 286 struct rb_node *node; 287 288 field_order = "comm,overhead,dso"; 289 sort_order = NULL; 290 291 setup_sorting(); 292 293 /* 294 * expected output: 295 * 296 * Command Overhead Shared Object 297 * ======= ======== ============= 298 * bash 20.00% bash 299 * bash 10.00% [kernel] 300 * perf 30.00% perf 301 * perf 20.00% [kernel] 302 * perf 20.00% libc 303 */ 304 err = add_hist_entries(hists, machine); 305 if (err < 0) 306 goto out; 307 308 hists__collapse_resort(hists, NULL); 309 hists__output_resort(hists, NULL); 310 311 if (verbose > 2) { 312 pr_info("[fields = %s, sort = %s]\n", field_order, sort_order); 313 print_hists_out(hists); 314 } 315 316 root = &hists->entries; 317 node = rb_first(root); 318 he = rb_entry(node, struct hist_entry, rb_node); 319 TEST_ASSERT_VAL("Invalid hist entry", 320 !strcmp(COMM(he), "bash") && !strcmp(DSO(he), "bash") && 321 he->stat.period == 200); 322 323 node = rb_next(node); 324 he = rb_entry(node, struct hist_entry, rb_node); 325 TEST_ASSERT_VAL("Invalid hist entry", 326 !strcmp(COMM(he), "bash") && !strcmp(DSO(he), "[kernel]") && 327 he->stat.period == 100); 328 329 node = rb_next(node); 330 he = rb_entry(node, struct hist_entry, rb_node); 331 TEST_ASSERT_VAL("Invalid hist entry", 332 !strcmp(COMM(he), "perf") && !strcmp(DSO(he), "perf") && 333 he->stat.period == 300); 334 335 node = rb_next(node); 336 he = rb_entry(node, struct hist_entry, rb_node); 337 TEST_ASSERT_VAL("Invalid hist entry", 338 !strcmp(COMM(he), "perf") && !strcmp(DSO(he), "[kernel]") && 339 he->stat.period == 200); 340 341 node = rb_next(node); 342 he = rb_entry(node, struct hist_entry, rb_node); 343 TEST_ASSERT_VAL("Invalid hist entry", 344 !strcmp(COMM(he), "perf") && !strcmp(DSO(he), "libc") && 345 he->stat.period == 200); 346 347 out: 348 del_hist_entries(hists); 349 reset_output_field(); 350 return err; 351 } 352 353 /* handle duplicate 'dso' field */ 354 static int test4(struct perf_evsel *evsel, struct machine *machine) 355 { 356 int err; 357 struct hists *hists = evsel__hists(evsel); 358 struct hist_entry *he; 359 struct rb_root *root; 360 struct rb_node *node; 361 362 field_order = "dso,sym,comm,overhead,dso"; 363 sort_order = "sym"; 364 365 setup_sorting(); 366 367 /* 368 * expected output: 369 * 370 * Shared Object Symbol Command Overhead 371 * ============= ============== ======= ======== 372 * perf [.] cmd_record perf 10.00% 373 * libc [.] free perf 10.00% 374 * bash [.] main bash 10.00% 375 * perf [.] main perf 20.00% 376 * libc [.] malloc perf 10.00% 377 * [kernel] [k] page_fault bash 10.00% 378 * [kernel] [k] page_fault perf 10.00% 379 * [kernel] [k] schedule perf 10.00% 380 * bash [.] xmalloc bash 10.00% 381 */ 382 err = add_hist_entries(hists, machine); 383 if (err < 0) 384 goto out; 385 386 hists__collapse_resort(hists, NULL); 387 hists__output_resort(hists, NULL); 388 389 if (verbose > 2) { 390 pr_info("[fields = %s, sort = %s]\n", field_order, sort_order); 391 print_hists_out(hists); 392 } 393 394 root = &hists->entries; 395 node = rb_first(root); 396 he = rb_entry(node, struct hist_entry, rb_node); 397 TEST_ASSERT_VAL("Invalid hist entry", 398 !strcmp(DSO(he), "perf") && !strcmp(SYM(he), "cmd_record") && 399 !strcmp(COMM(he), "perf") && he->stat.period == 100); 400 401 node = rb_next(node); 402 he = rb_entry(node, struct hist_entry, rb_node); 403 TEST_ASSERT_VAL("Invalid hist entry", 404 !strcmp(DSO(he), "libc") && !strcmp(SYM(he), "free") && 405 !strcmp(COMM(he), "perf") && he->stat.period == 100); 406 407 node = rb_next(node); 408 he = rb_entry(node, struct hist_entry, rb_node); 409 TEST_ASSERT_VAL("Invalid hist entry", 410 !strcmp(DSO(he), "bash") && !strcmp(SYM(he), "main") && 411 !strcmp(COMM(he), "bash") && he->stat.period == 100); 412 413 node = rb_next(node); 414 he = rb_entry(node, struct hist_entry, rb_node); 415 TEST_ASSERT_VAL("Invalid hist entry", 416 !strcmp(DSO(he), "perf") && !strcmp(SYM(he), "main") && 417 !strcmp(COMM(he), "perf") && he->stat.period == 200); 418 419 node = rb_next(node); 420 he = rb_entry(node, struct hist_entry, rb_node); 421 TEST_ASSERT_VAL("Invalid hist entry", 422 !strcmp(DSO(he), "libc") && !strcmp(SYM(he), "malloc") && 423 !strcmp(COMM(he), "perf") && he->stat.period == 100); 424 425 node = rb_next(node); 426 he = rb_entry(node, struct hist_entry, rb_node); 427 TEST_ASSERT_VAL("Invalid hist entry", 428 !strcmp(DSO(he), "[kernel]") && !strcmp(SYM(he), "page_fault") && 429 !strcmp(COMM(he), "bash") && he->stat.period == 100); 430 431 node = rb_next(node); 432 he = rb_entry(node, struct hist_entry, rb_node); 433 TEST_ASSERT_VAL("Invalid hist entry", 434 !strcmp(DSO(he), "[kernel]") && !strcmp(SYM(he), "page_fault") && 435 !strcmp(COMM(he), "perf") && he->stat.period == 100); 436 437 node = rb_next(node); 438 he = rb_entry(node, struct hist_entry, rb_node); 439 TEST_ASSERT_VAL("Invalid hist entry", 440 !strcmp(DSO(he), "[kernel]") && !strcmp(SYM(he), "schedule") && 441 !strcmp(COMM(he), "perf") && he->stat.period == 100); 442 443 node = rb_next(node); 444 he = rb_entry(node, struct hist_entry, rb_node); 445 TEST_ASSERT_VAL("Invalid hist entry", 446 !strcmp(DSO(he), "bash") && !strcmp(SYM(he), "xmalloc") && 447 !strcmp(COMM(he), "bash") && he->stat.period == 100); 448 449 out: 450 del_hist_entries(hists); 451 reset_output_field(); 452 return err; 453 } 454 455 /* full sort keys w/o overhead field */ 456 static int test5(struct perf_evsel *evsel, struct machine *machine) 457 { 458 int err; 459 struct hists *hists = evsel__hists(evsel); 460 struct hist_entry *he; 461 struct rb_root *root; 462 struct rb_node *node; 463 464 field_order = "cpu,pid,comm,dso,sym"; 465 sort_order = "dso,pid"; 466 467 setup_sorting(); 468 469 /* 470 * expected output: 471 * 472 * CPU Command: Pid Command Shared Object Symbol 473 * === ============= ======= ============= ============== 474 * 0 perf: 100 perf [kernel] [k] schedule 475 * 2 perf: 200 perf [kernel] [k] page_fault 476 * 1 bash: 300 bash [kernel] [k] page_fault 477 * 0 bash: 300 bash bash [.] xmalloc 478 * 3 bash: 300 bash bash [.] main 479 * 1 perf: 100 perf libc [.] malloc 480 * 2 perf: 100 perf libc [.] free 481 * 1 perf: 100 perf perf [.] cmd_record 482 * 1 perf: 100 perf perf [.] main 483 * 2 perf: 200 perf perf [.] main 484 */ 485 err = add_hist_entries(hists, machine); 486 if (err < 0) 487 goto out; 488 489 hists__collapse_resort(hists, NULL); 490 hists__output_resort(hists, NULL); 491 492 if (verbose > 2) { 493 pr_info("[fields = %s, sort = %s]\n", field_order, sort_order); 494 print_hists_out(hists); 495 } 496 497 root = &hists->entries; 498 node = rb_first(root); 499 he = rb_entry(node, struct hist_entry, rb_node); 500 501 TEST_ASSERT_VAL("Invalid hist entry", 502 CPU(he) == 0 && PID(he) == 100 && 503 !strcmp(COMM(he), "perf") && !strcmp(DSO(he), "[kernel]") && 504 !strcmp(SYM(he), "schedule") && he->stat.period == 100); 505 506 node = rb_next(node); 507 he = rb_entry(node, struct hist_entry, rb_node); 508 TEST_ASSERT_VAL("Invalid hist entry", 509 CPU(he) == 2 && PID(he) == 200 && 510 !strcmp(COMM(he), "perf") && !strcmp(DSO(he), "[kernel]") && 511 !strcmp(SYM(he), "page_fault") && he->stat.period == 100); 512 513 node = rb_next(node); 514 he = rb_entry(node, struct hist_entry, rb_node); 515 TEST_ASSERT_VAL("Invalid hist entry", 516 CPU(he) == 1 && PID(he) == 300 && 517 !strcmp(COMM(he), "bash") && !strcmp(DSO(he), "[kernel]") && 518 !strcmp(SYM(he), "page_fault") && he->stat.period == 100); 519 520 node = rb_next(node); 521 he = rb_entry(node, struct hist_entry, rb_node); 522 TEST_ASSERT_VAL("Invalid hist entry", 523 CPU(he) == 0 && PID(he) == 300 && 524 !strcmp(COMM(he), "bash") && !strcmp(DSO(he), "bash") && 525 !strcmp(SYM(he), "xmalloc") && he->stat.period == 100); 526 527 node = rb_next(node); 528 he = rb_entry(node, struct hist_entry, rb_node); 529 TEST_ASSERT_VAL("Invalid hist entry", 530 CPU(he) == 3 && PID(he) == 300 && 531 !strcmp(COMM(he), "bash") && !strcmp(DSO(he), "bash") && 532 !strcmp(SYM(he), "main") && he->stat.period == 100); 533 534 node = rb_next(node); 535 he = rb_entry(node, struct hist_entry, rb_node); 536 TEST_ASSERT_VAL("Invalid hist entry", 537 CPU(he) == 1 && PID(he) == 100 && 538 !strcmp(COMM(he), "perf") && !strcmp(DSO(he), "libc") && 539 !strcmp(SYM(he), "malloc") && he->stat.period == 100); 540 541 node = rb_next(node); 542 he = rb_entry(node, struct hist_entry, rb_node); 543 TEST_ASSERT_VAL("Invalid hist entry", 544 CPU(he) == 2 && PID(he) == 100 && 545 !strcmp(COMM(he), "perf") && !strcmp(DSO(he), "libc") && 546 !strcmp(SYM(he), "free") && he->stat.period == 100); 547 548 node = rb_next(node); 549 he = rb_entry(node, struct hist_entry, rb_node); 550 TEST_ASSERT_VAL("Invalid hist entry", 551 CPU(he) == 1 && PID(he) == 100 && 552 !strcmp(COMM(he), "perf") && !strcmp(DSO(he), "perf") && 553 !strcmp(SYM(he), "cmd_record") && he->stat.period == 100); 554 555 node = rb_next(node); 556 he = rb_entry(node, struct hist_entry, rb_node); 557 TEST_ASSERT_VAL("Invalid hist entry", 558 CPU(he) == 1 && PID(he) == 100 && 559 !strcmp(COMM(he), "perf") && !strcmp(DSO(he), "perf") && 560 !strcmp(SYM(he), "main") && he->stat.period == 100); 561 562 node = rb_next(node); 563 he = rb_entry(node, struct hist_entry, rb_node); 564 TEST_ASSERT_VAL("Invalid hist entry", 565 CPU(he) == 2 && PID(he) == 200 && 566 !strcmp(COMM(he), "perf") && !strcmp(DSO(he), "perf") && 567 !strcmp(SYM(he), "main") && he->stat.period == 100); 568 569 out: 570 del_hist_entries(hists); 571 reset_output_field(); 572 return err; 573 } 574 575 int test__hists_output(void) 576 { 577 int err = TEST_FAIL; 578 struct machines machines; 579 struct machine *machine; 580 struct perf_evsel *evsel; 581 struct perf_evlist *evlist = perf_evlist__new(); 582 size_t i; 583 test_fn_t testcases[] = { 584 test1, 585 test2, 586 test3, 587 test4, 588 test5, 589 }; 590 591 TEST_ASSERT_VAL("No memory", evlist); 592 593 err = parse_events(evlist, "cpu-clock", NULL); 594 if (err) 595 goto out; 596 597 machines__init(&machines); 598 599 /* setup threads/dso/map/symbols also */ 600 machine = setup_fake_machine(&machines); 601 if (!machine) 602 goto out; 603 604 if (verbose > 1) 605 machine__fprintf(machine, stderr); 606 607 evsel = perf_evlist__first(evlist); 608 609 for (i = 0; i < ARRAY_SIZE(testcases); i++) { 610 err = testcases[i](evsel, machine); 611 if (err < 0) 612 break; 613 } 614 615 out: 616 /* tear down everything */ 617 perf_evlist__delete(evlist); 618 machines__exit(&machines); 619 620 return err; 621 } 622