1#include "perf.h"
2#include "util/debug.h"
3#include "util/event.h"
4#include "util/symbol.h"
5#include "util/sort.h"
6#include "util/evsel.h"
7#include "util/evlist.h"
8#include "util/machine.h"
9#include "util/thread.h"
10#include "util/parse-events.h"
11#include "tests/tests.h"
12#include "tests/hists_common.h"
13#include <linux/kernel.h>
14
15struct sample {
16 u32 cpu;
17 u32 pid;
18 u64 ip;
19 struct thread *thread;
20 struct map *map;
21 struct symbol *sym;
22};
23
24
25static struct sample fake_samples[] = {
26
27 { .cpu = 0, .pid = FAKE_PID_PERF1, .ip = FAKE_IP_KERNEL_SCHEDULE, },
28
29 { .cpu = 1, .pid = FAKE_PID_PERF1, .ip = FAKE_IP_PERF_MAIN, },
30
31 { .cpu = 1, .pid = FAKE_PID_PERF1, .ip = FAKE_IP_PERF_CMD_RECORD, },
32
33 { .cpu = 1, .pid = FAKE_PID_PERF1, .ip = FAKE_IP_LIBC_MALLOC, },
34
35 { .cpu = 2, .pid = FAKE_PID_PERF1, .ip = FAKE_IP_LIBC_FREE, },
36
37 { .cpu = 2, .pid = FAKE_PID_PERF2, .ip = FAKE_IP_PERF_MAIN, },
38
39 { .cpu = 2, .pid = FAKE_PID_PERF2, .ip = FAKE_IP_KERNEL_PAGE_FAULT, },
40
41 { .cpu = 3, .pid = FAKE_PID_BASH, .ip = FAKE_IP_BASH_MAIN, },
42
43 { .cpu = 0, .pid = FAKE_PID_BASH, .ip = FAKE_IP_BASH_XMALLOC, },
44
45 { .cpu = 1, .pid = FAKE_PID_BASH, .ip = FAKE_IP_KERNEL_PAGE_FAULT, },
46};
47
48static int add_hist_entries(struct hists *hists, struct machine *machine)
49{
50 struct addr_location al;
51 struct perf_evsel *evsel = hists_to_evsel(hists);
52 struct perf_sample sample = { .period = 100, };
53 size_t i;
54
55 for (i = 0; i < ARRAY_SIZE(fake_samples); i++) {
56 struct hist_entry_iter iter = {
57 .evsel = evsel,
58 .sample = &sample,
59 .ops = &hist_iter_normal,
60 .hide_unresolved = false,
61 };
62
63 sample.cpumode = PERF_RECORD_MISC_USER;
64 sample.cpu = fake_samples[i].cpu;
65 sample.pid = fake_samples[i].pid;
66 sample.tid = fake_samples[i].pid;
67 sample.ip = fake_samples[i].ip;
68
69 if (machine__resolve(machine, &al, &sample) < 0)
70 goto out;
71
72 if (hist_entry_iter__add(&iter, &al, sysctl_perf_event_max_stack,
73 NULL) < 0) {
74 addr_location__put(&al);
75 goto out;
76 }
77
78 fake_samples[i].thread = al.thread;
79 fake_samples[i].map = al.map;
80 fake_samples[i].sym = al.sym;
81 }
82
83 return TEST_OK;
84
85out:
86 pr_debug("Not enough memory for adding a hist entry\n");
87 return TEST_FAIL;
88}
89
90static void del_hist_entries(struct hists *hists)
91{
92 struct hist_entry *he;
93 struct rb_root *root_in;
94 struct rb_root *root_out;
95 struct rb_node *node;
96
97 if (hists__has(hists, need_collapse))
98 root_in = &hists->entries_collapsed;
99 else
100 root_in = hists->entries_in;
101
102 root_out = &hists->entries;
103
104 while (!RB_EMPTY_ROOT(root_out)) {
105 node = rb_first(root_out);
106
107 he = rb_entry(node, struct hist_entry, rb_node);
108 rb_erase(node, root_out);
109 rb_erase(&he->rb_node_in, root_in);
110 hist_entry__delete(he);
111 }
112}
113
114typedef int (*test_fn_t)(struct perf_evsel *, struct machine *);
115
116#define COMM(he) (thread__comm_str(he->thread))
117#define DSO(he) (he->ms.map->dso->short_name)
118#define SYM(he) (he->ms.sym->name)
119#define CPU(he) (he->cpu)
120#define PID(he) (he->thread->tid)
121
122
123static int test1(struct perf_evsel *evsel, struct machine *machine)
124{
125 int err;
126 struct hists *hists = evsel__hists(evsel);
127 struct hist_entry *he;
128 struct rb_root *root;
129 struct rb_node *node;
130
131 field_order = NULL;
132 sort_order = NULL;
133
134 setup_sorting(NULL);
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151 err = add_hist_entries(hists, machine);
152 if (err < 0)
153 goto out;
154
155 hists__collapse_resort(hists, NULL);
156 perf_evsel__output_resort(evsel, NULL);
157
158 if (verbose > 2) {
159 pr_info("[fields = %s, sort = %s]\n", field_order, sort_order);
160 print_hists_out(hists);
161 }
162
163 root = &hists->entries;
164 node = rb_first(root);
165 he = rb_entry(node, struct hist_entry, rb_node);
166 TEST_ASSERT_VAL("Invalid hist entry",
167 !strcmp(COMM(he), "perf") && !strcmp(DSO(he), "perf") &&
168 !strcmp(SYM(he), "main") && he->stat.period == 200);
169
170 node = rb_next(node);
171 he = rb_entry(node, struct hist_entry, rb_node);
172 TEST_ASSERT_VAL("Invalid hist entry",
173 !strcmp(COMM(he), "bash") && !strcmp(DSO(he), "[kernel]") &&
174 !strcmp(SYM(he), "page_fault") && he->stat.period == 100);
175
176 node = rb_next(node);
177 he = rb_entry(node, struct hist_entry, rb_node);
178 TEST_ASSERT_VAL("Invalid hist entry",
179 !strcmp(COMM(he), "bash") && !strcmp(DSO(he), "bash") &&
180 !strcmp(SYM(he), "main") && he->stat.period == 100);
181
182 node = rb_next(node);
183 he = rb_entry(node, struct hist_entry, rb_node);
184 TEST_ASSERT_VAL("Invalid hist entry",
185 !strcmp(COMM(he), "bash") && !strcmp(DSO(he), "bash") &&
186 !strcmp(SYM(he), "xmalloc") && he->stat.period == 100);
187
188 node = rb_next(node);
189 he = rb_entry(node, struct hist_entry, rb_node);
190 TEST_ASSERT_VAL("Invalid hist entry",
191 !strcmp(COMM(he), "perf") && !strcmp(DSO(he), "[kernel]") &&
192 !strcmp(SYM(he), "page_fault") && he->stat.period == 100);
193
194 node = rb_next(node);
195 he = rb_entry(node, struct hist_entry, rb_node);
196 TEST_ASSERT_VAL("Invalid hist entry",
197 !strcmp(COMM(he), "perf") && !strcmp(DSO(he), "[kernel]") &&
198 !strcmp(SYM(he), "schedule") && he->stat.period == 100);
199
200 node = rb_next(node);
201 he = rb_entry(node, struct hist_entry, rb_node);
202 TEST_ASSERT_VAL("Invalid hist entry",
203 !strcmp(COMM(he), "perf") && !strcmp(DSO(he), "libc") &&
204 !strcmp(SYM(he), "free") && he->stat.period == 100);
205
206 node = rb_next(node);
207 he = rb_entry(node, struct hist_entry, rb_node);
208 TEST_ASSERT_VAL("Invalid hist entry",
209 !strcmp(COMM(he), "perf") && !strcmp(DSO(he), "libc") &&
210 !strcmp(SYM(he), "malloc") && he->stat.period == 100);
211
212 node = rb_next(node);
213 he = rb_entry(node, struct hist_entry, rb_node);
214 TEST_ASSERT_VAL("Invalid hist entry",
215 !strcmp(COMM(he), "perf") && !strcmp(DSO(he), "perf") &&
216 !strcmp(SYM(he), "cmd_record") && he->stat.period == 100);
217
218out:
219 del_hist_entries(hists);
220 reset_output_field();
221 return err;
222}
223
224
225static int test2(struct perf_evsel *evsel, struct machine *machine)
226{
227 int err;
228 struct hists *hists = evsel__hists(evsel);
229 struct hist_entry *he;
230 struct rb_root *root;
231 struct rb_node *node;
232
233 field_order = "overhead,cpu";
234 sort_order = "pid";
235
236 setup_sorting(NULL);
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251 err = add_hist_entries(hists, machine);
252 if (err < 0)
253 goto out;
254
255 hists__collapse_resort(hists, NULL);
256 perf_evsel__output_resort(evsel, NULL);
257
258 if (verbose > 2) {
259 pr_info("[fields = %s, sort = %s]\n", field_order, sort_order);
260 print_hists_out(hists);
261 }
262
263 root = &hists->entries;
264 node = rb_first(root);
265 he = rb_entry(node, struct hist_entry, rb_node);
266 TEST_ASSERT_VAL("Invalid hist entry",
267 CPU(he) == 1 && PID(he) == 100 && he->stat.period == 300);
268
269 node = rb_next(node);
270 he = rb_entry(node, struct hist_entry, rb_node);
271 TEST_ASSERT_VAL("Invalid hist entry",
272 CPU(he) == 0 && PID(he) == 100 && he->stat.period == 100);
273
274out:
275 del_hist_entries(hists);
276 reset_output_field();
277 return err;
278}
279
280
281static int test3(struct perf_evsel *evsel, struct machine *machine)
282{
283 int err;
284 struct hists *hists = evsel__hists(evsel);
285 struct hist_entry *he;
286 struct rb_root *root;
287 struct rb_node *node;
288
289 field_order = "comm,overhead,dso";
290 sort_order = NULL;
291
292 setup_sorting(NULL);
293
294
295
296
297
298
299
300
301
302
303
304
305 err = add_hist_entries(hists, machine);
306 if (err < 0)
307 goto out;
308
309 hists__collapse_resort(hists, NULL);
310 perf_evsel__output_resort(evsel, NULL);
311
312 if (verbose > 2) {
313 pr_info("[fields = %s, sort = %s]\n", field_order, sort_order);
314 print_hists_out(hists);
315 }
316
317 root = &hists->entries;
318 node = rb_first(root);
319 he = rb_entry(node, struct hist_entry, rb_node);
320 TEST_ASSERT_VAL("Invalid hist entry",
321 !strcmp(COMM(he), "bash") && !strcmp(DSO(he), "bash") &&
322 he->stat.period == 200);
323
324 node = rb_next(node);
325 he = rb_entry(node, struct hist_entry, rb_node);
326 TEST_ASSERT_VAL("Invalid hist entry",
327 !strcmp(COMM(he), "bash") && !strcmp(DSO(he), "[kernel]") &&
328 he->stat.period == 100);
329
330 node = rb_next(node);
331 he = rb_entry(node, struct hist_entry, rb_node);
332 TEST_ASSERT_VAL("Invalid hist entry",
333 !strcmp(COMM(he), "perf") && !strcmp(DSO(he), "perf") &&
334 he->stat.period == 300);
335
336 node = rb_next(node);
337 he = rb_entry(node, struct hist_entry, rb_node);
338 TEST_ASSERT_VAL("Invalid hist entry",
339 !strcmp(COMM(he), "perf") && !strcmp(DSO(he), "[kernel]") &&
340 he->stat.period == 200);
341
342 node = rb_next(node);
343 he = rb_entry(node, struct hist_entry, rb_node);
344 TEST_ASSERT_VAL("Invalid hist entry",
345 !strcmp(COMM(he), "perf") && !strcmp(DSO(he), "libc") &&
346 he->stat.period == 200);
347
348out:
349 del_hist_entries(hists);
350 reset_output_field();
351 return err;
352}
353
354
355static int test4(struct perf_evsel *evsel, struct machine *machine)
356{
357 int err;
358 struct hists *hists = evsel__hists(evsel);
359 struct hist_entry *he;
360 struct rb_root *root;
361 struct rb_node *node;
362
363 field_order = "dso,sym,comm,overhead,dso";
364 sort_order = "sym";
365
366 setup_sorting(NULL);
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383 err = add_hist_entries(hists, machine);
384 if (err < 0)
385 goto out;
386
387 hists__collapse_resort(hists, NULL);
388 perf_evsel__output_resort(evsel, NULL);
389
390 if (verbose > 2) {
391 pr_info("[fields = %s, sort = %s]\n", field_order, sort_order);
392 print_hists_out(hists);
393 }
394
395 root = &hists->entries;
396 node = rb_first(root);
397 he = rb_entry(node, struct hist_entry, rb_node);
398 TEST_ASSERT_VAL("Invalid hist entry",
399 !strcmp(DSO(he), "perf") && !strcmp(SYM(he), "cmd_record") &&
400 !strcmp(COMM(he), "perf") && he->stat.period == 100);
401
402 node = rb_next(node);
403 he = rb_entry(node, struct hist_entry, rb_node);
404 TEST_ASSERT_VAL("Invalid hist entry",
405 !strcmp(DSO(he), "libc") && !strcmp(SYM(he), "free") &&
406 !strcmp(COMM(he), "perf") && he->stat.period == 100);
407
408 node = rb_next(node);
409 he = rb_entry(node, struct hist_entry, rb_node);
410 TEST_ASSERT_VAL("Invalid hist entry",
411 !strcmp(DSO(he), "bash") && !strcmp(SYM(he), "main") &&
412 !strcmp(COMM(he), "bash") && he->stat.period == 100);
413
414 node = rb_next(node);
415 he = rb_entry(node, struct hist_entry, rb_node);
416 TEST_ASSERT_VAL("Invalid hist entry",
417 !strcmp(DSO(he), "perf") && !strcmp(SYM(he), "main") &&
418 !strcmp(COMM(he), "perf") && he->stat.period == 200);
419
420 node = rb_next(node);
421 he = rb_entry(node, struct hist_entry, rb_node);
422 TEST_ASSERT_VAL("Invalid hist entry",
423 !strcmp(DSO(he), "libc") && !strcmp(SYM(he), "malloc") &&
424 !strcmp(COMM(he), "perf") && he->stat.period == 100);
425
426 node = rb_next(node);
427 he = rb_entry(node, struct hist_entry, rb_node);
428 TEST_ASSERT_VAL("Invalid hist entry",
429 !strcmp(DSO(he), "[kernel]") && !strcmp(SYM(he), "page_fault") &&
430 !strcmp(COMM(he), "bash") && he->stat.period == 100);
431
432 node = rb_next(node);
433 he = rb_entry(node, struct hist_entry, rb_node);
434 TEST_ASSERT_VAL("Invalid hist entry",
435 !strcmp(DSO(he), "[kernel]") && !strcmp(SYM(he), "page_fault") &&
436 !strcmp(COMM(he), "perf") && he->stat.period == 100);
437
438 node = rb_next(node);
439 he = rb_entry(node, struct hist_entry, rb_node);
440 TEST_ASSERT_VAL("Invalid hist entry",
441 !strcmp(DSO(he), "[kernel]") && !strcmp(SYM(he), "schedule") &&
442 !strcmp(COMM(he), "perf") && he->stat.period == 100);
443
444 node = rb_next(node);
445 he = rb_entry(node, struct hist_entry, rb_node);
446 TEST_ASSERT_VAL("Invalid hist entry",
447 !strcmp(DSO(he), "bash") && !strcmp(SYM(he), "xmalloc") &&
448 !strcmp(COMM(he), "bash") && he->stat.period == 100);
449
450out:
451 del_hist_entries(hists);
452 reset_output_field();
453 return err;
454}
455
456
457static int test5(struct perf_evsel *evsel, struct machine *machine)
458{
459 int err;
460 struct hists *hists = evsel__hists(evsel);
461 struct hist_entry *he;
462 struct rb_root *root;
463 struct rb_node *node;
464
465 field_order = "cpu,pid,comm,dso,sym";
466 sort_order = "dso,pid";
467
468 setup_sorting(NULL);
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486 err = add_hist_entries(hists, machine);
487 if (err < 0)
488 goto out;
489
490 hists__collapse_resort(hists, NULL);
491 perf_evsel__output_resort(evsel, NULL);
492
493 if (verbose > 2) {
494 pr_info("[fields = %s, sort = %s]\n", field_order, sort_order);
495 print_hists_out(hists);
496 }
497
498 root = &hists->entries;
499 node = rb_first(root);
500 he = rb_entry(node, struct hist_entry, rb_node);
501
502 TEST_ASSERT_VAL("Invalid hist entry",
503 CPU(he) == 0 && PID(he) == 100 &&
504 !strcmp(COMM(he), "perf") && !strcmp(DSO(he), "[kernel]") &&
505 !strcmp(SYM(he), "schedule") && he->stat.period == 100);
506
507 node = rb_next(node);
508 he = rb_entry(node, struct hist_entry, rb_node);
509 TEST_ASSERT_VAL("Invalid hist entry",
510 CPU(he) == 2 && PID(he) == 200 &&
511 !strcmp(COMM(he), "perf") && !strcmp(DSO(he), "[kernel]") &&
512 !strcmp(SYM(he), "page_fault") && he->stat.period == 100);
513
514 node = rb_next(node);
515 he = rb_entry(node, struct hist_entry, rb_node);
516 TEST_ASSERT_VAL("Invalid hist entry",
517 CPU(he) == 1 && PID(he) == 300 &&
518 !strcmp(COMM(he), "bash") && !strcmp(DSO(he), "[kernel]") &&
519 !strcmp(SYM(he), "page_fault") && he->stat.period == 100);
520
521 node = rb_next(node);
522 he = rb_entry(node, struct hist_entry, rb_node);
523 TEST_ASSERT_VAL("Invalid hist entry",
524 CPU(he) == 0 && PID(he) == 300 &&
525 !strcmp(COMM(he), "bash") && !strcmp(DSO(he), "bash") &&
526 !strcmp(SYM(he), "xmalloc") && he->stat.period == 100);
527
528 node = rb_next(node);
529 he = rb_entry(node, struct hist_entry, rb_node);
530 TEST_ASSERT_VAL("Invalid hist entry",
531 CPU(he) == 3 && PID(he) == 300 &&
532 !strcmp(COMM(he), "bash") && !strcmp(DSO(he), "bash") &&
533 !strcmp(SYM(he), "main") && he->stat.period == 100);
534
535 node = rb_next(node);
536 he = rb_entry(node, struct hist_entry, rb_node);
537 TEST_ASSERT_VAL("Invalid hist entry",
538 CPU(he) == 1 && PID(he) == 100 &&
539 !strcmp(COMM(he), "perf") && !strcmp(DSO(he), "libc") &&
540 !strcmp(SYM(he), "malloc") && he->stat.period == 100);
541
542 node = rb_next(node);
543 he = rb_entry(node, struct hist_entry, rb_node);
544 TEST_ASSERT_VAL("Invalid hist entry",
545 CPU(he) == 2 && PID(he) == 100 &&
546 !strcmp(COMM(he), "perf") && !strcmp(DSO(he), "libc") &&
547 !strcmp(SYM(he), "free") && he->stat.period == 100);
548
549 node = rb_next(node);
550 he = rb_entry(node, struct hist_entry, rb_node);
551 TEST_ASSERT_VAL("Invalid hist entry",
552 CPU(he) == 1 && PID(he) == 100 &&
553 !strcmp(COMM(he), "perf") && !strcmp(DSO(he), "perf") &&
554 !strcmp(SYM(he), "cmd_record") && he->stat.period == 100);
555
556 node = rb_next(node);
557 he = rb_entry(node, struct hist_entry, rb_node);
558 TEST_ASSERT_VAL("Invalid hist entry",
559 CPU(he) == 1 && PID(he) == 100 &&
560 !strcmp(COMM(he), "perf") && !strcmp(DSO(he), "perf") &&
561 !strcmp(SYM(he), "main") && he->stat.period == 100);
562
563 node = rb_next(node);
564 he = rb_entry(node, struct hist_entry, rb_node);
565 TEST_ASSERT_VAL("Invalid hist entry",
566 CPU(he) == 2 && PID(he) == 200 &&
567 !strcmp(COMM(he), "perf") && !strcmp(DSO(he), "perf") &&
568 !strcmp(SYM(he), "main") && he->stat.period == 100);
569
570out:
571 del_hist_entries(hists);
572 reset_output_field();
573 return err;
574}
575
576int test__hists_output(int subtest __maybe_unused)
577{
578 int err = TEST_FAIL;
579 struct machines machines;
580 struct machine *machine;
581 struct perf_evsel *evsel;
582 struct perf_evlist *evlist = perf_evlist__new();
583 size_t i;
584 test_fn_t testcases[] = {
585 test1,
586 test2,
587 test3,
588 test4,
589 test5,
590 };
591
592 TEST_ASSERT_VAL("No memory", evlist);
593
594 err = parse_events(evlist, "cpu-clock", NULL);
595 if (err)
596 goto out;
597 err = TEST_FAIL;
598
599 machines__init(&machines);
600
601
602 machine = setup_fake_machine(&machines);
603 if (!machine)
604 goto out;
605
606 if (verbose > 1)
607 machine__fprintf(machine, stderr);
608
609 evsel = perf_evlist__first(evlist);
610
611 for (i = 0; i < ARRAY_SIZE(testcases); i++) {
612 err = testcases[i](evsel, machine);
613 if (err < 0)
614 break;
615 }
616
617out:
618
619 perf_evlist__delete(evlist);
620 machines__exit(&machines);
621
622 return err;
623}
624