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