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, sysctl_perf_event_max_stack,
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 (hists__has(hists, 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
220 TEST_ASSERT_VAL("callchains expected", !RB_EMPTY_ROOT(root));
221 cnode = rb_entry(rb_first(root), struct callchain_node, rb_node);
222
223 c = 0;
224 list_for_each_entry(clist, &cnode->val, list) {
225 scnprintf(buf, sizeof(buf), "Invalid callchain entry #%zd/%zd", i, c);
226
227 TEST_ASSERT_VAL("Incorrect number of callchain entry",
228 c < expected_callchain[i].nr);
229 TEST_ASSERT_VAL(buf,
230 !strcmp(CDSO(clist), expected_callchain[i].node[c].dso) &&
231 !strcmp(CSYM(clist), expected_callchain[i].node[c].sym));
232 c++;
233 }
234
235 TEST_ASSERT_VAL("Incorrect number of callchain entry",
236 c <= expected_callchain[i].nr);
237 }
238 TEST_ASSERT_VAL("Incorrect number of hist entry",
239 i == nr_expected);
240 TEST_ASSERT_VAL("Incorrect number of callchain entry",
241 !symbol_conf.use_callchain || nr_expected == nr_callchain);
242 return 0;
243}
244
245
246static int test1(struct perf_evsel *evsel, struct machine *machine)
247{
248 int err;
249 struct hists *hists = evsel__hists(evsel);
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265 struct result expected[] = {
266 { 0, 2000, "perf", "perf", "main" },
267 { 0, 1000, "bash", "[kernel]", "page_fault" },
268 { 0, 1000, "bash", "bash", "main" },
269 { 0, 1000, "bash", "bash", "xmalloc" },
270 { 0, 1000, "perf", "[kernel]", "page_fault" },
271 { 0, 1000, "perf", "[kernel]", "schedule" },
272 { 0, 1000, "perf", "libc", "free" },
273 { 0, 1000, "perf", "libc", "malloc" },
274 { 0, 1000, "perf", "perf", "cmd_record" },
275 };
276
277 symbol_conf.use_callchain = false;
278 symbol_conf.cumulate_callchain = false;
279 perf_evsel__reset_sample_bit(evsel, CALLCHAIN);
280
281 setup_sorting(NULL);
282 callchain_register_param(&callchain_param);
283
284 err = add_hist_entries(hists, machine);
285 if (err < 0)
286 goto out;
287
288 err = do_test(hists, expected, ARRAY_SIZE(expected), NULL, 0);
289
290out:
291 del_hist_entries(hists);
292 reset_output_field();
293 return err;
294}
295
296
297static int test2(struct perf_evsel *evsel, struct machine *machine)
298{
299 int err;
300 struct hists *hists = evsel__hists(evsel);
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
362
363 struct result expected[] = {
364 { 0, 2000, "perf", "perf", "main" },
365 { 0, 1000, "bash", "[kernel]", "page_fault" },
366 { 0, 1000, "bash", "bash", "main" },
367 { 0, 1000, "bash", "bash", "xmalloc" },
368 { 0, 1000, "perf", "[kernel]", "page_fault" },
369 { 0, 1000, "perf", "[kernel]", "schedule" },
370 { 0, 1000, "perf", "libc", "free" },
371 { 0, 1000, "perf", "libc", "malloc" },
372 { 0, 1000, "perf", "perf", "cmd_record" },
373 };
374 struct callchain_result expected_callchain[] = {
375 {
376 1, { { "perf", "main" }, },
377 },
378 {
379 3, { { "[kernel]", "page_fault" },
380 { "libc", "malloc" },
381 { "bash", "main" }, },
382 },
383 {
384 1, { { "bash", "main" }, },
385 },
386 {
387 6, { { "bash", "xmalloc" },
388 { "libc", "malloc" },
389 { "bash", "xmalloc" },
390 { "libc", "malloc" },
391 { "bash", "xmalloc" },
392 { "bash", "main" }, },
393 },
394 {
395 4, { { "[kernel]", "page_fault" },
396 { "[kernel]", "sys_perf_event_open" },
397 { "perf", "run_command" },
398 { "perf", "main" }, },
399 },
400 {
401 3, { { "[kernel]", "schedule" },
402 { "perf", "run_command" },
403 { "perf", "main" }, },
404 },
405 {
406 4, { { "libc", "free" },
407 { "perf", "cmd_record" },
408 { "perf", "run_command" },
409 { "perf", "main" }, },
410 },
411 {
412 4, { { "libc", "malloc" },
413 { "perf", "cmd_record" },
414 { "perf", "run_command" },
415 { "perf", "main" }, },
416 },
417 {
418 3, { { "perf", "cmd_record" },
419 { "perf", "run_command" },
420 { "perf", "main" }, },
421 },
422 };
423
424 symbol_conf.use_callchain = true;
425 symbol_conf.cumulate_callchain = false;
426 perf_evsel__set_sample_bit(evsel, CALLCHAIN);
427
428 setup_sorting(NULL);
429 callchain_register_param(&callchain_param);
430
431 err = add_hist_entries(hists, machine);
432 if (err < 0)
433 goto out;
434
435 err = do_test(hists, expected, ARRAY_SIZE(expected),
436 expected_callchain, ARRAY_SIZE(expected_callchain));
437
438out:
439 del_hist_entries(hists);
440 reset_output_field();
441 return err;
442}
443
444
445static int test3(struct perf_evsel *evsel, struct machine *machine)
446{
447 int err;
448 struct hists *hists = evsel__hists(evsel);
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467 struct result expected[] = {
468 { 7000, 2000, "perf", "perf", "main" },
469 { 5000, 0, "perf", "perf", "run_command" },
470 { 3000, 1000, "bash", "bash", "main" },
471 { 3000, 1000, "perf", "perf", "cmd_record" },
472 { 2000, 0, "bash", "libc", "malloc" },
473 { 1000, 1000, "bash", "[kernel]", "page_fault" },
474 { 1000, 1000, "bash", "bash", "xmalloc" },
475 { 1000, 1000, "perf", "[kernel]", "page_fault" },
476 { 1000, 1000, "perf", "[kernel]", "schedule" },
477 { 1000, 1000, "perf", "libc", "free" },
478 { 1000, 1000, "perf", "libc", "malloc" },
479 { 1000, 0, "perf", "[kernel]", "sys_perf_event_open" },
480 };
481
482 symbol_conf.use_callchain = false;
483 symbol_conf.cumulate_callchain = true;
484 perf_evsel__reset_sample_bit(evsel, CALLCHAIN);
485
486 setup_sorting(NULL);
487 callchain_register_param(&callchain_param);
488
489 err = add_hist_entries(hists, machine);
490 if (err < 0)
491 goto out;
492
493 err = do_test(hists, expected, ARRAY_SIZE(expected), NULL, 0);
494
495out:
496 del_hist_entries(hists);
497 reset_output_field();
498 return err;
499}
500
501
502static int test4(struct perf_evsel *evsel, struct machine *machine)
503{
504 int err;
505 struct hists *hists = evsel__hists(evsel);
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
586
587 struct result expected[] = {
588 { 7000, 2000, "perf", "perf", "main" },
589 { 5000, 0, "perf", "perf", "run_command" },
590 { 3000, 1000, "bash", "bash", "main" },
591 { 3000, 1000, "perf", "perf", "cmd_record" },
592 { 2000, 0, "bash", "libc", "malloc" },
593 { 1000, 1000, "bash", "[kernel]", "page_fault" },
594 { 1000, 1000, "bash", "bash", "xmalloc" },
595 { 1000, 0, "perf", "[kernel]", "sys_perf_event_open" },
596 { 1000, 1000, "perf", "[kernel]", "page_fault" },
597 { 1000, 1000, "perf", "[kernel]", "schedule" },
598 { 1000, 1000, "perf", "libc", "free" },
599 { 1000, 1000, "perf", "libc", "malloc" },
600 };
601 struct callchain_result expected_callchain[] = {
602 {
603 1, { { "perf", "main" }, },
604 },
605 {
606 2, { { "perf", "run_command" },
607 { "perf", "main" }, },
608 },
609 {
610 1, { { "bash", "main" }, },
611 },
612 {
613 3, { { "perf", "cmd_record" },
614 { "perf", "run_command" },
615 { "perf", "main" }, },
616 },
617 {
618 4, { { "libc", "malloc" },
619 { "bash", "xmalloc" },
620 { "bash", "main" },
621 { "bash", "main" }, },
622 },
623 {
624 3, { { "[kernel]", "page_fault" },
625 { "libc", "malloc" },
626 { "bash", "main" }, },
627 },
628 {
629 6, { { "bash", "xmalloc" },
630 { "libc", "malloc" },
631 { "bash", "xmalloc" },
632 { "libc", "malloc" },
633 { "bash", "xmalloc" },
634 { "bash", "main" }, },
635 },
636 {
637 3, { { "[kernel]", "sys_perf_event_open" },
638 { "perf", "run_command" },
639 { "perf", "main" }, },
640 },
641 {
642 4, { { "[kernel]", "page_fault" },
643 { "[kernel]", "sys_perf_event_open" },
644 { "perf", "run_command" },
645 { "perf", "main" }, },
646 },
647 {
648 3, { { "[kernel]", "schedule" },
649 { "perf", "run_command" },
650 { "perf", "main" }, },
651 },
652 {
653 4, { { "libc", "free" },
654 { "perf", "cmd_record" },
655 { "perf", "run_command" },
656 { "perf", "main" }, },
657 },
658 {
659 4, { { "libc", "malloc" },
660 { "perf", "cmd_record" },
661 { "perf", "run_command" },
662 { "perf", "main" }, },
663 },
664 };
665
666 symbol_conf.use_callchain = true;
667 symbol_conf.cumulate_callchain = true;
668 perf_evsel__set_sample_bit(evsel, CALLCHAIN);
669
670 setup_sorting(NULL);
671
672 callchain_param = callchain_param_default;
673 callchain_register_param(&callchain_param);
674
675 err = add_hist_entries(hists, machine);
676 if (err < 0)
677 goto out;
678
679 err = do_test(hists, expected, ARRAY_SIZE(expected),
680 expected_callchain, ARRAY_SIZE(expected_callchain));
681
682out:
683 del_hist_entries(hists);
684 reset_output_field();
685 return err;
686}
687
688int test__hists_cumulate(int subtest __maybe_unused)
689{
690 int err = TEST_FAIL;
691 struct machines machines;
692 struct machine *machine;
693 struct perf_evsel *evsel;
694 struct perf_evlist *evlist = perf_evlist__new();
695 size_t i;
696 test_fn_t testcases[] = {
697 test1,
698 test2,
699 test3,
700 test4,
701 };
702
703 TEST_ASSERT_VAL("No memory", evlist);
704
705 err = parse_events(evlist, "cpu-clock", NULL);
706 if (err)
707 goto out;
708 err = TEST_FAIL;
709
710 machines__init(&machines);
711
712
713 machine = setup_fake_machine(&machines);
714 if (!machine)
715 goto out;
716
717 if (verbose > 1)
718 machine__fprintf(machine, stderr);
719
720 evsel = perf_evlist__first(evlist);
721
722 for (i = 0; i < ARRAY_SIZE(testcases); i++) {
723 err = testcases[i](evsel, machine);
724 if (err < 0)
725 break;
726 }
727
728out:
729
730 perf_evlist__delete(evlist);
731 machines__exit(&machines);
732
733 return err;
734}
735