0001
0002 #include "util/debug.h"
0003 #include "util/dso.h"
0004 #include "util/event.h"
0005 #include "util/map.h"
0006 #include "util/symbol.h"
0007 #include "util/sort.h"
0008 #include "util/evsel.h"
0009 #include "util/evlist.h"
0010 #include "util/machine.h"
0011 #include "util/thread.h"
0012 #include "util/parse-events.h"
0013 #include "tests/tests.h"
0014 #include "tests/hists_common.h"
0015 #include <linux/kernel.h>
0016
0017 struct sample {
0018 u32 pid;
0019 u64 ip;
0020 struct thread *thread;
0021 struct map *map;
0022 struct symbol *sym;
0023 };
0024
0025
0026 static struct sample fake_samples[] = {
0027
0028 { .pid = FAKE_PID_PERF1, .ip = FAKE_IP_KERNEL_SCHEDULE, },
0029
0030 { .pid = FAKE_PID_PERF1, .ip = FAKE_IP_PERF_MAIN, },
0031
0032 { .pid = FAKE_PID_PERF1, .ip = FAKE_IP_PERF_CMD_RECORD, },
0033
0034 { .pid = FAKE_PID_PERF1, .ip = FAKE_IP_LIBC_MALLOC, },
0035
0036 { .pid = FAKE_PID_PERF1, .ip = FAKE_IP_LIBC_FREE, },
0037
0038 { .pid = FAKE_PID_PERF2, .ip = FAKE_IP_PERF_MAIN, },
0039
0040 { .pid = FAKE_PID_PERF2, .ip = FAKE_IP_KERNEL_PAGE_FAULT, },
0041
0042 { .pid = FAKE_PID_BASH, .ip = FAKE_IP_BASH_MAIN, },
0043
0044 { .pid = FAKE_PID_BASH, .ip = FAKE_IP_BASH_XMALLOC, },
0045
0046 { .pid = FAKE_PID_BASH, .ip = FAKE_IP_KERNEL_PAGE_FAULT, },
0047 };
0048
0049
0050
0051
0052
0053 static u64 fake_callchains[][10] = {
0054
0055 { 3, FAKE_IP_KERNEL_SCHEDULE, FAKE_IP_PERF_RUN_COMMAND, FAKE_IP_PERF_MAIN, },
0056
0057 { 1, FAKE_IP_PERF_MAIN, },
0058
0059 { 3, FAKE_IP_PERF_CMD_RECORD, FAKE_IP_PERF_RUN_COMMAND, FAKE_IP_PERF_MAIN, },
0060
0061 { 4, FAKE_IP_LIBC_MALLOC, FAKE_IP_PERF_CMD_RECORD, FAKE_IP_PERF_RUN_COMMAND,
0062 FAKE_IP_PERF_MAIN, },
0063
0064 { 4, FAKE_IP_LIBC_FREE, FAKE_IP_PERF_CMD_RECORD, FAKE_IP_PERF_RUN_COMMAND,
0065 FAKE_IP_PERF_MAIN, },
0066
0067 { 1, FAKE_IP_PERF_MAIN, },
0068
0069 { 4, FAKE_IP_KERNEL_PAGE_FAULT, FAKE_IP_KERNEL_SYS_PERF_EVENT_OPEN,
0070 FAKE_IP_PERF_RUN_COMMAND, FAKE_IP_PERF_MAIN, },
0071
0072 { 1, FAKE_IP_BASH_MAIN, },
0073
0074 { 6, FAKE_IP_BASH_XMALLOC, FAKE_IP_LIBC_MALLOC, FAKE_IP_BASH_XMALLOC,
0075 FAKE_IP_LIBC_MALLOC, FAKE_IP_BASH_XMALLOC, FAKE_IP_BASH_MAIN, },
0076
0077 { 3, FAKE_IP_KERNEL_PAGE_FAULT, FAKE_IP_LIBC_MALLOC, FAKE_IP_BASH_MAIN, },
0078 };
0079
0080 static int add_hist_entries(struct hists *hists, struct machine *machine)
0081 {
0082 struct addr_location al;
0083 struct evsel *evsel = hists_to_evsel(hists);
0084 struct perf_sample sample = { .period = 1000, };
0085 size_t i;
0086
0087 for (i = 0; i < ARRAY_SIZE(fake_samples); i++) {
0088 struct hist_entry_iter iter = {
0089 .evsel = evsel,
0090 .sample = &sample,
0091 .hide_unresolved = false,
0092 };
0093
0094 if (symbol_conf.cumulate_callchain)
0095 iter.ops = &hist_iter_cumulative;
0096 else
0097 iter.ops = &hist_iter_normal;
0098
0099 sample.cpumode = PERF_RECORD_MISC_USER;
0100 sample.pid = fake_samples[i].pid;
0101 sample.tid = fake_samples[i].pid;
0102 sample.ip = fake_samples[i].ip;
0103 sample.callchain = (struct ip_callchain *)fake_callchains[i];
0104
0105 if (machine__resolve(machine, &al, &sample) < 0)
0106 goto out;
0107
0108 if (hist_entry_iter__add(&iter, &al, sysctl_perf_event_max_stack,
0109 NULL) < 0) {
0110 addr_location__put(&al);
0111 goto out;
0112 }
0113
0114 fake_samples[i].thread = al.thread;
0115 fake_samples[i].map = al.map;
0116 fake_samples[i].sym = al.sym;
0117 }
0118
0119 return TEST_OK;
0120
0121 out:
0122 pr_debug("Not enough memory for adding a hist entry\n");
0123 return TEST_FAIL;
0124 }
0125
0126 static void del_hist_entries(struct hists *hists)
0127 {
0128 struct hist_entry *he;
0129 struct rb_root_cached *root_in;
0130 struct rb_root_cached *root_out;
0131 struct rb_node *node;
0132
0133 if (hists__has(hists, need_collapse))
0134 root_in = &hists->entries_collapsed;
0135 else
0136 root_in = hists->entries_in;
0137
0138 root_out = &hists->entries;
0139
0140 while (!RB_EMPTY_ROOT(&root_out->rb_root)) {
0141 node = rb_first_cached(root_out);
0142
0143 he = rb_entry(node, struct hist_entry, rb_node);
0144 rb_erase_cached(node, root_out);
0145 rb_erase_cached(&he->rb_node_in, root_in);
0146 hist_entry__delete(he);
0147 }
0148 }
0149
0150 typedef int (*test_fn_t)(struct evsel *, struct machine *);
0151
0152 #define COMM(he) (thread__comm_str(he->thread))
0153 #define DSO(he) (he->ms.map->dso->short_name)
0154 #define SYM(he) (he->ms.sym->name)
0155 #define CPU(he) (he->cpu)
0156 #define PID(he) (he->thread->tid)
0157 #define DEPTH(he) (he->callchain->max_depth)
0158 #define CDSO(cl) (cl->ms.map->dso->short_name)
0159 #define CSYM(cl) (cl->ms.sym->name)
0160
0161 struct result {
0162 u64 children;
0163 u64 self;
0164 const char *comm;
0165 const char *dso;
0166 const char *sym;
0167 };
0168
0169 struct callchain_result {
0170 u64 nr;
0171 struct {
0172 const char *dso;
0173 const char *sym;
0174 } node[10];
0175 };
0176
0177 static int do_test(struct hists *hists, struct result *expected, size_t nr_expected,
0178 struct callchain_result *expected_callchain, size_t nr_callchain)
0179 {
0180 char buf[32];
0181 size_t i, c;
0182 struct hist_entry *he;
0183 struct rb_root *root;
0184 struct rb_node *node;
0185 struct callchain_node *cnode;
0186 struct callchain_list *clist;
0187
0188
0189
0190
0191
0192 hists__collapse_resort(hists, NULL);
0193 evsel__output_resort(hists_to_evsel(hists), NULL);
0194
0195 if (verbose > 2) {
0196 pr_info("use callchain: %d, cumulate callchain: %d\n",
0197 symbol_conf.use_callchain,
0198 symbol_conf.cumulate_callchain);
0199 print_hists_out(hists);
0200 }
0201
0202 root = &hists->entries.rb_root;
0203 for (node = rb_first(root), i = 0;
0204 node && (he = rb_entry(node, struct hist_entry, rb_node));
0205 node = rb_next(node), i++) {
0206 scnprintf(buf, sizeof(buf), "Invalid hist entry #%zd", i);
0207
0208 TEST_ASSERT_VAL("Incorrect number of hist entry",
0209 i < nr_expected);
0210 TEST_ASSERT_VAL(buf, he->stat.period == expected[i].self &&
0211 !strcmp(COMM(he), expected[i].comm) &&
0212 !strcmp(DSO(he), expected[i].dso) &&
0213 !strcmp(SYM(he), expected[i].sym));
0214
0215 if (symbol_conf.cumulate_callchain)
0216 TEST_ASSERT_VAL(buf, he->stat_acc->period == expected[i].children);
0217
0218 if (!symbol_conf.use_callchain)
0219 continue;
0220
0221
0222 root = &he->callchain->node.rb_root;
0223
0224 TEST_ASSERT_VAL("callchains expected", !RB_EMPTY_ROOT(root));
0225 cnode = rb_entry(rb_first(root), struct callchain_node, rb_node);
0226
0227 c = 0;
0228 list_for_each_entry(clist, &cnode->val, list) {
0229 scnprintf(buf, sizeof(buf), "Invalid callchain entry #%zd/%zd", i, c);
0230
0231 TEST_ASSERT_VAL("Incorrect number of callchain entry",
0232 c < expected_callchain[i].nr);
0233 TEST_ASSERT_VAL(buf,
0234 !strcmp(CDSO(clist), expected_callchain[i].node[c].dso) &&
0235 !strcmp(CSYM(clist), expected_callchain[i].node[c].sym));
0236 c++;
0237 }
0238
0239 TEST_ASSERT_VAL("Incorrect number of callchain entry",
0240 c <= expected_callchain[i].nr);
0241 }
0242 TEST_ASSERT_VAL("Incorrect number of hist entry",
0243 i == nr_expected);
0244 TEST_ASSERT_VAL("Incorrect number of callchain entry",
0245 !symbol_conf.use_callchain || nr_expected == nr_callchain);
0246 return 0;
0247 }
0248
0249
0250 static int test1(struct evsel *evsel, struct machine *machine)
0251 {
0252 int err;
0253 struct hists *hists = evsel__hists(evsel);
0254
0255
0256
0257
0258
0259
0260
0261
0262
0263
0264
0265
0266
0267
0268
0269 struct result expected[] = {
0270 { 0, 2000, "perf", "perf", "main" },
0271 { 0, 1000, "bash", "[kernel]", "page_fault" },
0272 { 0, 1000, "bash", "bash", "main" },
0273 { 0, 1000, "bash", "bash", "xmalloc" },
0274 { 0, 1000, "perf", "[kernel]", "page_fault" },
0275 { 0, 1000, "perf", "[kernel]", "schedule" },
0276 { 0, 1000, "perf", "libc", "free" },
0277 { 0, 1000, "perf", "libc", "malloc" },
0278 { 0, 1000, "perf", "perf", "cmd_record" },
0279 };
0280
0281 symbol_conf.use_callchain = false;
0282 symbol_conf.cumulate_callchain = false;
0283 evsel__reset_sample_bit(evsel, CALLCHAIN);
0284
0285 setup_sorting(NULL);
0286 callchain_register_param(&callchain_param);
0287
0288 err = add_hist_entries(hists, machine);
0289 if (err < 0)
0290 goto out;
0291
0292 err = do_test(hists, expected, ARRAY_SIZE(expected), NULL, 0);
0293
0294 out:
0295 del_hist_entries(hists);
0296 reset_output_field();
0297 return err;
0298 }
0299
0300
0301 static int test2(struct evsel *evsel, struct machine *machine)
0302 {
0303 int err;
0304 struct hists *hists = evsel__hists(evsel);
0305
0306
0307
0308
0309
0310
0311
0312
0313
0314
0315
0316
0317
0318
0319
0320
0321
0322
0323
0324
0325
0326
0327
0328
0329
0330
0331
0332
0333
0334
0335
0336
0337
0338
0339
0340
0341
0342
0343
0344
0345
0346
0347
0348
0349
0350
0351
0352
0353
0354
0355
0356
0357
0358
0359
0360
0361
0362
0363
0364
0365
0366
0367 struct result expected[] = {
0368 { 0, 2000, "perf", "perf", "main" },
0369 { 0, 1000, "bash", "[kernel]", "page_fault" },
0370 { 0, 1000, "bash", "bash", "main" },
0371 { 0, 1000, "bash", "bash", "xmalloc" },
0372 { 0, 1000, "perf", "[kernel]", "page_fault" },
0373 { 0, 1000, "perf", "[kernel]", "schedule" },
0374 { 0, 1000, "perf", "libc", "free" },
0375 { 0, 1000, "perf", "libc", "malloc" },
0376 { 0, 1000, "perf", "perf", "cmd_record" },
0377 };
0378 struct callchain_result expected_callchain[] = {
0379 {
0380 1, { { "perf", "main" }, },
0381 },
0382 {
0383 3, { { "[kernel]", "page_fault" },
0384 { "libc", "malloc" },
0385 { "bash", "main" }, },
0386 },
0387 {
0388 1, { { "bash", "main" }, },
0389 },
0390 {
0391 6, { { "bash", "xmalloc" },
0392 { "libc", "malloc" },
0393 { "bash", "xmalloc" },
0394 { "libc", "malloc" },
0395 { "bash", "xmalloc" },
0396 { "bash", "main" }, },
0397 },
0398 {
0399 4, { { "[kernel]", "page_fault" },
0400 { "[kernel]", "sys_perf_event_open" },
0401 { "perf", "run_command" },
0402 { "perf", "main" }, },
0403 },
0404 {
0405 3, { { "[kernel]", "schedule" },
0406 { "perf", "run_command" },
0407 { "perf", "main" }, },
0408 },
0409 {
0410 4, { { "libc", "free" },
0411 { "perf", "cmd_record" },
0412 { "perf", "run_command" },
0413 { "perf", "main" }, },
0414 },
0415 {
0416 4, { { "libc", "malloc" },
0417 { "perf", "cmd_record" },
0418 { "perf", "run_command" },
0419 { "perf", "main" }, },
0420 },
0421 {
0422 3, { { "perf", "cmd_record" },
0423 { "perf", "run_command" },
0424 { "perf", "main" }, },
0425 },
0426 };
0427
0428 symbol_conf.use_callchain = true;
0429 symbol_conf.cumulate_callchain = false;
0430 evsel__set_sample_bit(evsel, CALLCHAIN);
0431
0432 setup_sorting(NULL);
0433 callchain_register_param(&callchain_param);
0434
0435 err = add_hist_entries(hists, machine);
0436 if (err < 0)
0437 goto out;
0438
0439 err = do_test(hists, expected, ARRAY_SIZE(expected),
0440 expected_callchain, ARRAY_SIZE(expected_callchain));
0441
0442 out:
0443 del_hist_entries(hists);
0444 reset_output_field();
0445 return err;
0446 }
0447
0448
0449 static int test3(struct evsel *evsel, struct machine *machine)
0450 {
0451 int err;
0452 struct hists *hists = evsel__hists(evsel);
0453
0454
0455
0456
0457
0458
0459
0460
0461
0462
0463
0464
0465
0466
0467
0468
0469
0470
0471 struct result expected[] = {
0472 { 7000, 2000, "perf", "perf", "main" },
0473 { 5000, 0, "perf", "perf", "run_command" },
0474 { 3000, 1000, "bash", "bash", "main" },
0475 { 3000, 1000, "perf", "perf", "cmd_record" },
0476 { 2000, 0, "bash", "libc", "malloc" },
0477 { 1000, 1000, "bash", "[kernel]", "page_fault" },
0478 { 1000, 1000, "bash", "bash", "xmalloc" },
0479 { 1000, 1000, "perf", "[kernel]", "page_fault" },
0480 { 1000, 1000, "perf", "[kernel]", "schedule" },
0481 { 1000, 1000, "perf", "libc", "free" },
0482 { 1000, 1000, "perf", "libc", "malloc" },
0483 { 1000, 0, "perf", "[kernel]", "sys_perf_event_open" },
0484 };
0485
0486 symbol_conf.use_callchain = false;
0487 symbol_conf.cumulate_callchain = true;
0488 evsel__reset_sample_bit(evsel, CALLCHAIN);
0489
0490 setup_sorting(NULL);
0491 callchain_register_param(&callchain_param);
0492
0493 err = add_hist_entries(hists, machine);
0494 if (err < 0)
0495 goto out;
0496
0497 err = do_test(hists, expected, ARRAY_SIZE(expected), NULL, 0);
0498
0499 out:
0500 del_hist_entries(hists);
0501 reset_output_field();
0502 return err;
0503 }
0504
0505
0506 static int test4(struct evsel *evsel, struct machine *machine)
0507 {
0508 int err;
0509 struct hists *hists = evsel__hists(evsel);
0510
0511
0512
0513
0514
0515
0516
0517
0518
0519
0520
0521
0522
0523
0524
0525
0526
0527
0528
0529
0530
0531
0532
0533
0534
0535
0536
0537
0538
0539
0540
0541
0542
0543
0544
0545
0546
0547
0548
0549
0550
0551
0552
0553
0554
0555
0556
0557
0558
0559
0560
0561
0562
0563
0564
0565
0566
0567
0568
0569
0570
0571
0572
0573
0574
0575
0576
0577
0578
0579
0580
0581
0582
0583
0584
0585
0586
0587
0588
0589
0590
0591 struct result expected[] = {
0592 { 7000, 2000, "perf", "perf", "main" },
0593 { 5000, 0, "perf", "perf", "run_command" },
0594 { 3000, 1000, "bash", "bash", "main" },
0595 { 3000, 1000, "perf", "perf", "cmd_record" },
0596 { 2000, 0, "bash", "libc", "malloc" },
0597 { 1000, 1000, "bash", "[kernel]", "page_fault" },
0598 { 1000, 1000, "bash", "bash", "xmalloc" },
0599 { 1000, 0, "perf", "[kernel]", "sys_perf_event_open" },
0600 { 1000, 1000, "perf", "[kernel]", "page_fault" },
0601 { 1000, 1000, "perf", "[kernel]", "schedule" },
0602 { 1000, 1000, "perf", "libc", "free" },
0603 { 1000, 1000, "perf", "libc", "malloc" },
0604 };
0605 struct callchain_result expected_callchain[] = {
0606 {
0607 1, { { "perf", "main" }, },
0608 },
0609 {
0610 2, { { "perf", "run_command" },
0611 { "perf", "main" }, },
0612 },
0613 {
0614 1, { { "bash", "main" }, },
0615 },
0616 {
0617 3, { { "perf", "cmd_record" },
0618 { "perf", "run_command" },
0619 { "perf", "main" }, },
0620 },
0621 {
0622 4, { { "libc", "malloc" },
0623 { "bash", "xmalloc" },
0624 { "bash", "main" },
0625 { "bash", "main" }, },
0626 },
0627 {
0628 3, { { "[kernel]", "page_fault" },
0629 { "libc", "malloc" },
0630 { "bash", "main" }, },
0631 },
0632 {
0633 6, { { "bash", "xmalloc" },
0634 { "libc", "malloc" },
0635 { "bash", "xmalloc" },
0636 { "libc", "malloc" },
0637 { "bash", "xmalloc" },
0638 { "bash", "main" }, },
0639 },
0640 {
0641 3, { { "[kernel]", "sys_perf_event_open" },
0642 { "perf", "run_command" },
0643 { "perf", "main" }, },
0644 },
0645 {
0646 4, { { "[kernel]", "page_fault" },
0647 { "[kernel]", "sys_perf_event_open" },
0648 { "perf", "run_command" },
0649 { "perf", "main" }, },
0650 },
0651 {
0652 3, { { "[kernel]", "schedule" },
0653 { "perf", "run_command" },
0654 { "perf", "main" }, },
0655 },
0656 {
0657 4, { { "libc", "free" },
0658 { "perf", "cmd_record" },
0659 { "perf", "run_command" },
0660 { "perf", "main" }, },
0661 },
0662 {
0663 4, { { "libc", "malloc" },
0664 { "perf", "cmd_record" },
0665 { "perf", "run_command" },
0666 { "perf", "main" }, },
0667 },
0668 };
0669
0670 symbol_conf.use_callchain = true;
0671 symbol_conf.cumulate_callchain = true;
0672 evsel__set_sample_bit(evsel, CALLCHAIN);
0673
0674 setup_sorting(NULL);
0675
0676 callchain_param = callchain_param_default;
0677 callchain_register_param(&callchain_param);
0678
0679 err = add_hist_entries(hists, machine);
0680 if (err < 0)
0681 goto out;
0682
0683 err = do_test(hists, expected, ARRAY_SIZE(expected),
0684 expected_callchain, ARRAY_SIZE(expected_callchain));
0685
0686 out:
0687 del_hist_entries(hists);
0688 reset_output_field();
0689 return err;
0690 }
0691
0692 static int test__hists_cumulate(struct test_suite *test __maybe_unused, int subtest __maybe_unused)
0693 {
0694 int err = TEST_FAIL;
0695 struct machines machines;
0696 struct machine *machine;
0697 struct evsel *evsel;
0698 struct evlist *evlist = evlist__new();
0699 size_t i;
0700 test_fn_t testcases[] = {
0701 test1,
0702 test2,
0703 test3,
0704 test4,
0705 };
0706
0707 TEST_ASSERT_VAL("No memory", evlist);
0708
0709 err = parse_event(evlist, "cpu-clock");
0710 if (err)
0711 goto out;
0712 err = TEST_FAIL;
0713
0714 machines__init(&machines);
0715
0716
0717 machine = setup_fake_machine(&machines);
0718 if (!machine)
0719 goto out;
0720
0721 if (verbose > 1)
0722 machine__fprintf(machine, stderr);
0723
0724 evsel = evlist__first(evlist);
0725
0726 for (i = 0; i < ARRAY_SIZE(testcases); i++) {
0727 err = testcases[i](evsel, machine);
0728 if (err < 0)
0729 break;
0730 }
0731
0732 out:
0733
0734 evlist__delete(evlist);
0735 machines__exit(&machines);
0736
0737 return err;
0738 }
0739
0740 DEFINE_SUITE("Cumulate child hist entries", hists_cumulate);