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 const union perf_event event = {
85 .header = {
86 .misc = PERF_RECORD_MISC_USER,
87 },
88 };
89 struct hist_entry_iter iter = {
90 .hide_unresolved = false,
91 };
92
93 if (symbol_conf.cumulate_callchain)
94 iter.ops = &hist_iter_cumulative;
95 else
96 iter.ops = &hist_iter_normal;
97
98 sample.pid = fake_samples[i].pid;
99 sample.tid = fake_samples[i].pid;
100 sample.ip = fake_samples[i].ip;
101 sample.callchain = (struct ip_callchain *)fake_callchains[i];
102
103 if (perf_event__preprocess_sample(&event, machine, &al,
104 &sample) < 0)
105 goto out;
106
107 if (hist_entry_iter__add(&iter, &al, evsel, &sample,
108 PERF_MAX_STACK_DEPTH, NULL) < 0)
109 goto out;
110
111 fake_samples[i].thread = al.thread;
112 fake_samples[i].map = al.map;
113 fake_samples[i].sym = al.sym;
114 }
115
116 return TEST_OK;
117
118out:
119 pr_debug("Not enough memory for adding a hist entry\n");
120 return TEST_FAIL;
121}
122
123static void del_hist_entries(struct hists *hists)
124{
125 struct hist_entry *he;
126 struct rb_root *root_in;
127 struct rb_root *root_out;
128 struct rb_node *node;
129
130 if (sort__need_collapse)
131 root_in = &hists->entries_collapsed;
132 else
133 root_in = hists->entries_in;
134
135 root_out = &hists->entries;
136
137 while (!RB_EMPTY_ROOT(root_out)) {
138 node = rb_first(root_out);
139
140 he = rb_entry(node, struct hist_entry, rb_node);
141 rb_erase(node, root_out);
142 rb_erase(&he->rb_node_in, root_in);
143 hist_entry__delete(he);
144 }
145}
146
147typedef int (*test_fn_t)(struct perf_evsel *, struct machine *);
148
149#define COMM(he) (thread__comm_str(he->thread))
150#define DSO(he) (he->ms.map->dso->short_name)
151#define SYM(he) (he->ms.sym->name)
152#define CPU(he) (he->cpu)
153#define PID(he) (he->thread->tid)
154#define DEPTH(he) (he->callchain->max_depth)
155#define CDSO(cl) (cl->ms.map->dso->short_name)
156#define CSYM(cl) (cl->ms.sym->name)
157
158struct result {
159 u64 children;
160 u64 self;
161 const char *comm;
162 const char *dso;
163 const char *sym;
164};
165
166struct callchain_result {
167 u64 nr;
168 struct {
169 const char *dso;
170 const char *sym;
171 } node[10];
172};
173
174static int do_test(struct hists *hists, struct result *expected, size_t nr_expected,
175 struct callchain_result *expected_callchain, size_t nr_callchain)
176{
177 char buf[32];
178 size_t i, c;
179 struct hist_entry *he;
180 struct rb_root *root;
181 struct rb_node *node;
182 struct callchain_node *cnode;
183 struct callchain_list *clist;
184
185
186
187
188
189 hists__collapse_resort(hists, NULL);
190 hists__output_resort(hists, NULL);
191
192 if (verbose > 2) {
193 pr_info("use callchain: %d, cumulate callchain: %d\n",
194 symbol_conf.use_callchain,
195 symbol_conf.cumulate_callchain);
196 print_hists_out(hists);
197 }
198
199 root = &hists->entries;
200 for (node = rb_first(root), i = 0;
201 node && (he = rb_entry(node, struct hist_entry, rb_node));
202 node = rb_next(node), i++) {
203 scnprintf(buf, sizeof(buf), "Invalid hist entry #%zd", i);
204
205 TEST_ASSERT_VAL("Incorrect number of hist entry",
206 i < nr_expected);
207 TEST_ASSERT_VAL(buf, he->stat.period == expected[i].self &&
208 !strcmp(COMM(he), expected[i].comm) &&
209 !strcmp(DSO(he), expected[i].dso) &&
210 !strcmp(SYM(he), expected[i].sym));
211
212 if (symbol_conf.cumulate_callchain)
213 TEST_ASSERT_VAL(buf, he->stat_acc->period == expected[i].children);
214
215 if (!symbol_conf.use_callchain)
216 continue;
217
218
219 root = &he->callchain->node.rb_root;
220 cnode = rb_entry(rb_first(root), struct callchain_node, rb_node);
221
222 c = 0;
223 list_for_each_entry(clist, &cnode->val, list) {
224 scnprintf(buf, sizeof(buf), "Invalid callchain entry #%zd/%zd", i, c);
225
226 TEST_ASSERT_VAL("Incorrect number of callchain entry",
227 c < expected_callchain[i].nr);
228 TEST_ASSERT_VAL(buf,
229 !strcmp(CDSO(clist), expected_callchain[i].node[c].dso) &&
230 !strcmp(CSYM(clist), expected_callchain[i].node[c].sym));
231 c++;
232 }
233
234 TEST_ASSERT_VAL("Incorrect number of callchain entry",
235 c <= expected_callchain[i].nr);
236 }
237 TEST_ASSERT_VAL("Incorrect number of hist entry",
238 i == nr_expected);
239 TEST_ASSERT_VAL("Incorrect number of callchain entry",
240 !symbol_conf.use_callchain || nr_expected == nr_callchain);
241 return 0;
242}
243
244
245static int test1(struct perf_evsel *evsel, struct machine *machine)
246{
247 int err;
248 struct hists *hists = evsel__hists(evsel);
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264 struct result expected[] = {
265 { 0, 2000, "perf", "perf", "main" },
266 { 0, 1000, "bash", "[kernel]", "page_fault" },
267 { 0, 1000, "bash", "bash", "main" },
268 { 0, 1000, "bash", "bash", "xmalloc" },
269 { 0, 1000, "perf", "[kernel]", "page_fault" },
270 { 0, 1000, "perf", "[kernel]", "schedule" },
271 { 0, 1000, "perf", "libc", "free" },
272 { 0, 1000, "perf", "libc", "malloc" },
273 { 0, 1000, "perf", "perf", "cmd_record" },
274 };
275
276 symbol_conf.use_callchain = false;
277 symbol_conf.cumulate_callchain = false;
278
279 setup_sorting();
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
425 setup_sorting();
426 callchain_register_param(&callchain_param);
427
428 err = add_hist_entries(hists, machine);
429 if (err < 0)
430 goto out;
431
432 err = do_test(hists, expected, ARRAY_SIZE(expected),
433 expected_callchain, ARRAY_SIZE(expected_callchain));
434
435out:
436 del_hist_entries(hists);
437 reset_output_field();
438 return err;
439}
440
441
442static int test3(struct perf_evsel *evsel, struct machine *machine)
443{
444 int err;
445 struct hists *hists = evsel__hists(evsel);
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464 struct result expected[] = {
465 { 7000, 2000, "perf", "perf", "main" },
466 { 5000, 0, "perf", "perf", "run_command" },
467 { 3000, 1000, "bash", "bash", "main" },
468 { 3000, 1000, "perf", "perf", "cmd_record" },
469 { 2000, 0, "bash", "libc", "malloc" },
470 { 1000, 1000, "bash", "[kernel]", "page_fault" },
471 { 1000, 1000, "bash", "bash", "xmalloc" },
472 { 1000, 1000, "perf", "[kernel]", "page_fault" },
473 { 1000, 1000, "perf", "[kernel]", "schedule" },
474 { 1000, 1000, "perf", "libc", "free" },
475 { 1000, 1000, "perf", "libc", "malloc" },
476 { 1000, 0, "perf", "[kernel]", "sys_perf_event_open" },
477 };
478
479 symbol_conf.use_callchain = false;
480 symbol_conf.cumulate_callchain = true;
481
482 setup_sorting();
483 callchain_register_param(&callchain_param);
484
485 err = add_hist_entries(hists, machine);
486 if (err < 0)
487 goto out;
488
489 err = do_test(hists, expected, ARRAY_SIZE(expected), NULL, 0);
490
491out:
492 del_hist_entries(hists);
493 reset_output_field();
494 return err;
495}
496
497
498static int test4(struct perf_evsel *evsel, struct machine *machine)
499{
500 int err;
501 struct hists *hists = evsel__hists(evsel);
502
503
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 struct result expected[] = {
584 { 7000, 2000, "perf", "perf", "main" },
585 { 5000, 0, "perf", "perf", "run_command" },
586 { 3000, 1000, "bash", "bash", "main" },
587 { 3000, 1000, "perf", "perf", "cmd_record" },
588 { 2000, 0, "bash", "libc", "malloc" },
589 { 1000, 1000, "bash", "[kernel]", "page_fault" },
590 { 1000, 1000, "bash", "bash", "xmalloc" },
591 { 1000, 0, "perf", "[kernel]", "sys_perf_event_open" },
592 { 1000, 1000, "perf", "[kernel]", "page_fault" },
593 { 1000, 1000, "perf", "[kernel]", "schedule" },
594 { 1000, 1000, "perf", "libc", "free" },
595 { 1000, 1000, "perf", "libc", "malloc" },
596 };
597 struct callchain_result expected_callchain[] = {
598 {
599 1, { { "perf", "main" }, },
600 },
601 {
602 2, { { "perf", "run_command" },
603 { "perf", "main" }, },
604 },
605 {
606 1, { { "bash", "main" }, },
607 },
608 {
609 3, { { "perf", "cmd_record" },
610 { "perf", "run_command" },
611 { "perf", "main" }, },
612 },
613 {
614 4, { { "libc", "malloc" },
615 { "bash", "xmalloc" },
616 { "bash", "main" },
617 { "bash", "main" }, },
618 },
619 {
620 3, { { "[kernel]", "page_fault" },
621 { "libc", "malloc" },
622 { "bash", "main" }, },
623 },
624 {
625 6, { { "bash", "xmalloc" },
626 { "libc", "malloc" },
627 { "bash", "xmalloc" },
628 { "libc", "malloc" },
629 { "bash", "xmalloc" },
630 { "bash", "main" }, },
631 },
632 {
633 3, { { "[kernel]", "sys_perf_event_open" },
634 { "perf", "run_command" },
635 { "perf", "main" }, },
636 },
637 {
638 4, { { "[kernel]", "page_fault" },
639 { "[kernel]", "sys_perf_event_open" },
640 { "perf", "run_command" },
641 { "perf", "main" }, },
642 },
643 {
644 3, { { "[kernel]", "schedule" },
645 { "perf", "run_command" },
646 { "perf", "main" }, },
647 },
648 {
649 4, { { "libc", "free" },
650 { "perf", "cmd_record" },
651 { "perf", "run_command" },
652 { "perf", "main" }, },
653 },
654 {
655 4, { { "libc", "malloc" },
656 { "perf", "cmd_record" },
657 { "perf", "run_command" },
658 { "perf", "main" }, },
659 },
660 };
661
662 symbol_conf.use_callchain = true;
663 symbol_conf.cumulate_callchain = true;
664
665 setup_sorting();
666 callchain_register_param(&callchain_param);
667
668 err = add_hist_entries(hists, machine);
669 if (err < 0)
670 goto out;
671
672 err = do_test(hists, expected, ARRAY_SIZE(expected),
673 expected_callchain, ARRAY_SIZE(expected_callchain));
674
675out:
676 del_hist_entries(hists);
677 reset_output_field();
678 return err;
679}
680
681int test__hists_cumulate(void)
682{
683 int err = TEST_FAIL;
684 struct machines machines;
685 struct machine *machine;
686 struct perf_evsel *evsel;
687 struct perf_evlist *evlist = perf_evlist__new();
688 size_t i;
689 test_fn_t testcases[] = {
690 test1,
691 test2,
692 test3,
693 test4,
694 };
695
696 TEST_ASSERT_VAL("No memory", evlist);
697
698 err = parse_events(evlist, "cpu-clock");
699 if (err)
700 goto out;
701
702 machines__init(&machines);
703
704
705 machine = setup_fake_machine(&machines);
706 if (!machine)
707 goto out;
708
709 if (verbose > 1)
710 machine__fprintf(machine, stderr);
711
712 evsel = perf_evlist__first(evlist);
713
714 for (i = 0; i < ARRAY_SIZE(testcases); i++) {
715 err = testcases[i](evsel, machine);
716 if (err < 0)
717 break;
718 }
719
720out:
721
722 perf_evlist__delete(evlist);
723 machines__exit(&machines);
724
725 return err;
726}
727