aboutsummaryrefslogtreecommitdiffstats
path: root/tools/perf/builtin-report.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/perf/builtin-report.c')
-rw-r--r--tools/perf/builtin-report.c210
1 files changed, 64 insertions, 146 deletions
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
index bc0eec1ce4be..21d830bafff3 100644
--- a/tools/perf/builtin-report.c
+++ b/tools/perf/builtin-report.c
@@ -72,6 +72,10 @@ static int report__config(const char *var, const char *value, void *cb)
72 rep->min_percent = strtof(value, NULL); 72 rep->min_percent = strtof(value, NULL);
73 return 0; 73 return 0;
74 } 74 }
75 if (!strcmp(var, "report.children")) {
76 symbol_conf.cumulate_callchain = perf_config_bool(var, value);
77 return 0;
78 }
75 79
76 return perf_default_config(var, value, cb); 80 return perf_default_config(var, value, cb);
77} 81}
@@ -85,156 +89,52 @@ static void report__inc_stats(struct report *rep, struct hist_entry *he)
85 */ 89 */
86 if (he->stat.nr_events == 1) 90 if (he->stat.nr_events == 1)
87 rep->nr_entries++; 91 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_events(he->hists, PERF_RECORD_SAMPLE);
96 if (!he->filtered)
97 he->hists->stats.nr_non_filtered_samples++;
98} 92}
99 93
100static int report__add_mem_hist_entry(struct report *rep, struct addr_location *al, 94static int hist_iter__report_callback(struct hist_entry_iter *iter,
101 struct perf_sample *sample, struct perf_evsel *evsel) 95 struct addr_location *al, bool single,
96 void *arg)
102{ 97{
103 struct symbol *parent = NULL; 98 int err = 0;
104 struct hist_entry *he; 99 struct report *rep = arg;
105 struct mem_info *mi, *mx; 100 struct hist_entry *he = iter->he;
106 uint64_t cost; 101 struct perf_evsel *evsel = iter->evsel;
107 int err = sample__resolve_callchain(sample, &parent, evsel, al, rep->max_stack); 102 struct mem_info *mi;
108 103 struct branch_info *bi;
109 if (err)
110 return err;
111 104
112 mi = sample__resolve_mem(sample, al); 105 report__inc_stats(rep, he);
113 if (!mi)
114 return -ENOMEM;
115 106
116 if (rep->hide_unresolved && !al->sym) 107 if (!ui__has_annotation())
117 return 0; 108 return 0;
118 109
119 cost = sample->weight; 110 if (sort__mode == SORT_MODE__BRANCH) {
120 if (!cost) 111 bi = he->branch_info;
121 cost = 1; 112 err = addr_map_symbol__inc_samples(&bi->from, evsel->idx);
122
123 /*
124 * must pass period=weight in order to get the correct
125 * sorting from hists__collapse_resort() which is solely
126 * based on periods. We want sorting be done on nr_events * weight
127 * and this is indirectly achieved by passing period=weight here
128 * and the he_stat__add_period() function.
129 */
130 he = __hists__add_entry(&evsel->hists, al, parent, NULL, mi,
131 cost, cost, 0);
132 if (!he)
133 return -ENOMEM;
134
135 if (ui__has_annotation()) {
136 err = hist_entry__inc_addr_samples(he, evsel->idx, al->addr);
137 if (err)
138 goto out;
139
140 mx = he->mem_info;
141 err = addr_map_symbol__inc_samples(&mx->daddr, evsel->idx);
142 if (err) 113 if (err)
143 goto out; 114 goto out;
144 }
145
146 report__inc_stats(rep, he);
147
148 err = hist_entry__append_callchain(he, sample);
149out:
150 return err;
151}
152
153static int report__add_branch_hist_entry(struct report *rep, struct addr_location *al,
154 struct perf_sample *sample, struct perf_evsel *evsel)
155{
156 struct symbol *parent = NULL;
157 unsigned i;
158 struct hist_entry *he;
159 struct branch_info *bi, *bx;
160 int err = sample__resolve_callchain(sample, &parent, evsel, al, rep->max_stack);
161 115
162 if (err) 116 err = addr_map_symbol__inc_samples(&bi->to, evsel->idx);
163 return err;
164
165 bi = sample__resolve_bstack(sample, al);
166 if (!bi)
167 return -ENOMEM;
168
169 for (i = 0; i < sample->branch_stack->nr; i++) {
170 if (rep->hide_unresolved && !(bi[i].from.sym && bi[i].to.sym))
171 continue;
172 117
173 err = -ENOMEM; 118 } else if (rep->mem_mode) {
174 119 mi = he->mem_info;
175 /* overwrite the 'al' to branch-to info */ 120 err = addr_map_symbol__inc_samples(&mi->daddr, evsel->idx);
176 al->map = bi[i].to.map; 121 if (err)
177 al->sym = bi[i].to.sym;
178 al->addr = bi[i].to.addr;
179 /*
180 * The report shows the percentage of total branches captured
181 * and not events sampled. Thus we use a pseudo period of 1.
182 */
183 he = __hists__add_entry(&evsel->hists, al, parent, &bi[i], NULL,
184 1, 1, 0);
185 if (he) {
186 if (ui__has_annotation()) {
187 bx = he->branch_info;
188 err = addr_map_symbol__inc_samples(&bx->from,
189 evsel->idx);
190 if (err)
191 goto out;
192
193 err = addr_map_symbol__inc_samples(&bx->to,
194 evsel->idx);
195 if (err)
196 goto out;
197 }
198 report__inc_stats(rep, he);
199 } else
200 goto out; 122 goto out;
201 }
202 err = 0;
203out:
204 free(bi);
205 return err;
206}
207
208static int report__add_hist_entry(struct report *rep, struct perf_evsel *evsel,
209 struct addr_location *al, struct perf_sample *sample)
210{
211 struct symbol *parent = NULL;
212 struct hist_entry *he;
213 int err = sample__resolve_callchain(sample, &parent, evsel, al, rep->max_stack);
214
215 if (err)
216 return err;
217 123
218 he = __hists__add_entry(&evsel->hists, al, parent, NULL, NULL,
219 sample->period, sample->weight,
220 sample->transaction);
221 if (he == NULL)
222 return -ENOMEM;
223
224 err = hist_entry__append_callchain(he, sample);
225 if (err)
226 goto out;
227
228 if (ui__has_annotation())
229 err = hist_entry__inc_addr_samples(he, evsel->idx, al->addr); 124 err = hist_entry__inc_addr_samples(he, evsel->idx, al->addr);
230 125
231 report__inc_stats(rep, he); 126 } else if (symbol_conf.cumulate_callchain) {
127 if (single)
128 err = hist_entry__inc_addr_samples(he, evsel->idx,
129 al->addr);
130 } else {
131 err = hist_entry__inc_addr_samples(he, evsel->idx, al->addr);
132 }
232 133
233out: 134out:
234 return err; 135 return err;
235} 136}
236 137
237
238static int process_sample_event(struct perf_tool *tool, 138static int process_sample_event(struct perf_tool *tool,
239 union perf_event *event, 139 union perf_event *event,
240 struct perf_sample *sample, 140 struct perf_sample *sample,
@@ -243,6 +143,10 @@ static int process_sample_event(struct perf_tool *tool,
243{ 143{
244 struct report *rep = container_of(tool, struct report, tool); 144 struct report *rep = container_of(tool, struct report, tool);
245 struct addr_location al; 145 struct addr_location al;
146 struct hist_entry_iter iter = {
147 .hide_unresolved = rep->hide_unresolved,
148 .add_entry_cb = hist_iter__report_callback,
149 };
246 int ret; 150 int ret;
247 151
248 if (perf_event__preprocess_sample(event, machine, &al, sample) < 0) { 152 if (perf_event__preprocess_sample(event, machine, &al, sample) < 0) {
@@ -257,22 +161,23 @@ static int process_sample_event(struct perf_tool *tool,
257 if (rep->cpu_list && !test_bit(sample->cpu, rep->cpu_bitmap)) 161 if (rep->cpu_list && !test_bit(sample->cpu, rep->cpu_bitmap))
258 return 0; 162 return 0;
259 163
260 if (sort__mode == SORT_MODE__BRANCH) { 164 if (sort__mode == SORT_MODE__BRANCH)
261 ret = report__add_branch_hist_entry(rep, &al, sample, evsel); 165 iter.ops = &hist_iter_branch;
262 if (ret < 0) 166 else if (rep->mem_mode)
263 pr_debug("problem adding lbr entry, skipping event\n"); 167 iter.ops = &hist_iter_mem;
264 } else if (rep->mem_mode == 1) { 168 else if (symbol_conf.cumulate_callchain)
265 ret = report__add_mem_hist_entry(rep, &al, sample, evsel); 169 iter.ops = &hist_iter_cumulative;
266 if (ret < 0) 170 else
267 pr_debug("problem adding mem entry, skipping event\n"); 171 iter.ops = &hist_iter_normal;
268 } else { 172
269 if (al.map != NULL) 173 if (al.map != NULL)
270 al.map->dso->hit = 1; 174 al.map->dso->hit = 1;
175
176 ret = hist_entry_iter__add(&iter, &al, evsel, sample, rep->max_stack,
177 rep);
178 if (ret < 0)
179 pr_debug("problem adding hist entry, skipping event\n");
271 180
272 ret = report__add_hist_entry(rep, evsel, &al, sample);
273 if (ret < 0)
274 pr_debug("problem incrementing symbol period, skipping event\n");
275 }
276 return ret; 181 return ret;
277} 182}
278 183
@@ -329,6 +234,14 @@ static int report__setup_sample_type(struct report *rep)
329 } 234 }
330 } 235 }
331 236
237 if (symbol_conf.cumulate_callchain) {
238 /* Silently ignore if callchain is missing */
239 if (!(sample_type & PERF_SAMPLE_CALLCHAIN)) {
240 symbol_conf.cumulate_callchain = false;
241 perf_hpp__cancel_cumulate();
242 }
243 }
244
332 if (sort__mode == SORT_MODE__BRANCH) { 245 if (sort__mode == SORT_MODE__BRANCH) {
333 if (!is_pipe && 246 if (!is_pipe &&
334 !(sample_type & PERF_SAMPLE_BRANCH_STACK)) { 247 !(sample_type & PERF_SAMPLE_BRANCH_STACK)) {
@@ -712,6 +625,8 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
712 OPT_CALLBACK_DEFAULT('g', "call-graph", &report, "output_type,min_percent[,print_limit],call_order", 625 OPT_CALLBACK_DEFAULT('g', "call-graph", &report, "output_type,min_percent[,print_limit],call_order",
713 "Display callchains using output_type (graph, flat, fractal, or none) , min percent threshold, optional print limit, callchain order, key (function or address). " 626 "Display callchains using output_type (graph, flat, fractal, or none) , min percent threshold, optional print limit, callchain order, key (function or address). "
714 "Default: fractal,0.5,callee,function", &report_parse_callchain_opt, callchain_default_opt), 627 "Default: fractal,0.5,callee,function", &report_parse_callchain_opt, callchain_default_opt),
628 OPT_BOOLEAN(0, "children", &symbol_conf.cumulate_callchain,
629 "Accumulate callchains of children and show total overhead as well"),
715 OPT_INTEGER(0, "max-stack", &report.max_stack, 630 OPT_INTEGER(0, "max-stack", &report.max_stack,
716 "Set the maximum stack depth when parsing the callchain, " 631 "Set the maximum stack depth when parsing the callchain, "
717 "anything beyond the specified depth will be ignored. " 632 "anything beyond the specified depth will be ignored. "
@@ -804,8 +719,10 @@ repeat:
804 has_br_stack = perf_header__has_feat(&session->header, 719 has_br_stack = perf_header__has_feat(&session->header,
805 HEADER_BRANCH_STACK); 720 HEADER_BRANCH_STACK);
806 721
807 if (branch_mode == -1 && has_br_stack) 722 if (branch_mode == -1 && has_br_stack) {
808 sort__mode = SORT_MODE__BRANCH; 723 sort__mode = SORT_MODE__BRANCH;
724 symbol_conf.cumulate_callchain = false;
725 }
809 726
810 if (report.mem_mode) { 727 if (report.mem_mode) {
811 if (sort__mode == SORT_MODE__BRANCH) { 728 if (sort__mode == SORT_MODE__BRANCH) {
@@ -813,6 +730,7 @@ repeat:
813 goto error; 730 goto error;
814 } 731 }
815 sort__mode = SORT_MODE__MEMORY; 732 sort__mode = SORT_MODE__MEMORY;
733 symbol_conf.cumulate_callchain = false;
816 } 734 }
817 735
818 if (setup_sorting() < 0) { 736 if (setup_sorting() < 0) {