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
13struct sample {
14 u32 pid;
15 u64 ip;
16 struct thread *thread;
17 struct map *map;
18 struct symbol *sym;
19};
20
21
22static struct sample fake_samples[] = {
23
24 { .pid = FAKE_PID_PERF1, .ip = FAKE_IP_KERNEL_SCHEDULE, },
25
26 { .pid = FAKE_PID_PERF1, .ip = FAKE_IP_PERF_MAIN, },
27
28 { .pid = FAKE_PID_PERF1, .ip = FAKE_IP_PERF_CMD_RECORD, },
29
30 { .pid = FAKE_PID_PERF1, .ip = FAKE_IP_LIBC_MALLOC, },
31
32 { .pid = FAKE_PID_PERF1, .ip = FAKE_IP_LIBC_FREE, },
33
34 { .pid = FAKE_PID_PERF2, .ip = FAKE_IP_PERF_MAIN, },
35
36 { .pid = FAKE_PID_PERF2, .ip = FAKE_IP_KERNEL_PAGE_FAULT, },
37
38 { .pid = FAKE_PID_BASH, .ip = FAKE_IP_BASH_MAIN, },
39
40 { .pid = FAKE_PID_BASH, .ip = FAKE_IP_BASH_XMALLOC, },
41
42 { .pid = FAKE_PID_BASH, .ip = FAKE_IP_KERNEL_PAGE_FAULT, },
43};
44
45
46
47
48
49static u64 fake_callchains[][10] = {
50
51 { 3, FAKE_IP_KERNEL_SCHEDULE, FAKE_IP_PERF_RUN_COMMAND, FAKE_IP_PERF_MAIN, },
52
53 { 1, FAKE_IP_PERF_MAIN, },
54
55 { 3, FAKE_IP_PERF_CMD_RECORD, FAKE_IP_PERF_RUN_COMMAND, FAKE_IP_PERF_MAIN, },
56
57 { 4, FAKE_IP_LIBC_MALLOC, FAKE_IP_PERF_CMD_RECORD, FAKE_IP_PERF_RUN_COMMAND,
58 FAKE_IP_PERF_MAIN, },
59
60 { 4, FAKE_IP_LIBC_FREE, FAKE_IP_PERF_CMD_RECORD, FAKE_IP_PERF_RUN_COMMAND,
61 FAKE_IP_PERF_MAIN, },
62
63 { 1, FAKE_IP_PERF_MAIN, },
64
65 { 4, FAKE_IP_KERNEL_PAGE_FAULT, FAKE_IP_KERNEL_SYS_PERF_EVENT_OPEN,
66 FAKE_IP_PERF_RUN_COMMAND, FAKE_IP_PERF_MAIN, },
67
68 { 1, FAKE_IP_BASH_MAIN, },
69
70 { 6, FAKE_IP_BASH_XMALLOC, FAKE_IP_LIBC_MALLOC, FAKE_IP_BASH_XMALLOC,
71 FAKE_IP_LIBC_MALLOC, FAKE_IP_BASH_XMALLOC, FAKE_IP_BASH_MAIN, },
72
73 { 3, FAKE_IP_KERNEL_PAGE_FAULT, FAKE_IP_LIBC_MALLOC, FAKE_IP_BASH_MAIN, },
74};
75
76static int add_hist_entries(struct hists *hists, struct machine *machine)
77{
78 struct addr_location al;
79 struct perf_evsel *evsel = hists_to_evsel(hists);
80 struct perf_sample sample = { .period = 1000, };
81 size_t i;
82
83 for (i = 0; i < ARRAY_SIZE(fake_samples); i++) {
84 struct hist_entry_iter iter = {
85 .evsel = evsel,
86 .sample = &sample,
87 .hide_unresolved = false,
88 };
89
90 if (symbol_conf.cumulate_callchain)
91 iter.ops = &hist_iter_cumulative;
92 else
93 iter.ops = &hist_iter_normal;
94
95 sample.cpumode = PERF_RECORD_MISC_USER;
96 sample.pid = fake_samples[i].pid;
97 sample.tid = fake_samples[i].pid;
98 sample.ip = fake_samples[i].ip;
99 sample.callchain = (struct ip_callchain *)fake_callchains[i];
100
101 if (machine__resolve(machine, &al, &sample) < 0)
102 goto out;
103
104 if (hist_entry_iter__add(&iter, &al, PERF_MAX_STACK_DEPTH,
105 NULL) < 0) {
106 addr_location__put(&al);
107 goto out;
108 }
109
110 fake_samples[i].thread = al.thread;
111 fake_samples[i].map = al.map;
112 fake_samples[i].sym = al.sym;
113 }
114
115 return TEST_OK;
116
117out:
118 pr_debug("Not enough memory for adding a hist entry\n");
119 return TEST_FAIL;
120}
121
122static void del_hist_entries(struct hists *hists)
123{
124 struct hist_entry *he;
125 struct rb_root *root_in;
126 struct rb_root *root_out;
127 struct rb_node *node;
128
129 if (sort__need_collapse)
130 root_in = &hists->entries_collapsed;
131 else
132 root_in = hists->entries_in;
133
134 root_out = &hists->entries;
135
136 while (!RB_EMPTY_ROOT(root_out)) {
137 node = rb_first(root_out);
138
139 he = rb_entry(node, struct hist_entry, rb_node);
140 rb_erase(node, root_out);
141 rb_erase(&he->rb_node_in, root_in);
142 hist_entry__delete(he);
143 }
144}
145
146typedef int (*test_fn_t)(struct perf_evsel *, struct machine *);
147
148#define COMM(he) (thread__comm_str(he->thread))
149#define DSO(he) (he->ms.map->dso->short_name)
150#define SYM(he) (he->ms.sym->name)
151#define CPU(he) (he->cpu)
152#define PID(he) (he->thread->tid)
153#define DEPTH(he) (he->callchain->max_depth)
154#define CDSO(cl) (cl->ms.map->dso->short_name)
155#define CSYM(cl) (cl->ms.sym->name)
156
157struct result {
158 u64 children;
159 u64 self;
160 const char *comm;
161 const char *dso;
162 const char *sym;
163};
164
165struct callchain_result {
166 u64 nr;
167 struct {
168 const char *dso;
169 const char *sym;
170 } node[10];
171};
172
173static int do_test(struct hists *hists, struct result *expected, size_t nr_expected,
174 struct callchain_result *expected_callchain, size_t nr_callchain)
175{
176 char buf[32];
177 size_t i, c;
178 struct hist_entry *he;
179 struct rb_root *root;
180 struct rb_node *node;
181 struct callchain_node *cnode;
182 struct callchain_list *clist;
183
184
185
186
187
188 hists__collapse_resort(hists, NULL);
189 perf_evsel__output_resort(hists_to_evsel(hists), NULL);
190
191 if (verbose > 2) {
192 pr_info("use callchain: %d, cumulate callchain: %d\n",
193 symbol_conf.use_callchain,
194 symbol_conf.cumulate_callchain);
195 print_hists_out(hists);
196 }
197
198 root = &hists->entries;
199 for (node = rb_first(root), i = 0;
200 node && (he = rb_entry(node, struct hist_entry, rb_node));
201 node = rb_next(node), i++) {
202 scnprintf(buf, sizeof(buf), "Invalid hist entry #%zd", i);
203
204 TEST_ASSERT_VAL("Incorrect number of hist entry",
205 i < nr_expected);
206 TEST_ASSERT_VAL(buf, he->stat.period == expected[i].self &&
207 !strcmp(COMM(he), expected[i].comm) &&
208 !strcmp(DSO(he), expected[i].dso) &&
209 !strcmp(SYM(he), expected[i].sym));
210
211 if (symbol_conf.cumulate_callchain)
212 TEST_ASSERT_VAL(buf, he->stat_acc->period == expected[i].children);
213
214 if (!symbol_conf.use_callchain)
215 continue;
216
217
218 root = &he->callchain->node.rb_root;
219 cnode = rb_entry(rb_first(root), struct callchain_node, rb_node);
220
221 c = 0;
222 list_for_each_entry(clist, &cnode->val, list) {
223 scnprintf(buf, sizeof(buf), "Invalid callchain entry #%zd/%zd", i, c);
224
225 TEST_ASSERT_VAL("Incorrect number of callchain entry",
226 c < expected_callchain[i].nr);
227 TEST_ASSERT_VAL(buf,
228 !strcmp(CDSO(clist), expected_callchain[i].node[c].dso) &&
229 !strcmp(CSYM(clist), expected_callchain[i].node[c].sym));
230 c++;
231 }
232
233 TEST_ASSERT_VAL("Incorrect number of callchain entry",
234 c <= expected_callchain[i].nr);
235 }
236 TEST_ASSERT_VAL("Incorrect number of hist entry",
237 i == nr_expected);
238 TEST_ASSERT_VAL("Incorrect number of callchain entry",
239 !symbol_conf.use_callchain || nr_expected == nr_callchain);
240 return 0;
241}
242
243
244static int test1(struct perf_evsel *evsel, struct machine *machine)
245{
246 int err;
247 struct hists *hists = evsel__hists(evsel);
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263 struct result expected[] = {
264 { 0, 2000, "perf", "perf", "main" },
265 { 0, 1000, "bash", "[kernel]", "page_fault" },
266 { 0, 1000, "bash", "bash", "main" },
267 { 0, 1000, "bash", "bash", "xmalloc" },
268 { 0, 1000, "perf", "[kernel]", "page_fault" },
269 { 0, 1000, "perf", "[kernel]", "schedule" },
270 { 0, 1000, "perf", "libc", "free" },
271 { 0, 1000, "perf", "libc", "malloc" },
272 { 0, 1000, "perf", "perf", "cmd_record" },
273 };
274
275 symbol_conf.use_callchain = false;
276 symbol_conf.cumulate_callchain = false;
277 perf_evsel__reset_sample_bit(evsel, CALLCHAIN);
278
279 setup_sorting(NULL);
280 callchain_register_param(&callchain_param);
281
282 err = add_hist_entries(hists, machine);
283 if (err < 0)
284 goto out;
285
286 err = do_test(hists, expected, ARRAY_SIZE(expected), NULL, 0);
287
288out:
289 del_hist_entries(hists);
290 reset_output_field();
291 return err;
292}
293
294
295static int test2(struct perf_evsel *evsel, struct machine *machine)
296{
297 int err;
298 struct hists *hists = evsel__hists(evsel);
299
300
301
302
303
304
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 struct result expected[] = {
362 { 0, 2000, "perf", "perf", "main" },
363 { 0, 1000, "bash", "[kernel]", "page_fault" },
364 { 0, 1000, "bash", "bash", "main" },
365 { 0, 1000, "bash", "bash", "xmalloc" },
366 { 0, 1000, "perf", "[kernel]", "page_fault" },
367 { 0, 1000, "perf", "[kernel]", "schedule" },
368 { 0, 1000, "perf", "libc", "free" },
369 { 0, 1000, "perf", "libc", "malloc" },
370 { 0, 1000, "perf", "perf", "cmd_record" },
371 };
372 struct callchain_result expected_callchain[] = {
373 {
374 1, { { "perf", "main" }, },
375 },
376 {
377 3, { { "[kernel]", "page_fault" },
378 { "libc", "malloc" },
379 { "bash", "main" }, },
380 },
381 {
382 1, { { "bash", "main" }, },
383 },
384 {
385 6, { { "bash", "xmalloc" },
386 { "libc", "malloc" },
387 { "bash", "xmalloc" },
388 { "libc", "malloc" },
389 { "bash", "xmalloc" },
390 { "bash", "main" }, },
391 },
392 {
393 4, { { "[kernel]", "page_fault" },
394 { "[kernel]", "sys_perf_event_open" },
395 { "perf", "run_command" },
396 { "perf", "main" }, },
397 },
398 {
399 3, { { "[kernel]", "schedule" },
400 { "perf", "run_command" },
401 { "perf", "main" }, },
402 },
403 {
404 4, { { "libc", "free" },
405 { "perf", "cmd_record" },
406 { "perf", "run_command" },
407 { "perf", "main" }, },
408 },
409 {
410 4, { { "libc", "malloc" },
411 { "perf", "cmd_record" },
412 { "perf", "run_command" },
413 { "perf", "main" }, },
414 },
415 {
416 3, { { "perf", "cmd_record" },
417 { "perf", "run_command" },
418 { "perf", "main" }, },
419 },
420 };
421
422 symbol_conf.use_callchain = true;
423 symbol_conf.cumulate_callchain = false;
424 perf_evsel__set_sample_bit(evsel, CALLCHAIN);
425
426 setup_sorting(NULL);
427 callchain_register_param(&callchain_param);
428
429 err = add_hist_entries(hists, machine);
430 if (err < 0)
431 goto out;
432
433 err = do_test(hists, expected, ARRAY_SIZE(expected),
434 expected_callchain, ARRAY_SIZE(expected_callchain));
435
436out:
437 del_hist_entries(hists);
438 reset_output_field();
439 return err;
440}
441
442
443static int test3(struct perf_evsel *evsel, struct machine *machine)
444{
445 int err;
446 struct hists *hists = evsel__hists(evsel);
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465 struct result expected[] = {
466 { 7000, 2000, "perf", "perf", "main" },
467 { 5000, 0, "perf", "perf", "run_command" },
468 { 3000, 1000, "bash", "bash", "main" },
469 { 3000, 1000, "perf", "perf", "cmd_record" },
470 { 2000, 0, "bash", "libc", "malloc" },
471 { 1000, 1000, "bash", "[kernel]", "page_fault" },
472 { 1000, 1000, "bash", "bash", "xmalloc" },
473 { 1000, 1000, "perf", "[kernel]", "page_fault" },
474 { 1000, 1000, "perf", "[kernel]", "schedule" },
475 { 1000, 1000, "perf", "libc", "free" },
476 { 1000, 1000, "perf", "libc", "malloc" },
477 { 1000, 0, "perf", "[kernel]", "sys_perf_event_open" },
478 };
479
480 symbol_conf.use_callchain = false;
481 symbol_conf.cumulate_callchain = true;
482 perf_evsel__reset_sample_bit(evsel, CALLCHAIN);
483
484 setup_sorting(NULL);
485 callchain_register_param(&callchain_param);
486
487 err = add_hist_entries(hists, machine);
488 if (err < 0)
489 goto out;
490
491 err = do_test(hists, expected, ARRAY_SIZE(expected), NULL, 0);
492
493out:
494 del_hist_entries(hists);
495 reset_output_field();
496 return err;
497}
498
499
500static int test4(struct perf_evsel *evsel, struct machine *machine)
501{
502 int err;
503 struct hists *hists = evsel__hists(evsel);
504
505
506
507
508
509
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 struct result expected[] = {
586 { 7000, 2000, "perf", "perf", "main" },
587 { 5000, 0, "perf", "perf", "run_command" },
588 { 3000, 1000, "bash", "bash", "main" },
589 { 3000, 1000, "perf", "perf", "cmd_record" },
590 { 2000, 0, "bash", "libc", "malloc" },
591 { 1000, 1000, "bash", "[kernel]", "page_fault" },
592 { 1000, 1000, "bash", "bash", "xmalloc" },
593 { 1000, 0, "perf", "[kernel]", "sys_perf_event_open" },
594 { 1000, 1000, "perf", "[kernel]", "page_fault" },
595 { 1000, 1000, "perf", "[kernel]", "schedule" },
596 { 1000, 1000, "perf", "libc", "free" },
597 { 1000, 1000, "perf", "libc", "malloc" },
598 };
599 struct callchain_result expected_callchain[] = {
600 {
601 1, { { "perf", "main" }, },
602 },
603 {
604 2, { { "perf", "run_command" },
605 { "perf", "main" }, },
606 },
607 {
608 1, { { "bash", "main" }, },
609 },
610 {
611 3, { { "perf", "cmd_record" },
612 { "perf", "run_command" },
613 { "perf", "main" }, },
614 },
615 {
616 4, { { "libc", "malloc" },
617 { "bash", "xmalloc" },
618 { "bash", "main" },
619 { "bash", "main" }, },
620 },
621 {
622 3, { { "[kernel]", "page_fault" },
623 { "libc", "malloc" },
624 { "bash", "main" }, },
625 },
626 {
627 6, { { "bash", "xmalloc" },
628 { "libc", "malloc" },
629 { "bash", "xmalloc" },
630 { "libc", "malloc" },
631 { "bash", "xmalloc" },
632 { "bash", "main" }, },
633 },
634 {
635 3, { { "[kernel]", "sys_perf_event_open" },
636 { "perf", "run_command" },
637 { "perf", "main" }, },
638 },
639 {
640 4, { { "[kernel]", "page_fault" },
641 { "[kernel]", "sys_perf_event_open" },
642 { "perf", "run_command" },
643 { "perf", "main" }, },
644 },
645 {
646 3, { { "[kernel]", "schedule" },
647 { "perf", "run_command" },
648 { "perf", "main" }, },
649 },
650 {
651 4, { { "libc", "free" },
652 { "perf", "cmd_record" },
653 { "perf", "run_command" },
654 { "perf", "main" }, },
655 },
656 {
657 4, { { "libc", "malloc" },
658 { "perf", "cmd_record" },
659 { "perf", "run_command" },
660 { "perf", "main" }, },
661 },
662 };
663
664 symbol_conf.use_callchain = true;
665 symbol_conf.cumulate_callchain = true;
666 perf_evsel__set_sample_bit(evsel, CALLCHAIN);
667
668 setup_sorting(NULL);
669 callchain_register_param(&callchain_param);
670
671 err = add_hist_entries(hists, machine);
672 if (err < 0)
673 goto out;
674
675 err = do_test(hists, expected, ARRAY_SIZE(expected),
676 expected_callchain, ARRAY_SIZE(expected_callchain));
677
678out:
679 del_hist_entries(hists);
680 reset_output_field();
681 return err;
682}
683
684int test__hists_cumulate(int subtest __maybe_unused)
685{
686 int err = TEST_FAIL;
687 struct machines machines;
688 struct machine *machine;
689 struct perf_evsel *evsel;
690 struct perf_evlist *evlist = perf_evlist__new();
691 size_t i;
692 test_fn_t testcases[] = {
693 test1,
694 test2,
695 test3,
696 test4,
697 };
698
699 TEST_ASSERT_VAL("No memory", evlist);
700
701 err = parse_events(evlist, "cpu-clock", NULL);
702 if (err)
703 goto out;
704 err = TEST_FAIL;
705
706 machines__init(&machines);
707
708
709 machine = setup_fake_machine(&machines);
710 if (!machine)
711 goto out;
712
713 if (verbose > 1)
714 machine__fprintf(machine, stderr);
715
716 evsel = perf_evlist__first(evlist);
717
718 for (i = 0; i < ARRAY_SIZE(testcases); i++) {
719 err = testcases[i](evsel, machine);
720 if (err < 0)
721 break;
722 }
723
724out:
725
726 perf_evlist__delete(evlist);
727 machines__exit(&machines);
728
729 return err;
730}
731