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)
annotate.session = perf_session__new(&file, false, &annotate.tool);
if (annotate.session == NULL)
return -ENOMEM;
return -1;
symbol_conf.priv_size = sizeof(struct annotation);
symbol_conf.try_vmlinux_path = true;
......
......@@ -683,7 +683,7 @@ static int __cmd_diff(void)
d->session = perf_session__new(&d->file, false, &tool);
if (!d->session) {
pr_err("Failed to open %s\n", d->file.path);
ret = -ENOMEM;
ret = -1;
goto out_delete;
}
......
......@@ -28,7 +28,7 @@ static int __cmd_evlist(const char *file_name, struct perf_attr_details *details
session = perf_session__new(&file, 0, NULL);
if (session == NULL)
return -ENOMEM;
return -1;
evlist__for_each(session->evlist, pos)
perf_evsel__fprintf(pos, details, stdout);
......
......@@ -460,7 +460,7 @@ int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused)
file.path = inject.input_name;
inject.session = perf_session__new(&file, true, &inject.tool);
if (inject.session == NULL)
return -ENOMEM;
return -1;
if (symbol__init(&inject.session->header.env) < 0)
return -1;
......
......@@ -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);
if (session == NULL)
return -ENOMEM;
return -1;
symbol__init(&session->header.env);
......
......@@ -1058,7 +1058,7 @@ static int read_events(struct perf_kvm_stat *kvm)
kvm->session = perf_session__new(&file, false, &kvm->tool);
if (!kvm->session) {
pr_err("Initializing perf session failed\n");
return -EINVAL;
return -1;
}
symbol__init(&kvm->session->header.env);
......@@ -1361,7 +1361,7 @@ static int kvm_events_live(struct perf_kvm_stat *kvm,
*/
kvm->session = perf_session__new(&file, false, &kvm->tool);
if (kvm->session == NULL) {
err = -ENOMEM;
err = -1;
goto out;
}
kvm->session->evlist = kvm->evlist;
......
......@@ -862,7 +862,7 @@ static int __cmd_report(bool display_info)
session = perf_session__new(&file, false, &eops);
if (!session) {
pr_err("Initializing perf session failed\n");
return -ENOMEM;
return -1;
}
symbol__init(&session->header.env);
......
......@@ -124,7 +124,7 @@ static int report_raw_events(struct perf_mem *mem)
&mem->tool);
if (session == NULL)
return -ENOMEM;
return -1;
if (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)
return ret;
}
#ifdef HAVE_DWARF_UNWIND_SUPPORT
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 void callchain_debug(void)
{
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",
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,
int unset)
{
struct record_opts *opts = opt->value;
int ret;
opts->call_graph_enabled = !unset;
callchain_param.enabled = !unset;
/* --no-call-graph */
if (unset) {
opts->call_graph = CALLCHAIN_NONE;
callchain_param.record_mode = CALLCHAIN_NONE;
pr_debug("callchain: disabled\n");
return 0;
}
ret = record_parse_callchain(arg, opts);
ret = parse_callchain_record_opt(arg);
if (!ret)
callchain_debug(opts);
callchain_debug();
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,
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)
opts->call_graph = CALLCHAIN_FP;
callchain_debug(opts);
callchain_debug();
return 0;
}
static int perf_record_config(const char *var, const char *value, void *cb)
{
struct record *rec = cb;
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);
}
......
......@@ -720,7 +720,7 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
repeat:
session = perf_session__new(&file, false, &report.tool);
if (session == NULL)
return -ENOMEM;
return -1;
if (report.queue_size) {
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)
session = perf_session__new(&file, false, &script.tool);
if (session == NULL)
return -ENOMEM;
return -1;
if (header || header_only) {
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)
}
}
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;
const char *fmt_v, *fmt_n;
......@@ -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_n = csv_output ? "%s" : "%-25s";
aggr_printout(evsel, cpu, nr);
aggr_printout(evsel, id, nr);
scnprintf(name, sizeof(name), "%s%s",
perf_evsel__name(evsel), csv_output ? "" : " (msec)");
......@@ -947,11 +947,12 @@ static void print_ll_cache_misses(int cpu,
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 sc = evsel->scale;
const char *fmt;
int cpu = cpu_map__id_to_cpu(id);
if (csv_output) {
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)
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)
cpu = 0;
......
......@@ -1605,7 +1605,7 @@ static int __cmd_timechart(struct timechart *tchart, const char *output_name)
int ret = -EINVAL;
if (session == NULL)
return -ENOMEM;
return -1;
symbol__init(&session->header.env);
......
......@@ -929,7 +929,7 @@ static int __cmd_top(struct perf_top *top)
top->session = perf_session__new(NULL, false, NULL);
if (top->session == NULL)
return -ENOMEM;
return -1;
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)
static int perf_top_config(const char *var, const char *value, void *cb)
{
struct perf_top *top = cb;
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")) {
symbol_conf.cumulate_callchain = perf_config_bool(var, value);
return 0;
......
......@@ -2250,7 +2250,7 @@ static int trace__replay(struct trace *trace)
session = perf_session__new(&file, false, &trace->tool);
if (session == NULL)
return -ENOMEM;
return -1;
if (symbol__init(&session->header.env) < 0)
goto out;
......
......@@ -41,8 +41,6 @@ void pthread__unblock_sigwinch(void);
struct record_opts {
struct target target;
int call_graph;
bool call_graph_enabled;
bool group;
bool inherit_stat;
bool no_buffering;
......@@ -60,7 +58,6 @@ struct record_opts {
u64 branch_stack;
u64 default_interval;
u64 user_interval;
u16 stack_dump_size;
bool sample_transaction;
unsigned initial_delay;
};
......
......@@ -804,9 +804,6 @@ static int hist_browser__show_entry(struct hist_browser *browser,
.is_current_entry = current_entry,
};
if (symbol_conf.cumulate_callchain)
total = entry->stat_acc->period;
printed += hist_browser__show_callchain(browser,
&entry->sorted_chain, 1, row, total,
hist_browser__show_callchain_entry, &arg,
......
......@@ -25,6 +25,133 @@
__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
parse_callchain_report_opt(const char *arg)
{
......@@ -44,25 +171,12 @@ parse_callchain_report_opt(const char *arg)
return 0;
}
/* try to get the output mode */
if (!strncmp(tok, "graph", strlen(tok)))
callchain_param.mode = CHAIN_GRAPH_ABS;
else if (!strncmp(tok, "flat", strlen(tok)))
callchain_param.mode = CHAIN_FLAT;
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 */
else if (!minpcnt_set) {
if (!parse_callchain_mode(tok) ||
!parse_callchain_order(tok) ||
!parse_callchain_sort_key(tok)) {
/* parsing ok - move on to the next */
} else if (!minpcnt_set) {
/* try to get the min percent */
callchain_param.min_percent = strtod(tok, &endptr);
if (tok == endptr)
return -1;
......@@ -84,6 +198,47 @@ parse_callchain_report_opt(const char *arg)
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
rb_insert_callchain(struct rb_root *root, struct callchain_node *chain,
enum chain_mode mode)
......
......@@ -54,6 +54,9 @@ enum chain_key {
};
struct callchain_param {
bool enabled;
enum perf_call_graph_mode record_mode;
u32 dump_size;
enum chain_mode mode;
u32 print_limit;
double min_percent;
......@@ -154,7 +157,6 @@ static inline void callchain_cursor_advance(struct callchain_cursor *cursor)
struct option;
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_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 *
bool hide_unresolved);
extern const char record_callchain_help[];
int parse_callchain_record_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,
struct callchain_cursor *src)
......
......@@ -222,7 +222,8 @@ static int perf_parse_file(config_fn_t fn, void *data)
const unsigned char *bomptr = utf8_bom;
for (;;) {
int c = get_next_char();
int line, c = get_next_char();
if (bomptr && *bomptr) {
/* We are at the file beginning; skip UTF8-encoded BOM
* if present. Sane editors won't put this in on their
......@@ -261,8 +262,16 @@ static int perf_parse_file(config_fn_t fn, void *data)
if (!isalpha(c))
break;
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;
}
}
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,
if (!prefixcmp(var, "ui."))
return perf_ui_config(var, value);
if (!prefixcmp(var, "call-graph."))
return perf_callchain_config(var, value);
/* Add other config variables here. */
return 0;
}
......
......@@ -558,13 +558,17 @@ int perf_event__synthesize_kernel_mmap(struct perf_tool *tool,
struct map *map;
struct kmap *kmap;
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
* available use this, and after it is use this as a fallback for older
* kernels.
*/
union perf_event *event = zalloc((sizeof(event->mmap) +
machine->id_hdr_size));
event = zalloc((sizeof(event->mmap) + machine->id_hdr_size));
if (event == NULL) {
pr_debug("Not enough memory synthesizing mmap event "
"for kernel modules\n");
......
......@@ -503,20 +503,19 @@ int perf_evsel__group_desc(struct perf_evsel *evsel, char *buf, size_t size)
}
static void
perf_evsel__config_callgraph(struct perf_evsel *evsel,
struct record_opts *opts)
perf_evsel__config_callgraph(struct perf_evsel *evsel)
{
bool function = perf_evsel__is_function_event(evsel);
struct perf_event_attr *attr = &evsel->attr;
perf_evsel__set_sample_bit(evsel, CALLCHAIN);
if (opts->call_graph == CALLCHAIN_DWARF) {
if (callchain_param.record_mode == CALLCHAIN_DWARF) {
if (!function) {
perf_evsel__set_sample_bit(evsel, REGS_USER);
perf_evsel__set_sample_bit(evsel, STACK_USER);
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;
} else {
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)
attr->mmap_data = track;
}
if (opts->call_graph_enabled && !evsel->no_aux_samples)
perf_evsel__config_callgraph(evsel, opts);
if (callchain_param.enabled && !evsel->no_aux_samples)
perf_evsel__config_callgraph(evsel);
if (target__has_cpu(&opts->target))
perf_evsel__set_sample_bit(evsel, CPU);
......
......@@ -119,7 +119,7 @@ struct perf_session *perf_session__new(struct perf_data_file *file,
* kernel MMAP event, in perf_event__process_mmap().
*/
if (perf_session__create_kernel_maps(session) < 0)
goto out_delete;
pr_warning("Cannot read kernel map\n");
}
if (tool && tool->ordering_requires_timestamps &&
......
......@@ -126,5 +126,5 @@ int __perf_session__set_tracepoints_handlers(struct perf_session *session,
extern volatile int session_done;
#define session_done() (*(volatile int *)(&session_done))
#define session_done() ACCESS_ONCE(session_done)
#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