Commit 07394b5f authored by Ingo Molnar's avatar Ingo Molnar

Merge tag 'perf-core-for-mingo' of...

Merge tag 'perf-core-for-mingo' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux into perf/core

Pull perf/core improvements and fixes from Arnaldo Carvalho de Melo:

User visible changes:

  o Restore "--callchain graph" output, broken in recent cset to end
    up being the same as "fractal" (Namhyung Kim)

  o Allow profiling when kptr_restrict == 1 for non root users,
    kernel samples will just remain unresolved (Andi Kleen)

  o Allow configuring default options for callchains in config file (Namhyung Kim)

  o Fix line number in the config file error message (Jiri Olsa)

  o Fix --per-core on multi socket systems (Andi Kleen)

Cleanups:

  o Use ACCESS_ONCE() instead of volatile cast. (Pranith Kumar)

  o Modify error code for when perf_session__new() fails (Taeung Song)
Signed-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: default avatarIngo Molnar <mingo@kernel.org>
parents cf8102f6 49757c9c
...@@ -340,7 +340,7 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __maybe_unused) ...@@ -340,7 +340,7 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __maybe_unused)
annotate.session = perf_session__new(&file, false, &annotate.tool); annotate.session = perf_session__new(&file, false, &annotate.tool);
if (annotate.session == NULL) if (annotate.session == NULL)
return -ENOMEM; return -1;
symbol_conf.priv_size = sizeof(struct annotation); symbol_conf.priv_size = sizeof(struct annotation);
symbol_conf.try_vmlinux_path = true; symbol_conf.try_vmlinux_path = true;
......
...@@ -683,7 +683,7 @@ static int __cmd_diff(void) ...@@ -683,7 +683,7 @@ static int __cmd_diff(void)
d->session = perf_session__new(&d->file, false, &tool); d->session = perf_session__new(&d->file, false, &tool);
if (!d->session) { if (!d->session) {
pr_err("Failed to open %s\n", d->file.path); pr_err("Failed to open %s\n", d->file.path);
ret = -ENOMEM; ret = -1;
goto out_delete; goto out_delete;
} }
......
...@@ -28,7 +28,7 @@ static int __cmd_evlist(const char *file_name, struct perf_attr_details *details ...@@ -28,7 +28,7 @@ static int __cmd_evlist(const char *file_name, struct perf_attr_details *details
session = perf_session__new(&file, 0, NULL); session = perf_session__new(&file, 0, NULL);
if (session == NULL) if (session == NULL)
return -ENOMEM; return -1;
evlist__for_each(session->evlist, pos) evlist__for_each(session->evlist, pos)
perf_evsel__fprintf(pos, details, stdout); perf_evsel__fprintf(pos, details, stdout);
......
...@@ -460,7 +460,7 @@ int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused) ...@@ -460,7 +460,7 @@ int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused)
file.path = inject.input_name; file.path = inject.input_name;
inject.session = perf_session__new(&file, true, &inject.tool); inject.session = perf_session__new(&file, true, &inject.tool);
if (inject.session == NULL) if (inject.session == NULL)
return -ENOMEM; return -1;
if (symbol__init(&inject.session->header.env) < 0) if (symbol__init(&inject.session->header.env) < 0)
return -1; return -1;
......
...@@ -698,7 +698,7 @@ int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused) ...@@ -698,7 +698,7 @@ int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused)
session = perf_session__new(&file, false, &perf_kmem); session = perf_session__new(&file, false, &perf_kmem);
if (session == NULL) if (session == NULL)
return -ENOMEM; return -1;
symbol__init(&session->header.env); symbol__init(&session->header.env);
......
...@@ -1058,7 +1058,7 @@ static int read_events(struct perf_kvm_stat *kvm) ...@@ -1058,7 +1058,7 @@ static int read_events(struct perf_kvm_stat *kvm)
kvm->session = perf_session__new(&file, false, &kvm->tool); kvm->session = perf_session__new(&file, false, &kvm->tool);
if (!kvm->session) { if (!kvm->session) {
pr_err("Initializing perf session failed\n"); pr_err("Initializing perf session failed\n");
return -EINVAL; return -1;
} }
symbol__init(&kvm->session->header.env); symbol__init(&kvm->session->header.env);
...@@ -1361,7 +1361,7 @@ static int kvm_events_live(struct perf_kvm_stat *kvm, ...@@ -1361,7 +1361,7 @@ static int kvm_events_live(struct perf_kvm_stat *kvm,
*/ */
kvm->session = perf_session__new(&file, false, &kvm->tool); kvm->session = perf_session__new(&file, false, &kvm->tool);
if (kvm->session == NULL) { if (kvm->session == NULL) {
err = -ENOMEM; err = -1;
goto out; goto out;
} }
kvm->session->evlist = kvm->evlist; kvm->session->evlist = kvm->evlist;
......
...@@ -862,7 +862,7 @@ static int __cmd_report(bool display_info) ...@@ -862,7 +862,7 @@ static int __cmd_report(bool display_info)
session = perf_session__new(&file, false, &eops); session = perf_session__new(&file, false, &eops);
if (!session) { if (!session) {
pr_err("Initializing perf session failed\n"); pr_err("Initializing perf session failed\n");
return -ENOMEM; return -1;
} }
symbol__init(&session->header.env); symbol__init(&session->header.env);
......
...@@ -124,7 +124,7 @@ static int report_raw_events(struct perf_mem *mem) ...@@ -124,7 +124,7 @@ static int report_raw_events(struct perf_mem *mem)
&mem->tool); &mem->tool);
if (session == NULL) if (session == NULL)
return -ENOMEM; return -1;
if (mem->cpu_list) { if (mem->cpu_list) {
ret = perf_session__cpu_bitmap(session, mem->cpu_list, ret = perf_session__cpu_bitmap(session, mem->cpu_list,
......
...@@ -624,145 +624,56 @@ parse_branch_stack(const struct option *opt, const char *str, int unset) ...@@ -624,145 +624,56 @@ parse_branch_stack(const struct option *opt, const char *str, int unset)
return ret; return ret;
} }
#ifdef HAVE_DWARF_UNWIND_SUPPORT static void callchain_debug(void)
static int get_stack_size(char *str, unsigned long *_size)
{
char *endptr;
unsigned long size;
unsigned long max_size = round_down(USHRT_MAX, sizeof(u64));
size = strtoul(str, &endptr, 0);
do {
if (*endptr)
break;
size = round_up(size, sizeof(u64));
if (!size || size > max_size)
break;
*_size = size;
return 0;
} while (0);
pr_err("callchain: Incorrect stack dump size (max %ld): %s\n",
max_size, str);
return -1;
}
#endif /* HAVE_DWARF_UNWIND_SUPPORT */
int record_parse_callchain(const char *arg, struct record_opts *opts)
{
char *tok, *name, *saveptr = NULL;
char *buf;
int ret = -1;
/* We need buffer that we know we can write to. */
buf = malloc(strlen(arg) + 1);
if (!buf)
return -ENOMEM;
strcpy(buf, arg);
tok = strtok_r((char *)buf, ",", &saveptr);
name = tok ? : (char *)buf;
do {
/* Framepointer style */
if (!strncmp(name, "fp", sizeof("fp"))) {
if (!strtok_r(NULL, ",", &saveptr)) {
opts->call_graph = CALLCHAIN_FP;
ret = 0;
} else
pr_err("callchain: No more arguments "
"needed for -g fp\n");
break;
#ifdef HAVE_DWARF_UNWIND_SUPPORT
/* Dwarf style */
} else if (!strncmp(name, "dwarf", sizeof("dwarf"))) {
const unsigned long default_stack_dump_size = 8192;
ret = 0;
opts->call_graph = CALLCHAIN_DWARF;
opts->stack_dump_size = default_stack_dump_size;
tok = strtok_r(NULL, ",", &saveptr);
if (tok) {
unsigned long size = 0;
ret = get_stack_size(tok, &size);
opts->stack_dump_size = size;
}
#endif /* HAVE_DWARF_UNWIND_SUPPORT */
} else {
pr_err("callchain: Unknown --call-graph option "
"value: %s\n", arg);
break;
}
} while (0);
free(buf);
return ret;
}
static void callchain_debug(struct record_opts *opts)
{ {
static const char *str[CALLCHAIN_MAX] = { "NONE", "FP", "DWARF" }; static const char *str[CALLCHAIN_MAX] = { "NONE", "FP", "DWARF" };
pr_debug("callchain: type %s\n", str[opts->call_graph]); pr_debug("callchain: type %s\n", str[callchain_param.record_mode]);
if (opts->call_graph == CALLCHAIN_DWARF) if (callchain_param.record_mode == CALLCHAIN_DWARF)
pr_debug("callchain: stack dump size %d\n", pr_debug("callchain: stack dump size %d\n",
opts->stack_dump_size); callchain_param.dump_size);
} }
int record_parse_callchain_opt(const struct option *opt, int record_parse_callchain_opt(const struct option *opt __maybe_unused,
const char *arg, const char *arg,
int unset) int unset)
{ {
struct record_opts *opts = opt->value;
int ret; int ret;
opts->call_graph_enabled = !unset; callchain_param.enabled = !unset;
/* --no-call-graph */ /* --no-call-graph */
if (unset) { if (unset) {
opts->call_graph = CALLCHAIN_NONE; callchain_param.record_mode = CALLCHAIN_NONE;
pr_debug("callchain: disabled\n"); pr_debug("callchain: disabled\n");
return 0; return 0;
} }
ret = record_parse_callchain(arg, opts); ret = parse_callchain_record_opt(arg);
if (!ret) if (!ret)
callchain_debug(opts); callchain_debug();
return ret; return ret;
} }
int record_callchain_opt(const struct option *opt, int record_callchain_opt(const struct option *opt __maybe_unused,
const char *arg __maybe_unused, const char *arg __maybe_unused,
int unset __maybe_unused) int unset __maybe_unused)
{ {
struct record_opts *opts = opt->value; callchain_param.enabled = true;
opts->call_graph_enabled = !unset; if (callchain_param.record_mode == CALLCHAIN_NONE)
callchain_param.record_mode = CALLCHAIN_FP;
if (opts->call_graph == CALLCHAIN_NONE) callchain_debug();
opts->call_graph = CALLCHAIN_FP;
callchain_debug(opts);
return 0; return 0;
} }
static int perf_record_config(const char *var, const char *value, void *cb) static int perf_record_config(const char *var, const char *value, void *cb)
{ {
struct record *rec = cb;
if (!strcmp(var, "record.call-graph")) if (!strcmp(var, "record.call-graph"))
return record_parse_callchain(value, &rec->opts); var = "call-graph.record-mode"; /* fall-through */
return perf_default_config(var, value, cb); return perf_default_config(var, value, cb);
} }
......
...@@ -720,7 +720,7 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) ...@@ -720,7 +720,7 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
repeat: repeat:
session = perf_session__new(&file, false, &report.tool); session = perf_session__new(&file, false, &report.tool);
if (session == NULL) if (session == NULL)
return -ENOMEM; return -1;
if (report.queue_size) { if (report.queue_size) {
ordered_events__set_alloc_size(&session->ordered_events, ordered_events__set_alloc_size(&session->ordered_events,
......
...@@ -1744,7 +1744,7 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused) ...@@ -1744,7 +1744,7 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused)
session = perf_session__new(&file, false, &script.tool); session = perf_session__new(&file, false, &script.tool);
if (session == NULL) if (session == NULL)
return -ENOMEM; return -1;
if (header || header_only) { if (header || header_only) {
perf_session__fprintf_info(session, stdout, show_full_info); perf_session__fprintf_info(session, stdout, show_full_info);
......
...@@ -732,7 +732,7 @@ static void aggr_printout(struct perf_evsel *evsel, int id, int nr) ...@@ -732,7 +732,7 @@ static void aggr_printout(struct perf_evsel *evsel, int id, int nr)
} }
} }
static void nsec_printout(int cpu, int nr, struct perf_evsel *evsel, double avg) static void nsec_printout(int id, int nr, struct perf_evsel *evsel, double avg)
{ {
double msecs = avg / 1e6; double msecs = avg / 1e6;
const char *fmt_v, *fmt_n; const char *fmt_v, *fmt_n;
...@@ -741,7 +741,7 @@ static void nsec_printout(int cpu, int nr, struct perf_evsel *evsel, double avg) ...@@ -741,7 +741,7 @@ static void nsec_printout(int cpu, int nr, struct perf_evsel *evsel, double avg)
fmt_v = csv_output ? "%.6f%s" : "%18.6f%s"; fmt_v = csv_output ? "%.6f%s" : "%18.6f%s";
fmt_n = csv_output ? "%s" : "%-25s"; fmt_n = csv_output ? "%s" : "%-25s";
aggr_printout(evsel, cpu, nr); aggr_printout(evsel, id, nr);
scnprintf(name, sizeof(name), "%s%s", scnprintf(name, sizeof(name), "%s%s",
perf_evsel__name(evsel), csv_output ? "" : " (msec)"); perf_evsel__name(evsel), csv_output ? "" : " (msec)");
...@@ -947,11 +947,12 @@ static void print_ll_cache_misses(int cpu, ...@@ -947,11 +947,12 @@ static void print_ll_cache_misses(int cpu,
fprintf(output, " of all LL-cache hits "); fprintf(output, " of all LL-cache hits ");
} }
static void abs_printout(int cpu, int nr, struct perf_evsel *evsel, double avg) static void abs_printout(int id, int nr, struct perf_evsel *evsel, double avg)
{ {
double total, ratio = 0.0, total2; double total, ratio = 0.0, total2;
double sc = evsel->scale; double sc = evsel->scale;
const char *fmt; const char *fmt;
int cpu = cpu_map__id_to_cpu(id);
if (csv_output) { if (csv_output) {
fmt = sc != 1.0 ? "%.2f%s" : "%.0f%s"; fmt = sc != 1.0 ? "%.2f%s" : "%.0f%s";
...@@ -962,7 +963,7 @@ static void abs_printout(int cpu, int nr, struct perf_evsel *evsel, double avg) ...@@ -962,7 +963,7 @@ static void abs_printout(int cpu, int nr, struct perf_evsel *evsel, double avg)
fmt = sc != 1.0 ? "%18.2f%s" : "%18.0f%s"; fmt = sc != 1.0 ? "%18.2f%s" : "%18.0f%s";
} }
aggr_printout(evsel, cpu, nr); aggr_printout(evsel, id, nr);
if (aggr_mode == AGGR_GLOBAL) if (aggr_mode == AGGR_GLOBAL)
cpu = 0; cpu = 0;
......
...@@ -1605,7 +1605,7 @@ static int __cmd_timechart(struct timechart *tchart, const char *output_name) ...@@ -1605,7 +1605,7 @@ static int __cmd_timechart(struct timechart *tchart, const char *output_name)
int ret = -EINVAL; int ret = -EINVAL;
if (session == NULL) if (session == NULL)
return -ENOMEM; return -1;
symbol__init(&session->header.env); symbol__init(&session->header.env);
......
...@@ -929,7 +929,7 @@ static int __cmd_top(struct perf_top *top) ...@@ -929,7 +929,7 @@ static int __cmd_top(struct perf_top *top)
top->session = perf_session__new(NULL, false, NULL); top->session = perf_session__new(NULL, false, NULL);
if (top->session == NULL) if (top->session == NULL)
return -ENOMEM; return -1;
machines__set_symbol_filter(&top->session->machines, symbol_filter); machines__set_symbol_filter(&top->session->machines, symbol_filter);
...@@ -1020,10 +1020,8 @@ parse_callchain_opt(const struct option *opt, const char *arg, int unset) ...@@ -1020,10 +1020,8 @@ parse_callchain_opt(const struct option *opt, const char *arg, int unset)
static int perf_top_config(const char *var, const char *value, void *cb) static int perf_top_config(const char *var, const char *value, void *cb)
{ {
struct perf_top *top = cb;
if (!strcmp(var, "top.call-graph")) if (!strcmp(var, "top.call-graph"))
return record_parse_callchain(value, &top->record_opts); var = "call-graph.record-mode"; /* fall-through */
if (!strcmp(var, "top.children")) { if (!strcmp(var, "top.children")) {
symbol_conf.cumulate_callchain = perf_config_bool(var, value); symbol_conf.cumulate_callchain = perf_config_bool(var, value);
return 0; return 0;
......
...@@ -2250,7 +2250,7 @@ static int trace__replay(struct trace *trace) ...@@ -2250,7 +2250,7 @@ static int trace__replay(struct trace *trace)
session = perf_session__new(&file, false, &trace->tool); session = perf_session__new(&file, false, &trace->tool);
if (session == NULL) if (session == NULL)
return -ENOMEM; return -1;
if (symbol__init(&session->header.env) < 0) if (symbol__init(&session->header.env) < 0)
goto out; goto out;
......
...@@ -41,8 +41,6 @@ void pthread__unblock_sigwinch(void); ...@@ -41,8 +41,6 @@ void pthread__unblock_sigwinch(void);
struct record_opts { struct record_opts {
struct target target; struct target target;
int call_graph;
bool call_graph_enabled;
bool group; bool group;
bool inherit_stat; bool inherit_stat;
bool no_buffering; bool no_buffering;
...@@ -60,7 +58,6 @@ struct record_opts { ...@@ -60,7 +58,6 @@ struct record_opts {
u64 branch_stack; u64 branch_stack;
u64 default_interval; u64 default_interval;
u64 user_interval; u64 user_interval;
u16 stack_dump_size;
bool sample_transaction; bool sample_transaction;
unsigned initial_delay; unsigned initial_delay;
}; };
......
...@@ -804,9 +804,6 @@ static int hist_browser__show_entry(struct hist_browser *browser, ...@@ -804,9 +804,6 @@ static int hist_browser__show_entry(struct hist_browser *browser,
.is_current_entry = current_entry, .is_current_entry = current_entry,
}; };
if (symbol_conf.cumulate_callchain)
total = entry->stat_acc->period;
printed += hist_browser__show_callchain(browser, printed += hist_browser__show_callchain(browser,
&entry->sorted_chain, 1, row, total, &entry->sorted_chain, 1, row, total,
hist_browser__show_callchain_entry, &arg, hist_browser__show_callchain_entry, &arg,
......
...@@ -25,6 +25,133 @@ ...@@ -25,6 +25,133 @@
__thread struct callchain_cursor callchain_cursor; __thread struct callchain_cursor callchain_cursor;
#ifdef HAVE_DWARF_UNWIND_SUPPORT
static int get_stack_size(const char *str, unsigned long *_size)
{
char *endptr;
unsigned long size;
unsigned long max_size = round_down(USHRT_MAX, sizeof(u64));
size = strtoul(str, &endptr, 0);
do {
if (*endptr)
break;
size = round_up(size, sizeof(u64));
if (!size || size > max_size)
break;
*_size = size;
return 0;
} while (0);
pr_err("callchain: Incorrect stack dump size (max %ld): %s\n",
max_size, str);
return -1;
}
#endif /* HAVE_DWARF_UNWIND_SUPPORT */
int parse_callchain_record_opt(const char *arg)
{
char *tok, *name, *saveptr = NULL;
char *buf;
int ret = -1;
/* We need buffer that we know we can write to. */
buf = malloc(strlen(arg) + 1);
if (!buf)
return -ENOMEM;
strcpy(buf, arg);
tok = strtok_r((char *)buf, ",", &saveptr);
name = tok ? : (char *)buf;
do {
/* Framepointer style */
if (!strncmp(name, "fp", sizeof("fp"))) {
if (!strtok_r(NULL, ",", &saveptr)) {
callchain_param.record_mode = CALLCHAIN_FP;
ret = 0;
} else
pr_err("callchain: No more arguments "
"needed for -g fp\n");
break;
#ifdef HAVE_DWARF_UNWIND_SUPPORT
/* Dwarf style */
} else if (!strncmp(name, "dwarf", sizeof("dwarf"))) {
const unsigned long default_stack_dump_size = 8192;
ret = 0;
callchain_param.record_mode = CALLCHAIN_DWARF;
callchain_param.dump_size = default_stack_dump_size;
tok = strtok_r(NULL, ",", &saveptr);
if (tok) {
unsigned long size = 0;
ret = get_stack_size(tok, &size);
callchain_param.dump_size = size;
}
#endif /* HAVE_DWARF_UNWIND_SUPPORT */
} else {
pr_err("callchain: Unknown --call-graph option "
"value: %s\n", arg);
break;
}
} while (0);
free(buf);
return ret;
}
static int parse_callchain_mode(const char *value)
{
if (!strncmp(value, "graph", strlen(value))) {
callchain_param.mode = CHAIN_GRAPH_ABS;
return 0;
}
if (!strncmp(value, "flat", strlen(value))) {
callchain_param.mode = CHAIN_FLAT;
return 0;
}
if (!strncmp(value, "fractal", strlen(value))) {
callchain_param.mode = CHAIN_GRAPH_REL;
return 0;
}
return -1;
}
static int parse_callchain_order(const char *value)
{
if (!strncmp(value, "caller", strlen(value))) {
callchain_param.order = ORDER_CALLER;
return 0;
}
if (!strncmp(value, "callee", strlen(value))) {
callchain_param.order = ORDER_CALLEE;
return 0;
}
return -1;
}
static int parse_callchain_sort_key(const char *value)
{
if (!strncmp(value, "function", strlen(value))) {
callchain_param.key = CCKEY_FUNCTION;
return 0;
}
if (!strncmp(value, "address", strlen(value))) {
callchain_param.key = CCKEY_ADDRESS;
return 0;
}
return -1;
}
int int
parse_callchain_report_opt(const char *arg) parse_callchain_report_opt(const char *arg)
{ {
...@@ -44,25 +171,12 @@ parse_callchain_report_opt(const char *arg) ...@@ -44,25 +171,12 @@ parse_callchain_report_opt(const char *arg)
return 0; return 0;
} }
/* try to get the output mode */ if (!parse_callchain_mode(tok) ||
if (!strncmp(tok, "graph", strlen(tok))) !parse_callchain_order(tok) ||
callchain_param.mode = CHAIN_GRAPH_ABS; !parse_callchain_sort_key(tok)) {
else if (!strncmp(tok, "flat", strlen(tok))) /* parsing ok - move on to the next */
callchain_param.mode = CHAIN_FLAT; } else if (!minpcnt_set) {
else if (!strncmp(tok, "fractal", strlen(tok)))
callchain_param.mode = CHAIN_GRAPH_REL;
/* try to get the call chain order */
else if (!strncmp(tok, "caller", strlen(tok)))
callchain_param.order = ORDER_CALLER;
else if (!strncmp(tok, "callee", strlen(tok)))
callchain_param.order = ORDER_CALLEE;
/* try to get the sort key */
else if (!strncmp(tok, "function", strlen(tok)))
callchain_param.key = CCKEY_FUNCTION;
else if (!strncmp(tok, "address", strlen(tok)))
callchain_param.key = CCKEY_ADDRESS;
/* try to get the min percent */ /* try to get the min percent */
else if (!minpcnt_set) {
callchain_param.min_percent = strtod(tok, &endptr); callchain_param.min_percent = strtod(tok, &endptr);
if (tok == endptr) if (tok == endptr)
return -1; return -1;
...@@ -84,6 +198,47 @@ parse_callchain_report_opt(const char *arg) ...@@ -84,6 +198,47 @@ parse_callchain_report_opt(const char *arg)
return 0; return 0;
} }
int perf_callchain_config(const char *var, const char *value)
{
char *endptr;
if (prefixcmp(var, "call-graph."))
return 0;
var += sizeof("call-graph.") - 1;
if (!strcmp(var, "record-mode"))
return parse_callchain_record_opt(value);
#ifdef HAVE_DWARF_UNWIND_SUPPORT
if (!strcmp(var, "dump-size")) {
unsigned long size = 0;
int ret;
ret = get_stack_size(value, &size);
callchain_param.dump_size = size;
return ret;
}
#endif
if (!strcmp(var, "print-type"))
return parse_callchain_mode(value);
if (!strcmp(var, "order"))
return parse_callchain_order(value);
if (!strcmp(var, "sort-key"))
return parse_callchain_sort_key(value);
if (!strcmp(var, "threshold")) {
callchain_param.min_percent = strtod(value, &endptr);
if (value == endptr)
return -1;
}
if (!strcmp(var, "print-limit")) {
callchain_param.print_limit = strtod(value, &endptr);
if (value == endptr)
return -1;
}
return 0;
}
static void static void
rb_insert_callchain(struct rb_root *root, struct callchain_node *chain, rb_insert_callchain(struct rb_root *root, struct callchain_node *chain,
enum chain_mode mode) enum chain_mode mode)
......
...@@ -54,6 +54,9 @@ enum chain_key { ...@@ -54,6 +54,9 @@ enum chain_key {
}; };
struct callchain_param { struct callchain_param {
bool enabled;
enum perf_call_graph_mode record_mode;
u32 dump_size;
enum chain_mode mode; enum chain_mode mode;
u32 print_limit; u32 print_limit;
double min_percent; double min_percent;
...@@ -154,7 +157,6 @@ static inline void callchain_cursor_advance(struct callchain_cursor *cursor) ...@@ -154,7 +157,6 @@ static inline void callchain_cursor_advance(struct callchain_cursor *cursor)
struct option; struct option;
struct hist_entry; struct hist_entry;
int record_parse_callchain(const char *arg, struct record_opts *opts);
int record_parse_callchain_opt(const struct option *opt, const char *arg, int unset); int record_parse_callchain_opt(const struct option *opt, const char *arg, int unset);
int record_callchain_opt(const struct option *opt, const char *arg, int unset); int record_callchain_opt(const struct option *opt, const char *arg, int unset);
...@@ -166,7 +168,9 @@ int fill_callchain_info(struct addr_location *al, struct callchain_cursor_node * ...@@ -166,7 +168,9 @@ int fill_callchain_info(struct addr_location *al, struct callchain_cursor_node *
bool hide_unresolved); bool hide_unresolved);
extern const char record_callchain_help[]; extern const char record_callchain_help[];
int parse_callchain_record_opt(const char *arg);
int parse_callchain_report_opt(const char *arg); int parse_callchain_report_opt(const char *arg);
int perf_callchain_config(const char *var, const char *value);
static inline void callchain_cursor_snapshot(struct callchain_cursor *dest, static inline void callchain_cursor_snapshot(struct callchain_cursor *dest,
struct callchain_cursor *src) struct callchain_cursor *src)
......
...@@ -222,7 +222,8 @@ static int perf_parse_file(config_fn_t fn, void *data) ...@@ -222,7 +222,8 @@ static int perf_parse_file(config_fn_t fn, void *data)
const unsigned char *bomptr = utf8_bom; const unsigned char *bomptr = utf8_bom;
for (;;) { for (;;) {
int c = get_next_char(); int line, c = get_next_char();
if (bomptr && *bomptr) { if (bomptr && *bomptr) {
/* We are at the file beginning; skip UTF8-encoded BOM /* We are at the file beginning; skip UTF8-encoded BOM
* if present. Sane editors won't put this in on their * if present. Sane editors won't put this in on their
...@@ -261,9 +262,17 @@ static int perf_parse_file(config_fn_t fn, void *data) ...@@ -261,9 +262,17 @@ static int perf_parse_file(config_fn_t fn, void *data)
if (!isalpha(c)) if (!isalpha(c))
break; break;
var[baselen] = tolower(c); var[baselen] = tolower(c);
if (get_value(fn, data, var, baselen+1) < 0)
/*
* The get_value function might or might not reach the '\n',
* so saving the current line number for error reporting.
*/
line = config_linenr;
if (get_value(fn, data, var, baselen+1) < 0) {
config_linenr = line;
break; break;
} }
}
die("bad config file line %d in %s", config_linenr, config_file_name); die("bad config file line %d in %s", config_linenr, config_file_name);
} }
...@@ -396,6 +405,9 @@ int perf_default_config(const char *var, const char *value, ...@@ -396,6 +405,9 @@ int perf_default_config(const char *var, const char *value,
if (!prefixcmp(var, "ui.")) if (!prefixcmp(var, "ui."))
return perf_ui_config(var, value); return perf_ui_config(var, value);
if (!prefixcmp(var, "call-graph."))
return perf_callchain_config(var, value);
/* Add other config variables here. */ /* Add other config variables here. */
return 0; return 0;
} }
......
...@@ -558,13 +558,17 @@ int perf_event__synthesize_kernel_mmap(struct perf_tool *tool, ...@@ -558,13 +558,17 @@ int perf_event__synthesize_kernel_mmap(struct perf_tool *tool,
struct map *map; struct map *map;
struct kmap *kmap; struct kmap *kmap;
int err; int err;
union perf_event *event;
if (machine->vmlinux_maps[0] == NULL)
return -1;
/* /*
* We should get this from /sys/kernel/sections/.text, but till that is * We should get this from /sys/kernel/sections/.text, but till that is
* available use this, and after it is use this as a fallback for older * available use this, and after it is use this as a fallback for older
* kernels. * kernels.
*/ */
union perf_event *event = zalloc((sizeof(event->mmap) + event = zalloc((sizeof(event->mmap) + machine->id_hdr_size));
machine->id_hdr_size));
if (event == NULL) { if (event == NULL) {
pr_debug("Not enough memory synthesizing mmap event " pr_debug("Not enough memory synthesizing mmap event "
"for kernel modules\n"); "for kernel modules\n");
......
...@@ -503,20 +503,19 @@ int perf_evsel__group_desc(struct perf_evsel *evsel, char *buf, size_t size) ...@@ -503,20 +503,19 @@ int perf_evsel__group_desc(struct perf_evsel *evsel, char *buf, size_t size)
} }
static void static void
perf_evsel__config_callgraph(struct perf_evsel *evsel, perf_evsel__config_callgraph(struct perf_evsel *evsel)
struct record_opts *opts)
{ {
bool function = perf_evsel__is_function_event(evsel); bool function = perf_evsel__is_function_event(evsel);
struct perf_event_attr *attr = &evsel->attr; struct perf_event_attr *attr = &evsel->attr;
perf_evsel__set_sample_bit(evsel, CALLCHAIN); perf_evsel__set_sample_bit(evsel, CALLCHAIN);
if (opts->call_graph == CALLCHAIN_DWARF) { if (callchain_param.record_mode == CALLCHAIN_DWARF) {
if (!function) { if (!function) {
perf_evsel__set_sample_bit(evsel, REGS_USER); perf_evsel__set_sample_bit(evsel, REGS_USER);
perf_evsel__set_sample_bit(evsel, STACK_USER); perf_evsel__set_sample_bit(evsel, STACK_USER);
attr->sample_regs_user = PERF_REGS_MASK; attr->sample_regs_user = PERF_REGS_MASK;
attr->sample_stack_user = opts->stack_dump_size; attr->sample_stack_user = callchain_param.dump_size;
attr->exclude_callchain_user = 1; attr->exclude_callchain_user = 1;
} else { } else {
pr_info("Cannot use DWARF unwind for function trace event," pr_info("Cannot use DWARF unwind for function trace event,"
...@@ -625,8 +624,8 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts) ...@@ -625,8 +624,8 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts)
attr->mmap_data = track; attr->mmap_data = track;
} }
if (opts->call_graph_enabled && !evsel->no_aux_samples) if (callchain_param.enabled && !evsel->no_aux_samples)
perf_evsel__config_callgraph(evsel, opts); perf_evsel__config_callgraph(evsel);
if (target__has_cpu(&opts->target)) if (target__has_cpu(&opts->target))
perf_evsel__set_sample_bit(evsel, CPU); perf_evsel__set_sample_bit(evsel, CPU);
......
...@@ -119,7 +119,7 @@ struct perf_session *perf_session__new(struct perf_data_file *file, ...@@ -119,7 +119,7 @@ struct perf_session *perf_session__new(struct perf_data_file *file,
* kernel MMAP event, in perf_event__process_mmap(). * kernel MMAP event, in perf_event__process_mmap().
*/ */
if (perf_session__create_kernel_maps(session) < 0) if (perf_session__create_kernel_maps(session) < 0)
goto out_delete; pr_warning("Cannot read kernel map\n");
} }
if (tool && tool->ordering_requires_timestamps && if (tool && tool->ordering_requires_timestamps &&
......
...@@ -126,5 +126,5 @@ int __perf_session__set_tracepoints_handlers(struct perf_session *session, ...@@ -126,5 +126,5 @@ int __perf_session__set_tracepoints_handlers(struct perf_session *session,
extern volatile int session_done; extern volatile int session_done;
#define session_done() (*(volatile int *)(&session_done)) #define session_done() ACCESS_ONCE(session_done)
#endif /* __PERF_SESSION_H */ #endif /* __PERF_SESSION_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment