1
2#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/thread.h"
12#include "util/parse-events.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
26static struct sample fake_samples[] = {
27
28 { .pid = FAKE_PID_PERF1, .ip = FAKE_IP_KERNEL_SCHEDULE, },
29
30 { .pid = FAKE_PID_PERF1, .ip = FAKE_IP_PERF_MAIN, },
31
32 { .pid = FAKE_PID_PERF1, .ip = FAKE_IP_PERF_CMD_RECORD, },
33
34 { .pid = FAKE_PID_PERF1, .ip = FAKE_IP_LIBC_MALLOC, },
35
36 { .pid = FAKE_PID_PERF1, .ip = FAKE_IP_LIBC_FREE, },
37
38 { .pid = FAKE_PID_PERF2, .ip = FAKE_IP_PERF_MAIN, },
39
40 { .pid = FAKE_PID_PERF2, .ip = FAKE_IP_KERNEL_PAGE_FAULT, },
41
42 { .pid = FAKE_PID_BASH, .ip = FAKE_IP_BASH_MAIN, },
43
44 { .pid = FAKE_PID_BASH, .ip = FAKE_IP_BASH_XMALLOC, },
45
46 { .pid = FAKE_PID_BASH, .ip = FAKE_IP_KERNEL_PAGE_FAULT, },
47};
48
49
50
51
52
53static u64 fake_callchains[][10] = {
54
55 { 3, FAKE_IP_KERNEL_SCHEDULE, FAKE_IP_PERF_RUN_COMMAND, FAKE_IP_PERF_MAIN, },
56
57 { 1, FAKE_IP_PERF_MAIN, },
58
59 { 3, FAKE_IP_PERF_CMD_RECORD, FAKE_IP_PERF_RUN_COMMAND, FAKE_IP_PERF_MAIN, },
60
61 { 4, FAKE_IP_LIBC_MALLOC, FAKE_IP_PERF_CMD_RECORD, FAKE_IP_PERF_RUN_COMMAND,
62 FAKE_IP_PERF_MAIN, },
63
64 { 4, FAKE_IP_LIBC_FREE, FAKE_IP_PERF_CMD_RECORD, FAKE_IP_PERF_RUN_COMMAND,
65 FAKE_IP_PERF_MAIN, },
66
67 { 1, FAKE_IP_PERF_MAIN, },
68
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
72 { 1, FAKE_IP_BASH_MAIN, },
73
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
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 for (i = 0; i < ARRAY_SIZE(fake_samples); i++) {
88 struct hist_entry_iter iter = {
89 .evsel = evsel,
90 .sample = &sample,
91 .hide_unresolved = false,
92 };
93
94 if (symbol_conf.cumulate_callchain)
95 iter.ops = &hist_iter_cumulative;
96 else
97 iter.ops = &hist_iter_normal;
98
99 sample.cpumode = PERF_RECORD_MISC_USER;
100 sample.pid = fake_samples[i].pid;
101 sample.tid = fake_samples[i].pid;
102 sample.ip = fake_samples[i].ip;
103 sample.callchain = (struct ip_callchain *)fake_callchains[i];
104
105 if (machine__resolve(machine, &al, &sample) < 0)
106 goto out;
107
108 if (hist_entry_iter__add(&iter, &al, sysctl_perf_event_max_stack,
109 NULL) < 0) {
110 addr_location__put(&al);
111 goto out;
112 }
113
114 fake_samples[i].thread = al.thread;
115 fake_samples[i].map = al.map;
116 fake_samples[i].sym = al.sym;
117 }
118
119 return TEST_OK;
120
121out:
122 pr_debug("Not enough memory for adding a hist entry\n");
123 return TEST_FAIL;
124}
125
126static void del_hist_entries(struct hists *hists)
127{
128 struct hist_entry *he;
129 struct rb_root_cached *root_in;
130 struct rb_root_cached *root_out;
131 struct rb_node *node;
132
133 if (hists__has(hists, need_collapse))
134 root_in = &hists->entries_collapsed;
135 else
136 root_in = hists->entries_in;
137
138 root_out = &hists->entries;
139
140 while (!RB_EMPTY_ROOT(&root_out->rb_root)) {
141 node = rb_first_cached(root_out);
142
143 he = rb_entry(node, struct hist_entry, rb_node);
144 rb_erase_cached(node, root_out);
145 rb_erase_cached(&he->rb_node_in, root_in);
146 hist_entry__delete(he);
147 }
148}
149
150typedef int (*test_fn_t)(struct evsel *, struct machine *);
151
152#define COMM(he) (thread__comm_str(he->thread))
153#define DSO(he) (he->ms.map->dso->short_name)
154#define SYM(he) (he->ms.sym->name)
155#define CPU(he) (he->cpu)
156#define PID(he) (he->thread->tid)
157#define DEPTH(he) (he->callchain->max_depth)
158#define CDSO(cl) (cl->ms.map->dso->short_name)
159#define CSYM(cl) (cl->ms.sym->name)
160
161struct result {
162 u64 children;
163 u64 self;
164 const char *comm;
165 const char *dso;
166 const char *sym;
167};
168
169struct callchain_result {
170 u64 nr;
171 struct {
172 const char *dso;
173 const char *sym;
174 } node[10];
175};
176
177static int do_test(struct hists *hists, struct result *expected, size_t nr_expected,
178 struct callchain_result *expected_callchain, size_t nr_callchain)
179{
180 char buf[32];
181 size_t i, c;
182 struct hist_entry *he;
183 struct rb_root *root;
184 struct rb_node *node;
185 struct callchain_node *cnode;
186 struct callchain_list *clist;
187
188
189
190
191
192 hists__collapse_resort(hists, NULL);
193 evsel__output_resort(hists_to_evsel(hists), NULL);
194
195 if (verbose > 2) {
196 pr_info("use callchain: %d, cumulate callchain: %d\n",
197 symbol_conf.use_callchain,
198 symbol_conf.cumulate_callchain);
199 print_hists_out(hists);
200 }
201
202 root = &hists->entries.rb_root;
203 for (node = rb_first(root), i = 0;
204 node && (he = rb_entry(node, struct hist_entry, rb_node));
205 node = rb_next(node), i++) {
206 scnprintf(buf, sizeof(buf), "Invalid hist entry #%zd", i);
207
208 TEST_ASSERT_VAL("Incorrect number of hist entry",
209 i < nr_expected);
210 TEST_ASSERT_VAL(buf, he->stat.period == expected[i].self &&
211 !strcmp(COMM(he), expected[i].comm) &&
212 !strcmp(DSO(he), expected[i].dso) &&
213 !strcmp(SYM(he), expected[i].sym));
214
215 if (symbol_conf.cumulate_callchain)
216 TEST_ASSERT_VAL(buf, he->stat_acc->period == expected[i].children);
217
218 if (!symbol_conf.use_callchain)
219 continue;
220
221
222 root = &he->callchain->node.rb_root;
223
224 TEST_ASSERT_VAL("callchains expected", !RB_EMPTY_ROOT(root));
225 cnode = rb_entry(rb_first(root), struct callchain_node, rb_node);
226
227 c = 0;
228 list_for_each_entry(clist, &cnode->val, list) {
229 scnprintf(buf, sizeof(buf), "Invalid callchain entry #%zd/%zd", i, c);
230
231 TEST_ASSERT_VAL("Incorrect number of callchain entry",
232 c < expected_callchain[i].nr);
233 TEST_ASSERT_VAL(buf,
234 !strcmp(CDSO(clist), expected_callchain[i].node[c].dso) &&
235 !strcmp(CSYM(clist), expected_callchain[i].node[c].sym));
236 c++;
237 }
238
239 TEST_ASSERT_VAL("Incorrect number of callchain entry",
240 c <= expected_callchain[i].nr);
241 }
242 TEST_ASSERT_VAL("Incorrect number of hist entry",
243 i == nr_expected);
244 TEST_ASSERT_VAL("Incorrect number of callchain entry",
245 !symbol_conf.use_callchain || nr_expected == nr_callchain);
246 return 0;
247}
248
249
250static int test1(struct evsel *evsel, struct machine *machine)
251{
252 int err;
253 struct hists *hists = evsel__hists(evsel);
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269 struct result expected[] = {
270 { 0, 2000, "perf", "perf", "main" },
271 { 0, 1000, "bash", "[kernel]", "page_fault" },
272 { 0, 1000, "bash", "bash", "main" },
273 { 0, 1000, "bash", "bash", "xmalloc" },
274 { 0, 1000, "perf", "[kernel]", "page_fault" },
275 { 0, 1000, "perf", "[kernel]", "schedule" },
276 { 0, 1000, "perf", "libc", "free" },
277 { 0, 1000, "perf", "libc", "malloc" },
278 { 0, 1000, "perf", "perf", "cmd_record" },
279 };
280
281 symbol_conf.use_callchain = false;
282 symbol_conf.cumulate_callchain = false;
283 evsel__reset_sample_bit(evsel, CALLCHAIN);
284
285 setup_sorting(NULL);
286 callchain_register_param(&callchain_param);
287
288 err = add_hist_entries(hists, machine);
289 if (err < 0)
290 goto out;
291
292 err = do_test(hists, expected, ARRAY_SIZE(expected), NULL, 0);
293
294out:
295 del_hist_entries(hists);
296 reset_output_field();
297 return err;
298}
299
300
301static int test2(struct evsel *evsel, struct machine *machine)
302{
303 int err;
304 struct hists *hists = evsel__hists(evsel);
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367 struct result expected[] = {
368 { 0, 2000, "perf", "perf", "main" },
369 { 0, 1000, "bash", "[kernel]", "page_fault" },
370 { 0, 1000, "bash", "bash", "main" },
371 { 0, 1000, "bash", "bash", "xmalloc" },
372 { 0, 1000, "perf", "[kernel]", "page_fault" },
373 { 0, 1000, "perf", "[kernel]", "schedule" },
374 { 0, 1000, "perf", "libc", "free" },
375 { 0, 1000, "perf", "libc", "malloc" },
376 { 0, 1000, "perf", "perf", "cmd_record" },
377 };
378 struct callchain_result expected_callchain[] = {
379 {
380 1, { { "perf", "main" }, },
381 },
382 {
383 3, { { "[kernel]", "page_fault" },
384 { "libc", "malloc" },
385 { "bash", "main" }, },
386 },
387 {
388 1, { { "bash", "main" }, },
389 },
390 {
391 6, { { "bash", "xmalloc" },
392 { "libc", "malloc" },
393 { "bash", "xmalloc" },
394 { "libc", "malloc" },
395 { "bash", "xmalloc" },
396 { "bash", "main" }, },
397 },
398 {
399 4, { { "[kernel]", "page_fault" },
400 { "[kernel]", "sys_perf_event_open" },
401 { "perf", "run_command" },
402 { "perf", "main" }, },
403 },
404 {
405 3, { { "[kernel]", "schedule" },
406 { "perf", "run_command" },
407 { "perf", "main" }, },
408 },
409 {
410 4, { { "libc", "free" },
411 { "perf", "cmd_record" },
412 { "perf", "run_command" },
413 { "perf", "main" }, },
414 },
415 {
416 4, { { "libc", "malloc" },
417 { "perf", "cmd_record" },
418 { "perf", "run_command" },
419 { "perf", "main" }, },
420 },
421 {
422 3, { { "perf", "cmd_record" },
423 { "perf", "run_command" },
424 { "perf", "main" }, },
425 },
426 };
427
428 symbol_conf.use_callchain = true;
429 symbol_conf.cumulate_callchain = false;
430 evsel__set_sample_bit(evsel, CALLCHAIN);
431
432 setup_sorting(NULL);
433 callchain_register_param(&callchain_param);
434
435 err = add_hist_entries(hists, machine);
436 if (err < 0)
437 goto out;
438
439 err = do_test(hists, expected, ARRAY_SIZE(expected),
440 expected_callchain, ARRAY_SIZE(expected_callchain));
441
442out:
443 del_hist_entries(hists);
444 reset_output_field();
445 return err;
446}
447
448
449static int test3(struct evsel *evsel, struct machine *machine)
450{
451 int err;
452 struct hists *hists = evsel__hists(evsel);
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471 struct result expected[] = {
472 { 7000, 2000, "perf", "perf", "main" },
473 { 5000, 0, "perf", "perf", "run_command" },
474 { 3000, 1000, "bash", "bash", "main" },
475 { 3000, 1000, "perf", "perf", "cmd_record" },
476 { 2000, 0, "bash", "libc", "malloc" },
477 { 1000, 1000, "bash", "[kernel]", "page_fault" },
478 { 1000, 1000, "bash", "bash", "xmalloc" },
479 { 1000, 1000, "perf", "[kernel]", "page_fault" },
480 { 1000, 1000, "perf", "[kernel]", "schedule" },
481 { 1000, 1000, "perf", "libc", "free" },
482 { 1000, 1000, "perf", "libc", "malloc" },
483 { 1000, 0, "perf", "[kernel]", "sys_perf_event_open" },
484 };
485
486 symbol_conf.use_callchain = false;
487 symbol_conf.cumulate_callchain = true;
488 evsel__reset_sample_bit(evsel, CALLCHAIN);
489
490 setup_sorting(NULL);
491 callchain_register_param(&callchain_param);
492
493 err = add_hist_entries(hists, machine);
494 if (err < 0)
495 goto out;
496
497 err = do_test(hists, expected, ARRAY_SIZE(expected), NULL, 0);
498
499out:
500 del_hist_entries(hists);
501 reset_output_field();
502 return err;
503}
504
505
506static int test4(struct evsel *evsel, struct machine *machine)
507{
508 int err;
509 struct hists *hists = evsel__hists(evsel);
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591 struct result expected[] = {
592 { 7000, 2000, "perf", "perf", "main" },
593 { 5000, 0, "perf", "perf", "run_command" },
594 { 3000, 1000, "bash", "bash", "main" },
595 { 3000, 1000, "perf", "perf", "cmd_record" },
596 { 2000, 0, "bash", "libc", "malloc" },
597 { 1000, 1000, "bash", "[kernel]", "page_fault" },
598 { 1000, 1000, "bash", "bash", "xmalloc" },
599 { 1000, 0, "perf", "[kernel]", "sys_perf_event_open" },
600 { 1000, 1000, "perf", "[kernel]", "page_fault" },
601 { 1000, 1000, "perf", "[kernel]", "schedule" },
602 { 1000, 1000, "perf", "libc", "free" },
603 { 1000, 1000, "perf", "libc", "malloc" },
604 };
605 struct callchain_result expected_callchain[] = {
606 {
607 1, { { "perf", "main" }, },
608 },
609 {
610 2, { { "perf", "run_command" },
611 { "perf", "main" }, },
612 },
613 {
614 1, { { "bash", "main" }, },
615 },
616 {
617 3, { { "perf", "cmd_record" },
618 { "perf", "run_command" },
619 { "perf", "main" }, },
620 },
621 {
622 4, { { "libc", "malloc" },
623 { "bash", "xmalloc" },
624 { "bash", "main" },
625 { "bash", "main" }, },
626 },
627 {
628 3, { { "[kernel]", "page_fault" },
629 { "libc", "malloc" },
630 { "bash", "main" }, },
631 },
632 {
633 6, { { "bash", "xmalloc" },
634 { "libc", "malloc" },
635 { "bash", "xmalloc" },
636 { "libc", "malloc" },
637 { "bash", "xmalloc" },
638 { "bash", "main" }, },
639 },
640 {
641 3, { { "[kernel]", "sys_perf_event_open" },
642 { "perf", "run_command" },
643 { "perf", "main" }, },
644 },
645 {
646 4, { { "[kernel]", "page_fault" },
647 { "[kernel]", "sys_perf_event_open" },
648 { "perf", "run_command" },
649 { "perf", "main" }, },
650 },
651 {
652 3, { { "[kernel]", "schedule" },
653 { "perf", "run_command" },
654 { "perf", "main" }, },
655 },
656 {
657 4, { { "libc", "free" },
658 { "perf", "cmd_record" },
659 { "perf", "run_command" },
660 { "perf", "main" }, },
661 },
662 {
663 4, { { "libc", "malloc" },
664 { "perf", "cmd_record" },
665 { "perf", "run_command" },
666 { "perf", "main" }, },
667 },
668 };
669
670 symbol_conf.use_callchain = true;
671 symbol_conf.cumulate_callchain = true;
672 evsel__set_sample_bit(evsel, CALLCHAIN);
673
674 setup_sorting(NULL);
675
676 callchain_param = callchain_param_default;
677 callchain_register_param(&callchain_param);
678
679 err = add_hist_entries(hists, machine);
680 if (err < 0)
681 goto out;
682
683 err = do_test(hists, expected, ARRAY_SIZE(expected),
684 expected_callchain, ARRAY_SIZE(expected_callchain));
685
686out:
687 del_hist_entries(hists);
688 reset_output_field();
689 return err;
690}
691
692int test__hists_cumulate(struct test *test __maybe_unused, int subtest __maybe_unused)
693{
694 int err = TEST_FAIL;
695 struct machines machines;
696 struct machine *machine;
697 struct evsel *evsel;
698 struct evlist *evlist = evlist__new();
699 size_t i;
700 test_fn_t testcases[] = {
701 test1,
702 test2,
703 test3,
704 test4,
705 };
706
707 TEST_ASSERT_VAL("No memory", evlist);
708
709 err = parse_events(evlist, "cpu-clock", NULL);
710 if (err)
711 goto out;
712 err = TEST_FAIL;
713
714 machines__init(&machines);
715
716
717 machine = setup_fake_machine(&machines);
718 if (!machine)
719 goto out;
720
721 if (verbose > 1)
722 machine__fprintf(machine, stderr);
723
724 evsel = evlist__first(evlist);
725
726 for (i = 0; i < ARRAY_SIZE(testcases); i++) {
727 err = testcases[i](evsel, machine);
728 if (err < 0)
729 break;
730 }
731
732out:
733
734 evlist__delete(evlist);
735 machines__exit(&machines);
736
737 return err;
738}
739