755 lines · c
1// SPDX-License-Identifier: GPL-2.02#include "util/debug.h"3#include "util/dso.h"4#include "util/event.h"5#include "util/map.h"6#include "util/symbol.h"7#include "util/sort.h"8#include "util/evsel.h"9#include "util/evlist.h"10#include "util/machine.h"11#include "util/parse-events.h"12#include "util/thread.h"13#include "tests/tests.h"14#include "tests/hists_common.h"15#include <linux/kernel.h>16 17struct sample {18 u32 pid;19 u64 ip;20 struct thread *thread;21 struct map *map;22 struct symbol *sym;23};24 25/* For the numbers, see hists_common.c */26static struct sample fake_samples[] = {27 /* perf [kernel] schedule() */28 { .pid = FAKE_PID_PERF1, .ip = FAKE_IP_KERNEL_SCHEDULE, },29 /* perf [perf] main() */30 { .pid = FAKE_PID_PERF1, .ip = FAKE_IP_PERF_MAIN, },31 /* perf [perf] cmd_record() */32 { .pid = FAKE_PID_PERF1, .ip = FAKE_IP_PERF_CMD_RECORD, },33 /* perf [libc] malloc() */34 { .pid = FAKE_PID_PERF1, .ip = FAKE_IP_LIBC_MALLOC, },35 /* perf [libc] free() */36 { .pid = FAKE_PID_PERF1, .ip = FAKE_IP_LIBC_FREE, },37 /* perf [perf] main() */38 { .pid = FAKE_PID_PERF2, .ip = FAKE_IP_PERF_MAIN, },39 /* perf [kernel] page_fault() */40 { .pid = FAKE_PID_PERF2, .ip = FAKE_IP_KERNEL_PAGE_FAULT, },41 /* bash [bash] main() */42 { .pid = FAKE_PID_BASH, .ip = FAKE_IP_BASH_MAIN, },43 /* bash [bash] xmalloc() */44 { .pid = FAKE_PID_BASH, .ip = FAKE_IP_BASH_XMALLOC, },45 /* bash [kernel] page_fault() */46 { .pid = FAKE_PID_BASH, .ip = FAKE_IP_KERNEL_PAGE_FAULT, },47};48 49/*50 * Will be cast to struct ip_callchain which has all 64 bit entries51 * of nr and ips[].52 */53static u64 fake_callchains[][10] = {54 /* schedule => run_command => main */55 { 3, FAKE_IP_KERNEL_SCHEDULE, FAKE_IP_PERF_RUN_COMMAND, FAKE_IP_PERF_MAIN, },56 /* main */57 { 1, FAKE_IP_PERF_MAIN, },58 /* cmd_record => run_command => main */59 { 3, FAKE_IP_PERF_CMD_RECORD, FAKE_IP_PERF_RUN_COMMAND, FAKE_IP_PERF_MAIN, },60 /* malloc => cmd_record => run_command => main */61 { 4, FAKE_IP_LIBC_MALLOC, FAKE_IP_PERF_CMD_RECORD, FAKE_IP_PERF_RUN_COMMAND,62 FAKE_IP_PERF_MAIN, },63 /* free => cmd_record => run_command => main */64 { 4, FAKE_IP_LIBC_FREE, FAKE_IP_PERF_CMD_RECORD, FAKE_IP_PERF_RUN_COMMAND,65 FAKE_IP_PERF_MAIN, },66 /* main */67 { 1, FAKE_IP_PERF_MAIN, },68 /* page_fault => sys_perf_event_open => run_command => main */69 { 4, FAKE_IP_KERNEL_PAGE_FAULT, FAKE_IP_KERNEL_SYS_PERF_EVENT_OPEN,70 FAKE_IP_PERF_RUN_COMMAND, FAKE_IP_PERF_MAIN, },71 /* main */72 { 1, FAKE_IP_BASH_MAIN, },73 /* xmalloc => malloc => xmalloc => malloc => xmalloc => main */74 { 6, FAKE_IP_BASH_XMALLOC, FAKE_IP_LIBC_MALLOC, FAKE_IP_BASH_XMALLOC,75 FAKE_IP_LIBC_MALLOC, FAKE_IP_BASH_XMALLOC, FAKE_IP_BASH_MAIN, },76 /* page_fault => malloc => main */77 { 3, FAKE_IP_KERNEL_PAGE_FAULT, FAKE_IP_LIBC_MALLOC, FAKE_IP_BASH_MAIN, },78};79 80static int add_hist_entries(struct hists *hists, struct machine *machine)81{82 struct addr_location al;83 struct evsel *evsel = hists_to_evsel(hists);84 struct perf_sample sample = { .period = 1000, };85 size_t i;86 87 addr_location__init(&al);88 for (i = 0; i < ARRAY_SIZE(fake_samples); i++) {89 struct hist_entry_iter iter = {90 .evsel = evsel,91 .sample = &sample,92 .hide_unresolved = false,93 };94 95 if (symbol_conf.cumulate_callchain)96 iter.ops = &hist_iter_cumulative;97 else98 iter.ops = &hist_iter_normal;99 100 sample.cpumode = PERF_RECORD_MISC_USER;101 sample.pid = fake_samples[i].pid;102 sample.tid = fake_samples[i].pid;103 sample.ip = fake_samples[i].ip;104 sample.callchain = (struct ip_callchain *)fake_callchains[i];105 106 if (machine__resolve(machine, &al, &sample) < 0)107 goto out;108 109 if (hist_entry_iter__add(&iter, &al, sysctl_perf_event_max_stack,110 NULL) < 0) {111 goto out;112 }113 114 thread__put(fake_samples[i].thread);115 fake_samples[i].thread = thread__get(al.thread);116 map__put(fake_samples[i].map);117 fake_samples[i].map = map__get(al.map);118 fake_samples[i].sym = al.sym;119 }120 121 addr_location__exit(&al);122 return TEST_OK;123 124out:125 pr_debug("Not enough memory for adding a hist entry\n");126 addr_location__exit(&al);127 return TEST_FAIL;128}129 130static void del_hist_entries(struct hists *hists)131{132 struct hist_entry *he;133 struct rb_root_cached *root_in;134 struct rb_root_cached *root_out;135 struct rb_node *node;136 137 if (hists__has(hists, need_collapse))138 root_in = &hists->entries_collapsed;139 else140 root_in = hists->entries_in;141 142 root_out = &hists->entries;143 144 while (!RB_EMPTY_ROOT(&root_out->rb_root)) {145 node = rb_first_cached(root_out);146 147 he = rb_entry(node, struct hist_entry, rb_node);148 rb_erase_cached(node, root_out);149 rb_erase_cached(&he->rb_node_in, root_in);150 hist_entry__delete(he);151 }152}153 154static void put_fake_samples(void)155{156 size_t i;157 158 for (i = 0; i < ARRAY_SIZE(fake_samples); i++) {159 map__zput(fake_samples[i].map);160 thread__zput(fake_samples[i].thread);161 }162}163 164typedef int (*test_fn_t)(struct evsel *, struct machine *);165 166#define COMM(he) (thread__comm_str(he->thread))167#define DSO(he) (dso__short_name(map__dso(he->ms.map)))168#define SYM(he) (he->ms.sym->name)169#define CPU(he) (he->cpu)170#define DEPTH(he) (he->callchain->max_depth)171#define CDSO(cl) (dso__short_name(map__dso(cl->ms.map)))172#define CSYM(cl) (cl->ms.sym->name)173 174struct result {175 u64 children;176 u64 self;177 const char *comm;178 const char *dso;179 const char *sym;180};181 182struct callchain_result {183 u64 nr;184 struct {185 const char *dso;186 const char *sym;187 } node[10];188};189 190static int do_test(struct hists *hists, struct result *expected, size_t nr_expected,191 struct callchain_result *expected_callchain, size_t nr_callchain)192{193 char buf[32];194 size_t i, c;195 struct hist_entry *he;196 struct rb_root *root;197 struct rb_node *node;198 struct callchain_node *cnode;199 struct callchain_list *clist;200 201 /*202 * adding and deleting hist entries must be done outside of this203 * function since TEST_ASSERT_VAL() returns in case of failure.204 */205 hists__collapse_resort(hists, NULL);206 evsel__output_resort(hists_to_evsel(hists), NULL);207 208 if (verbose > 2) {209 pr_info("use callchain: %d, cumulate callchain: %d\n",210 symbol_conf.use_callchain,211 symbol_conf.cumulate_callchain);212 print_hists_out(hists);213 }214 215 root = &hists->entries.rb_root;216 for (node = rb_first(root), i = 0;217 node && (he = rb_entry(node, struct hist_entry, rb_node));218 node = rb_next(node), i++) {219 scnprintf(buf, sizeof(buf), "Invalid hist entry #%zd", i);220 221 TEST_ASSERT_VAL("Incorrect number of hist entry",222 i < nr_expected);223 TEST_ASSERT_VAL(buf, he->stat.period == expected[i].self &&224 !strcmp(COMM(he), expected[i].comm) &&225 !strcmp(DSO(he), expected[i].dso) &&226 !strcmp(SYM(he), expected[i].sym));227 228 if (symbol_conf.cumulate_callchain)229 TEST_ASSERT_VAL(buf, he->stat_acc->period == expected[i].children);230 231 if (!symbol_conf.use_callchain)232 continue;233 234 /* check callchain entries */235 root = &he->callchain->node.rb_root;236 237 TEST_ASSERT_VAL("callchains expected", !RB_EMPTY_ROOT(root));238 cnode = rb_entry(rb_first(root), struct callchain_node, rb_node);239 240 c = 0;241 list_for_each_entry(clist, &cnode->val, list) {242 scnprintf(buf, sizeof(buf), "Invalid callchain entry #%zd/%zd", i, c);243 244 TEST_ASSERT_VAL("Incorrect number of callchain entry",245 c < expected_callchain[i].nr);246 TEST_ASSERT_VAL(buf,247 !strcmp(CDSO(clist), expected_callchain[i].node[c].dso) &&248 !strcmp(CSYM(clist), expected_callchain[i].node[c].sym));249 c++;250 }251 /* TODO: handle multiple child nodes properly */252 TEST_ASSERT_VAL("Incorrect number of callchain entry",253 c <= expected_callchain[i].nr);254 }255 TEST_ASSERT_VAL("Incorrect number of hist entry",256 i == nr_expected);257 TEST_ASSERT_VAL("Incorrect number of callchain entry",258 !symbol_conf.use_callchain || nr_expected == nr_callchain);259 return 0;260}261 262/* NO callchain + NO children */263static int test1(struct evsel *evsel, struct machine *machine)264{265 int err;266 struct hists *hists = evsel__hists(evsel);267 /*268 * expected output:269 *270 * Overhead Command Shared Object Symbol271 * ======== ======= ============= ==============272 * 20.00% perf perf [.] main273 * 10.00% bash [kernel] [k] page_fault274 * 10.00% bash bash [.] main275 * 10.00% bash bash [.] xmalloc276 * 10.00% perf [kernel] [k] page_fault277 * 10.00% perf [kernel] [k] schedule278 * 10.00% perf libc [.] free279 * 10.00% perf libc [.] malloc280 * 10.00% perf perf [.] cmd_record281 */282 struct result expected[] = {283 { 0, 2000, "perf", "perf", "main" },284 { 0, 1000, "bash", "[kernel]", "page_fault" },285 { 0, 1000, "bash", "bash", "main" },286 { 0, 1000, "bash", "bash", "xmalloc" },287 { 0, 1000, "perf", "[kernel]", "page_fault" },288 { 0, 1000, "perf", "[kernel]", "schedule" },289 { 0, 1000, "perf", "libc", "free" },290 { 0, 1000, "perf", "libc", "malloc" },291 { 0, 1000, "perf", "perf", "cmd_record" },292 };293 294 symbol_conf.use_callchain = false;295 symbol_conf.cumulate_callchain = false;296 evsel__reset_sample_bit(evsel, CALLCHAIN);297 298 setup_sorting(NULL);299 callchain_register_param(&callchain_param);300 301 err = add_hist_entries(hists, machine);302 if (err < 0)303 goto out;304 305 err = do_test(hists, expected, ARRAY_SIZE(expected), NULL, 0);306 307out:308 del_hist_entries(hists);309 reset_output_field();310 return err;311}312 313/* callchain + NO children */314static int test2(struct evsel *evsel, struct machine *machine)315{316 int err;317 struct hists *hists = evsel__hists(evsel);318 /*319 * expected output:320 *321 * Overhead Command Shared Object Symbol322 * ======== ======= ============= ==============323 * 20.00% perf perf [.] main324 * |325 * --- main326 *327 * 10.00% bash [kernel] [k] page_fault328 * |329 * --- page_fault330 * malloc331 * main332 *333 * 10.00% bash bash [.] main334 * |335 * --- main336 *337 * 10.00% bash bash [.] xmalloc338 * |339 * --- xmalloc340 * malloc341 * xmalloc <--- NOTE: there's a cycle342 * malloc343 * xmalloc344 * main345 *346 * 10.00% perf [kernel] [k] page_fault347 * |348 * --- page_fault349 * sys_perf_event_open350 * run_command351 * main352 *353 * 10.00% perf [kernel] [k] schedule354 * |355 * --- schedule356 * run_command357 * main358 *359 * 10.00% perf libc [.] free360 * |361 * --- free362 * cmd_record363 * run_command364 * main365 *366 * 10.00% perf libc [.] malloc367 * |368 * --- malloc369 * cmd_record370 * run_command371 * main372 *373 * 10.00% perf perf [.] cmd_record374 * |375 * --- cmd_record376 * run_command377 * main378 *379 */380 struct result expected[] = {381 { 0, 2000, "perf", "perf", "main" },382 { 0, 1000, "bash", "[kernel]", "page_fault" },383 { 0, 1000, "bash", "bash", "main" },384 { 0, 1000, "bash", "bash", "xmalloc" },385 { 0, 1000, "perf", "[kernel]", "page_fault" },386 { 0, 1000, "perf", "[kernel]", "schedule" },387 { 0, 1000, "perf", "libc", "free" },388 { 0, 1000, "perf", "libc", "malloc" },389 { 0, 1000, "perf", "perf", "cmd_record" },390 };391 struct callchain_result expected_callchain[] = {392 {393 1, { { "perf", "main" }, },394 },395 {396 3, { { "[kernel]", "page_fault" },397 { "libc", "malloc" },398 { "bash", "main" }, },399 },400 {401 1, { { "bash", "main" }, },402 },403 {404 6, { { "bash", "xmalloc" },405 { "libc", "malloc" },406 { "bash", "xmalloc" },407 { "libc", "malloc" },408 { "bash", "xmalloc" },409 { "bash", "main" }, },410 },411 {412 4, { { "[kernel]", "page_fault" },413 { "[kernel]", "sys_perf_event_open" },414 { "perf", "run_command" },415 { "perf", "main" }, },416 },417 {418 3, { { "[kernel]", "schedule" },419 { "perf", "run_command" },420 { "perf", "main" }, },421 },422 {423 4, { { "libc", "free" },424 { "perf", "cmd_record" },425 { "perf", "run_command" },426 { "perf", "main" }, },427 },428 {429 4, { { "libc", "malloc" },430 { "perf", "cmd_record" },431 { "perf", "run_command" },432 { "perf", "main" }, },433 },434 {435 3, { { "perf", "cmd_record" },436 { "perf", "run_command" },437 { "perf", "main" }, },438 },439 };440 441 symbol_conf.use_callchain = true;442 symbol_conf.cumulate_callchain = false;443 evsel__set_sample_bit(evsel, CALLCHAIN);444 445 setup_sorting(NULL);446 callchain_register_param(&callchain_param);447 448 err = add_hist_entries(hists, machine);449 if (err < 0)450 goto out;451 452 err = do_test(hists, expected, ARRAY_SIZE(expected),453 expected_callchain, ARRAY_SIZE(expected_callchain));454 455out:456 del_hist_entries(hists);457 reset_output_field();458 return err;459}460 461/* NO callchain + children */462static int test3(struct evsel *evsel, struct machine *machine)463{464 int err;465 struct hists *hists = evsel__hists(evsel);466 /*467 * expected output:468 *469 * Children Self Command Shared Object Symbol470 * ======== ======== ======= ============= =======================471 * 70.00% 20.00% perf perf [.] main472 * 50.00% 0.00% perf perf [.] run_command473 * 30.00% 10.00% bash bash [.] main474 * 30.00% 10.00% perf perf [.] cmd_record475 * 20.00% 0.00% bash libc [.] malloc476 * 10.00% 10.00% bash [kernel] [k] page_fault477 * 10.00% 10.00% bash bash [.] xmalloc478 * 10.00% 10.00% perf [kernel] [k] page_fault479 * 10.00% 10.00% perf libc [.] malloc480 * 10.00% 10.00% perf [kernel] [k] schedule481 * 10.00% 10.00% perf libc [.] free482 * 10.00% 0.00% perf [kernel] [k] sys_perf_event_open483 */484 struct result expected[] = {485 { 7000, 2000, "perf", "perf", "main" },486 { 5000, 0, "perf", "perf", "run_command" },487 { 3000, 1000, "bash", "bash", "main" },488 { 3000, 1000, "perf", "perf", "cmd_record" },489 { 2000, 0, "bash", "libc", "malloc" },490 { 1000, 1000, "bash", "[kernel]", "page_fault" },491 { 1000, 1000, "bash", "bash", "xmalloc" },492 { 1000, 1000, "perf", "[kernel]", "page_fault" },493 { 1000, 1000, "perf", "[kernel]", "schedule" },494 { 1000, 1000, "perf", "libc", "free" },495 { 1000, 1000, "perf", "libc", "malloc" },496 { 1000, 0, "perf", "[kernel]", "sys_perf_event_open" },497 };498 499 symbol_conf.use_callchain = false;500 symbol_conf.cumulate_callchain = true;501 evsel__reset_sample_bit(evsel, CALLCHAIN);502 503 setup_sorting(NULL);504 callchain_register_param(&callchain_param);505 506 err = add_hist_entries(hists, machine);507 if (err < 0)508 goto out;509 510 err = do_test(hists, expected, ARRAY_SIZE(expected), NULL, 0);511 512out:513 del_hist_entries(hists);514 reset_output_field();515 return err;516}517 518/* callchain + children */519static int test4(struct evsel *evsel, struct machine *machine)520{521 int err;522 struct hists *hists = evsel__hists(evsel);523 /*524 * expected output:525 *526 * Children Self Command Shared Object Symbol527 * ======== ======== ======= ============= =======================528 * 70.00% 20.00% perf perf [.] main529 * |530 * --- main531 *532 * 50.00% 0.00% perf perf [.] run_command533 * |534 * --- run_command535 * main536 *537 * 30.00% 10.00% bash bash [.] main538 * |539 * --- main540 *541 * 30.00% 10.00% perf perf [.] cmd_record542 * |543 * --- cmd_record544 * run_command545 * main546 *547 * 20.00% 0.00% bash libc [.] malloc548 * |549 * --- malloc550 * |551 * |--50.00%-- xmalloc552 * | main553 * --50.00%-- main554 *555 * 10.00% 10.00% bash [kernel] [k] page_fault556 * |557 * --- page_fault558 * malloc559 * main560 *561 * 10.00% 10.00% bash bash [.] xmalloc562 * |563 * --- xmalloc564 * malloc565 * xmalloc <--- NOTE: there's a cycle566 * malloc567 * xmalloc568 * main569 *570 * 10.00% 0.00% perf [kernel] [k] sys_perf_event_open571 * |572 * --- sys_perf_event_open573 * run_command574 * main575 *576 * 10.00% 10.00% perf [kernel] [k] page_fault577 * |578 * --- page_fault579 * sys_perf_event_open580 * run_command581 * main582 *583 * 10.00% 10.00% perf [kernel] [k] schedule584 * |585 * --- schedule586 * run_command587 * main588 *589 * 10.00% 10.00% perf libc [.] free590 * |591 * --- free592 * cmd_record593 * run_command594 * main595 *596 * 10.00% 10.00% perf libc [.] malloc597 * |598 * --- malloc599 * cmd_record600 * run_command601 * main602 *603 */604 struct result expected[] = {605 { 7000, 2000, "perf", "perf", "main" },606 { 5000, 0, "perf", "perf", "run_command" },607 { 3000, 1000, "bash", "bash", "main" },608 { 3000, 1000, "perf", "perf", "cmd_record" },609 { 2000, 0, "bash", "libc", "malloc" },610 { 1000, 1000, "bash", "[kernel]", "page_fault" },611 { 1000, 1000, "bash", "bash", "xmalloc" },612 { 1000, 0, "perf", "[kernel]", "sys_perf_event_open" },613 { 1000, 1000, "perf", "[kernel]", "page_fault" },614 { 1000, 1000, "perf", "[kernel]", "schedule" },615 { 1000, 1000, "perf", "libc", "free" },616 { 1000, 1000, "perf", "libc", "malloc" },617 };618 struct callchain_result expected_callchain[] = {619 {620 1, { { "perf", "main" }, },621 },622 {623 2, { { "perf", "run_command" },624 { "perf", "main" }, },625 },626 {627 1, { { "bash", "main" }, },628 },629 {630 3, { { "perf", "cmd_record" },631 { "perf", "run_command" },632 { "perf", "main" }, },633 },634 {635 4, { { "libc", "malloc" },636 { "bash", "xmalloc" },637 { "bash", "main" },638 { "bash", "main" }, },639 },640 {641 3, { { "[kernel]", "page_fault" },642 { "libc", "malloc" },643 { "bash", "main" }, },644 },645 {646 6, { { "bash", "xmalloc" },647 { "libc", "malloc" },648 { "bash", "xmalloc" },649 { "libc", "malloc" },650 { "bash", "xmalloc" },651 { "bash", "main" }, },652 },653 {654 3, { { "[kernel]", "sys_perf_event_open" },655 { "perf", "run_command" },656 { "perf", "main" }, },657 },658 {659 4, { { "[kernel]", "page_fault" },660 { "[kernel]", "sys_perf_event_open" },661 { "perf", "run_command" },662 { "perf", "main" }, },663 },664 {665 3, { { "[kernel]", "schedule" },666 { "perf", "run_command" },667 { "perf", "main" }, },668 },669 {670 4, { { "libc", "free" },671 { "perf", "cmd_record" },672 { "perf", "run_command" },673 { "perf", "main" }, },674 },675 {676 4, { { "libc", "malloc" },677 { "perf", "cmd_record" },678 { "perf", "run_command" },679 { "perf", "main" }, },680 },681 };682 683 symbol_conf.use_callchain = true;684 symbol_conf.cumulate_callchain = true;685 evsel__set_sample_bit(evsel, CALLCHAIN);686 687 setup_sorting(NULL);688 689 callchain_param = callchain_param_default;690 callchain_register_param(&callchain_param);691 692 err = add_hist_entries(hists, machine);693 if (err < 0)694 goto out;695 696 err = do_test(hists, expected, ARRAY_SIZE(expected),697 expected_callchain, ARRAY_SIZE(expected_callchain));698 699out:700 del_hist_entries(hists);701 reset_output_field();702 return err;703}704 705static int test__hists_cumulate(struct test_suite *test __maybe_unused, int subtest __maybe_unused)706{707 int err = TEST_FAIL;708 struct machines machines;709 struct machine *machine;710 struct evsel *evsel;711 struct evlist *evlist = evlist__new();712 size_t i;713 test_fn_t testcases[] = {714 test1,715 test2,716 test3,717 test4,718 };719 720 TEST_ASSERT_VAL("No memory", evlist);721 722 err = parse_event(evlist, "cpu-clock");723 if (err)724 goto out;725 err = TEST_FAIL;726 727 machines__init(&machines);728 729 /* setup threads/dso/map/symbols also */730 machine = setup_fake_machine(&machines);731 if (!machine)732 goto out;733 734 if (verbose > 1)735 machine__fprintf(machine, stderr);736 737 evsel = evlist__first(evlist);738 739 for (i = 0; i < ARRAY_SIZE(testcases); i++) {740 err = testcases[i](evsel, machine);741 if (err < 0)742 break;743 }744 745out:746 /* tear down everything */747 evlist__delete(evlist);748 machines__exit(&machines);749 put_fake_samples();750 751 return err;752}753 754DEFINE_SUITE("Cumulate child hist entries", hists_cumulate);755