mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2025-01-23 12:49:47 +07:00
perf/core improvements and fixes:
User visible: 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: Arnaldo Carvalho de Melo <acme@redhat.com> -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJUJcoSAAoJENZQFvNTUqpA/fkP/RliiqtsSRve1/+Xw+G82i+h h5ljcd0WXM5BCrFznvu5HdPFFFdBmKY1RsKa3bKB6Ixv8maUHIqaMw5tLHmqC5KN 5CUgel2dZushTRORZbKA6aLY8jWk6sLdAL275+R8yX8t2pXqOLsVmsYvuJh6JO+i O5Ck7MvL24fzKT3qHjFUIo6ljX53gol40FWKfpYap7gyDxrbmeJVXsb5vJl7VB69 m+TbSh3IQpi1ySQrXgkMkcw9YCAzPpOhFsSW1pfzwe6do1pgLT00m1q3ILIxEuTE 2WkA+vfITUcHDeix8+M1Tl72N7kpwMjZja6Dqds3yNuzsYudVcM2+Z8hB5AFgtBo VLONqBLOOm2Q9BOsMLTYXq28x3/RuI9jTkeoKWwEqN4bTtlazw6yejiJOHq/x1v3 5HmWARNPkPXdPhu1ffdGxqb7i/9NoF67EVvESXd0W6WjiKleLeQg8gOzN0ehguBJ TSEGgV0sOTHAsWg23hDGA+BB0BgoVhiXb6T9oixskXjL/9iWWkRTsU7VNyeWnehs sCXDIGG0WHU56cesAeW7cmmarIsxQ8n7BY5y3dqe68pB3hEVf12Rrvc0d0rQNupz YNxUEnqnfShabZfhso/LAl4S82Z/LwsI+lBPMWTxmTngyWc7XSBJzXgPwFZ8StKw NFe1x8/Tjx9tWzYtSt0D =E7t6 -----END PGP SIGNATURE----- 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: Arnaldo Carvalho de Melo <acme@redhat.com> Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
commit
07394b5f13
@ -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 */
|
||||
|
Loading…
Reference in New Issue
Block a user