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/thread.h"
12 #include "util/parse-events.h"
13 #include "tests/tests.h"
14 #include "tests/hists_common.h"
15 #include <linux/kernel.h>
21 struct thread
*thread
;
26 /* For the numbers, see hists_common.c */
27 static struct sample fake_samples
[] = {
28 /* perf [kernel] schedule() */
29 { .cpu
= 0, .pid
= FAKE_PID_PERF1
, .ip
= FAKE_IP_KERNEL_SCHEDULE
, },
30 /* perf [perf] main() */
31 { .cpu
= 1, .pid
= FAKE_PID_PERF1
, .ip
= FAKE_IP_PERF_MAIN
, },
32 /* perf [perf] cmd_record() */
33 { .cpu
= 1, .pid
= FAKE_PID_PERF1
, .ip
= FAKE_IP_PERF_CMD_RECORD
, },
34 /* perf [libc] malloc() */
35 { .cpu
= 1, .pid
= FAKE_PID_PERF1
, .ip
= FAKE_IP_LIBC_MALLOC
, },
36 /* perf [libc] free() */
37 { .cpu
= 2, .pid
= FAKE_PID_PERF1
, .ip
= FAKE_IP_LIBC_FREE
, },
38 /* perf [perf] main() */
39 { .cpu
= 2, .pid
= FAKE_PID_PERF2
, .ip
= FAKE_IP_PERF_MAIN
, },
40 /* perf [kernel] page_fault() */
41 { .cpu
= 2, .pid
= FAKE_PID_PERF2
, .ip
= FAKE_IP_KERNEL_PAGE_FAULT
, },
42 /* bash [bash] main() */
43 { .cpu
= 3, .pid
= FAKE_PID_BASH
, .ip
= FAKE_IP_BASH_MAIN
, },
44 /* bash [bash] xmalloc() */
45 { .cpu
= 0, .pid
= FAKE_PID_BASH
, .ip
= FAKE_IP_BASH_XMALLOC
, },
46 /* bash [kernel] page_fault() */
47 { .cpu
= 1, .pid
= FAKE_PID_BASH
, .ip
= FAKE_IP_KERNEL_PAGE_FAULT
, },
50 static int add_hist_entries(struct hists
*hists
, struct machine
*machine
)
52 struct addr_location al
;
53 struct evsel
*evsel
= hists_to_evsel(hists
);
54 struct perf_sample sample
= { .period
= 100, };
57 addr_location__init(&al
);
58 for (i
= 0; i
< ARRAY_SIZE(fake_samples
); i
++) {
59 struct hist_entry_iter iter
= {
62 .ops
= &hist_iter_normal
,
63 .hide_unresolved
= false,
66 sample
.cpumode
= PERF_RECORD_MISC_USER
;
67 sample
.cpu
= fake_samples
[i
].cpu
;
68 sample
.pid
= fake_samples
[i
].pid
;
69 sample
.tid
= fake_samples
[i
].pid
;
70 sample
.ip
= fake_samples
[i
].ip
;
72 if (machine__resolve(machine
, &al
, &sample
) < 0)
75 if (hist_entry_iter__add(&iter
, &al
, sysctl_perf_event_max_stack
,
80 fake_samples
[i
].thread
= al
.thread
;
81 map__put(fake_samples
[i
].map
);
82 fake_samples
[i
].map
= map__get(al
.map
);
83 fake_samples
[i
].sym
= al
.sym
;
86 addr_location__exit(&al
);
90 pr_debug("Not enough memory for adding a hist entry\n");
91 addr_location__exit(&al
);
95 static void del_hist_entries(struct hists
*hists
)
97 struct hist_entry
*he
;
98 struct rb_root_cached
*root_in
;
99 struct rb_root_cached
*root_out
;
100 struct rb_node
*node
;
102 if (hists__has(hists
, need_collapse
))
103 root_in
= &hists
->entries_collapsed
;
105 root_in
= hists
->entries_in
;
107 root_out
= &hists
->entries
;
109 while (!RB_EMPTY_ROOT(&root_out
->rb_root
)) {
110 node
= rb_first_cached(root_out
);
112 he
= rb_entry(node
, struct hist_entry
, rb_node
);
113 rb_erase_cached(node
, root_out
);
114 rb_erase_cached(&he
->rb_node_in
, root_in
);
115 hist_entry__delete(he
);
119 static void put_fake_samples(void)
123 for (i
= 0; i
< ARRAY_SIZE(fake_samples
); i
++) {
124 map__put(fake_samples
[i
].map
);
125 fake_samples
[i
].map
= NULL
;
129 typedef int (*test_fn_t
)(struct evsel
*, struct machine
*);
131 #define COMM(he) (thread__comm_str(he->thread))
132 #define DSO(he) (dso__short_name(map__dso(he->ms.map)))
133 #define SYM(he) (he->ms.sym->name)
134 #define CPU(he) (he->cpu)
135 #define PID(he) (thread__tid(he->thread))
137 /* default sort keys (no field) */
138 static int test1(struct evsel
*evsel
, struct machine
*machine
)
141 struct hists
*hists
= evsel__hists(evsel
);
142 struct hist_entry
*he
;
143 struct rb_root_cached
*root
;
144 struct rb_node
*node
;
147 sort_order
= NULL
; /* equivalent to sort_order = "comm,dso,sym" */
154 * Overhead Command Shared Object Symbol
155 * ======== ======= ============= ==============
156 * 20.00% perf perf [.] main
157 * 10.00% bash [kernel] [k] page_fault
158 * 10.00% bash bash [.] main
159 * 10.00% bash bash [.] xmalloc
160 * 10.00% perf [kernel] [k] page_fault
161 * 10.00% perf [kernel] [k] schedule
162 * 10.00% perf libc [.] free
163 * 10.00% perf libc [.] malloc
164 * 10.00% perf perf [.] cmd_record
166 err
= add_hist_entries(hists
, machine
);
170 hists__collapse_resort(hists
, NULL
);
171 evsel__output_resort(evsel
, NULL
);
174 pr_info("[fields = %s, sort = %s]\n", field_order
, sort_order
);
175 print_hists_out(hists
);
178 root
= &hists
->entries
;
179 node
= rb_first_cached(root
);
180 he
= rb_entry(node
, struct hist_entry
, rb_node
);
181 TEST_ASSERT_VAL("Invalid hist entry",
182 !strcmp(COMM(he
), "perf") && !strcmp(DSO(he
), "perf") &&
183 !strcmp(SYM(he
), "main") && he
->stat
.period
== 200);
185 node
= rb_next(node
);
186 he
= rb_entry(node
, struct hist_entry
, rb_node
);
187 TEST_ASSERT_VAL("Invalid hist entry",
188 !strcmp(COMM(he
), "bash") && !strcmp(DSO(he
), "[kernel]") &&
189 !strcmp(SYM(he
), "page_fault") && he
->stat
.period
== 100);
191 node
= rb_next(node
);
192 he
= rb_entry(node
, struct hist_entry
, rb_node
);
193 TEST_ASSERT_VAL("Invalid hist entry",
194 !strcmp(COMM(he
), "bash") && !strcmp(DSO(he
), "bash") &&
195 !strcmp(SYM(he
), "main") && he
->stat
.period
== 100);
197 node
= rb_next(node
);
198 he
= rb_entry(node
, struct hist_entry
, rb_node
);
199 TEST_ASSERT_VAL("Invalid hist entry",
200 !strcmp(COMM(he
), "bash") && !strcmp(DSO(he
), "bash") &&
201 !strcmp(SYM(he
), "xmalloc") && he
->stat
.period
== 100);
203 node
= rb_next(node
);
204 he
= rb_entry(node
, struct hist_entry
, rb_node
);
205 TEST_ASSERT_VAL("Invalid hist entry",
206 !strcmp(COMM(he
), "perf") && !strcmp(DSO(he
), "[kernel]") &&
207 !strcmp(SYM(he
), "page_fault") && he
->stat
.period
== 100);
209 node
= rb_next(node
);
210 he
= rb_entry(node
, struct hist_entry
, rb_node
);
211 TEST_ASSERT_VAL("Invalid hist entry",
212 !strcmp(COMM(he
), "perf") && !strcmp(DSO(he
), "[kernel]") &&
213 !strcmp(SYM(he
), "schedule") && he
->stat
.period
== 100);
215 node
= rb_next(node
);
216 he
= rb_entry(node
, struct hist_entry
, rb_node
);
217 TEST_ASSERT_VAL("Invalid hist entry",
218 !strcmp(COMM(he
), "perf") && !strcmp(DSO(he
), "libc") &&
219 !strcmp(SYM(he
), "free") && he
->stat
.period
== 100);
221 node
= rb_next(node
);
222 he
= rb_entry(node
, struct hist_entry
, rb_node
);
223 TEST_ASSERT_VAL("Invalid hist entry",
224 !strcmp(COMM(he
), "perf") && !strcmp(DSO(he
), "libc") &&
225 !strcmp(SYM(he
), "malloc") && he
->stat
.period
== 100);
227 node
= rb_next(node
);
228 he
= rb_entry(node
, struct hist_entry
, rb_node
);
229 TEST_ASSERT_VAL("Invalid hist entry",
230 !strcmp(COMM(he
), "perf") && !strcmp(DSO(he
), "perf") &&
231 !strcmp(SYM(he
), "cmd_record") && he
->stat
.period
== 100);
234 del_hist_entries(hists
);
235 reset_output_field();
239 /* mixed fields and sort keys */
240 static int test2(struct evsel
*evsel
, struct machine
*machine
)
243 struct hists
*hists
= evsel__hists(evsel
);
244 struct hist_entry
*he
;
245 struct rb_root_cached
*root
;
246 struct rb_node
*node
;
248 field_order
= "overhead,cpu";
256 * Overhead CPU Command: Pid
257 * ======== === =============
258 * 30.00% 1 perf : 100
259 * 10.00% 0 perf : 100
260 * 10.00% 2 perf : 100
261 * 20.00% 2 perf : 200
262 * 10.00% 0 bash : 300
263 * 10.00% 1 bash : 300
264 * 10.00% 3 bash : 300
266 err
= add_hist_entries(hists
, machine
);
270 hists__collapse_resort(hists
, NULL
);
271 evsel__output_resort(evsel
, NULL
);
274 pr_info("[fields = %s, sort = %s]\n", field_order
, sort_order
);
275 print_hists_out(hists
);
278 root
= &hists
->entries
;
279 node
= rb_first_cached(root
);
280 he
= rb_entry(node
, struct hist_entry
, rb_node
);
281 TEST_ASSERT_VAL("Invalid hist entry",
282 CPU(he
) == 1 && PID(he
) == 100 && he
->stat
.period
== 300);
284 node
= rb_next(node
);
285 he
= rb_entry(node
, struct hist_entry
, rb_node
);
286 TEST_ASSERT_VAL("Invalid hist entry",
287 CPU(he
) == 0 && PID(he
) == 100 && he
->stat
.period
== 100);
290 del_hist_entries(hists
);
291 reset_output_field();
295 /* fields only (no sort key) */
296 static int test3(struct evsel
*evsel
, struct machine
*machine
)
299 struct hists
*hists
= evsel__hists(evsel
);
300 struct hist_entry
*he
;
301 struct rb_root_cached
*root
;
302 struct rb_node
*node
;
304 field_order
= "comm,overhead,dso";
312 * Command Overhead Shared Object
313 * ======= ======== =============
315 * bash 10.00% [kernel]
317 * perf 20.00% [kernel]
320 err
= add_hist_entries(hists
, machine
);
324 hists__collapse_resort(hists
, NULL
);
325 evsel__output_resort(evsel
, NULL
);
328 pr_info("[fields = %s, sort = %s]\n", field_order
, sort_order
);
329 print_hists_out(hists
);
332 root
= &hists
->entries
;
333 node
= rb_first_cached(root
);
334 he
= rb_entry(node
, struct hist_entry
, rb_node
);
335 TEST_ASSERT_VAL("Invalid hist entry",
336 !strcmp(COMM(he
), "bash") && !strcmp(DSO(he
), "bash") &&
337 he
->stat
.period
== 200);
339 node
= rb_next(node
);
340 he
= rb_entry(node
, struct hist_entry
, rb_node
);
341 TEST_ASSERT_VAL("Invalid hist entry",
342 !strcmp(COMM(he
), "bash") && !strcmp(DSO(he
), "[kernel]") &&
343 he
->stat
.period
== 100);
345 node
= rb_next(node
);
346 he
= rb_entry(node
, struct hist_entry
, rb_node
);
347 TEST_ASSERT_VAL("Invalid hist entry",
348 !strcmp(COMM(he
), "perf") && !strcmp(DSO(he
), "perf") &&
349 he
->stat
.period
== 300);
351 node
= rb_next(node
);
352 he
= rb_entry(node
, struct hist_entry
, rb_node
);
353 TEST_ASSERT_VAL("Invalid hist entry",
354 !strcmp(COMM(he
), "perf") && !strcmp(DSO(he
), "[kernel]") &&
355 he
->stat
.period
== 200);
357 node
= rb_next(node
);
358 he
= rb_entry(node
, struct hist_entry
, rb_node
);
359 TEST_ASSERT_VAL("Invalid hist entry",
360 !strcmp(COMM(he
), "perf") && !strcmp(DSO(he
), "libc") &&
361 he
->stat
.period
== 200);
364 del_hist_entries(hists
);
365 reset_output_field();
369 /* handle duplicate 'dso' field */
370 static int test4(struct evsel
*evsel
, struct machine
*machine
)
373 struct hists
*hists
= evsel__hists(evsel
);
374 struct hist_entry
*he
;
375 struct rb_root_cached
*root
;
376 struct rb_node
*node
;
378 field_order
= "dso,sym,comm,overhead,dso";
386 * Shared Object Symbol Command Overhead
387 * ============= ============== ======= ========
388 * perf [.] cmd_record perf 10.00%
389 * libc [.] free perf 10.00%
390 * bash [.] main bash 10.00%
391 * perf [.] main perf 20.00%
392 * libc [.] malloc perf 10.00%
393 * [kernel] [k] page_fault bash 10.00%
394 * [kernel] [k] page_fault perf 10.00%
395 * [kernel] [k] schedule perf 10.00%
396 * bash [.] xmalloc bash 10.00%
398 err
= add_hist_entries(hists
, machine
);
402 hists__collapse_resort(hists
, NULL
);
403 evsel__output_resort(evsel
, NULL
);
406 pr_info("[fields = %s, sort = %s]\n", field_order
, sort_order
);
407 print_hists_out(hists
);
410 root
= &hists
->entries
;
411 node
= rb_first_cached(root
);
412 he
= rb_entry(node
, struct hist_entry
, rb_node
);
413 TEST_ASSERT_VAL("Invalid hist entry",
414 !strcmp(DSO(he
), "perf") && !strcmp(SYM(he
), "cmd_record") &&
415 !strcmp(COMM(he
), "perf") && he
->stat
.period
== 100);
417 node
= rb_next(node
);
418 he
= rb_entry(node
, struct hist_entry
, rb_node
);
419 TEST_ASSERT_VAL("Invalid hist entry",
420 !strcmp(DSO(he
), "libc") && !strcmp(SYM(he
), "free") &&
421 !strcmp(COMM(he
), "perf") && he
->stat
.period
== 100);
423 node
= rb_next(node
);
424 he
= rb_entry(node
, struct hist_entry
, rb_node
);
425 TEST_ASSERT_VAL("Invalid hist entry",
426 !strcmp(DSO(he
), "bash") && !strcmp(SYM(he
), "main") &&
427 !strcmp(COMM(he
), "bash") && he
->stat
.period
== 100);
429 node
= rb_next(node
);
430 he
= rb_entry(node
, struct hist_entry
, rb_node
);
431 TEST_ASSERT_VAL("Invalid hist entry",
432 !strcmp(DSO(he
), "perf") && !strcmp(SYM(he
), "main") &&
433 !strcmp(COMM(he
), "perf") && he
->stat
.period
== 200);
435 node
= rb_next(node
);
436 he
= rb_entry(node
, struct hist_entry
, rb_node
);
437 TEST_ASSERT_VAL("Invalid hist entry",
438 !strcmp(DSO(he
), "libc") && !strcmp(SYM(he
), "malloc") &&
439 !strcmp(COMM(he
), "perf") && he
->stat
.period
== 100);
441 node
= rb_next(node
);
442 he
= rb_entry(node
, struct hist_entry
, rb_node
);
443 TEST_ASSERT_VAL("Invalid hist entry",
444 !strcmp(DSO(he
), "[kernel]") && !strcmp(SYM(he
), "page_fault") &&
445 !strcmp(COMM(he
), "bash") && he
->stat
.period
== 100);
447 node
= rb_next(node
);
448 he
= rb_entry(node
, struct hist_entry
, rb_node
);
449 TEST_ASSERT_VAL("Invalid hist entry",
450 !strcmp(DSO(he
), "[kernel]") && !strcmp(SYM(he
), "page_fault") &&
451 !strcmp(COMM(he
), "perf") && he
->stat
.period
== 100);
453 node
= rb_next(node
);
454 he
= rb_entry(node
, struct hist_entry
, rb_node
);
455 TEST_ASSERT_VAL("Invalid hist entry",
456 !strcmp(DSO(he
), "[kernel]") && !strcmp(SYM(he
), "schedule") &&
457 !strcmp(COMM(he
), "perf") && he
->stat
.period
== 100);
459 node
= rb_next(node
);
460 he
= rb_entry(node
, struct hist_entry
, rb_node
);
461 TEST_ASSERT_VAL("Invalid hist entry",
462 !strcmp(DSO(he
), "bash") && !strcmp(SYM(he
), "xmalloc") &&
463 !strcmp(COMM(he
), "bash") && he
->stat
.period
== 100);
466 del_hist_entries(hists
);
467 reset_output_field();
471 /* full sort keys w/o overhead field */
472 static int test5(struct evsel
*evsel
, struct machine
*machine
)
475 struct hists
*hists
= evsel__hists(evsel
);
476 struct hist_entry
*he
;
477 struct rb_root_cached
*root
;
478 struct rb_node
*node
;
480 field_order
= "cpu,pid,comm,dso,sym";
481 sort_order
= "dso,pid";
488 * CPU Command: Pid Command Shared Object Symbol
489 * === ============= ======= ============= ==============
490 * 0 perf: 100 perf [kernel] [k] schedule
491 * 2 perf: 200 perf [kernel] [k] page_fault
492 * 1 bash: 300 bash [kernel] [k] page_fault
493 * 0 bash: 300 bash bash [.] xmalloc
494 * 3 bash: 300 bash bash [.] main
495 * 1 perf: 100 perf libc [.] malloc
496 * 2 perf: 100 perf libc [.] free
497 * 1 perf: 100 perf perf [.] cmd_record
498 * 1 perf: 100 perf perf [.] main
499 * 2 perf: 200 perf perf [.] main
501 err
= add_hist_entries(hists
, machine
);
505 hists__collapse_resort(hists
, NULL
);
506 evsel__output_resort(evsel
, NULL
);
509 pr_info("[fields = %s, sort = %s]\n", field_order
, sort_order
);
510 print_hists_out(hists
);
513 root
= &hists
->entries
;
514 node
= rb_first_cached(root
);
515 he
= rb_entry(node
, struct hist_entry
, rb_node
);
517 TEST_ASSERT_VAL("Invalid hist entry",
518 CPU(he
) == 0 && PID(he
) == 100 &&
519 !strcmp(COMM(he
), "perf") && !strcmp(DSO(he
), "[kernel]") &&
520 !strcmp(SYM(he
), "schedule") && he
->stat
.period
== 100);
522 node
= rb_next(node
);
523 he
= rb_entry(node
, struct hist_entry
, rb_node
);
524 TEST_ASSERT_VAL("Invalid hist entry",
525 CPU(he
) == 2 && PID(he
) == 200 &&
526 !strcmp(COMM(he
), "perf") && !strcmp(DSO(he
), "[kernel]") &&
527 !strcmp(SYM(he
), "page_fault") && he
->stat
.period
== 100);
529 node
= rb_next(node
);
530 he
= rb_entry(node
, struct hist_entry
, rb_node
);
531 TEST_ASSERT_VAL("Invalid hist entry",
532 CPU(he
) == 1 && PID(he
) == 300 &&
533 !strcmp(COMM(he
), "bash") && !strcmp(DSO(he
), "[kernel]") &&
534 !strcmp(SYM(he
), "page_fault") && he
->stat
.period
== 100);
536 node
= rb_next(node
);
537 he
= rb_entry(node
, struct hist_entry
, rb_node
);
538 TEST_ASSERT_VAL("Invalid hist entry",
539 CPU(he
) == 0 && PID(he
) == 300 &&
540 !strcmp(COMM(he
), "bash") && !strcmp(DSO(he
), "bash") &&
541 !strcmp(SYM(he
), "xmalloc") && he
->stat
.period
== 100);
543 node
= rb_next(node
);
544 he
= rb_entry(node
, struct hist_entry
, rb_node
);
545 TEST_ASSERT_VAL("Invalid hist entry",
546 CPU(he
) == 3 && PID(he
) == 300 &&
547 !strcmp(COMM(he
), "bash") && !strcmp(DSO(he
), "bash") &&
548 !strcmp(SYM(he
), "main") && he
->stat
.period
== 100);
550 node
= rb_next(node
);
551 he
= rb_entry(node
, struct hist_entry
, rb_node
);
552 TEST_ASSERT_VAL("Invalid hist entry",
553 CPU(he
) == 1 && PID(he
) == 100 &&
554 !strcmp(COMM(he
), "perf") && !strcmp(DSO(he
), "libc") &&
555 !strcmp(SYM(he
), "malloc") && he
->stat
.period
== 100);
557 node
= rb_next(node
);
558 he
= rb_entry(node
, struct hist_entry
, rb_node
);
559 TEST_ASSERT_VAL("Invalid hist entry",
560 CPU(he
) == 2 && PID(he
) == 100 &&
561 !strcmp(COMM(he
), "perf") && !strcmp(DSO(he
), "libc") &&
562 !strcmp(SYM(he
), "free") && he
->stat
.period
== 100);
564 node
= rb_next(node
);
565 he
= rb_entry(node
, struct hist_entry
, rb_node
);
566 TEST_ASSERT_VAL("Invalid hist entry",
567 CPU(he
) == 1 && PID(he
) == 100 &&
568 !strcmp(COMM(he
), "perf") && !strcmp(DSO(he
), "perf") &&
569 !strcmp(SYM(he
), "cmd_record") && he
->stat
.period
== 100);
571 node
= rb_next(node
);
572 he
= rb_entry(node
, struct hist_entry
, rb_node
);
573 TEST_ASSERT_VAL("Invalid hist entry",
574 CPU(he
) == 1 && PID(he
) == 100 &&
575 !strcmp(COMM(he
), "perf") && !strcmp(DSO(he
), "perf") &&
576 !strcmp(SYM(he
), "main") && he
->stat
.period
== 100);
578 node
= rb_next(node
);
579 he
= rb_entry(node
, struct hist_entry
, rb_node
);
580 TEST_ASSERT_VAL("Invalid hist entry",
581 CPU(he
) == 2 && PID(he
) == 200 &&
582 !strcmp(COMM(he
), "perf") && !strcmp(DSO(he
), "perf") &&
583 !strcmp(SYM(he
), "main") && he
->stat
.period
== 100);
586 del_hist_entries(hists
);
587 reset_output_field();
591 static int test__hists_output(struct test_suite
*test __maybe_unused
, int subtest __maybe_unused
)
594 struct machines machines
;
595 struct machine
*machine
;
597 struct evlist
*evlist
= evlist__new();
599 test_fn_t testcases
[] = {
607 TEST_ASSERT_VAL("No memory", evlist
);
609 err
= parse_event(evlist
, "cpu-clock");
614 machines__init(&machines
);
616 /* setup threads/dso/map/symbols also */
617 machine
= setup_fake_machine(&machines
);
622 machine__fprintf(machine
, stderr
);
624 evsel
= evlist__first(evlist
);
626 for (i
= 0; i
< ARRAY_SIZE(testcases
); i
++) {
627 err
= testcases
[i
](evsel
, machine
);
633 /* tear down everything */
634 evlist__delete(evlist
);
635 machines__exit(&machines
);
641 DEFINE_SUITE("Sort output of hist entries", hists_output
);