@@ -647,6 +647,16 @@ parse_callchain_opt(const struct option *opt, const char *arg, int unset)
callchain_param.key = CCKEY_ADDRESS;
else
return -1;
+
+ tok2 = strtok(NULL, ",");
+ if (!tok2)
+ goto setup;
+
+ if (!strncmp(tok2, "merge-recursive", strlen("merge-recursive")))
+ callchain_param.merge_recursive = 1;
+ else
+ return -1;
+
setup:
if (callchain_register_param(&callchain_param) < 0) {
pr_err("Can't register callchain params\n");
@@ -762,8 +772,8 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
"regex filter to identify parent, see: '--sort parent'"),
OPT_BOOLEAN('x', "exclude-other", &symbol_conf.exclude_other,
"Only display entries with parent-match"),
- OPT_CALLBACK_DEFAULT('g', "call-graph", &report, "output_type,min_percent[,print_limit],call_order",
- "Display callchains using output_type (graph, flat, fractal, or none) , min percent threshold, optional print limit, callchain order, key (function or address). "
+ OPT_CALLBACK_DEFAULT('g', "call-graph", &report, "output_type,min_percent[,print_limit],call_order,merge-recursive",
+ "Display callchains using output_type (graph, flat, fractal, or none) , min percent threshold, optional print limit, callchain order, key (function or address), merge or don't merge recursive calls"
"Default: fractal,0.5,callee,function", &parse_callchain_opt, callchain_default_opt),
OPT_INTEGER(0, "max-stack", &report.max_stack,
"Set the maximum stack depth when parsing the callchain, "
@@ -53,6 +53,7 @@ struct callchain_param {
sort_chain_func_t sort;
enum chain_order order;
enum chain_key key;
+ u32 merge_recursive;
};
struct callchain_list {
@@ -1272,6 +1272,32 @@ struct branch_info *sample__resolve_bstack(struct perf_sample *sample,
return bi;
}
+static int duplicate_symbol(struct symbol *sym, char **prev_name)
+{
+ char *prev = *prev_name;
+
+ if (sym) {
+ if (prev) {
+ if (!strcmp(sym->name, prev))
+ return 1; /* duplicate */
+ free(prev);
+ }
+
+ *prev_name = prev = strdup(sym->name);
+ if (!prev) {
+ pr_debug("%s: Unable to alloc memory\n", __func__);
+ return -ENOMEM;
+ }
+ } else if (prev) {
+ /* new symbol is NULL, so forget prev name */
+ free(prev);
+ *prev_name = NULL;
+ }
+
+ /* Not duplicate */
+ return 0;
+}
+
static int machine__resolve_callchain_sample(struct machine *machine,
struct thread *thread,
struct ip_callchain *chain,
@@ -1283,6 +1309,8 @@ static int machine__resolve_callchain_sample(struct machine *machine,
int chain_nr = min(max_stack, (int)chain->nr);
int i;
int err;
+ u64 prev_ip = 0;
+ char *prev_name = NULL;
callchain_cursor_reset(&callchain_cursor);
@@ -1324,6 +1352,10 @@ static int machine__resolve_callchain_sample(struct machine *machine,
continue;
}
+ if (callchain_param.merge_recursive && prev_ip == ip)
+ continue;
+ prev_ip = ip;
+
al.filtered = false;
thread__find_addr_location(thread, machine, cpumode,
MAP__FUNCTION, ip, &al);
@@ -1340,6 +1372,13 @@ static int machine__resolve_callchain_sample(struct machine *machine,
}
}
+ if (callchain_param.merge_recursive) {
+ if ((err = duplicate_symbol(al.sym, &prev_name)) > 0)
+ continue; /* duplicate */
+ else if (err < 0)
+ return err;
+ }
+
err = callchain_cursor_append(&callchain_cursor,
ip, al.map, al.sym);
if (err)