diff options
Diffstat (limited to 'tools/perf')
-rw-r--r-- | tools/perf/builtin-report.c | 192 | ||||
-rw-r--r-- | tools/perf/tests/hists_filter.c | 16 | ||||
-rw-r--r-- | tools/perf/tests/hists_output.c | 11 | ||||
-rw-r--r-- | tools/perf/util/hist.c | 299 | ||||
-rw-r--r-- | tools/perf/util/hist.h | 33 |
5 files changed, 372 insertions, 179 deletions
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 4a3b84dd4f41..3201bdfa8c3f 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c | |||
@@ -76,163 +76,16 @@ static int report__config(const char *var, const char *value, void *cb) | |||
76 | return perf_default_config(var, value, cb); | 76 | return perf_default_config(var, value, cb); |
77 | } | 77 | } |
78 | 78 | ||
79 | static void report__inc_stats(struct report *rep, struct hist_entry *he) | 79 | static void report__inc_stats(struct report *rep, |
80 | struct hist_entry *he __maybe_unused) | ||
80 | { | 81 | { |
81 | /* | 82 | /* |
82 | * The @he is either of a newly created one or an existing one | 83 | * We cannot access @he at this time. Just assume it's a new entry. |
83 | * merging current sample. We only want to count a new one so | 84 | * It'll be fixed once we have a callback mechanism in hist_iter. |
84 | * checking ->nr_events being 1. | ||
85 | */ | 85 | */ |
86 | if (he->stat.nr_events == 1) | 86 | rep->nr_entries++; |
87 | rep->nr_entries++; | ||
88 | |||
89 | /* | ||
90 | * Only counts number of samples at this stage as it's more | ||
91 | * natural to do it here and non-sample events are also | ||
92 | * counted in perf_session_deliver_event(). The dump_trace | ||
93 | * requires this info is ready before going to the output tree. | ||
94 | */ | ||
95 | hists__inc_nr_samples(he->hists, he->filtered); | ||
96 | } | ||
97 | |||
98 | static int report__add_mem_hist_entry(struct report *rep, struct addr_location *al, | ||
99 | struct perf_sample *sample, struct perf_evsel *evsel) | ||
100 | { | ||
101 | struct symbol *parent = NULL; | ||
102 | struct hist_entry *he; | ||
103 | struct mem_info *mi, *mx; | ||
104 | uint64_t cost; | ||
105 | int err = sample__resolve_callchain(sample, &parent, evsel, al, rep->max_stack); | ||
106 | |||
107 | if (err) | ||
108 | return err; | ||
109 | |||
110 | mi = sample__resolve_mem(sample, al); | ||
111 | if (!mi) | ||
112 | return -ENOMEM; | ||
113 | |||
114 | if (rep->hide_unresolved && !al->sym) | ||
115 | return 0; | ||
116 | |||
117 | cost = sample->weight; | ||
118 | if (!cost) | ||
119 | cost = 1; | ||
120 | |||
121 | /* | ||
122 | * must pass period=weight in order to get the correct | ||
123 | * sorting from hists__collapse_resort() which is solely | ||
124 | * based on periods. We want sorting be done on nr_events * weight | ||
125 | * and this is indirectly achieved by passing period=weight here | ||
126 | * and the he_stat__add_period() function. | ||
127 | */ | ||
128 | he = __hists__add_entry(&evsel->hists, al, parent, NULL, mi, | ||
129 | cost, cost, 0); | ||
130 | if (!he) | ||
131 | return -ENOMEM; | ||
132 | |||
133 | if (ui__has_annotation()) { | ||
134 | err = hist_entry__inc_addr_samples(he, evsel->idx, al->addr); | ||
135 | if (err) | ||
136 | goto out; | ||
137 | |||
138 | mx = he->mem_info; | ||
139 | err = addr_map_symbol__inc_samples(&mx->daddr, evsel->idx); | ||
140 | if (err) | ||
141 | goto out; | ||
142 | } | ||
143 | |||
144 | report__inc_stats(rep, he); | ||
145 | |||
146 | err = hist_entry__append_callchain(he, sample); | ||
147 | out: | ||
148 | return err; | ||
149 | } | ||
150 | |||
151 | static int report__add_branch_hist_entry(struct report *rep, struct addr_location *al, | ||
152 | struct perf_sample *sample, struct perf_evsel *evsel) | ||
153 | { | ||
154 | struct symbol *parent = NULL; | ||
155 | unsigned i; | ||
156 | struct hist_entry *he; | ||
157 | struct branch_info *bi, *bx; | ||
158 | int err = sample__resolve_callchain(sample, &parent, evsel, al, rep->max_stack); | ||
159 | |||
160 | if (err) | ||
161 | return err; | ||
162 | |||
163 | bi = sample__resolve_bstack(sample, al); | ||
164 | if (!bi) | ||
165 | return -ENOMEM; | ||
166 | |||
167 | for (i = 0; i < sample->branch_stack->nr; i++) { | ||
168 | if (rep->hide_unresolved && !(bi[i].from.sym && bi[i].to.sym)) | ||
169 | continue; | ||
170 | |||
171 | err = -ENOMEM; | ||
172 | |||
173 | /* overwrite the 'al' to branch-to info */ | ||
174 | al->map = bi[i].to.map; | ||
175 | al->sym = bi[i].to.sym; | ||
176 | al->addr = bi[i].to.addr; | ||
177 | /* | ||
178 | * The report shows the percentage of total branches captured | ||
179 | * and not events sampled. Thus we use a pseudo period of 1. | ||
180 | */ | ||
181 | he = __hists__add_entry(&evsel->hists, al, parent, &bi[i], NULL, | ||
182 | 1, 1, 0); | ||
183 | if (he) { | ||
184 | if (ui__has_annotation()) { | ||
185 | bx = he->branch_info; | ||
186 | err = addr_map_symbol__inc_samples(&bx->from, | ||
187 | evsel->idx); | ||
188 | if (err) | ||
189 | goto out; | ||
190 | |||
191 | err = addr_map_symbol__inc_samples(&bx->to, | ||
192 | evsel->idx); | ||
193 | if (err) | ||
194 | goto out; | ||
195 | } | ||
196 | report__inc_stats(rep, he); | ||
197 | } else | ||
198 | goto out; | ||
199 | } | ||
200 | err = 0; | ||
201 | out: | ||
202 | free(bi); | ||
203 | return err; | ||
204 | } | 87 | } |
205 | 88 | ||
206 | static int report__add_hist_entry(struct report *rep, struct perf_evsel *evsel, | ||
207 | struct addr_location *al, struct perf_sample *sample) | ||
208 | { | ||
209 | struct symbol *parent = NULL; | ||
210 | struct hist_entry *he; | ||
211 | int err = sample__resolve_callchain(sample, &parent, evsel, al, rep->max_stack); | ||
212 | |||
213 | if (err) | ||
214 | return err; | ||
215 | |||
216 | he = __hists__add_entry(&evsel->hists, al, parent, NULL, NULL, | ||
217 | sample->period, sample->weight, | ||
218 | sample->transaction); | ||
219 | if (he == NULL) | ||
220 | return -ENOMEM; | ||
221 | |||
222 | err = hist_entry__append_callchain(he, sample); | ||
223 | if (err) | ||
224 | goto out; | ||
225 | |||
226 | if (ui__has_annotation()) | ||
227 | err = hist_entry__inc_addr_samples(he, evsel->idx, al->addr); | ||
228 | |||
229 | report__inc_stats(rep, he); | ||
230 | |||
231 | out: | ||
232 | return err; | ||
233 | } | ||
234 | |||
235 | |||
236 | static int process_sample_event(struct perf_tool *tool, | 89 | static int process_sample_event(struct perf_tool *tool, |
237 | union perf_event *event, | 90 | union perf_event *event, |
238 | struct perf_sample *sample, | 91 | struct perf_sample *sample, |
@@ -241,6 +94,9 @@ static int process_sample_event(struct perf_tool *tool, | |||
241 | { | 94 | { |
242 | struct report *rep = container_of(tool, struct report, tool); | 95 | struct report *rep = container_of(tool, struct report, tool); |
243 | struct addr_location al; | 96 | struct addr_location al; |
97 | struct hist_entry_iter iter = { | ||
98 | .hide_unresolved = rep->hide_unresolved, | ||
99 | }; | ||
244 | int ret; | 100 | int ret; |
245 | 101 | ||
246 | if (perf_event__preprocess_sample(event, machine, &al, sample) < 0) { | 102 | if (perf_event__preprocess_sample(event, machine, &al, sample) < 0) { |
@@ -255,22 +111,22 @@ static int process_sample_event(struct perf_tool *tool, | |||
255 | if (rep->cpu_list && !test_bit(sample->cpu, rep->cpu_bitmap)) | 111 | if (rep->cpu_list && !test_bit(sample->cpu, rep->cpu_bitmap)) |
256 | return 0; | 112 | return 0; |
257 | 113 | ||
258 | if (sort__mode == SORT_MODE__BRANCH) { | 114 | if (sort__mode == SORT_MODE__BRANCH) |
259 | ret = report__add_branch_hist_entry(rep, &al, sample, evsel); | 115 | iter.ops = &hist_iter_branch; |
260 | if (ret < 0) | 116 | else if (rep->mem_mode) |
261 | pr_debug("problem adding lbr entry, skipping event\n"); | 117 | iter.ops = &hist_iter_mem; |
262 | } else if (rep->mem_mode == 1) { | 118 | else |
263 | ret = report__add_mem_hist_entry(rep, &al, sample, evsel); | 119 | iter.ops = &hist_iter_normal; |
264 | if (ret < 0) | 120 | |
265 | pr_debug("problem adding mem entry, skipping event\n"); | 121 | if (al.map != NULL) |
266 | } else { | 122 | al.map->dso->hit = 1; |
267 | if (al.map != NULL) | 123 | |
268 | al.map->dso->hit = 1; | 124 | report__inc_stats(rep, NULL); |
269 | 125 | ||
270 | ret = report__add_hist_entry(rep, evsel, &al, sample); | 126 | ret = hist_entry_iter__add(&iter, &al, evsel, sample, rep->max_stack); |
271 | if (ret < 0) | 127 | if (ret < 0) |
272 | pr_debug("problem incrementing symbol period, skipping event\n"); | 128 | pr_debug("problem adding hist entry, skipping event\n"); |
273 | } | 129 | |
274 | return ret; | 130 | return ret; |
275 | } | 131 | } |
276 | 132 | ||
diff --git a/tools/perf/tests/hists_filter.c b/tools/perf/tests/hists_filter.c index 0a71ef4b9158..76b02e1de701 100644 --- a/tools/perf/tests/hists_filter.c +++ b/tools/perf/tests/hists_filter.c | |||
@@ -42,11 +42,11 @@ static struct sample fake_samples[] = { | |||
42 | { .pid = 300, .ip = 0xf0000 + 800, }, | 42 | { .pid = 300, .ip = 0xf0000 + 800, }, |
43 | }; | 43 | }; |
44 | 44 | ||
45 | static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine) | 45 | static int add_hist_entries(struct perf_evlist *evlist, |
46 | struct machine *machine __maybe_unused) | ||
46 | { | 47 | { |
47 | struct perf_evsel *evsel; | 48 | struct perf_evsel *evsel; |
48 | struct addr_location al; | 49 | struct addr_location al; |
49 | struct hist_entry *he; | ||
50 | struct perf_sample sample = { .cpu = 0, }; | 50 | struct perf_sample sample = { .cpu = 0, }; |
51 | size_t i; | 51 | size_t i; |
52 | 52 | ||
@@ -62,6 +62,10 @@ static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine) | |||
62 | .misc = PERF_RECORD_MISC_USER, | 62 | .misc = PERF_RECORD_MISC_USER, |
63 | }, | 63 | }, |
64 | }; | 64 | }; |
65 | struct hist_entry_iter iter = { | ||
66 | .ops = &hist_iter_normal, | ||
67 | .hide_unresolved = false, | ||
68 | }; | ||
65 | 69 | ||
66 | /* make sure it has no filter at first */ | 70 | /* make sure it has no filter at first */ |
67 | evsel->hists.thread_filter = NULL; | 71 | evsel->hists.thread_filter = NULL; |
@@ -71,21 +75,19 @@ static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine) | |||
71 | sample.pid = fake_samples[i].pid; | 75 | sample.pid = fake_samples[i].pid; |
72 | sample.tid = fake_samples[i].pid; | 76 | sample.tid = fake_samples[i].pid; |
73 | sample.ip = fake_samples[i].ip; | 77 | sample.ip = fake_samples[i].ip; |
78 | sample.period = 100; | ||
74 | 79 | ||
75 | if (perf_event__preprocess_sample(&event, machine, &al, | 80 | if (perf_event__preprocess_sample(&event, machine, &al, |
76 | &sample) < 0) | 81 | &sample) < 0) |
77 | goto out; | 82 | goto out; |
78 | 83 | ||
79 | he = __hists__add_entry(&evsel->hists, &al, NULL, | 84 | if (hist_entry_iter__add(&iter, &al, evsel, &sample, |
80 | NULL, NULL, 100, 1, 0); | 85 | PERF_MAX_STACK_DEPTH) < 0) |
81 | if (he == NULL) | ||
82 | goto out; | 86 | goto out; |
83 | 87 | ||
84 | fake_samples[i].thread = al.thread; | 88 | fake_samples[i].thread = al.thread; |
85 | fake_samples[i].map = al.map; | 89 | fake_samples[i].map = al.map; |
86 | fake_samples[i].sym = al.sym; | 90 | fake_samples[i].sym = al.sym; |
87 | |||
88 | hists__inc_nr_samples(he->hists, he->filtered); | ||
89 | } | 91 | } |
90 | } | 92 | } |
91 | 93 | ||
diff --git a/tools/perf/tests/hists_output.c b/tools/perf/tests/hists_output.c index a16850551797..1308f88a9169 100644 --- a/tools/perf/tests/hists_output.c +++ b/tools/perf/tests/hists_output.c | |||
@@ -46,7 +46,7 @@ static struct sample fake_samples[] = { | |||
46 | static int add_hist_entries(struct hists *hists, struct machine *machine) | 46 | static int add_hist_entries(struct hists *hists, struct machine *machine) |
47 | { | 47 | { |
48 | struct addr_location al; | 48 | struct addr_location al; |
49 | struct hist_entry *he; | 49 | struct perf_evsel *evsel = hists_to_evsel(hists); |
50 | struct perf_sample sample = { .period = 100, }; | 50 | struct perf_sample sample = { .period = 100, }; |
51 | size_t i; | 51 | size_t i; |
52 | 52 | ||
@@ -56,6 +56,10 @@ static int add_hist_entries(struct hists *hists, struct machine *machine) | |||
56 | .misc = PERF_RECORD_MISC_USER, | 56 | .misc = PERF_RECORD_MISC_USER, |
57 | }, | 57 | }, |
58 | }; | 58 | }; |
59 | struct hist_entry_iter iter = { | ||
60 | .ops = &hist_iter_normal, | ||
61 | .hide_unresolved = false, | ||
62 | }; | ||
59 | 63 | ||
60 | sample.cpu = fake_samples[i].cpu; | 64 | sample.cpu = fake_samples[i].cpu; |
61 | sample.pid = fake_samples[i].pid; | 65 | sample.pid = fake_samples[i].pid; |
@@ -66,9 +70,8 @@ static int add_hist_entries(struct hists *hists, struct machine *machine) | |||
66 | &sample) < 0) | 70 | &sample) < 0) |
67 | goto out; | 71 | goto out; |
68 | 72 | ||
69 | he = __hists__add_entry(hists, &al, NULL, NULL, NULL, | 73 | if (hist_entry_iter__add(&iter, &al, evsel, &sample, |
70 | sample.period, 1, 0); | 74 | PERF_MAX_STACK_DEPTH) < 0) |
71 | if (he == NULL) | ||
72 | goto out; | 75 | goto out; |
73 | 76 | ||
74 | fake_samples[i].thread = al.thread; | 77 | fake_samples[i].thread = al.thread; |
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 5943ba60f193..d8662356de20 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c | |||
@@ -4,6 +4,7 @@ | |||
4 | #include "session.h" | 4 | #include "session.h" |
5 | #include "sort.h" | 5 | #include "sort.h" |
6 | #include "evsel.h" | 6 | #include "evsel.h" |
7 | #include "annotate.h" | ||
7 | #include <math.h> | 8 | #include <math.h> |
8 | 9 | ||
9 | static bool hists__filter_entry_by_dso(struct hists *hists, | 10 | static bool hists__filter_entry_by_dso(struct hists *hists, |
@@ -429,6 +430,304 @@ struct hist_entry *__hists__add_entry(struct hists *hists, | |||
429 | return add_hist_entry(hists, &entry, al); | 430 | return add_hist_entry(hists, &entry, al); |
430 | } | 431 | } |
431 | 432 | ||
433 | static int | ||
434 | iter_next_nop_entry(struct hist_entry_iter *iter __maybe_unused, | ||
435 | struct addr_location *al __maybe_unused) | ||
436 | { | ||
437 | return 0; | ||
438 | } | ||
439 | |||
440 | static int | ||
441 | iter_add_next_nop_entry(struct hist_entry_iter *iter __maybe_unused, | ||
442 | struct addr_location *al __maybe_unused) | ||
443 | { | ||
444 | return 0; | ||
445 | } | ||
446 | |||
447 | static int | ||
448 | iter_prepare_mem_entry(struct hist_entry_iter *iter, struct addr_location *al) | ||
449 | { | ||
450 | struct perf_sample *sample = iter->sample; | ||
451 | struct mem_info *mi; | ||
452 | |||
453 | mi = sample__resolve_mem(sample, al); | ||
454 | if (mi == NULL) | ||
455 | return -ENOMEM; | ||
456 | |||
457 | iter->priv = mi; | ||
458 | return 0; | ||
459 | } | ||
460 | |||
461 | static int | ||
462 | iter_add_single_mem_entry(struct hist_entry_iter *iter, struct addr_location *al) | ||
463 | { | ||
464 | u64 cost; | ||
465 | struct mem_info *mi = iter->priv; | ||
466 | struct hist_entry *he; | ||
467 | |||
468 | if (mi == NULL) | ||
469 | return -EINVAL; | ||
470 | |||
471 | cost = iter->sample->weight; | ||
472 | if (!cost) | ||
473 | cost = 1; | ||
474 | |||
475 | /* | ||
476 | * must pass period=weight in order to get the correct | ||
477 | * sorting from hists__collapse_resort() which is solely | ||
478 | * based on periods. We want sorting be done on nr_events * weight | ||
479 | * and this is indirectly achieved by passing period=weight here | ||
480 | * and the he_stat__add_period() function. | ||
481 | */ | ||
482 | he = __hists__add_entry(&iter->evsel->hists, al, iter->parent, NULL, mi, | ||
483 | cost, cost, 0); | ||
484 | if (!he) | ||
485 | return -ENOMEM; | ||
486 | |||
487 | iter->he = he; | ||
488 | return 0; | ||
489 | } | ||
490 | |||
491 | static int | ||
492 | iter_finish_mem_entry(struct hist_entry_iter *iter, struct addr_location *al) | ||
493 | { | ||
494 | struct perf_evsel *evsel = iter->evsel; | ||
495 | struct hist_entry *he = iter->he; | ||
496 | struct mem_info *mx; | ||
497 | int err = -EINVAL; | ||
498 | |||
499 | if (he == NULL) | ||
500 | goto out; | ||
501 | |||
502 | if (ui__has_annotation()) { | ||
503 | err = hist_entry__inc_addr_samples(he, evsel->idx, al->addr); | ||
504 | if (err) | ||
505 | goto out; | ||
506 | |||
507 | mx = he->mem_info; | ||
508 | err = addr_map_symbol__inc_samples(&mx->daddr, evsel->idx); | ||
509 | if (err) | ||
510 | goto out; | ||
511 | } | ||
512 | |||
513 | hists__inc_nr_samples(&evsel->hists, he->filtered); | ||
514 | |||
515 | err = hist_entry__append_callchain(he, iter->sample); | ||
516 | |||
517 | out: | ||
518 | /* | ||
519 | * We don't need to free iter->priv (mem_info) here since | ||
520 | * the mem info was either already freed in add_hist_entry() or | ||
521 | * passed to a new hist entry by hist_entry__new(). | ||
522 | */ | ||
523 | iter->priv = NULL; | ||
524 | |||
525 | iter->he = NULL; | ||
526 | return err; | ||
527 | } | ||
528 | |||
529 | static int | ||
530 | iter_prepare_branch_entry(struct hist_entry_iter *iter, struct addr_location *al) | ||
531 | { | ||
532 | struct branch_info *bi; | ||
533 | struct perf_sample *sample = iter->sample; | ||
534 | |||
535 | bi = sample__resolve_bstack(sample, al); | ||
536 | if (!bi) | ||
537 | return -ENOMEM; | ||
538 | |||
539 | iter->curr = 0; | ||
540 | iter->total = sample->branch_stack->nr; | ||
541 | |||
542 | iter->priv = bi; | ||
543 | return 0; | ||
544 | } | ||
545 | |||
546 | static int | ||
547 | iter_add_single_branch_entry(struct hist_entry_iter *iter __maybe_unused, | ||
548 | struct addr_location *al __maybe_unused) | ||
549 | { | ||
550 | return 0; | ||
551 | } | ||
552 | |||
553 | static int | ||
554 | iter_next_branch_entry(struct hist_entry_iter *iter, struct addr_location *al) | ||
555 | { | ||
556 | struct branch_info *bi = iter->priv; | ||
557 | int i = iter->curr; | ||
558 | |||
559 | if (bi == NULL) | ||
560 | return 0; | ||
561 | |||
562 | if (iter->curr >= iter->total) | ||
563 | return 0; | ||
564 | |||
565 | al->map = bi[i].to.map; | ||
566 | al->sym = bi[i].to.sym; | ||
567 | al->addr = bi[i].to.addr; | ||
568 | return 1; | ||
569 | } | ||
570 | |||
571 | static int | ||
572 | iter_add_next_branch_entry(struct hist_entry_iter *iter, struct addr_location *al) | ||
573 | { | ||
574 | struct branch_info *bi, *bx; | ||
575 | struct perf_evsel *evsel = iter->evsel; | ||
576 | struct hist_entry *he = NULL; | ||
577 | int i = iter->curr; | ||
578 | int err = 0; | ||
579 | |||
580 | bi = iter->priv; | ||
581 | |||
582 | if (iter->hide_unresolved && !(bi[i].from.sym && bi[i].to.sym)) | ||
583 | goto out; | ||
584 | |||
585 | /* | ||
586 | * The report shows the percentage of total branches captured | ||
587 | * and not events sampled. Thus we use a pseudo period of 1. | ||
588 | */ | ||
589 | he = __hists__add_entry(&evsel->hists, al, iter->parent, &bi[i], NULL, | ||
590 | 1, 1, 0); | ||
591 | if (he == NULL) | ||
592 | return -ENOMEM; | ||
593 | |||
594 | if (ui__has_annotation()) { | ||
595 | bx = he->branch_info; | ||
596 | err = addr_map_symbol__inc_samples(&bx->from, evsel->idx); | ||
597 | if (err) | ||
598 | goto out; | ||
599 | |||
600 | err = addr_map_symbol__inc_samples(&bx->to, evsel->idx); | ||
601 | if (err) | ||
602 | goto out; | ||
603 | } | ||
604 | |||
605 | hists__inc_nr_samples(&evsel->hists, he->filtered); | ||
606 | |||
607 | out: | ||
608 | iter->he = he; | ||
609 | iter->curr++; | ||
610 | return err; | ||
611 | } | ||
612 | |||
613 | static int | ||
614 | iter_finish_branch_entry(struct hist_entry_iter *iter, | ||
615 | struct addr_location *al __maybe_unused) | ||
616 | { | ||
617 | zfree(&iter->priv); | ||
618 | iter->he = NULL; | ||
619 | |||
620 | return iter->curr >= iter->total ? 0 : -1; | ||
621 | } | ||
622 | |||
623 | static int | ||
624 | iter_prepare_normal_entry(struct hist_entry_iter *iter __maybe_unused, | ||
625 | struct addr_location *al __maybe_unused) | ||
626 | { | ||
627 | return 0; | ||
628 | } | ||
629 | |||
630 | static int | ||
631 | iter_add_single_normal_entry(struct hist_entry_iter *iter, struct addr_location *al) | ||
632 | { | ||
633 | struct perf_evsel *evsel = iter->evsel; | ||
634 | struct perf_sample *sample = iter->sample; | ||
635 | struct hist_entry *he; | ||
636 | |||
637 | he = __hists__add_entry(&evsel->hists, al, iter->parent, NULL, NULL, | ||
638 | sample->period, sample->weight, | ||
639 | sample->transaction); | ||
640 | if (he == NULL) | ||
641 | return -ENOMEM; | ||
642 | |||
643 | iter->he = he; | ||
644 | return 0; | ||
645 | } | ||
646 | |||
647 | static int | ||
648 | iter_finish_normal_entry(struct hist_entry_iter *iter, struct addr_location *al) | ||
649 | { | ||
650 | int err; | ||
651 | struct hist_entry *he = iter->he; | ||
652 | struct perf_evsel *evsel = iter->evsel; | ||
653 | struct perf_sample *sample = iter->sample; | ||
654 | |||
655 | if (he == NULL) | ||
656 | return 0; | ||
657 | |||
658 | iter->he = NULL; | ||
659 | |||
660 | if (ui__has_annotation()) { | ||
661 | err = hist_entry__inc_addr_samples(he, evsel->idx, al->addr); | ||
662 | if (err) | ||
663 | return err; | ||
664 | } | ||
665 | |||
666 | hists__inc_nr_samples(&evsel->hists, he->filtered); | ||
667 | |||
668 | return hist_entry__append_callchain(he, sample); | ||
669 | } | ||
670 | |||
671 | const struct hist_iter_ops hist_iter_mem = { | ||
672 | .prepare_entry = iter_prepare_mem_entry, | ||
673 | .add_single_entry = iter_add_single_mem_entry, | ||
674 | .next_entry = iter_next_nop_entry, | ||
675 | .add_next_entry = iter_add_next_nop_entry, | ||
676 | .finish_entry = iter_finish_mem_entry, | ||
677 | }; | ||
678 | |||
679 | const struct hist_iter_ops hist_iter_branch = { | ||
680 | .prepare_entry = iter_prepare_branch_entry, | ||
681 | .add_single_entry = iter_add_single_branch_entry, | ||
682 | .next_entry = iter_next_branch_entry, | ||
683 | .add_next_entry = iter_add_next_branch_entry, | ||
684 | .finish_entry = iter_finish_branch_entry, | ||
685 | }; | ||
686 | |||
687 | const struct hist_iter_ops hist_iter_normal = { | ||
688 | .prepare_entry = iter_prepare_normal_entry, | ||
689 | .add_single_entry = iter_add_single_normal_entry, | ||
690 | .next_entry = iter_next_nop_entry, | ||
691 | .add_next_entry = iter_add_next_nop_entry, | ||
692 | .finish_entry = iter_finish_normal_entry, | ||
693 | }; | ||
694 | |||
695 | int hist_entry_iter__add(struct hist_entry_iter *iter, struct addr_location *al, | ||
696 | struct perf_evsel *evsel, struct perf_sample *sample, | ||
697 | int max_stack_depth) | ||
698 | { | ||
699 | int err, err2; | ||
700 | |||
701 | err = sample__resolve_callchain(sample, &iter->parent, evsel, al, | ||
702 | max_stack_depth); | ||
703 | if (err) | ||
704 | return err; | ||
705 | |||
706 | iter->evsel = evsel; | ||
707 | iter->sample = sample; | ||
708 | |||
709 | err = iter->ops->prepare_entry(iter, al); | ||
710 | if (err) | ||
711 | goto out; | ||
712 | |||
713 | err = iter->ops->add_single_entry(iter, al); | ||
714 | if (err) | ||
715 | goto out; | ||
716 | |||
717 | while (iter->ops->next_entry(iter, al)) { | ||
718 | err = iter->ops->add_next_entry(iter, al); | ||
719 | if (err) | ||
720 | break; | ||
721 | } | ||
722 | |||
723 | out: | ||
724 | err2 = iter->ops->finish_entry(iter, al); | ||
725 | if (!err) | ||
726 | err = err2; | ||
727 | |||
728 | return err; | ||
729 | } | ||
730 | |||
432 | int64_t | 731 | int64_t |
433 | hist_entry__cmp(struct hist_entry *left, struct hist_entry *right) | 732 | hist_entry__cmp(struct hist_entry *left, struct hist_entry *right) |
434 | { | 733 | { |
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 03ae1dbb1b15..8894f184357c 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h | |||
@@ -96,12 +96,45 @@ struct hists { | |||
96 | u16 col_len[HISTC_NR_COLS]; | 96 | u16 col_len[HISTC_NR_COLS]; |
97 | }; | 97 | }; |
98 | 98 | ||
99 | struct hist_entry_iter; | ||
100 | |||
101 | struct hist_iter_ops { | ||
102 | int (*prepare_entry)(struct hist_entry_iter *, struct addr_location *); | ||
103 | int (*add_single_entry)(struct hist_entry_iter *, struct addr_location *); | ||
104 | int (*next_entry)(struct hist_entry_iter *, struct addr_location *); | ||
105 | int (*add_next_entry)(struct hist_entry_iter *, struct addr_location *); | ||
106 | int (*finish_entry)(struct hist_entry_iter *, struct addr_location *); | ||
107 | }; | ||
108 | |||
109 | struct hist_entry_iter { | ||
110 | int total; | ||
111 | int curr; | ||
112 | |||
113 | bool hide_unresolved; | ||
114 | |||
115 | struct perf_evsel *evsel; | ||
116 | struct perf_sample *sample; | ||
117 | struct hist_entry *he; | ||
118 | struct symbol *parent; | ||
119 | void *priv; | ||
120 | |||
121 | const struct hist_iter_ops *ops; | ||
122 | }; | ||
123 | |||
124 | extern const struct hist_iter_ops hist_iter_normal; | ||
125 | extern const struct hist_iter_ops hist_iter_branch; | ||
126 | extern const struct hist_iter_ops hist_iter_mem; | ||
127 | |||
99 | struct hist_entry *__hists__add_entry(struct hists *hists, | 128 | struct hist_entry *__hists__add_entry(struct hists *hists, |
100 | struct addr_location *al, | 129 | struct addr_location *al, |
101 | struct symbol *parent, | 130 | struct symbol *parent, |
102 | struct branch_info *bi, | 131 | struct branch_info *bi, |
103 | struct mem_info *mi, u64 period, | 132 | struct mem_info *mi, u64 period, |
104 | u64 weight, u64 transaction); | 133 | u64 weight, u64 transaction); |
134 | int hist_entry_iter__add(struct hist_entry_iter *iter, struct addr_location *al, | ||
135 | struct perf_evsel *evsel, struct perf_sample *sample, | ||
136 | int max_stack_depth); | ||
137 | |||
105 | int64_t hist_entry__cmp(struct hist_entry *left, struct hist_entry *right); | 138 | int64_t hist_entry__cmp(struct hist_entry *left, struct hist_entry *right); |
106 | int64_t hist_entry__collapse(struct hist_entry *left, struct hist_entry *right); | 139 | int64_t hist_entry__collapse(struct hist_entry *left, struct hist_entry *right); |
107 | int hist_entry__transaction_len(void); | 140 | int hist_entry__transaction_len(void); |