mirror of
https://github.com/torvalds/linux.git
synced 2026-05-31 18:43:33 +02:00
perf callchain: Refactor callchain option parsing
record_opts__parse_callchain is shared by builtin-record and builtin-trace, it is declared in callchain.h. Move the declaration to callchain.c for consistency with the header. In other cases make the option callback a small static stub that then calls into callchain.c. Make the no argument '-g' callchain option just a short-cut for '--call-graph fp' so that there is consistency in how the arguments are handled. This requires the const char* string to be strdup-ed in __parse_callchain_report_opt. For consistency also make parse_callchain_record use strdup and remove some unnecessary casts. Also, be more explicit about the '-g' behavior if there is a .perfconfig file setting. Signed-off-by: Ian Rogers <irogers@google.com> Tested-by: Thomas Richter <tmricht@linux.ibm.com> Signed-off-by: Namhyung Kim <namhyung@kernel.org>
This commit is contained in:
parent
443556be8a
commit
c006753c3a
|
|
@ -2975,65 +2975,30 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
|
|||
return status;
|
||||
}
|
||||
|
||||
static void callchain_debug(struct callchain_param *callchain)
|
||||
{
|
||||
static const char *str[CALLCHAIN_MAX] = { "NONE", "FP", "DWARF", "LBR" };
|
||||
|
||||
pr_debug("callchain: type %s\n", str[callchain->record_mode]);
|
||||
|
||||
if (callchain->record_mode == CALLCHAIN_DWARF)
|
||||
pr_debug("callchain: stack dump size %d\n",
|
||||
callchain->dump_size);
|
||||
}
|
||||
|
||||
int record_opts__parse_callchain(struct record_opts *record,
|
||||
struct callchain_param *callchain,
|
||||
const char *arg, bool unset)
|
||||
{
|
||||
int ret;
|
||||
callchain->enabled = !unset;
|
||||
|
||||
/* --no-call-graph */
|
||||
if (unset) {
|
||||
callchain->record_mode = CALLCHAIN_NONE;
|
||||
pr_debug("callchain: disabled\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = parse_callchain_record_opt(arg, callchain);
|
||||
if (!ret) {
|
||||
/* Enable data address sampling for DWARF unwind. */
|
||||
if (callchain->record_mode == CALLCHAIN_DWARF &&
|
||||
!record->record_data_mmap_set)
|
||||
record->record_data_mmap = true;
|
||||
callchain_debug(callchain);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int record_parse_callchain_opt(const struct option *opt,
|
||||
static int record_parse_callchain_opt(const struct option *opt,
|
||||
const char *arg,
|
||||
int unset)
|
||||
{
|
||||
return record_opts__parse_callchain(opt->value, &callchain_param, arg, unset);
|
||||
}
|
||||
|
||||
int record_callchain_opt(const struct option *opt,
|
||||
const char *arg __maybe_unused,
|
||||
int unset __maybe_unused)
|
||||
static int record_callchain_opt(const struct option *opt,
|
||||
const char *arg __maybe_unused,
|
||||
int unset)
|
||||
{
|
||||
struct callchain_param *callchain = opt->value;
|
||||
/*
|
||||
* The -g option only sets the callchain if not already configured by
|
||||
* .perfconfig. It does, however, enable it.
|
||||
*/
|
||||
if (callchain_param.record_mode != CALLCHAIN_NONE) {
|
||||
callchain_param.enabled = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
callchain->enabled = true;
|
||||
|
||||
if (callchain->record_mode == CALLCHAIN_NONE)
|
||||
callchain->record_mode = CALLCHAIN_FP;
|
||||
|
||||
callchain_debug(callchain);
|
||||
return 0;
|
||||
return record_opts__parse_callchain(opt->value, &callchain_param, "fp", unset);
|
||||
}
|
||||
|
||||
|
||||
static int perf_record_config(const char *var, const char *value, void *cb)
|
||||
{
|
||||
struct record *rec = cb;
|
||||
|
|
@ -3525,7 +3490,7 @@ static struct option __record_options[] = {
|
|||
OPT_CALLBACK(0, "mmap-flush", &record.opts, "number",
|
||||
"Minimal number of bytes that is extracted from mmap data pages (default: 1)",
|
||||
record__mmap_flush_parse),
|
||||
OPT_CALLBACK_NOOPT('g', NULL, &callchain_param,
|
||||
OPT_CALLBACK_NOOPT('g', NULL, &record.opts,
|
||||
NULL, "enables call-graph recording" ,
|
||||
&record_callchain_opt),
|
||||
OPT_CALLBACK(0, "call-graph", &record.opts,
|
||||
|
|
|
|||
|
|
@ -1386,13 +1386,6 @@ static int __cmd_top(struct perf_top *top)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
callchain_opt(const struct option *opt, const char *arg, int unset)
|
||||
{
|
||||
symbol_conf.use_callchain = true;
|
||||
return record_callchain_opt(opt, arg, unset);
|
||||
}
|
||||
|
||||
static int
|
||||
parse_callchain_opt(const struct option *opt, const char *arg, int unset)
|
||||
{
|
||||
|
|
@ -1413,6 +1406,24 @@ parse_callchain_opt(const struct option *opt, const char *arg, int unset)
|
|||
return parse_callchain_top_opt(arg);
|
||||
}
|
||||
|
||||
static int
|
||||
callchain_opt(const struct option *opt, const char *arg __maybe_unused, int unset)
|
||||
{
|
||||
struct callchain_param *callchain = opt->value;
|
||||
|
||||
/*
|
||||
* The -g option only sets the callchain if not already configured by
|
||||
* .perfconfig. It does, however, enable it.
|
||||
*/
|
||||
if (callchain->record_mode != CALLCHAIN_NONE) {
|
||||
callchain->enabled = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return parse_callchain_opt(opt, "fp", unset);
|
||||
}
|
||||
|
||||
|
||||
static int perf_top_config(const char *var, const char *value, void *cb __maybe_unused)
|
||||
{
|
||||
if (!strcmp(var, "top.call-graph")) {
|
||||
|
|
|
|||
|
|
@ -5300,6 +5300,13 @@ static int trace__parse_summary_mode(const struct option *opt, const char *str,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int trace_parse_callchain_opt(const struct option *opt,
|
||||
const char *arg,
|
||||
int unset)
|
||||
{
|
||||
return record_opts__parse_callchain(opt->value, &callchain_param, arg, unset);
|
||||
}
|
||||
|
||||
static int trace__config(const char *var, const char *value, void *arg)
|
||||
{
|
||||
struct trace *trace = arg;
|
||||
|
|
@ -5447,7 +5454,7 @@ int cmd_trace(int argc, const char **argv)
|
|||
OPT_BOOLEAN('f', "force", &trace.force, "don't complain, do it"),
|
||||
OPT_CALLBACK(0, "call-graph", &trace.opts,
|
||||
"record_mode[,record_size]", record_callchain_help,
|
||||
&record_parse_callchain_opt),
|
||||
&trace_parse_callchain_opt),
|
||||
OPT_BOOLEAN(0, "libtraceevent_print", &trace.libtraceevent_print,
|
||||
"Use libtraceevent to print the tracepoint arguments."),
|
||||
OPT_BOOLEAN(0, "kernel-syscall-graph", &trace.kernel_syscallchains,
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@
|
|||
#include "map.h"
|
||||
#include "callchain.h"
|
||||
#include "branch.h"
|
||||
#include "record.h"
|
||||
#include "symbol.h"
|
||||
#include "thread.h"
|
||||
#include "util.h"
|
||||
|
|
@ -170,7 +171,7 @@ static int get_stack_size(const char *str, unsigned long *_size)
|
|||
static int
|
||||
__parse_callchain_report_opt(const char *arg, bool allow_record_opt)
|
||||
{
|
||||
char *tok;
|
||||
char *tok, *arg_copy;
|
||||
char *endptr, *saveptr = NULL;
|
||||
bool minpcnt_set = false;
|
||||
bool record_opt_set = false;
|
||||
|
|
@ -182,12 +183,17 @@ __parse_callchain_report_opt(const char *arg, bool allow_record_opt)
|
|||
if (!arg)
|
||||
return 0;
|
||||
|
||||
while ((tok = strtok_r((char *)arg, ",", &saveptr)) != NULL) {
|
||||
arg_copy = strdup(arg);
|
||||
if (!arg_copy)
|
||||
return -ENOMEM;
|
||||
|
||||
tok = strtok_r(arg_copy, ",", &saveptr);
|
||||
while (tok) {
|
||||
if (!strncmp(tok, "none", strlen(tok))) {
|
||||
callchain_param.mode = CHAIN_NONE;
|
||||
callchain_param.enabled = false;
|
||||
symbol_conf.use_callchain = false;
|
||||
return 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!parse_callchain_mode(tok) ||
|
||||
|
|
@ -214,30 +220,35 @@ __parse_callchain_report_opt(const char *arg, bool allow_record_opt)
|
|||
unsigned long size = 0;
|
||||
|
||||
if (get_stack_size(tok, &size) < 0)
|
||||
return -1;
|
||||
goto err_out;
|
||||
callchain_param.dump_size = size;
|
||||
try_stack_size = false;
|
||||
} else if (!minpcnt_set) {
|
||||
/* try to get the min percent */
|
||||
callchain_param.min_percent = strtod(tok, &endptr);
|
||||
if (tok == endptr)
|
||||
return -1;
|
||||
goto err_out;
|
||||
minpcnt_set = true;
|
||||
} else {
|
||||
/* try print limit at last */
|
||||
callchain_param.print_limit = strtoul(tok, &endptr, 0);
|
||||
if (tok == endptr)
|
||||
return -1;
|
||||
goto err_out;
|
||||
}
|
||||
next:
|
||||
arg = NULL;
|
||||
tok = strtok_r(NULL, ",", &saveptr);
|
||||
}
|
||||
|
||||
if (callchain_register_param(&callchain_param) < 0) {
|
||||
pr_err("Can't register callchain params\n");
|
||||
return -1;
|
||||
goto err_out;
|
||||
}
|
||||
out:
|
||||
free(arg_copy);
|
||||
return 0;
|
||||
err_out:
|
||||
free(arg_copy);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int parse_callchain_report_opt(const char *arg)
|
||||
|
|
@ -257,14 +268,12 @@ int parse_callchain_record(const char *arg, struct callchain_param *param)
|
|||
int ret = -1;
|
||||
|
||||
/* We need buffer that we know we can write to. */
|
||||
buf = malloc(strlen(arg) + 1);
|
||||
buf = strdup(arg);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
strcpy(buf, arg);
|
||||
|
||||
tok = strtok_r((char *)buf, ",", &saveptr);
|
||||
name = tok ? : (char *)buf;
|
||||
tok = strtok_r(buf, ",", &saveptr);
|
||||
name = tok ? : buf;
|
||||
|
||||
do {
|
||||
/* Framepointer style */
|
||||
|
|
@ -328,6 +337,44 @@ int parse_callchain_record(const char *arg, struct callchain_param *param)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static void callchain_debug(const struct callchain_param *callchain)
|
||||
{
|
||||
static const char *str[CALLCHAIN_MAX] = { "NONE", "FP", "DWARF", "LBR" };
|
||||
|
||||
pr_debug("callchain: type %s\n", str[callchain->record_mode]);
|
||||
|
||||
if (callchain->record_mode == CALLCHAIN_DWARF)
|
||||
pr_debug("callchain: stack dump size %d\n",
|
||||
callchain->dump_size);
|
||||
}
|
||||
|
||||
int record_opts__parse_callchain(struct record_opts *record,
|
||||
struct callchain_param *callchain,
|
||||
const char *arg, bool unset)
|
||||
{
|
||||
int ret;
|
||||
|
||||
callchain->enabled = !unset;
|
||||
|
||||
/* --no-call-graph */
|
||||
if (unset) {
|
||||
callchain->record_mode = CALLCHAIN_NONE;
|
||||
pr_debug("callchain: disabled\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = parse_callchain_record_opt(arg, callchain);
|
||||
if (!ret) {
|
||||
/* Enable data address sampling for DWARF unwind. */
|
||||
if (callchain->record_mode == CALLCHAIN_DWARF &&
|
||||
!record->record_data_mmap_set)
|
||||
record->record_data_mmap = true;
|
||||
callchain_debug(callchain);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int perf_callchain_config(const char *var, const char *value)
|
||||
{
|
||||
char *endptr;
|
||||
|
|
|
|||
|
|
@ -9,11 +9,13 @@
|
|||
|
||||
struct addr_location;
|
||||
struct evsel;
|
||||
struct hist_entry;
|
||||
struct hists;
|
||||
struct ip_callchain;
|
||||
struct map;
|
||||
struct perf_sample;
|
||||
struct record_opts;
|
||||
struct thread;
|
||||
struct hists;
|
||||
|
||||
#define HELP_PAD "\t\t\t\t"
|
||||
|
||||
|
|
@ -237,14 +239,6 @@ struct callchain_cursor *get_tls_callchain_cursor(void);
|
|||
int callchain_cursor__copy(struct callchain_cursor *dst,
|
||||
struct callchain_cursor *src);
|
||||
|
||||
struct option;
|
||||
struct hist_entry;
|
||||
|
||||
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);
|
||||
|
||||
struct record_opts;
|
||||
|
||||
int record_opts__parse_callchain(struct record_opts *record,
|
||||
struct callchain_param *callchain,
|
||||
const char *arg, bool unset);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user