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