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 for (i
= 0; i
< ARRAY_SIZE(fake_samples
); i
++) {
58 struct hist_entry_iter iter
= {
61 .ops
= &hist_iter_normal
,
62 .hide_unresolved
= false,
65 sample
.cpumode
= PERF_RECORD_MISC_USER
;
66 sample
.cpu
= fake_samples
[i
].cpu
;
67 sample
.pid
= fake_samples
[i
].pid
;
68 sample
.tid
= fake_samples
[i
].pid
;
69 sample
.ip
= fake_samples
[i
].ip
;
71 if (machine__resolve(machine
, &al
, &sample
) < 0)
74 if (hist_entry_iter__add(&iter
, &al
, sysctl_perf_event_max_stack
,
76 addr_location__put(&al
);
80 fake_samples
[i
].thread
= al
.thread
;
81 fake_samples
[i
].map
= al
.map
;
82 fake_samples
[i
].sym
= al
.sym
;
88 pr_debug("Not enough memory for adding a hist entry\n");
92 static void del_hist_entries(struct hists
*hists
)
94 struct hist_entry
*he
;
95 struct rb_root_cached
*root_in
;
96 struct rb_root_cached
*root_out
;
99 if (hists__has(hists
, need_collapse
))
100 root_in
= &hists
->entries_collapsed
;
102 root_in
= hists
->entries_in
;
104 root_out
= &hists
->entries
;
106 while (!RB_EMPTY_ROOT(&root_out
->rb_root
)) {
107 node
= rb_first_cached(root_out
);
109 he
= rb_entry(node
, struct hist_entry
, rb_node
);
110 rb_erase_cached(node
, root_out
);
111 rb_erase_cached(&he
->rb_node_in
, root_in
);
112 hist_entry__delete(he
);
116 typedef int (*test_fn_t
)(struct evsel
*, struct machine
*);
118 #define COMM(he) (thread__comm_str(he->thread))
119 #define DSO(he) (he->ms.map->dso->short_name)
120 #define SYM(he) (he->ms.sym->name)
121 #define CPU(he) (he->cpu)
122 #define PID(he) (he->thread->tid)
124 /* default sort keys (no field) */
125 static int test1(struct evsel
*evsel
, struct machine
*machine
)
128 struct hists
*hists
= evsel__hists(evsel
);
129 struct hist_entry
*he
;
130 struct rb_root_cached
*root
;
131 struct rb_node
*node
;
134 sort_order
= NULL
; /* equivalent to sort_order = "comm,dso,sym" */
141 * Overhead Command Shared Object Symbol
142 * ======== ======= ============= ==============
143 * 20.00% perf perf [.] main
144 * 10.00% bash [kernel] [k] page_fault
145 * 10.00% bash bash [.] main
146 * 10.00% bash bash [.] xmalloc
147 * 10.00% perf [kernel] [k] page_fault
148 * 10.00% perf [kernel] [k] schedule
149 * 10.00% perf libc [.] free
150 * 10.00% perf libc [.] malloc
151 * 10.00% perf perf [.] cmd_record
153 err
= add_hist_entries(hists
, machine
);
157 hists__collapse_resort(hists
, NULL
);
158 evsel__output_resort(evsel
, NULL
);
161 pr_info("[fields = %s, sort = %s]\n", field_order
, sort_order
);
162 print_hists_out(hists
);
165 root
= &hists
->entries
;
166 node
= rb_first_cached(root
);
167 he
= rb_entry(node
, struct hist_entry
, rb_node
);
168 TEST_ASSERT_VAL("Invalid hist entry",
169 !strcmp(COMM(he
), "perf") && !strcmp(DSO(he
), "perf") &&
170 !strcmp(SYM(he
), "main") && he
->stat
.period
== 200);
172 node
= rb_next(node
);
173 he
= rb_entry(node
, struct hist_entry
, rb_node
);
174 TEST_ASSERT_VAL("Invalid hist entry",
175 !strcmp(COMM(he
), "bash") && !strcmp(DSO(he
), "[kernel]") &&
176 !strcmp(SYM(he
), "page_fault") && he
->stat
.period
== 100);
178 node
= rb_next(node
);
179 he
= rb_entry(node
, struct hist_entry
, rb_node
);
180 TEST_ASSERT_VAL("Invalid hist entry",
181 !strcmp(COMM(he
), "bash") && !strcmp(DSO(he
), "bash") &&
182 !strcmp(SYM(he
), "main") && he
->stat
.period
== 100);
184 node
= rb_next(node
);
185 he
= rb_entry(node
, struct hist_entry
, rb_node
);
186 TEST_ASSERT_VAL("Invalid hist entry",
187 !strcmp(COMM(he
), "bash") && !strcmp(DSO(he
), "bash") &&
188 !strcmp(SYM(he
), "xmalloc") && he
->stat
.period
== 100);
190 node
= rb_next(node
);
191 he
= rb_entry(node
, struct hist_entry
, rb_node
);
192 TEST_ASSERT_VAL("Invalid hist entry",
193 !strcmp(COMM(he
), "perf") && !strcmp(DSO(he
), "[kernel]") &&
194 !strcmp(SYM(he
), "page_fault") && he
->stat
.period
== 100);
196 node
= rb_next(node
);
197 he
= rb_entry(node
, struct hist_entry
, rb_node
);
198 TEST_ASSERT_VAL("Invalid hist entry",
199 !strcmp(COMM(he
), "perf") && !strcmp(DSO(he
), "[kernel]") &&
200 !strcmp(SYM(he
), "schedule") && he
->stat
.period
== 100);
202 node
= rb_next(node
);
203 he
= rb_entry(node
, struct hist_entry
, rb_node
);
204 TEST_ASSERT_VAL("Invalid hist entry",
205 !strcmp(COMM(he
), "perf") && !strcmp(DSO(he
), "libc") &&
206 !strcmp(SYM(he
), "free") && he
->stat
.period
== 100);
208 node
= rb_next(node
);
209 he
= rb_entry(node
, struct hist_entry
, rb_node
);
210 TEST_ASSERT_VAL("Invalid hist entry",
211 !strcmp(COMM(he
), "perf") && !strcmp(DSO(he
), "libc") &&
212 !strcmp(SYM(he
), "malloc") && he
->stat
.period
== 100);
214 node
= rb_next(node
);
215 he
= rb_entry(node
, struct hist_entry
, rb_node
);
216 TEST_ASSERT_VAL("Invalid hist entry",
217 !strcmp(COMM(he
), "perf") && !strcmp(DSO(he
), "perf") &&
218 !strcmp(SYM(he
), "cmd_record") && he
->stat
.period
== 100);
221 del_hist_entries(hists
);
222 reset_output_field();
226 /* mixed fields and sort keys */
227 static int test2(struct evsel
*evsel
, struct machine
*machine
)
230 struct hists
*hists
= evsel__hists(evsel
);
231 struct hist_entry
*he
;
232 struct rb_root_cached
*root
;
233 struct rb_node
*node
;
235 field_order
= "overhead,cpu";
243 * Overhead CPU Command: Pid
244 * ======== === =============
245 * 30.00% 1 perf : 100
246 * 10.00% 0 perf : 100
247 * 10.00% 2 perf : 100
248 * 20.00% 2 perf : 200
249 * 10.00% 0 bash : 300
250 * 10.00% 1 bash : 300
251 * 10.00% 3 bash : 300
253 err
= add_hist_entries(hists
, machine
);
257 hists__collapse_resort(hists
, NULL
);
258 evsel__output_resort(evsel
, NULL
);
261 pr_info("[fields = %s, sort = %s]\n", field_order
, sort_order
);
262 print_hists_out(hists
);
265 root
= &hists
->entries
;
266 node
= rb_first_cached(root
);
267 he
= rb_entry(node
, struct hist_entry
, rb_node
);
268 TEST_ASSERT_VAL("Invalid hist entry",
269 CPU(he
) == 1 && PID(he
) == 100 && he
->stat
.period
== 300);
271 node
= rb_next(node
);
272 he
= rb_entry(node
, struct hist_entry
, rb_node
);
273 TEST_ASSERT_VAL("Invalid hist entry",
274 CPU(he
) == 0 && PID(he
) == 100 && he
->stat
.period
== 100);
277 del_hist_entries(hists
);
278 reset_output_field();
282 /* fields only (no sort key) */
283 static int test3(struct evsel
*evsel
, struct machine
*machine
)
286 struct hists
*hists
= evsel__hists(evsel
);
287 struct hist_entry
*he
;
288 struct rb_root_cached
*root
;
289 struct rb_node
*node
;
291 field_order
= "comm,overhead,dso";
299 * Command Overhead Shared Object
300 * ======= ======== =============
302 * bash 10.00% [kernel]
304 * perf 20.00% [kernel]
307 err
= add_hist_entries(hists
, machine
);
311 hists__collapse_resort(hists
, NULL
);
312 evsel__output_resort(evsel
, NULL
);
315 pr_info("[fields = %s, sort = %s]\n", field_order
, sort_order
);
316 print_hists_out(hists
);
319 root
= &hists
->entries
;
320 node
= rb_first_cached(root
);
321 he
= rb_entry(node
, struct hist_entry
, rb_node
);
322 TEST_ASSERT_VAL("Invalid hist entry",
323 !strcmp(COMM(he
), "bash") && !strcmp(DSO(he
), "bash") &&
324 he
->stat
.period
== 200);
326 node
= rb_next(node
);
327 he
= rb_entry(node
, struct hist_entry
, rb_node
);
328 TEST_ASSERT_VAL("Invalid hist entry",
329 !strcmp(COMM(he
), "bash") && !strcmp(DSO(he
), "[kernel]") &&
330 he
->stat
.period
== 100);
332 node
= rb_next(node
);
333 he
= rb_entry(node
, struct hist_entry
, rb_node
);
334 TEST_ASSERT_VAL("Invalid hist entry",
335 !strcmp(COMM(he
), "perf") && !strcmp(DSO(he
), "perf") &&
336 he
->stat
.period
== 300);
338 node
= rb_next(node
);
339 he
= rb_entry(node
, struct hist_entry
, rb_node
);
340 TEST_ASSERT_VAL("Invalid hist entry",
341 !strcmp(COMM(he
), "perf") && !strcmp(DSO(he
), "[kernel]") &&
342 he
->stat
.period
== 200);
344 node
= rb_next(node
);
345 he
= rb_entry(node
, struct hist_entry
, rb_node
);
346 TEST_ASSERT_VAL("Invalid hist entry",
347 !strcmp(COMM(he
), "perf") && !strcmp(DSO(he
), "libc") &&
348 he
->stat
.period
== 200);
351 del_hist_entries(hists
);
352 reset_output_field();
356 /* handle duplicate 'dso' field */
357 static int test4(struct evsel
*evsel
, struct machine
*machine
)
360 struct hists
*hists
= evsel__hists(evsel
);
361 struct hist_entry
*he
;
362 struct rb_root_cached
*root
;
363 struct rb_node
*node
;
365 field_order
= "dso,sym,comm,overhead,dso";
373 * Shared Object Symbol Command Overhead
374 * ============= ============== ======= ========
375 * perf [.] cmd_record perf 10.00%
376 * libc [.] free perf 10.00%
377 * bash [.] main bash 10.00%
378 * perf [.] main perf 20.00%
379 * libc [.] malloc perf 10.00%
380 * [kernel] [k] page_fault bash 10.00%
381 * [kernel] [k] page_fault perf 10.00%
382 * [kernel] [k] schedule perf 10.00%
383 * bash [.] xmalloc bash 10.00%
385 err
= add_hist_entries(hists
, machine
);
389 hists__collapse_resort(hists
, NULL
);
390 evsel__output_resort(evsel
, NULL
);
393 pr_info("[fields = %s, sort = %s]\n", field_order
, sort_order
);
394 print_hists_out(hists
);
397 root
= &hists
->entries
;
398 node
= rb_first_cached(root
);
399 he
= rb_entry(node
, struct hist_entry
, rb_node
);
400 TEST_ASSERT_VAL("Invalid hist entry",
401 !strcmp(DSO(he
), "perf") && !strcmp(SYM(he
), "cmd_record") &&
402 !strcmp(COMM(he
), "perf") && he
->stat
.period
== 100);
404 node
= rb_next(node
);
405 he
= rb_entry(node
, struct hist_entry
, rb_node
);
406 TEST_ASSERT_VAL("Invalid hist entry",
407 !strcmp(DSO(he
), "libc") && !strcmp(SYM(he
), "free") &&
408 !strcmp(COMM(he
), "perf") && he
->stat
.period
== 100);
410 node
= rb_next(node
);
411 he
= rb_entry(node
, struct hist_entry
, rb_node
);
412 TEST_ASSERT_VAL("Invalid hist entry",
413 !strcmp(DSO(he
), "bash") && !strcmp(SYM(he
), "main") &&
414 !strcmp(COMM(he
), "bash") && he
->stat
.period
== 100);
416 node
= rb_next(node
);
417 he
= rb_entry(node
, struct hist_entry
, rb_node
);
418 TEST_ASSERT_VAL("Invalid hist entry",
419 !strcmp(DSO(he
), "perf") && !strcmp(SYM(he
), "main") &&
420 !strcmp(COMM(he
), "perf") && he
->stat
.period
== 200);
422 node
= rb_next(node
);
423 he
= rb_entry(node
, struct hist_entry
, rb_node
);
424 TEST_ASSERT_VAL("Invalid hist entry",
425 !strcmp(DSO(he
), "libc") && !strcmp(SYM(he
), "malloc") &&
426 !strcmp(COMM(he
), "perf") && he
->stat
.period
== 100);
428 node
= rb_next(node
);
429 he
= rb_entry(node
, struct hist_entry
, rb_node
);
430 TEST_ASSERT_VAL("Invalid hist entry",
431 !strcmp(DSO(he
), "[kernel]") && !strcmp(SYM(he
), "page_fault") &&
432 !strcmp(COMM(he
), "bash") && he
->stat
.period
== 100);
434 node
= rb_next(node
);
435 he
= rb_entry(node
, struct hist_entry
, rb_node
);
436 TEST_ASSERT_VAL("Invalid hist entry",
437 !strcmp(DSO(he
), "[kernel]") && !strcmp(SYM(he
), "page_fault") &&
438 !strcmp(COMM(he
), "perf") && he
->stat
.period
== 100);
440 node
= rb_next(node
);
441 he
= rb_entry(node
, struct hist_entry
, rb_node
);
442 TEST_ASSERT_VAL("Invalid hist entry",
443 !strcmp(DSO(he
), "[kernel]") && !strcmp(SYM(he
), "schedule") &&
444 !strcmp(COMM(he
), "perf") && he
->stat
.period
== 100);
446 node
= rb_next(node
);
447 he
= rb_entry(node
, struct hist_entry
, rb_node
);
448 TEST_ASSERT_VAL("Invalid hist entry",
449 !strcmp(DSO(he
), "bash") && !strcmp(SYM(he
), "xmalloc") &&
450 !strcmp(COMM(he
), "bash") && he
->stat
.period
== 100);
453 del_hist_entries(hists
);
454 reset_output_field();
458 /* full sort keys w/o overhead field */
459 static int test5(struct evsel
*evsel
, struct machine
*machine
)
462 struct hists
*hists
= evsel__hists(evsel
);
463 struct hist_entry
*he
;
464 struct rb_root_cached
*root
;
465 struct rb_node
*node
;
467 field_order
= "cpu,pid,comm,dso,sym";
468 sort_order
= "dso,pid";
475 * CPU Command: Pid Command Shared Object Symbol
476 * === ============= ======= ============= ==============
477 * 0 perf: 100 perf [kernel] [k] schedule
478 * 2 perf: 200 perf [kernel] [k] page_fault
479 * 1 bash: 300 bash [kernel] [k] page_fault
480 * 0 bash: 300 bash bash [.] xmalloc
481 * 3 bash: 300 bash bash [.] main
482 * 1 perf: 100 perf libc [.] malloc
483 * 2 perf: 100 perf libc [.] free
484 * 1 perf: 100 perf perf [.] cmd_record
485 * 1 perf: 100 perf perf [.] main
486 * 2 perf: 200 perf perf [.] main
488 err
= add_hist_entries(hists
, machine
);
492 hists__collapse_resort(hists
, NULL
);
493 evsel__output_resort(evsel
, NULL
);
496 pr_info("[fields = %s, sort = %s]\n", field_order
, sort_order
);
497 print_hists_out(hists
);
500 root
= &hists
->entries
;
501 node
= rb_first_cached(root
);
502 he
= rb_entry(node
, struct hist_entry
, rb_node
);
504 TEST_ASSERT_VAL("Invalid hist entry",
505 CPU(he
) == 0 && PID(he
) == 100 &&
506 !strcmp(COMM(he
), "perf") && !strcmp(DSO(he
), "[kernel]") &&
507 !strcmp(SYM(he
), "schedule") && he
->stat
.period
== 100);
509 node
= rb_next(node
);
510 he
= rb_entry(node
, struct hist_entry
, rb_node
);
511 TEST_ASSERT_VAL("Invalid hist entry",
512 CPU(he
) == 2 && PID(he
) == 200 &&
513 !strcmp(COMM(he
), "perf") && !strcmp(DSO(he
), "[kernel]") &&
514 !strcmp(SYM(he
), "page_fault") && he
->stat
.period
== 100);
516 node
= rb_next(node
);
517 he
= rb_entry(node
, struct hist_entry
, rb_node
);
518 TEST_ASSERT_VAL("Invalid hist entry",
519 CPU(he
) == 1 && PID(he
) == 300 &&
520 !strcmp(COMM(he
), "bash") && !strcmp(DSO(he
), "[kernel]") &&
521 !strcmp(SYM(he
), "page_fault") && he
->stat
.period
== 100);
523 node
= rb_next(node
);
524 he
= rb_entry(node
, struct hist_entry
, rb_node
);
525 TEST_ASSERT_VAL("Invalid hist entry",
526 CPU(he
) == 0 && PID(he
) == 300 &&
527 !strcmp(COMM(he
), "bash") && !strcmp(DSO(he
), "bash") &&
528 !strcmp(SYM(he
), "xmalloc") && he
->stat
.period
== 100);
530 node
= rb_next(node
);
531 he
= rb_entry(node
, struct hist_entry
, rb_node
);
532 TEST_ASSERT_VAL("Invalid hist entry",
533 CPU(he
) == 3 && PID(he
) == 300 &&
534 !strcmp(COMM(he
), "bash") && !strcmp(DSO(he
), "bash") &&
535 !strcmp(SYM(he
), "main") && he
->stat
.period
== 100);
537 node
= rb_next(node
);
538 he
= rb_entry(node
, struct hist_entry
, rb_node
);
539 TEST_ASSERT_VAL("Invalid hist entry",
540 CPU(he
) == 1 && PID(he
) == 100 &&
541 !strcmp(COMM(he
), "perf") && !strcmp(DSO(he
), "libc") &&
542 !strcmp(SYM(he
), "malloc") && he
->stat
.period
== 100);
544 node
= rb_next(node
);
545 he
= rb_entry(node
, struct hist_entry
, rb_node
);
546 TEST_ASSERT_VAL("Invalid hist entry",
547 CPU(he
) == 2 && PID(he
) == 100 &&
548 !strcmp(COMM(he
), "perf") && !strcmp(DSO(he
), "libc") &&
549 !strcmp(SYM(he
), "free") && he
->stat
.period
== 100);
551 node
= rb_next(node
);
552 he
= rb_entry(node
, struct hist_entry
, rb_node
);
553 TEST_ASSERT_VAL("Invalid hist entry",
554 CPU(he
) == 1 && PID(he
) == 100 &&
555 !strcmp(COMM(he
), "perf") && !strcmp(DSO(he
), "perf") &&
556 !strcmp(SYM(he
), "cmd_record") && he
->stat
.period
== 100);
558 node
= rb_next(node
);
559 he
= rb_entry(node
, struct hist_entry
, rb_node
);
560 TEST_ASSERT_VAL("Invalid hist entry",
561 CPU(he
) == 1 && PID(he
) == 100 &&
562 !strcmp(COMM(he
), "perf") && !strcmp(DSO(he
), "perf") &&
563 !strcmp(SYM(he
), "main") && he
->stat
.period
== 100);
565 node
= rb_next(node
);
566 he
= rb_entry(node
, struct hist_entry
, rb_node
);
567 TEST_ASSERT_VAL("Invalid hist entry",
568 CPU(he
) == 2 && PID(he
) == 200 &&
569 !strcmp(COMM(he
), "perf") && !strcmp(DSO(he
), "perf") &&
570 !strcmp(SYM(he
), "main") && he
->stat
.period
== 100);
573 del_hist_entries(hists
);
574 reset_output_field();
578 int test__hists_output(struct test
*test __maybe_unused
, int subtest __maybe_unused
)
581 struct machines machines
;
582 struct machine
*machine
;
584 struct evlist
*evlist
= evlist__new();
586 test_fn_t testcases
[] = {
594 TEST_ASSERT_VAL("No memory", evlist
);
596 err
= parse_events(evlist
, "cpu-clock", NULL
);
601 machines__init(&machines
);
603 /* setup threads/dso/map/symbols also */
604 machine
= setup_fake_machine(&machines
);
609 machine__fprintf(machine
, stderr
);
611 evsel
= evlist__first(evlist
);
613 for (i
= 0; i
< ARRAY_SIZE(testcases
); i
++) {
614 err
= testcases
[i
](evsel
, machine
);
620 /* tear down everything */
621 evlist__delete(evlist
);
622 machines__exit(&machines
);