1 // SPDX-License-Identifier: GPL-2.0
2 #include "util/debug.h"
4 #include "util/event.h"
6 #include "util/symbol.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>
20 struct thread
*thread
;
25 /* For the numbers, see hists_common.c */
26 static 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
, },
50 * Will be cast to struct ip_callchain which has all 64 bit entries
53 static u64 fake_callchains
[][10] = {
54 /* schedule => run_command => main */
55 { 3, FAKE_IP_KERNEL_SCHEDULE
, FAKE_IP_PERF_RUN_COMMAND
, FAKE_IP_PERF_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
,
63 /* free => cmd_record => run_command => main */
64 { 4, FAKE_IP_LIBC_FREE
, FAKE_IP_PERF_CMD_RECORD
, FAKE_IP_PERF_RUN_COMMAND
,
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
, },
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
, },
80 static int add_hist_entries(struct hists
*hists
, struct machine
*machine
)
82 struct addr_location al
;
83 struct evsel
*evsel
= hists_to_evsel(hists
);
84 struct perf_sample sample
= { .period
= 1000, };
87 addr_location__init(&al
);
88 for (i
= 0; i
< ARRAY_SIZE(fake_samples
); i
++) {
89 struct hist_entry_iter iter
= {
92 .hide_unresolved
= false,
95 if (symbol_conf
.cumulate_callchain
)
96 iter
.ops
= &hist_iter_cumulative
;
98 iter
.ops
= &hist_iter_normal
;
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
];
106 if (machine__resolve(machine
, &al
, &sample
) < 0)
109 if (hist_entry_iter__add(&iter
, &al
, sysctl_perf_event_max_stack
,
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
;
121 addr_location__exit(&al
);
125 pr_debug("Not enough memory for adding a hist entry\n");
126 addr_location__exit(&al
);
130 static void del_hist_entries(struct hists
*hists
)
132 struct hist_entry
*he
;
133 struct rb_root_cached
*root_in
;
134 struct rb_root_cached
*root_out
;
135 struct rb_node
*node
;
137 if (hists__has(hists
, need_collapse
))
138 root_in
= &hists
->entries_collapsed
;
140 root_in
= hists
->entries_in
;
142 root_out
= &hists
->entries
;
144 while (!RB_EMPTY_ROOT(&root_out
->rb_root
)) {
145 node
= rb_first_cached(root_out
);
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
);
154 static void put_fake_samples(void)
158 for (i
= 0; i
< ARRAY_SIZE(fake_samples
); i
++) {
159 map__zput(fake_samples
[i
].map
);
160 thread__zput(fake_samples
[i
].thread
);
164 typedef int (*test_fn_t
)(struct evsel
*, struct machine
*);
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)
182 struct callchain_result
{
190 static int do_test(struct hists
*hists
, struct result
*expected
, size_t nr_expected
,
191 struct callchain_result
*expected_callchain
, size_t nr_callchain
)
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
;
202 * adding and deleting hist entries must be done outside of this
203 * function since TEST_ASSERT_VAL() returns in case of failure.
205 hists__collapse_resort(hists
, NULL
);
206 evsel__output_resort(hists_to_evsel(hists
), NULL
);
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
);
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
);
221 TEST_ASSERT_VAL("Incorrect number of hist entry",
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
));
228 if (symbol_conf
.cumulate_callchain
)
229 TEST_ASSERT_VAL(buf
, he
->stat_acc
->period
== expected
[i
].children
);
231 if (!symbol_conf
.use_callchain
)
234 /* check callchain entries */
235 root
= &he
->callchain
->node
.rb_root
;
237 TEST_ASSERT_VAL("callchains expected", !RB_EMPTY_ROOT(root
));
238 cnode
= rb_entry(rb_first(root
), struct callchain_node
, rb_node
);
241 list_for_each_entry(clist
, &cnode
->val
, list
) {
242 scnprintf(buf
, sizeof(buf
), "Invalid callchain entry #%zd/%zd", i
, c
);
244 TEST_ASSERT_VAL("Incorrect number of callchain entry",
245 c
< expected_callchain
[i
].nr
);
247 !strcmp(CDSO(clist
), expected_callchain
[i
].node
[c
].dso
) &&
248 !strcmp(CSYM(clist
), expected_callchain
[i
].node
[c
].sym
));
251 /* TODO: handle multiple child nodes properly */
252 TEST_ASSERT_VAL("Incorrect number of callchain entry",
253 c
<= expected_callchain
[i
].nr
);
255 TEST_ASSERT_VAL("Incorrect number of hist entry",
257 TEST_ASSERT_VAL("Incorrect number of callchain entry",
258 !symbol_conf
.use_callchain
|| nr_expected
== nr_callchain
);
262 /* NO callchain + NO children */
263 static int test1(struct evsel
*evsel
, struct machine
*machine
)
266 struct hists
*hists
= evsel__hists(evsel
);
270 * Overhead Command Shared Object Symbol
271 * ======== ======= ============= ==============
272 * 20.00% perf perf [.] main
273 * 10.00% bash [kernel] [k] page_fault
274 * 10.00% bash bash [.] main
275 * 10.00% bash bash [.] xmalloc
276 * 10.00% perf [kernel] [k] page_fault
277 * 10.00% perf [kernel] [k] schedule
278 * 10.00% perf libc [.] free
279 * 10.00% perf libc [.] malloc
280 * 10.00% perf perf [.] cmd_record
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" },
294 symbol_conf
.use_callchain
= false;
295 symbol_conf
.cumulate_callchain
= false;
296 evsel__reset_sample_bit(evsel
, CALLCHAIN
);
299 callchain_register_param(&callchain_param
);
301 err
= add_hist_entries(hists
, machine
);
305 err
= do_test(hists
, expected
, ARRAY_SIZE(expected
), NULL
, 0);
308 del_hist_entries(hists
);
309 reset_output_field();
313 /* callchain + NO children */
314 static int test2(struct evsel
*evsel
, struct machine
*machine
)
317 struct hists
*hists
= evsel__hists(evsel
);
321 * Overhead Command Shared Object Symbol
322 * ======== ======= ============= ==============
323 * 20.00% perf perf [.] main
327 * 10.00% bash [kernel] [k] page_fault
333 * 10.00% bash bash [.] main
337 * 10.00% bash bash [.] xmalloc
341 * xmalloc <--- NOTE: there's a cycle
346 * 10.00% perf [kernel] [k] page_fault
349 * sys_perf_event_open
353 * 10.00% perf [kernel] [k] schedule
359 * 10.00% perf libc [.] free
366 * 10.00% perf libc [.] malloc
373 * 10.00% perf perf [.] cmd_record
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" },
391 struct callchain_result expected_callchain
[] = {
393 1, { { "perf", "main" }, },
396 3, { { "[kernel]", "page_fault" },
397 { "libc", "malloc" },
398 { "bash", "main" }, },
401 1, { { "bash", "main" }, },
404 6, { { "bash", "xmalloc" },
405 { "libc", "malloc" },
406 { "bash", "xmalloc" },
407 { "libc", "malloc" },
408 { "bash", "xmalloc" },
409 { "bash", "main" }, },
412 4, { { "[kernel]", "page_fault" },
413 { "[kernel]", "sys_perf_event_open" },
414 { "perf", "run_command" },
415 { "perf", "main" }, },
418 3, { { "[kernel]", "schedule" },
419 { "perf", "run_command" },
420 { "perf", "main" }, },
423 4, { { "libc", "free" },
424 { "perf", "cmd_record" },
425 { "perf", "run_command" },
426 { "perf", "main" }, },
429 4, { { "libc", "malloc" },
430 { "perf", "cmd_record" },
431 { "perf", "run_command" },
432 { "perf", "main" }, },
435 3, { { "perf", "cmd_record" },
436 { "perf", "run_command" },
437 { "perf", "main" }, },
441 symbol_conf
.use_callchain
= true;
442 symbol_conf
.cumulate_callchain
= false;
443 evsel__set_sample_bit(evsel
, CALLCHAIN
);
446 callchain_register_param(&callchain_param
);
448 err
= add_hist_entries(hists
, machine
);
452 err
= do_test(hists
, expected
, ARRAY_SIZE(expected
),
453 expected_callchain
, ARRAY_SIZE(expected_callchain
));
456 del_hist_entries(hists
);
457 reset_output_field();
461 /* NO callchain + children */
462 static int test3(struct evsel
*evsel
, struct machine
*machine
)
465 struct hists
*hists
= evsel__hists(evsel
);
469 * Children Self Command Shared Object Symbol
470 * ======== ======== ======= ============= =======================
471 * 70.00% 20.00% perf perf [.] main
472 * 50.00% 0.00% perf perf [.] run_command
473 * 30.00% 10.00% bash bash [.] main
474 * 30.00% 10.00% perf perf [.] cmd_record
475 * 20.00% 0.00% bash libc [.] malloc
476 * 10.00% 10.00% bash [kernel] [k] page_fault
477 * 10.00% 10.00% bash bash [.] xmalloc
478 * 10.00% 10.00% perf [kernel] [k] page_fault
479 * 10.00% 10.00% perf libc [.] malloc
480 * 10.00% 10.00% perf [kernel] [k] schedule
481 * 10.00% 10.00% perf libc [.] free
482 * 10.00% 0.00% perf [kernel] [k] sys_perf_event_open
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" },
499 symbol_conf
.use_callchain
= false;
500 symbol_conf
.cumulate_callchain
= true;
501 evsel__reset_sample_bit(evsel
, CALLCHAIN
);
504 callchain_register_param(&callchain_param
);
506 err
= add_hist_entries(hists
, machine
);
510 err
= do_test(hists
, expected
, ARRAY_SIZE(expected
), NULL
, 0);
513 del_hist_entries(hists
);
514 reset_output_field();
518 /* callchain + children */
519 static int test4(struct evsel
*evsel
, struct machine
*machine
)
522 struct hists
*hists
= evsel__hists(evsel
);
526 * Children Self Command Shared Object Symbol
527 * ======== ======== ======= ============= =======================
528 * 70.00% 20.00% perf perf [.] main
532 * 50.00% 0.00% perf perf [.] run_command
537 * 30.00% 10.00% bash bash [.] main
541 * 30.00% 10.00% perf perf [.] cmd_record
547 * 20.00% 0.00% bash libc [.] malloc
551 * |--50.00%-- xmalloc
555 * 10.00% 10.00% bash [kernel] [k] page_fault
561 * 10.00% 10.00% bash bash [.] xmalloc
565 * xmalloc <--- NOTE: there's a cycle
570 * 10.00% 0.00% perf [kernel] [k] sys_perf_event_open
572 * --- sys_perf_event_open
576 * 10.00% 10.00% perf [kernel] [k] page_fault
579 * sys_perf_event_open
583 * 10.00% 10.00% perf [kernel] [k] schedule
589 * 10.00% 10.00% perf libc [.] free
596 * 10.00% 10.00% perf libc [.] malloc
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" },
618 struct callchain_result expected_callchain
[] = {
620 1, { { "perf", "main" }, },
623 2, { { "perf", "run_command" },
624 { "perf", "main" }, },
627 1, { { "bash", "main" }, },
630 3, { { "perf", "cmd_record" },
631 { "perf", "run_command" },
632 { "perf", "main" }, },
635 4, { { "libc", "malloc" },
636 { "bash", "xmalloc" },
638 { "bash", "main" }, },
641 3, { { "[kernel]", "page_fault" },
642 { "libc", "malloc" },
643 { "bash", "main" }, },
646 6, { { "bash", "xmalloc" },
647 { "libc", "malloc" },
648 { "bash", "xmalloc" },
649 { "libc", "malloc" },
650 { "bash", "xmalloc" },
651 { "bash", "main" }, },
654 3, { { "[kernel]", "sys_perf_event_open" },
655 { "perf", "run_command" },
656 { "perf", "main" }, },
659 4, { { "[kernel]", "page_fault" },
660 { "[kernel]", "sys_perf_event_open" },
661 { "perf", "run_command" },
662 { "perf", "main" }, },
665 3, { { "[kernel]", "schedule" },
666 { "perf", "run_command" },
667 { "perf", "main" }, },
670 4, { { "libc", "free" },
671 { "perf", "cmd_record" },
672 { "perf", "run_command" },
673 { "perf", "main" }, },
676 4, { { "libc", "malloc" },
677 { "perf", "cmd_record" },
678 { "perf", "run_command" },
679 { "perf", "main" }, },
683 symbol_conf
.use_callchain
= true;
684 symbol_conf
.cumulate_callchain
= true;
685 evsel__set_sample_bit(evsel
, CALLCHAIN
);
689 callchain_param
= callchain_param_default
;
690 callchain_register_param(&callchain_param
);
692 err
= add_hist_entries(hists
, machine
);
696 err
= do_test(hists
, expected
, ARRAY_SIZE(expected
),
697 expected_callchain
, ARRAY_SIZE(expected_callchain
));
700 del_hist_entries(hists
);
701 reset_output_field();
705 static int test__hists_cumulate(struct test_suite
*test __maybe_unused
, int subtest __maybe_unused
)
708 struct machines machines
;
709 struct machine
*machine
;
711 struct evlist
*evlist
= evlist__new();
713 test_fn_t testcases
[] = {
720 TEST_ASSERT_VAL("No memory", evlist
);
722 err
= parse_event(evlist
, "cpu-clock");
727 machines__init(&machines
);
729 /* setup threads/dso/map/symbols also */
730 machine
= setup_fake_machine(&machines
);
735 machine__fprintf(machine
, stderr
);
737 evsel
= evlist__first(evlist
);
739 for (i
= 0; i
< ARRAY_SIZE(testcases
); i
++) {
740 err
= testcases
[i
](evsel
, machine
);
746 /* tear down everything */
747 evlist__delete(evlist
);
748 machines__exit(&machines
);
754 DEFINE_SUITE("Cumulate child hist entries", hists_cumulate
);