perf/core improvements and fixes:
User visible: - Enable per-event perf_event_attr.inherit setting by config terms, i.e. this becomes possible: $ perf record -e cycles/inherit/ -e instructions/no-inherit/ This affects the default, that can be changed globally using the --no-inherit option. This fine grained control appeared in the eBPF patchkit, but this added flexibility may end up being useful in other scenarios (Wang Nan) - Setup pager when printing usage and help, we have long lists of options, better use the pager like we do with normal tooling output, i.e. when needed, and including any error messages in the paged output (Namhyung Kim) - Search for more options when passing args to -h, e.g.: (Arnaldo Carvalho de Melo) $ perf report -h interface Usage: perf report [<options>] --gtk Use the GTK2 interface --stdio Use the stdio interface --tui Use the TUI interface - Fix reading separate debuginfo files based on a build-id, problem found on a Debian system (Dima Kogan) - Fix endless loop when splitting kallsyms symbols per section for handling kcore files, problem found on a s390x system (Jiri Olsa) Infrastructure: - Prep work for the 'perf stat record' work that will allow generating perf.data files with counting data in addition to the sampling mode we have now (Jiri Olsa) Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJWMOlbAAoJENZQFvNTUqpAZ6AQALTc4D/DvHRRwy7fypFPeSmy SNHp8ScgbNxrfXuc94UXF73G2afGO3IlH2duAlEF8wUbKXWSHpEWGb7exd/vGY2M 6GdLVgE4j+3pWbjv6PzE41tExozb2MsmDN2KGlXdklqrjf9PVXgMT/eJLNm/9wEX Wuh+4fwDpoHTk7WS7aRWAQYF9qhxt5a4mk3tVgbHZskFL4xF9wJIs3ihTxuI1IOU rxmfyzORgcM9wFFdfqiFG4JHrjmtzmXGi8qQTqiStPUcWMP6H9TZLMnh0JPpSiyZ 33vZVuXGszFQnWJkoAmg6EC2mxXXcv1Q/Z1AbRxuHHotC2Syq6B/vUVB7GobWwDB QkqgesAX6dke3JrmVheUSBmQv205oBTXsEqCk+Z+PesvufVBGaYO/uuwAs99bS9r f8bsYSsacGmqdFJECIuPrXGq5BHM/jaTAtISlsvos8pVWCMKpHke/OdG5f1apHKf iJRJ13zCkxnbq5O+HwlUZftVOw4EnKd7dhcYnVpRAtlE8wFnshjyYYMwVKTXyiZF VLAlUMc0/MK3AKybAwvmgrysGNTK1jKjzhWAKHdZ+qokeoeafwl9Q/hFjHeZvNhz n49CxBX00mmOerjZFdSlDfiqX0gEPRp5Wzmu+v3SBtPz81+hODR3vfFOI975lvxT b2v8kyW8lw8Sgr1ocDcK =wuI+ -----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: - Enable per-event perf_event_attr.inherit setting by config terms, i.e. this becomes possible: $ perf record -e cycles/inherit/ -e instructions/no-inherit/ This affects the default, that can be changed globally using the --no-inherit option. This fine grained control appeared in the eBPF patchkit, but this added flexibility may end up being useful in other scenarios. (Wang Nan) - Setup pager when printing usage and help, we have long lists of options, better use the pager like we do with normal tooling output, i.e. when needed, and including any error messages in the paged output. (Namhyung Kim) - Search for more options when passing args to -h, e.g.: (Arnaldo Carvalho de Melo) $ perf report -h interface Usage: perf report [<options>] --gtk Use the GTK2 interface --stdio Use the stdio interface --tui Use the TUI interface - Fix reading separate debuginfo files based on a build-id, problem found on a Debian system. (Dima Kogan) - Fix endless loop when splitting kallsyms symbols per section for handling kcore files, problem found on a s390x system. (Jiri Olsa) Infrastructure changes: - Prep work for the 'perf stat record' work that will allow generating perf.data files with counting data in addition to the sampling mode we have now (Jiri Olsa) Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
commit
6fc774ef4c
21 changed files with 216 additions and 49 deletions
|
@ -29,7 +29,7 @@ OPTIONS
|
|||
--show-nr-samples::
|
||||
Show the number of samples for each symbol
|
||||
|
||||
--showcpuutilization::
|
||||
--show-cpu-utilization::
|
||||
Show sample percentage for different cpu modes.
|
||||
|
||||
-T::
|
||||
|
|
|
@ -61,8 +61,8 @@ int cmd_evlist(int argc, const char **argv, const char *prefix __maybe_unused)
|
|||
usage_with_options(evlist_usage, options);
|
||||
|
||||
if (details.event_group && (details.verbose || details.freq)) {
|
||||
pr_err("--group option is not compatible with other options\n");
|
||||
usage_with_options(evlist_usage, options);
|
||||
usage_with_options_msg(evlist_usage, options,
|
||||
"--group option is not compatible with other options\n");
|
||||
}
|
||||
|
||||
return __cmd_evlist(input_name, &details);
|
||||
|
|
|
@ -528,12 +528,12 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)
|
|||
PARSE_OPT_STOP_AT_NON_OPTION);
|
||||
if (argc > 0) {
|
||||
if (strcmp(argv[0], "-") == 0) {
|
||||
pr_warning(" Error: '-' is not supported.\n");
|
||||
usage_with_options(probe_usage, options);
|
||||
usage_with_options_msg(probe_usage, options,
|
||||
"'-' is not supported.\n");
|
||||
}
|
||||
if (params.command && params.command != 'a') {
|
||||
pr_warning(" Error: another command except --add is set.\n");
|
||||
usage_with_options(probe_usage, options);
|
||||
usage_with_options_msg(probe_usage, options,
|
||||
"another command except --add is set.\n");
|
||||
}
|
||||
ret = parse_probe_event_argv(argc, argv);
|
||||
if (ret < 0) {
|
||||
|
@ -562,8 +562,10 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)
|
|||
switch (params.command) {
|
||||
case 'l':
|
||||
if (params.uprobes) {
|
||||
pr_warning(" Error: Don't use --list with --exec.\n");
|
||||
usage_with_options(probe_usage, options);
|
||||
pr_err(" Error: Don't use --list with --exec.\n");
|
||||
parse_options_usage(probe_usage, options, "l", true);
|
||||
parse_options_usage(NULL, options, "x", true);
|
||||
return -EINVAL;
|
||||
}
|
||||
ret = show_perf_probe_events(params.filter);
|
||||
if (ret < 0)
|
||||
|
@ -603,8 +605,10 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)
|
|||
case 'a':
|
||||
/* Ensure the last given target is used */
|
||||
if (params.target && !params.target_used) {
|
||||
pr_warning(" Error: -x/-m must follow the probe definitions.\n");
|
||||
usage_with_options(probe_usage, options);
|
||||
pr_err(" Error: -x/-m must follow the probe definitions.\n");
|
||||
parse_options_usage(probe_usage, options, "m", true);
|
||||
parse_options_usage(NULL, options, "x", true);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = perf_add_probe_events(params.events, params.nevents);
|
||||
|
|
|
@ -1135,14 +1135,15 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused)
|
|||
usage_with_options(record_usage, record_options);
|
||||
|
||||
if (nr_cgroups && !rec->opts.target.system_wide) {
|
||||
ui__error("cgroup monitoring only available in"
|
||||
" system-wide mode\n");
|
||||
usage_with_options(record_usage, record_options);
|
||||
usage_with_options_msg(record_usage, record_options,
|
||||
"cgroup monitoring only available in system-wide mode");
|
||||
|
||||
}
|
||||
if (rec->opts.record_switch_events &&
|
||||
!perf_can_record_switch_events()) {
|
||||
ui__error("kernel does not support recording context switch events (--switch-events option)\n");
|
||||
usage_with_options(record_usage, record_options);
|
||||
ui__error("kernel does not support recording context switch events\n");
|
||||
parse_options_usage(record_usage, record_options, "switch-events", 0);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!rec->itr) {
|
||||
|
|
|
@ -699,8 +699,10 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
|
|||
" Please refer the man page for the complete list."),
|
||||
OPT_STRING('F', "fields", &field_order, "key[,keys...]",
|
||||
"output field(s): overhead, period, sample plus all of sort keys"),
|
||||
OPT_BOOLEAN(0, "showcpuutilization", &symbol_conf.show_cpu_utilization,
|
||||
OPT_BOOLEAN(0, "show-cpu-utilization", &symbol_conf.show_cpu_utilization,
|
||||
"Show sample percentage for different cpu modes"),
|
||||
OPT_BOOLEAN_FLAG(0, "showcpuutilization", &symbol_conf.show_cpu_utilization,
|
||||
"Show sample percentage for different cpu modes", PARSE_OPT_HIDDEN),
|
||||
OPT_STRING('p', "parent", &parent_pattern, "regex",
|
||||
"regex filter to identify parent, see: '--sort parent'"),
|
||||
OPT_BOOLEAN('x', "exclude-other", &symbol_conf.exclude_other,
|
||||
|
|
|
@ -1728,8 +1728,8 @@ static void setup_sorting(struct perf_sched *sched, const struct option *options
|
|||
for (tok = strtok_r(str, ", ", &tmp);
|
||||
tok; tok = strtok_r(NULL, ", ", &tmp)) {
|
||||
if (sort_dimension__add(tok, &sched->sort_list) < 0) {
|
||||
error("Unknown --sort key: `%s'", tok);
|
||||
usage_with_options(usage_msg, options);
|
||||
usage_with_options_msg(usage_msg, options,
|
||||
"Unknown --sort key: `%s'", tok);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1767,9 +1767,9 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused)
|
|||
rep_script_path = get_script_path(argv[0], REPORT_SUFFIX);
|
||||
|
||||
if (!rec_script_path && !rep_script_path) {
|
||||
fprintf(stderr, " Couldn't find script %s\n\n See perf"
|
||||
usage_with_options_msg(script_usage, options,
|
||||
"Couldn't find script `%s'\n\n See perf"
|
||||
" script -l for available scripts.\n", argv[0]);
|
||||
usage_with_options(script_usage, options);
|
||||
}
|
||||
|
||||
if (is_top_script(argv[0])) {
|
||||
|
@ -1780,10 +1780,10 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused)
|
|||
rep_args = has_required_arg(rep_script_path);
|
||||
rec_args = (argc - 1) - rep_args;
|
||||
if (rec_args < 0) {
|
||||
fprintf(stderr, " %s script requires options."
|
||||
usage_with_options_msg(script_usage, options,
|
||||
"`%s' script requires options."
|
||||
"\n\n See perf script -l for available "
|
||||
"scripts and options.\n", argv[0]);
|
||||
usage_with_options(script_usage, options);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -100,6 +100,8 @@ static struct target target = {
|
|||
.uid = UINT_MAX,
|
||||
};
|
||||
|
||||
typedef int (*aggr_get_id_t)(struct cpu_map *m, int cpu);
|
||||
|
||||
static int run_count = 1;
|
||||
static bool no_inherit = false;
|
||||
static volatile pid_t child_pid = -1;
|
||||
|
@ -119,7 +121,7 @@ static unsigned int unit_width = 4; /* strlen("unit") */
|
|||
static bool forever = false;
|
||||
static struct timespec ref_time;
|
||||
static struct cpu_map *aggr_map;
|
||||
static int (*aggr_get_id)(struct cpu_map *m, int cpu);
|
||||
static aggr_get_id_t aggr_get_id;
|
||||
|
||||
static volatile int done = 0;
|
||||
|
||||
|
@ -954,22 +956,63 @@ static int perf_stat__get_core(struct cpu_map *map, int cpu)
|
|||
return cpu_map__get_core(map, cpu, NULL);
|
||||
}
|
||||
|
||||
static int cpu_map__get_max(struct cpu_map *map)
|
||||
{
|
||||
int i, max = -1;
|
||||
|
||||
for (i = 0; i < map->nr; i++) {
|
||||
if (map->map[i] > max)
|
||||
max = map->map[i];
|
||||
}
|
||||
|
||||
return max;
|
||||
}
|
||||
|
||||
static struct cpu_map *cpus_aggr_map;
|
||||
|
||||
static int perf_stat__get_aggr(aggr_get_id_t get_id, struct cpu_map *map, int idx)
|
||||
{
|
||||
int cpu;
|
||||
|
||||
if (idx >= map->nr)
|
||||
return -1;
|
||||
|
||||
cpu = map->map[idx];
|
||||
|
||||
if (cpus_aggr_map->map[cpu] == -1)
|
||||
cpus_aggr_map->map[cpu] = get_id(map, idx);
|
||||
|
||||
return cpus_aggr_map->map[cpu];
|
||||
}
|
||||
|
||||
static int perf_stat__get_socket_cached(struct cpu_map *map, int idx)
|
||||
{
|
||||
return perf_stat__get_aggr(perf_stat__get_socket, map, idx);
|
||||
}
|
||||
|
||||
static int perf_stat__get_core_cached(struct cpu_map *map, int idx)
|
||||
{
|
||||
return perf_stat__get_aggr(perf_stat__get_core, map, idx);
|
||||
}
|
||||
|
||||
static int perf_stat_init_aggr_mode(void)
|
||||
{
|
||||
int nr;
|
||||
|
||||
switch (stat_config.aggr_mode) {
|
||||
case AGGR_SOCKET:
|
||||
if (cpu_map__build_socket_map(evsel_list->cpus, &aggr_map)) {
|
||||
perror("cannot build socket map");
|
||||
return -1;
|
||||
}
|
||||
aggr_get_id = perf_stat__get_socket;
|
||||
aggr_get_id = perf_stat__get_socket_cached;
|
||||
break;
|
||||
case AGGR_CORE:
|
||||
if (cpu_map__build_core_map(evsel_list->cpus, &aggr_map)) {
|
||||
perror("cannot build core map");
|
||||
return -1;
|
||||
}
|
||||
aggr_get_id = perf_stat__get_core;
|
||||
aggr_get_id = perf_stat__get_core_cached;
|
||||
break;
|
||||
case AGGR_NONE:
|
||||
case AGGR_GLOBAL:
|
||||
|
@ -978,7 +1021,15 @@ static int perf_stat_init_aggr_mode(void)
|
|||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* The evsel_list->cpus is the base we operate on,
|
||||
* taking the highest cpu number to be the size of
|
||||
* the aggregation translate cpumap.
|
||||
*/
|
||||
nr = cpu_map__get_max(evsel_list->cpus);
|
||||
cpus_aggr_map = cpu_map__empty_new(nr + 1);
|
||||
return cpus_aggr_map ? 0 : -ENOMEM;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -203,6 +203,23 @@ struct cpu_map *cpu_map__dummy_new(void)
|
|||
return cpus;
|
||||
}
|
||||
|
||||
struct cpu_map *cpu_map__empty_new(int nr)
|
||||
{
|
||||
struct cpu_map *cpus = malloc(sizeof(*cpus) + sizeof(int) * nr);
|
||||
|
||||
if (cpus != NULL) {
|
||||
int i;
|
||||
|
||||
cpus->nr = nr;
|
||||
for (i = 0; i < nr; i++)
|
||||
cpus->map[i] = -1;
|
||||
|
||||
atomic_set(&cpus->refcnt, 1);
|
||||
}
|
||||
|
||||
return cpus;
|
||||
}
|
||||
|
||||
static void cpu_map__delete(struct cpu_map *map)
|
||||
{
|
||||
if (map) {
|
||||
|
|
|
@ -15,6 +15,7 @@ struct cpu_map {
|
|||
};
|
||||
|
||||
struct cpu_map *cpu_map__new(const char *cpu_list);
|
||||
struct cpu_map *cpu_map__empty_new(int nr);
|
||||
struct cpu_map *cpu_map__dummy_new(void);
|
||||
struct cpu_map *cpu_map__read(FILE *file);
|
||||
size_t cpu_map__fprintf(struct cpu_map *map, FILE *fp);
|
||||
|
|
|
@ -652,6 +652,15 @@ static void apply_config_terms(struct perf_evsel *evsel,
|
|||
case PERF_EVSEL__CONFIG_TERM_STACK_USER:
|
||||
dump_size = term->val.stack_user;
|
||||
break;
|
||||
case PERF_EVSEL__CONFIG_TERM_INHERIT:
|
||||
/*
|
||||
* attr->inherit should has already been set by
|
||||
* perf_evsel__config. If user explicitly set
|
||||
* inherit using config terms, override global
|
||||
* opt->no_inherit setting.
|
||||
*/
|
||||
attr->inherit = term->val.inherit ? 1 : 0;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -43,6 +43,7 @@ enum {
|
|||
PERF_EVSEL__CONFIG_TERM_TIME,
|
||||
PERF_EVSEL__CONFIG_TERM_CALLGRAPH,
|
||||
PERF_EVSEL__CONFIG_TERM_STACK_USER,
|
||||
PERF_EVSEL__CONFIG_TERM_INHERIT,
|
||||
PERF_EVSEL__CONFIG_TERM_MAX,
|
||||
};
|
||||
|
||||
|
@ -55,6 +56,7 @@ struct perf_evsel_config_term {
|
|||
bool time;
|
||||
char *callgraph;
|
||||
u64 stack_user;
|
||||
bool inherit;
|
||||
} val;
|
||||
};
|
||||
|
||||
|
@ -90,9 +92,9 @@ struct perf_evsel {
|
|||
double scale;
|
||||
const char *unit;
|
||||
struct event_format *tp_format;
|
||||
off_t id_offset;
|
||||
union {
|
||||
void *priv;
|
||||
off_t id_offset;
|
||||
u64 db_id;
|
||||
};
|
||||
struct cgroup_sel *cgrp;
|
||||
|
|
|
@ -666,6 +666,12 @@ do { \
|
|||
case PARSE_EVENTS__TERM_TYPE_STACKSIZE:
|
||||
CHECK_TYPE_VAL(NUM);
|
||||
break;
|
||||
case PARSE_EVENTS__TERM_TYPE_INHERIT:
|
||||
CHECK_TYPE_VAL(NUM);
|
||||
break;
|
||||
case PARSE_EVENTS__TERM_TYPE_NOINHERIT:
|
||||
CHECK_TYPE_VAL(NUM);
|
||||
break;
|
||||
case PARSE_EVENTS__TERM_TYPE_NAME:
|
||||
CHECK_TYPE_VAL(STR);
|
||||
break;
|
||||
|
@ -701,6 +707,8 @@ static int config_term_tracepoint(struct perf_event_attr *attr,
|
|||
switch (term->type_term) {
|
||||
case PARSE_EVENTS__TERM_TYPE_CALLGRAPH:
|
||||
case PARSE_EVENTS__TERM_TYPE_STACKSIZE:
|
||||
case PARSE_EVENTS__TERM_TYPE_INHERIT:
|
||||
case PARSE_EVENTS__TERM_TYPE_NOINHERIT:
|
||||
return config_term_common(attr, term, err);
|
||||
default:
|
||||
if (err) {
|
||||
|
@ -764,6 +772,12 @@ do { \
|
|||
case PARSE_EVENTS__TERM_TYPE_STACKSIZE:
|
||||
ADD_CONFIG_TERM(STACK_USER, stack_user, term->val.num);
|
||||
break;
|
||||
case PARSE_EVENTS__TERM_TYPE_INHERIT:
|
||||
ADD_CONFIG_TERM(INHERIT, inherit, term->val.num ? 1 : 0);
|
||||
break;
|
||||
case PARSE_EVENTS__TERM_TYPE_NOINHERIT:
|
||||
ADD_CONFIG_TERM(INHERIT, inherit, term->val.num ? 0 : 1);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -67,6 +67,8 @@ enum {
|
|||
PARSE_EVENTS__TERM_TYPE_TIME,
|
||||
PARSE_EVENTS__TERM_TYPE_CALLGRAPH,
|
||||
PARSE_EVENTS__TERM_TYPE_STACKSIZE,
|
||||
PARSE_EVENTS__TERM_TYPE_NOINHERIT,
|
||||
PARSE_EVENTS__TERM_TYPE_INHERIT
|
||||
};
|
||||
|
||||
struct parse_events_term {
|
||||
|
|
|
@ -187,6 +187,8 @@ branch_type { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE
|
|||
time { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_TIME); }
|
||||
call-graph { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CALLGRAPH); }
|
||||
stack-size { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_STACKSIZE); }
|
||||
inherit { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_INHERIT); }
|
||||
no-inherit { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_NOINHERIT); }
|
||||
, { return ','; }
|
||||
"/" { BEGIN(INITIAL); return '/'; }
|
||||
{name_minus} { return str(yyscanner, PE_NAME); }
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
#define OPT_SHORT 1
|
||||
#define OPT_UNSET 2
|
||||
|
||||
static struct strbuf error_buf = STRBUF_INIT;
|
||||
|
||||
static int opterror(const struct option *opt, const char *reason, int flags)
|
||||
{
|
||||
if (flags & OPT_SHORT)
|
||||
|
@ -540,9 +542,11 @@ int parse_options_subcommand(int argc, const char **argv, const struct option *o
|
|||
exit(130);
|
||||
default: /* PARSE_OPT_UNKNOWN */
|
||||
if (ctx.argv[0][1] == '-') {
|
||||
error("unknown option `%s'", ctx.argv[0] + 2);
|
||||
strbuf_addf(&error_buf, "unknown option `%s'",
|
||||
ctx.argv[0] + 2);
|
||||
} else {
|
||||
error("unknown switch `%c'", *ctx.opt);
|
||||
strbuf_addf(&error_buf, "unknown switch `%c'",
|
||||
*ctx.opt);
|
||||
}
|
||||
usage_with_options(usagestr, options);
|
||||
}
|
||||
|
@ -691,8 +695,21 @@ static bool option__in_argv(const struct option *opt, const struct parse_opt_ctx
|
|||
for (i = 1; i < ctx->argc; ++i) {
|
||||
const char *arg = ctx->argv[i];
|
||||
|
||||
if (arg[0] != '-')
|
||||
if (arg[0] != '-') {
|
||||
if (arg[1] == '\0') {
|
||||
if (arg[0] == opt->short_name)
|
||||
return true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (opt->long_name && strcmp(opt->long_name, arg) == 0)
|
||||
return true;
|
||||
|
||||
if (opt->help && strcasestr(opt->help, arg) != NULL)
|
||||
return true;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (arg[1] == opt->short_name ||
|
||||
(arg[1] == '-' && opt->long_name && strcmp(opt->long_name, arg + 2) == 0))
|
||||
|
@ -711,6 +728,13 @@ int usage_with_options_internal(const char * const *usagestr,
|
|||
if (!usagestr)
|
||||
return PARSE_OPT_HELP;
|
||||
|
||||
setup_pager();
|
||||
|
||||
if (strbuf_avail(&error_buf)) {
|
||||
fprintf(stderr, " Error: %s\n", error_buf.buf);
|
||||
strbuf_release(&error_buf);
|
||||
}
|
||||
|
||||
fprintf(stderr, "\n Usage: %s\n", *usagestr++);
|
||||
while (*usagestr && **usagestr)
|
||||
fprintf(stderr, " or: %s\n", *usagestr++);
|
||||
|
@ -749,6 +773,21 @@ void usage_with_options(const char * const *usagestr,
|
|||
exit(129);
|
||||
}
|
||||
|
||||
void usage_with_options_msg(const char * const *usagestr,
|
||||
const struct option *opts, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
exit_browser(false);
|
||||
|
||||
va_start(ap, fmt);
|
||||
strbuf_addv(&error_buf, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
usage_with_options_internal(usagestr, opts, 0, NULL);
|
||||
exit(129);
|
||||
}
|
||||
|
||||
int parse_options_usage(const char * const *usagestr,
|
||||
const struct option *opts,
|
||||
const char *optstr, bool short_opt)
|
||||
|
@ -770,24 +809,23 @@ int parse_options_usage(const char * const *usagestr,
|
|||
opt:
|
||||
for ( ; opts->type != OPTION_END; opts++) {
|
||||
if (short_opt) {
|
||||
if (opts->short_name == *optstr)
|
||||
if (opts->short_name == *optstr) {
|
||||
print_option_help(opts, 0);
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (opts->long_name == NULL)
|
||||
continue;
|
||||
|
||||
if (!prefixcmp(optstr, opts->long_name))
|
||||
break;
|
||||
if (!prefixcmp(optstr, "no-") &&
|
||||
!prefixcmp(optstr + 3, opts->long_name))
|
||||
break;
|
||||
if (!prefixcmp(opts->long_name, optstr))
|
||||
print_option_help(opts, 0);
|
||||
if (!prefixcmp("no-", optstr) &&
|
||||
!prefixcmp(opts->long_name, optstr + 3))
|
||||
print_option_help(opts, 0);
|
||||
}
|
||||
|
||||
if (opts->type != OPTION_END)
|
||||
print_option_help(opts, 0);
|
||||
|
||||
return PARSE_OPT_HELP;
|
||||
}
|
||||
|
||||
|
|
|
@ -111,6 +111,7 @@ struct option {
|
|||
#define OPT_GROUP(h) { .type = OPTION_GROUP, .help = (h) }
|
||||
#define OPT_BIT(s, l, v, h, b) { .type = OPTION_BIT, .short_name = (s), .long_name = (l), .value = check_vtype(v, int *), .help = (h), .defval = (b) }
|
||||
#define OPT_BOOLEAN(s, l, v, h) { .type = OPTION_BOOLEAN, .short_name = (s), .long_name = (l), .value = check_vtype(v, bool *), .help = (h) }
|
||||
#define OPT_BOOLEAN_FLAG(s, l, v, h, f) { .type = OPTION_BOOLEAN, .short_name = (s), .long_name = (l), .value = check_vtype(v, bool *), .help = (h), .flags = (f) }
|
||||
#define OPT_BOOLEAN_SET(s, l, v, os, h) \
|
||||
{ .type = OPTION_BOOLEAN, .short_name = (s), .long_name = (l), \
|
||||
.value = check_vtype(v, bool *), .help = (h), \
|
||||
|
@ -160,6 +161,10 @@ extern int parse_options_subcommand(int argc, const char **argv,
|
|||
|
||||
extern NORETURN void usage_with_options(const char * const *usagestr,
|
||||
const struct option *options);
|
||||
extern NORETURN __attribute__((format(printf,3,4)))
|
||||
void usage_with_options_msg(const char * const *usagestr,
|
||||
const struct option *options,
|
||||
const char *fmt, ...);
|
||||
|
||||
/*----- incremantal advanced APIs -----*/
|
||||
|
||||
|
|
|
@ -82,23 +82,22 @@ void strbuf_add(struct strbuf *sb, const void *data, size_t len)
|
|||
strbuf_setlen(sb, sb->len + len);
|
||||
}
|
||||
|
||||
void strbuf_addf(struct strbuf *sb, const char *fmt, ...)
|
||||
void strbuf_addv(struct strbuf *sb, const char *fmt, va_list ap)
|
||||
{
|
||||
int len;
|
||||
va_list ap;
|
||||
va_list ap_saved;
|
||||
|
||||
if (!strbuf_avail(sb))
|
||||
strbuf_grow(sb, 64);
|
||||
va_start(ap, fmt);
|
||||
|
||||
va_copy(ap_saved, ap);
|
||||
len = vsnprintf(sb->buf + sb->len, sb->alloc - sb->len, fmt, ap);
|
||||
va_end(ap);
|
||||
if (len < 0)
|
||||
die("your vsnprintf is broken");
|
||||
if (len > strbuf_avail(sb)) {
|
||||
strbuf_grow(sb, len);
|
||||
va_start(ap, fmt);
|
||||
len = vsnprintf(sb->buf + sb->len, sb->alloc - sb->len, fmt, ap);
|
||||
va_end(ap);
|
||||
len = vsnprintf(sb->buf + sb->len, sb->alloc - sb->len, fmt, ap_saved);
|
||||
va_end(ap_saved);
|
||||
if (len > strbuf_avail(sb)) {
|
||||
die("this should not happen, your vsnprintf is broken");
|
||||
}
|
||||
|
@ -106,6 +105,15 @@ void strbuf_addf(struct strbuf *sb, const char *fmt, ...)
|
|||
strbuf_setlen(sb, sb->len + len);
|
||||
}
|
||||
|
||||
void strbuf_addf(struct strbuf *sb, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
strbuf_addv(sb, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
ssize_t strbuf_read(struct strbuf *sb, int fd, ssize_t hint)
|
||||
{
|
||||
size_t oldlen = sb->len;
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
extern char strbuf_slopbuf[];
|
||||
struct strbuf {
|
||||
|
@ -85,6 +86,7 @@ static inline void strbuf_addstr(struct strbuf *sb, const char *s) {
|
|||
|
||||
__attribute__((format(printf,2,3)))
|
||||
extern void strbuf_addf(struct strbuf *sb, const char *fmt, ...);
|
||||
extern void strbuf_addv(struct strbuf *sb, const char *fmt, va_list ap);
|
||||
|
||||
/* XXX: if read fails, any partial read is undone */
|
||||
extern ssize_t strbuf_read(struct strbuf *, int fd, ssize_t hint);
|
||||
|
|
|
@ -337,7 +337,7 @@ int dso__load_sym(struct dso *dso, struct map *map __maybe_unused,
|
|||
symbol_filter_t filter __maybe_unused,
|
||||
int kmodule __maybe_unused)
|
||||
{
|
||||
unsigned char *build_id[BUILD_ID_SIZE];
|
||||
unsigned char build_id[BUILD_ID_SIZE];
|
||||
int ret;
|
||||
|
||||
ret = fd__is_64_bit(ss->fd);
|
||||
|
|
|
@ -680,7 +680,7 @@ static int dso__split_kallsyms_for_kcore(struct dso *dso, struct map *map,
|
|||
pos->start -= curr_map->start - curr_map->pgoff;
|
||||
if (pos->end)
|
||||
pos->end -= curr_map->start - curr_map->pgoff;
|
||||
if (curr_map != map) {
|
||||
if (curr_map->dso != map->dso) {
|
||||
rb_erase_init(&pos->rb_node, root);
|
||||
symbols__insert(
|
||||
&curr_map->dso->symbols[curr_map->type],
|
||||
|
@ -1406,6 +1406,7 @@ int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter)
|
|||
struct symsrc ss_[2];
|
||||
struct symsrc *syms_ss = NULL, *runtime_ss = NULL;
|
||||
bool kmod;
|
||||
unsigned char build_id[BUILD_ID_SIZE];
|
||||
|
||||
pthread_mutex_lock(&dso->lock);
|
||||
|
||||
|
@ -1461,6 +1462,14 @@ int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter)
|
|||
dso->symtab_type == DSO_BINARY_TYPE__GUEST_KMODULE ||
|
||||
dso->symtab_type == DSO_BINARY_TYPE__GUEST_KMODULE_COMP;
|
||||
|
||||
|
||||
/*
|
||||
* Read the build id if possible. This is required for
|
||||
* DSO_BINARY_TYPE__BUILDID_DEBUGINFO to work
|
||||
*/
|
||||
if (filename__read_build_id(dso->name, build_id, BUILD_ID_SIZE) > 0)
|
||||
dso__set_build_id(dso, build_id);
|
||||
|
||||
/*
|
||||
* Iterate over candidate debug images.
|
||||
* Keep track of "interesting" ones (those which have a symtab, dynsym,
|
||||
|
|
Loading…
Add table
Reference in a new issue