perf/core improvements and fixes:

User visible:
 
 - Improve support of compressed kernel modules (Jiri Olsa)
 
 - Add --kallsyms option to 'perf diff' (David Ahern)
 
 - Add pid/tid filtering to 'report' and 'script' commands (David Ahern)
 
 - Add support for __print_array() in libtraceevent (Javi Merino)
 
 - Save DSO loading errno to better report errors (Arnaldo Carvalho de Melo)
 
 - Fix 'probe' to get ummapped symbol address on kernel (Masami Hiramatsu)
 
 - Print big numbers using thousands' group in 'kmem' (Namhyung Kim)
 
 - Remove (null) value of "Sort order" for perf mem report (Yunlong Song)
 
 Infrastructure:
 
 - Handle NULL comm name in libtracevent (Josef Bacik)
 
 - Libtraceevent synchronization with trace-cmd repo (Steven Rostedt)
 
 - Work around lack of sched_getcpu in glibc < 2.6. (Vinson Lee)
 
 Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQEcBAABAgAGBQJVEY02AAoJEBpxZoYYoA71LpYH/i8UiaLXFtOZiaPz880MkhKy
 G7FVvsJ7mNn89/m3NyO65ueH9PwJU9SMHPEIwhVHQKPW/1l6Sp/t1IzvSvg9qua0
 SW4L41ZdFDcCdLWhHpm/RhE8Pd8n54cmqiS6hBUCRR58moOHB1J1kSJQzyeYABvz
 TvlodCLvzddsX5R8ErEpPO1pZFRljX/xOuePJP8A5dz8ceih1MyoUv7juTwHJiGY
 Ra4aGw+EpozipAzrA1h2pFsGltUKFHNnxKnpvQKibk/2HQ6RnR3TUagRMIb4vL1Y
 RzdZATz77GTCLGX5BDe1LgK0Ogsnyev+bSx4Cfcl4d0F5IXI348+IMZb96GAGQE=
 =WelS
 -----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:

  - Improve support of compressed kernel modules (Jiri Olsa)

  - Add --kallsyms option to 'perf diff' (David Ahern)

  - Add pid/tid filtering to 'report' and 'script' commands (David Ahern)

  - Add support for __print_array() in libtraceevent (Javi Merino)

  - Save DSO loading errno to better report errors (Arnaldo Carvalho de Melo)

  - Fix 'probe' to get ummapped symbol address on kernel (Masami Hiramatsu)

  - Print big numbers using thousands' group in 'kmem' (Namhyung Kim)

  - Remove (null) value of "Sort order" for perf mem report (Yunlong Song)

Infrastructure changes:

  - Handle NULL comm name in libtracevent (Josef Bacik)

  - Libtraceevent synchronization with trace-cmd repo (Steven Rostedt)

  - Work around lack of sched_getcpu() in glibc < 2.6. (Vinson Lee)

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
Ingo Molnar 2015-03-24 17:22:44 +01:00
commit baa5a7bc5d
31 changed files with 624 additions and 134 deletions

View File

@ -304,7 +304,10 @@ int pevent_register_comm(struct pevent *pevent, const char *comm, int pid)
if (!item)
return -1;
item->comm = strdup(comm);
if (comm)
item->comm = strdup(comm);
else
item->comm = strdup("<...>");
if (!item->comm) {
free(item);
return -1;
@ -318,9 +321,14 @@ int pevent_register_comm(struct pevent *pevent, const char *comm, int pid)
return 0;
}
void pevent_register_trace_clock(struct pevent *pevent, char *trace_clock)
int pevent_register_trace_clock(struct pevent *pevent, const char *trace_clock)
{
pevent->trace_clock = trace_clock;
pevent->trace_clock = strdup(trace_clock);
if (!pevent->trace_clock) {
errno = ENOMEM;
return -1;
}
return 0;
}
struct func_map {
@ -758,6 +766,11 @@ static void free_arg(struct print_arg *arg)
free_arg(arg->hex.field);
free_arg(arg->hex.size);
break;
case PRINT_INT_ARRAY:
free_arg(arg->int_array.field);
free_arg(arg->int_array.count);
free_arg(arg->int_array.el_size);
break;
case PRINT_TYPE:
free(arg->typecast.type);
free_arg(arg->typecast.item);
@ -2014,6 +2027,38 @@ process_entry(struct event_format *event __maybe_unused, struct print_arg *arg,
return EVENT_ERROR;
}
static int alloc_and_process_delim(struct event_format *event, char *next_token,
struct print_arg **print_arg)
{
struct print_arg *field;
enum event_type type;
char *token;
int ret = 0;
field = alloc_arg();
if (!field) {
do_warning_event(event, "%s: not enough memory!", __func__);
errno = ENOMEM;
return -1;
}
type = process_arg(event, field, &token);
if (test_type_token(type, token, EVENT_DELIM, next_token)) {
errno = EINVAL;
ret = -1;
free_arg(field);
goto out_free_token;
}
*print_arg = field;
out_free_token:
free_token(token);
return ret;
}
static char *arg_eval (struct print_arg *arg);
static unsigned long long
@ -2486,49 +2531,46 @@ out_free:
static enum event_type
process_hex(struct event_format *event, struct print_arg *arg, char **tok)
{
struct print_arg *field;
enum event_type type;
char *token = NULL;
memset(arg, 0, sizeof(*arg));
arg->type = PRINT_HEX;
field = alloc_arg();
if (!field) {
do_warning_event(event, "%s: not enough memory!", __func__);
goto out_free;
}
if (alloc_and_process_delim(event, ",", &arg->hex.field))
goto out;
type = process_arg(event, field, &token);
if (alloc_and_process_delim(event, ")", &arg->hex.size))
goto free_field;
if (test_type_token(type, token, EVENT_DELIM, ","))
goto out_free;
return read_token_item(tok);
arg->hex.field = field;
free_field:
free_arg(arg->hex.field);
out:
*tok = NULL;
return EVENT_ERROR;
}
free_token(token);
static enum event_type
process_int_array(struct event_format *event, struct print_arg *arg, char **tok)
{
memset(arg, 0, sizeof(*arg));
arg->type = PRINT_INT_ARRAY;
field = alloc_arg();
if (!field) {
do_warning_event(event, "%s: not enough memory!", __func__);
*tok = NULL;
return EVENT_ERROR;
}
if (alloc_and_process_delim(event, ",", &arg->int_array.field))
goto out;
type = process_arg(event, field, &token);
if (alloc_and_process_delim(event, ",", &arg->int_array.count))
goto free_field;
if (test_type_token(type, token, EVENT_DELIM, ")"))
goto out_free;
if (alloc_and_process_delim(event, ")", &arg->int_array.el_size))
goto free_size;
arg->hex.size = field;
return read_token_item(tok);
free_token(token);
type = read_token_item(tok);
return type;
out_free:
free_arg(field);
free_token(token);
free_size:
free_arg(arg->int_array.count);
free_field:
free_arg(arg->int_array.field);
out:
*tok = NULL;
return EVENT_ERROR;
}
@ -2828,6 +2870,10 @@ process_function(struct event_format *event, struct print_arg *arg,
free_token(token);
return process_hex(event, arg, tok);
}
if (strcmp(token, "__print_array") == 0) {
free_token(token);
return process_int_array(event, arg, tok);
}
if (strcmp(token, "__get_str") == 0) {
free_token(token);
return process_str(event, arg, tok);
@ -3356,6 +3402,7 @@ eval_num_arg(void *data, int size, struct event_format *event, struct print_arg
break;
case PRINT_FLAGS:
case PRINT_SYMBOL:
case PRINT_INT_ARRAY:
case PRINT_HEX:
break;
case PRINT_TYPE:
@ -3766,6 +3813,54 @@ static void print_str_arg(struct trace_seq *s, void *data, int size,
}
break;
case PRINT_INT_ARRAY: {
void *num;
int el_size;
if (arg->int_array.field->type == PRINT_DYNAMIC_ARRAY) {
unsigned long offset;
struct format_field *field =
arg->int_array.field->dynarray.field;
offset = pevent_read_number(pevent,
data + field->offset,
field->size);
num = data + (offset & 0xffff);
} else {
field = arg->int_array.field->field.field;
if (!field) {
str = arg->int_array.field->field.name;
field = pevent_find_any_field(event, str);
if (!field)
goto out_warning_field;
arg->int_array.field->field.field = field;
}
num = data + field->offset;
}
len = eval_num_arg(data, size, event, arg->int_array.count);
el_size = eval_num_arg(data, size, event,
arg->int_array.el_size);
for (i = 0; i < len; i++) {
if (i)
trace_seq_putc(s, ' ');
if (el_size == 1) {
trace_seq_printf(s, "%u", *(uint8_t *)num);
} else if (el_size == 2) {
trace_seq_printf(s, "%u", *(uint16_t *)num);
} else if (el_size == 4) {
trace_seq_printf(s, "%u", *(uint32_t *)num);
} else if (el_size == 8) {
trace_seq_printf(s, "%lu", *(uint64_t *)num);
} else {
trace_seq_printf(s, "BAD SIZE:%d 0x%x",
el_size, *(uint8_t *)num);
el_size = 1;
}
num += el_size;
}
break;
}
case PRINT_TYPE:
break;
case PRINT_STRING: {
@ -3997,6 +4092,10 @@ static struct print_arg *make_bprint_args(char *fmt, void *data, int size, struc
goto process_again;
case '.':
goto process_again;
case 'z':
case 'Z':
ls = 1;
goto process_again;
case 'p':
ls = 1;
/* fall through */
@ -4939,6 +5038,96 @@ const char *pevent_data_comm_from_pid(struct pevent *pevent, int pid)
return comm;
}
static struct cmdline *
pid_from_cmdlist(struct pevent *pevent, const char *comm, struct cmdline *next)
{
struct cmdline_list *cmdlist = (struct cmdline_list *)next;
if (cmdlist)
cmdlist = cmdlist->next;
else
cmdlist = pevent->cmdlist;
while (cmdlist && strcmp(cmdlist->comm, comm) != 0)
cmdlist = cmdlist->next;
return (struct cmdline *)cmdlist;
}
/**
* pevent_data_pid_from_comm - return the pid from a given comm
* @pevent: a handle to the pevent
* @comm: the cmdline to find the pid from
* @next: the cmdline structure to find the next comm
*
* This returns the cmdline structure that holds a pid for a given
* comm, or NULL if none found. As there may be more than one pid for
* a given comm, the result of this call can be passed back into
* a recurring call in the @next paramater, and then it will find the
* next pid.
* Also, it does a linear seach, so it may be slow.
*/
struct cmdline *pevent_data_pid_from_comm(struct pevent *pevent, const char *comm,
struct cmdline *next)
{
struct cmdline *cmdline;
/*
* If the cmdlines have not been converted yet, then use
* the list.
*/
if (!pevent->cmdlines)
return pid_from_cmdlist(pevent, comm, next);
if (next) {
/*
* The next pointer could have been still from
* a previous call before cmdlines were created
*/
if (next < pevent->cmdlines ||
next >= pevent->cmdlines + pevent->cmdline_count)
next = NULL;
else
cmdline = next++;
}
if (!next)
cmdline = pevent->cmdlines;
while (cmdline < pevent->cmdlines + pevent->cmdline_count) {
if (strcmp(cmdline->comm, comm) == 0)
return cmdline;
cmdline++;
}
return NULL;
}
/**
* pevent_cmdline_pid - return the pid associated to a given cmdline
* @cmdline: The cmdline structure to get the pid from
*
* Returns the pid for a give cmdline. If @cmdline is NULL, then
* -1 is returned.
*/
int pevent_cmdline_pid(struct pevent *pevent, struct cmdline *cmdline)
{
struct cmdline_list *cmdlist = (struct cmdline_list *)cmdline;
if (!cmdline)
return -1;
/*
* If cmdlines have not been created yet, or cmdline is
* not part of the array, then treat it as a cmdlist instead.
*/
if (!pevent->cmdlines ||
cmdline < pevent->cmdlines ||
cmdline >= pevent->cmdlines + pevent->cmdline_count)
return cmdlist->pid;
return cmdline->pid;
}
/**
* pevent_data_comm_from_pid - parse the data into the print format
* @s: the trace_seq to write to
@ -5256,6 +5445,15 @@ static void print_args(struct print_arg *args)
print_args(args->hex.size);
printf(")");
break;
case PRINT_INT_ARRAY:
printf("__print_array(");
print_args(args->int_array.field);
printf(", ");
print_args(args->int_array.count);
printf(", ");
print_args(args->int_array.el_size);
printf(")");
break;
case PRINT_STRING:
case PRINT_BSTRING:
printf("__get_str(%s)", args->string.string);
@ -6346,6 +6544,7 @@ void pevent_free(struct pevent *pevent)
free_handler(handle);
}
free(pevent->trace_clock);
free(pevent->events);
free(pevent->sort_events);

View File

@ -116,7 +116,7 @@ struct pevent_plugin_option {
char *name;
char *plugin_alias;
char *description;
char *value;
const char *value;
void *priv;
int set;
};
@ -154,6 +154,10 @@ struct pevent_plugin_option {
* .plugin_alias is used to give a shorter name to access
* the vairable. Useful if a plugin handles more than one event.
*
* If .value is not set, then it is considered a boolean and only
* .set will be processed. If .value is defined, then it is considered
* a string option and .set will be ignored.
*
* PEVENT_PLUGIN_ALIAS: (optional)
* The name to use for finding options (uses filename if not defined)
*/
@ -247,6 +251,12 @@ struct print_arg_hex {
struct print_arg *size;
};
struct print_arg_int_array {
struct print_arg *field;
struct print_arg *count;
struct print_arg *el_size;
};
struct print_arg_dynarray {
struct format_field *field;
struct print_arg *index;
@ -275,6 +285,7 @@ enum print_arg_type {
PRINT_FLAGS,
PRINT_SYMBOL,
PRINT_HEX,
PRINT_INT_ARRAY,
PRINT_TYPE,
PRINT_STRING,
PRINT_BSTRING,
@ -294,6 +305,7 @@ struct print_arg {
struct print_arg_flags flags;
struct print_arg_symbol symbol;
struct print_arg_hex hex;
struct print_arg_int_array int_array;
struct print_arg_func func;
struct print_arg_string string;
struct print_arg_bitmask bitmask;
@ -599,7 +611,7 @@ enum trace_flag_type {
};
int pevent_register_comm(struct pevent *pevent, const char *comm, int pid);
void pevent_register_trace_clock(struct pevent *pevent, char *trace_clock);
int pevent_register_trace_clock(struct pevent *pevent, const char *trace_clock);
int pevent_register_function(struct pevent *pevent, char *name,
unsigned long long addr, char *mod);
int pevent_register_print_string(struct pevent *pevent, const char *fmt,
@ -678,6 +690,11 @@ int pevent_data_type(struct pevent *pevent, struct pevent_record *rec);
struct event_format *pevent_data_event_from_type(struct pevent *pevent, int type);
int pevent_data_pid(struct pevent *pevent, struct pevent_record *rec);
const char *pevent_data_comm_from_pid(struct pevent *pevent, int pid);
struct cmdline;
struct cmdline *pevent_data_pid_from_comm(struct pevent *pevent, const char *comm,
struct cmdline *next);
int pevent_cmdline_pid(struct pevent *pevent, struct cmdline *cmdline);
void pevent_event_info(struct trace_seq *s, struct event_format *event,
struct pevent_record *record);
int pevent_strerror(struct pevent *pevent, enum pevent_errno errnum,

View File

@ -18,6 +18,7 @@
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <dlfcn.h>
@ -49,6 +50,52 @@ struct plugin_list {
void *handle;
};
static void lower_case(char *str)
{
if (!str)
return;
for (; *str; str++)
*str = tolower(*str);
}
static int update_option_value(struct pevent_plugin_option *op, const char *val)
{
char *op_val;
if (!val) {
/* toggle, only if option is boolean */
if (op->value)
/* Warn? */
return 0;
op->set ^= 1;
return 0;
}
/*
* If the option has a value then it takes a string
* otherwise the option is a boolean.
*/
if (op->value) {
op->value = val;
return 0;
}
/* Option is boolean, must be either "1", "0", "true" or "false" */
op_val = strdup(val);
if (!op_val)
return -1;
lower_case(op_val);
if (strcmp(val, "1") == 0 || strcmp(val, "true") == 0)
op->set = 1;
else if (strcmp(val, "0") == 0 || strcmp(val, "false") == 0)
op->set = 0;
free(op_val);
return 0;
}
/**
* traceevent_plugin_list_options - get list of plugin options
*
@ -120,6 +167,7 @@ update_option(const char *file, struct pevent_plugin_option *option)
{
struct trace_plugin_options *op;
char *plugin;
int ret = 0;
if (option->plugin_alias) {
plugin = strdup(option->plugin_alias);
@ -144,9 +192,10 @@ update_option(const char *file, struct pevent_plugin_option *option)
if (strcmp(op->option, option->name) != 0)
continue;
option->value = op->value;
option->set ^= 1;
goto out;
ret = update_option_value(option, op->value);
if (ret)
goto out;
break;
}
/* first look for unnamed options */
@ -156,14 +205,13 @@ update_option(const char *file, struct pevent_plugin_option *option)
if (strcmp(op->option, option->name) != 0)
continue;
option->value = op->value;
option->set ^= 1;
ret = update_option_value(option, op->value);
break;
}
out:
free(plugin);
return 0;
return ret;
}
/**

View File

@ -372,7 +372,6 @@ translate_data(struct kbuffer *kbuf, void *data, void **rptr,
switch (type_len) {
case KBUFFER_TYPE_PADDING:
*length = read_4(kbuf, data);
data += *length;
break;
case KBUFFER_TYPE_TIME_EXTEND:
@ -730,3 +729,14 @@ void kbuffer_set_old_format(struct kbuffer *kbuf)
kbuf->next_event = __old_next_event;
}
/**
* kbuffer_start_of_data - return offset of where data starts on subbuffer
* @kbuf: The kbuffer
*
* Returns the location on the subbuffer where the data starts.
*/
int kbuffer_start_of_data(struct kbuffer *kbuf)
{
return kbuf->start;
}

View File

@ -63,5 +63,6 @@ int kbuffer_missed_events(struct kbuffer *kbuf);
int kbuffer_subbuffer_size(struct kbuffer *kbuf);
void kbuffer_set_old_format(struct kbuffer *kbuf);
int kbuffer_start_of_data(struct kbuffer *kbuf);
#endif /* _K_BUFFER_H */

View File

@ -1058,6 +1058,7 @@ process_filter(struct event_format *event, struct filter_arg **parg,
*parg = current_op;
else
*parg = current_exp;
free(token);
return PEVENT_ERRNO__UNBALANCED_PAREN;
}
break;
@ -1168,6 +1169,7 @@ process_filter(struct event_format *event, struct filter_arg **parg,
*parg = current_op;
free(token);
return 0;
fail_alloc:

View File

@ -31,6 +31,9 @@ OPTIONS
--dump-raw-trace::
Dump raw trace in ASCII.
--kallsyms=<file>::
kallsyms pathname
-m::
--modules::
Load module symbols. WARNING: use only with -k and LIVE kernel

View File

@ -40,6 +40,11 @@ OPTIONS
Only consider symbols in these comms. CSV that understands
file://filename entries. This option will affect the percentage of
the overhead column. See --percentage for more info.
--pid=::
Only show events for given process ID (comma separated list).
--tid=::
Only show events for given thread ID (comma separated list).
-d::
--dsos=::
Only consider symbols in these dsos. CSV that understands

View File

@ -193,6 +193,12 @@ OPTIONS
Only display events for these comms. CSV that understands
file://filename entries.
--pid=::
Only show events for given process ID (comma separated list).
--tid=::
Only show events for given thread ID (comma separated list).
-I::
--show-info::
Display extended information about the perf.data file. This adds

View File

@ -791,6 +791,8 @@ static const struct option options[] = {
OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
"dump raw trace in ASCII"),
OPT_BOOLEAN('f', "force", &force, "don't complain, do it"),
OPT_STRING(0, "kallsyms", &symbol_conf.kallsyms_name,
"file", "kallsyms pathname"),
OPT_BOOLEAN('m', "modules", &symbol_conf.use_modules,
"load module symbols - WARNING: use only with -k and LIVE kernel"),
OPT_STRING('d', "dsos", &symbol_conf.dso_list_str, "dso[,dso...]",

View File

@ -20,6 +20,7 @@
#include <linux/rbtree.h>
#include <linux/string.h>
#include <locale.h>
struct alloc_stat;
typedef int (*sort_fn_t)(struct alloc_stat *, struct alloc_stat *);
@ -325,13 +326,13 @@ static void __print_result(struct rb_root *root, struct perf_session *session,
static void print_summary(void)
{
printf("\nSUMMARY\n=======\n");
printf("Total bytes requested: %lu\n", total_requested);
printf("Total bytes allocated: %lu\n", total_allocated);
printf("Total bytes wasted on internal fragmentation: %lu\n",
printf("Total bytes requested: %'lu\n", total_requested);
printf("Total bytes allocated: %'lu\n", total_allocated);
printf("Total bytes wasted on internal fragmentation: %'lu\n",
total_allocated - total_requested);
printf("Internal fragmentation: %f%%\n",
fragmentation(total_requested, total_allocated));
printf("Cross CPU allocations: %lu/%lu\n", nr_cross_allocs, nr_allocs);
printf("Cross CPU allocations: %'lu/%'lu\n", nr_cross_allocs, nr_allocs);
}
static void print_result(struct perf_session *session)
@ -706,6 +707,8 @@ int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused)
symbol__init(&session->header.env);
if (!strcmp(argv[0], "stat")) {
setlocale(LC_ALL, "");
if (cpu__setup_cpunode_map())
goto out_delete;

View File

@ -304,7 +304,7 @@ static size_t hists__fprintf_nr_sample_events(struct hists *hists, struct report
if (rep->mem_mode) {
ret += fprintf(fp, "\n# Total weight : %" PRIu64, nr_events);
ret += fprintf(fp, "\n# Sort order : %s", sort_order);
ret += fprintf(fp, "\n# Sort order : %s", sort_order ? : default_mem_sort_order);
} else
ret += fprintf(fp, "\n# Event count (approx.): %" PRIu64, nr_events);
return ret + fprintf(fp, "\n#\n");
@ -669,6 +669,10 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
"only consider symbols in these dsos"),
OPT_STRING('c', "comms", &symbol_conf.comm_list_str, "comm[,comm...]",
"only consider symbols in these comms"),
OPT_STRING(0, "pid", &symbol_conf.pid_list_str, "pid[,pid...]",
"only consider symbols in these pids"),
OPT_STRING(0, "tid", &symbol_conf.tid_list_str, "tid[,tid...]",
"only consider symbols in these tids"),
OPT_STRING('S', "symbols", &symbol_conf.sym_list_str, "symbol[,symbol...]",
"only consider these symbols"),
OPT_STRING(0, "symbol-filter", &report.symbol_filter_str, "filter",

View File

@ -1562,6 +1562,10 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused)
OPT_STRING('C', "cpu", &cpu_list, "cpu", "list of cpus to profile"),
OPT_STRING('c', "comms", &symbol_conf.comm_list_str, "comm[,comm...]",
"only display events for these comms"),
OPT_STRING(0, "pid", &symbol_conf.pid_list_str, "pid[,pid...]",
"only consider symbols in these pids"),
OPT_STRING(0, "tid", &symbol_conf.tid_list_str, "tid[,tid...]",
"only consider symbols in these tids"),
OPT_BOOLEAN('I', "show-info", &show_full_info,
"display extended information from perf.data file"),
OPT_BOOLEAN('\0', "show-kernel-path", &symbol_conf.show_kernel_path,

View File

@ -757,8 +757,10 @@ static void perf_event__process_sample(struct perf_tool *tool,
al.map == machine->vmlinux_maps[MAP__FUNCTION] &&
RB_EMPTY_ROOT(&al.map->dso->symbols[MAP__FUNCTION])) {
if (symbol_conf.vmlinux_name) {
ui__warning("The %s file can't be used.\n%s",
symbol_conf.vmlinux_name, msg);
char serr[256];
dso__strerror_load(al.map->dso, serr, sizeof(serr));
ui__warning("The %s file can't be used: %s\n%s",
symbol_conf.vmlinux_name, serr, msg);
} else {
ui__warning("A vmlinux file was not found.\n%s",
msg);

View File

@ -1008,6 +1008,32 @@ fallback:
}
filename = symfs_filename;
}
} else if (dso__needs_decompress(dso)) {
char tmp[PATH_MAX];
struct kmod_path m;
int fd;
bool ret;
if (kmod_path__parse_ext(&m, symfs_filename))
goto out_free_filename;
snprintf(tmp, PATH_MAX, "/tmp/perf-kmod-XXXXXX");
fd = mkstemp(tmp);
if (fd < 0) {
free(m.ext);
goto out_free_filename;
}
ret = decompress_to_file(m.ext, symfs_filename, fd);
free(m.ext);
close(fd);
if (!ret)
goto out_free_filename;
strcpy(symfs_filename, tmp);
}
snprintf(command, sizeof(command),
@ -1027,7 +1053,7 @@ fallback:
file = popen(command, "r");
if (!file)
goto out_free_filename;
goto out_remove_tmp;
while (!feof(file))
if (symbol__parse_objdump_line(sym, map, file, privsize,
@ -1042,6 +1068,10 @@ fallback:
delete_last_nop(sym);
pclose(file);
out_remove_tmp:
if (dso__needs_decompress(dso))
unlink(symfs_filename);
out_free_filename:
if (delete_extract)
kcore_extract__delete(&kce);

View File

@ -7,6 +7,12 @@
static unsigned long flag = PERF_FLAG_FD_CLOEXEC;
int __weak sched_getcpu(void)
{
errno = ENOSYS;
return -1;
}
static int perf_flag_probe(void)
{
/* use 'safest' configuration as used in perf_evsel__fallback() */

View File

@ -3,4 +3,10 @@
unsigned long perf_event_open_cloexec_flag(void);
#ifdef __GLIBC_PREREQ
#if !__GLIBC_PREREQ(2, 6)
extern int sched_getcpu(void) __THROW;
#endif
#endif
#endif /* __PERF_CLOEXEC_H */

View File

@ -165,32 +165,14 @@ bool is_supported_compression(const char *ext)
return false;
}
bool is_kmodule_extension(const char *ext)
bool is_kernel_module(const char *pathname)
{
if (strncmp(ext, "ko", 2))
return false;
struct kmod_path m;
if (ext[2] == '\0' || (ext[2] == '.' && is_supported_compression(ext+3)))
return true;
if (kmod_path__parse(&m, pathname))
return NULL;
return false;
}
bool is_kernel_module(const char *pathname, bool *compressed)
{
const char *ext = strrchr(pathname, '.');
if (ext == NULL)
return false;
if (is_supported_compression(ext + 1)) {
if (compressed)
*compressed = true;
ext -= 3;
} else if (compressed)
*compressed = false;
return is_kmodule_extension(ext + 1);
return m.kmod;
}
bool decompress_to_file(const char *ext, const char *filename, int output_fd)
@ -1155,3 +1137,36 @@ enum dso_type dso__type(struct dso *dso, struct machine *machine)
return dso__type_fd(fd);
}
int dso__strerror_load(struct dso *dso, char *buf, size_t buflen)
{
int idx, errnum = dso->load_errno;
/*
* This must have a same ordering as the enum dso_load_errno.
*/
static const char *dso_load__error_str[] = {
"Internal tools/perf/ library error",
"Invalid ELF file",
"Can not read build id",
"Mismatching build id",
"Decompression failure",
};
BUG_ON(buflen == 0);
if (errnum >= 0) {
const char *err = strerror_r(errnum, buf, buflen);
if (err != buf)
scnprintf(buf, buflen, "%s", err);
return 0;
}
if (errnum < __DSO_LOAD_ERRNO__START || errnum >= __DSO_LOAD_ERRNO__END)
return -1;
idx = errnum - __DSO_LOAD_ERRNO__START;
scnprintf(buf, buflen, "%s", dso_load__error_str[idx]);
return 0;
}

View File

@ -60,6 +60,31 @@ enum dso_type {
DSO__TYPE_X32BIT,
};
enum dso_load_errno {
DSO_LOAD_ERRNO__SUCCESS = 0,
/*
* Choose an arbitrary negative big number not to clash with standard
* errno since SUS requires the errno has distinct positive values.
* See 'Issue 6' in the link below.
*
* http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/errno.h.html
*/
__DSO_LOAD_ERRNO__START = -10000,
DSO_LOAD_ERRNO__INTERNAL_ERROR = __DSO_LOAD_ERRNO__START,
/* for symsrc__init() */
DSO_LOAD_ERRNO__INVALID_ELF,
DSO_LOAD_ERRNO__CANNOT_READ_BUILDID,
DSO_LOAD_ERRNO__MISMATCHING_BUILDID,
/* for decompress_kmodule */
DSO_LOAD_ERRNO__DECOMPRESSION_FAILURE,
__DSO_LOAD_ERRNO__END,
};
#define DSO__SWAP(dso, type, val) \
({ \
type ____r = val; \
@ -113,6 +138,7 @@ struct dso {
enum dso_swap_type needs_swap;
enum dso_binary_type symtab_type;
enum dso_binary_type binary_type;
enum dso_load_errno load_errno;
u8 adjust_symbols:1;
u8 has_build_id:1;
u8 has_srcline:1;
@ -190,8 +216,7 @@ char dso__symtab_origin(const struct dso *dso);
int dso__read_binary_type_filename(const struct dso *dso, enum dso_binary_type type,
char *root_dir, char *filename, size_t size);
bool is_supported_compression(const char *ext);
bool is_kmodule_extension(const char *ext);
bool is_kernel_module(const char *pathname, bool *compressed);
bool is_kernel_module(const char *pathname);
bool decompress_to_file(const char *ext, const char *filename, int output_fd);
bool dso__needs_decompress(struct dso *dso);
@ -295,4 +320,6 @@ void dso__free_a2l(struct dso *dso);
enum dso_type dso__type(struct dso *dso, struct machine *machine);
int dso__strerror_load(struct dso *dso, char *buf, size_t buflen);
#endif /* __PERF_DSO */

View File

@ -1266,7 +1266,7 @@ static int __event_process_build_id(struct build_id_event *bev,
dso__set_build_id(dso, &bev->build_id);
if (!is_kernel_module(filename, NULL))
if (!is_kernel_module(filename))
dso->kernel = dso_type;
build_id__sprintf(dso->build_id, sizeof(dso->build_id),

View File

@ -498,6 +498,11 @@ struct map *machine__new_module(struct machine *machine, u64 start,
if (kmod_path__parse_name(&m, filename))
return NULL;
map = map_groups__find_by_name(&machine->kmaps, MAP__FUNCTION,
m.name);
if (map)
goto out;
dso = machine__module_dso(machine, &m, filename);
if (dso == NULL)
goto out;
@ -851,6 +856,39 @@ static char *get_kernel_version(const char *root_dir)
return strdup(name);
}
static bool is_kmod_dso(struct dso *dso)
{
return dso->symtab_type == DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE ||
dso->symtab_type == DSO_BINARY_TYPE__GUEST_KMODULE;
}
static int map_groups__set_module_path(struct map_groups *mg, const char *path,
struct kmod_path *m)
{
struct map *map;
char *long_name;
map = map_groups__find_by_name(mg, MAP__FUNCTION, m->name);
if (map == NULL)
return 0;
long_name = strdup(path);
if (long_name == NULL)
return -ENOMEM;
dso__set_long_name(map->dso, long_name, true);
dso__kernel_module_get_build_id(map->dso, "");
/*
* Full name could reveal us kmod compression, so
* we need to update the symtab_type if needed.
*/
if (m->comp && is_kmod_dso(map->dso))
map->dso->symtab_type++;
return 0;
}
static int map_groups__set_modules_path_dir(struct map_groups *mg,
const char *dir_name, int depth)
{
@ -889,35 +927,19 @@ static int map_groups__set_modules_path_dir(struct map_groups *mg,
if (ret < 0)
goto out;
} else {
char *dot = strrchr(dent->d_name, '.'),
dso_name[PATH_MAX];
struct map *map;
char *long_name;
struct kmod_path m;
if (dot == NULL)
continue;
/* On some system, modules are compressed like .ko.gz */
if (is_supported_compression(dot + 1) &&
is_kmodule_extension(dot - 2))
dot -= 3;
snprintf(dso_name, sizeof(dso_name), "[%.*s]",
(int)(dot - dent->d_name), dent->d_name);
strxfrchar(dso_name, '-', '_');
map = map_groups__find_by_name(mg, MAP__FUNCTION,
dso_name);
if (map == NULL)
continue;
long_name = strdup(path);
if (long_name == NULL) {
ret = -1;
ret = kmod_path__parse_name(&m, dent->d_name);
if (ret)
goto out;
if (m.kmod)
ret = map_groups__set_module_path(mg, path, &m);
free(m.name);
if (ret)
goto out;
}
dso__set_long_name(map->dso, long_name, true);
dso__kernel_module_get_build_id(map->dso, "");
}
}
@ -1087,7 +1109,7 @@ static int machine__process_kernel_mmap_event(struct machine *machine,
struct dso *dso;
list_for_each_entry(dso, &machine->kernel_dsos.head, node) {
if (is_kernel_module(dso->long_name, NULL))
if (is_kernel_module(dso->long_name))
continue;
kernel = dso;

View File

@ -310,7 +310,10 @@ static int find_alternative_probe_point(struct debuginfo *dinfo,
/* Find the address of given function */
map__for_each_symbol_by_name(map, pp->function, sym) {
address = sym->start;
if (uprobes)
address = sym->start;
else
address = map->unmap_ip(map, sym->start);
break;
}
if (!address) {

View File

@ -214,6 +214,11 @@ static void define_event_symbols(struct event_format *event,
define_event_symbols(event, ev_name, args->hex.field);
define_event_symbols(event, ev_name, args->hex.size);
break;
case PRINT_INT_ARRAY:
define_event_symbols(event, ev_name, args->int_array.field);
define_event_symbols(event, ev_name, args->int_array.count);
define_event_symbols(event, ev_name, args->int_array.el_size);
break;
case PRINT_BSTRING:
case PRINT_DYNAMIC_ARRAY:
case PRINT_STRING:

View File

@ -231,6 +231,11 @@ static void define_event_symbols(struct event_format *event,
define_event_symbols(event, ev_name, args->hex.field);
define_event_symbols(event, ev_name, args->hex.size);
break;
case PRINT_INT_ARRAY:
define_event_symbols(event, ev_name, args->int_array.field);
define_event_symbols(event, ev_name, args->int_array.count);
define_event_symbols(event, ev_name, args->int_array.el_size);
break;
case PRINT_STRING:
break;
case PRINT_TYPE:

View File

@ -44,6 +44,7 @@ extern struct sort_entry sort_dso_to;
extern struct sort_entry sort_sym_from;
extern struct sort_entry sort_sym_to;
extern enum sort_type sort__first_dimension;
extern const char default_mem_sort_order[];
struct he_stat {
u64 period;

View File

@ -579,32 +579,37 @@ static int dso__swap_init(struct dso *dso, unsigned char eidata)
static int decompress_kmodule(struct dso *dso, const char *name,
enum dso_binary_type type)
{
int fd;
const char *ext = strrchr(name, '.');
int fd = -1;
char tmpbuf[] = "/tmp/perf-kmod-XXXXXX";
struct kmod_path m;
if (type != DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE_COMP &&
type != DSO_BINARY_TYPE__GUEST_KMODULE_COMP &&
type != DSO_BINARY_TYPE__BUILD_ID_CACHE)
return -1;
if (!ext || !is_supported_compression(ext + 1)) {
ext = strrchr(dso->name, '.');
if (!ext || !is_supported_compression(ext + 1))
return -1;
}
if (type == DSO_BINARY_TYPE__BUILD_ID_CACHE)
name = dso->long_name;
fd = mkstemp(tmpbuf);
if (fd < 0)
if (kmod_path__parse_ext(&m, name) || !m.comp)
return -1;
if (!decompress_to_file(ext + 1, name, fd)) {
fd = mkstemp(tmpbuf);
if (fd < 0) {
dso->load_errno = errno;
goto out;
}
if (!decompress_to_file(m.ext, name, fd)) {
dso->load_errno = DSO_LOAD_ERRNO__DECOMPRESSION_FAILURE;
close(fd);
fd = -1;
}
unlink(tmpbuf);
out:
free(m.ext);
return fd;
}
@ -633,37 +638,49 @@ int symsrc__init(struct symsrc *ss, struct dso *dso, const char *name,
Elf *elf;
int fd;
if (dso__needs_decompress(dso))
if (dso__needs_decompress(dso)) {
fd = decompress_kmodule(dso, name, type);
else
if (fd < 0)
return -1;
} else {
fd = open(name, O_RDONLY);
if (fd < 0)
return -1;
if (fd < 0) {
dso->load_errno = errno;
return -1;
}
}
elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL);
if (elf == NULL) {
pr_debug("%s: cannot read %s ELF file.\n", __func__, name);
dso->load_errno = DSO_LOAD_ERRNO__INVALID_ELF;
goto out_close;
}
if (gelf_getehdr(elf, &ehdr) == NULL) {
dso->load_errno = DSO_LOAD_ERRNO__INVALID_ELF;
pr_debug("%s: cannot get elf header.\n", __func__);
goto out_elf_end;
}
if (dso__swap_init(dso, ehdr.e_ident[EI_DATA]))
if (dso__swap_init(dso, ehdr.e_ident[EI_DATA])) {
dso->load_errno = DSO_LOAD_ERRNO__INTERNAL_ERROR;
goto out_elf_end;
}
/* Always reject images with a mismatched build-id: */
if (dso->has_build_id) {
u8 build_id[BUILD_ID_SIZE];
if (elf_read_build_id(elf, build_id, BUILD_ID_SIZE) < 0)
if (elf_read_build_id(elf, build_id, BUILD_ID_SIZE) < 0) {
dso->load_errno = DSO_LOAD_ERRNO__CANNOT_READ_BUILDID;
goto out_elf_end;
}
if (!dso__build_id_equal(dso, build_id))
if (!dso__build_id_equal(dso, build_id)) {
dso->load_errno = DSO_LOAD_ERRNO__MISMATCHING_BUILDID;
goto out_elf_end;
}
}
ss->is_64_bit = (gelf_getclass(elf) == ELFCLASS64);
@ -699,8 +716,10 @@ int symsrc__init(struct symsrc *ss, struct dso *dso, const char *name,
}
ss->name = strdup(name);
if (!ss->name)
if (!ss->name) {
dso->load_errno = errno;
goto out_elf_end;
}
ss->elf = elf;
ss->fd = fd;

View File

@ -246,13 +246,12 @@ out:
return ret;
}
int symsrc__init(struct symsrc *ss, struct dso *dso __maybe_unused,
const char *name,
int symsrc__init(struct symsrc *ss, struct dso *dso, const char *name,
enum dso_binary_type type)
{
int fd = open(name, O_RDONLY);
if (fd < 0)
return -1;
goto out_errno;
ss->name = strdup(name);
if (!ss->name)
@ -264,6 +263,8 @@ int symsrc__init(struct symsrc *ss, struct dso *dso __maybe_unused,
return 0;
out_close:
close(fd);
out_errno:
dso->load_errno = errno;
return -1;
}

View File

@ -15,6 +15,7 @@
#include "machine.h"
#include "symbol.h"
#include "strlist.h"
#include "intlist.h"
#include "header.h"
#include <elf.h>
@ -1859,6 +1860,20 @@ int setup_list(struct strlist **list, const char *list_str,
return 0;
}
int setup_intlist(struct intlist **list, const char *list_str,
const char *list_name)
{
if (list_str == NULL)
return 0;
*list = intlist__new(list_str);
if (!*list) {
pr_err("problems parsing %s list\n", list_name);
return -1;
}
return 0;
}
static bool symbol__read_kptr_restrict(void)
{
bool value = false;
@ -1909,9 +1924,17 @@ int symbol__init(struct perf_session_env *env)
symbol_conf.comm_list_str, "comm") < 0)
goto out_free_dso_list;
if (setup_intlist(&symbol_conf.pid_list,
symbol_conf.pid_list_str, "pid") < 0)
goto out_free_comm_list;
if (setup_intlist(&symbol_conf.tid_list,
symbol_conf.tid_list_str, "tid") < 0)
goto out_free_pid_list;
if (setup_list(&symbol_conf.sym_list,
symbol_conf.sym_list_str, "symbol") < 0)
goto out_free_comm_list;
goto out_free_tid_list;
/*
* A path to symbols of "/" is identical to ""
@ -1930,6 +1953,10 @@ int symbol__init(struct perf_session_env *env)
symbol_conf.initialized = true;
return 0;
out_free_tid_list:
intlist__delete(symbol_conf.tid_list);
out_free_pid_list:
intlist__delete(symbol_conf.pid_list);
out_free_comm_list:
strlist__delete(symbol_conf.comm_list);
out_free_dso_list:
@ -1944,6 +1971,8 @@ void symbol__exit(void)
strlist__delete(symbol_conf.sym_list);
strlist__delete(symbol_conf.dso_list);
strlist__delete(symbol_conf.comm_list);
intlist__delete(symbol_conf.tid_list);
intlist__delete(symbol_conf.pid_list);
vmlinux_path__exit();
symbol_conf.sym_list = symbol_conf.dso_list = symbol_conf.comm_list = NULL;
symbol_conf.initialized = false;

View File

@ -78,6 +78,7 @@ static inline size_t symbol__size(const struct symbol *sym)
}
struct strlist;
struct intlist;
struct symbol_conf {
unsigned short priv_size;
@ -115,6 +116,8 @@ struct symbol_conf {
const char *guestmount;
const char *dso_list_str,
*comm_list_str,
*pid_list_str,
*tid_list_str,
*sym_list_str,
*col_width_list_str;
struct strlist *dso_list,
@ -124,6 +127,8 @@ struct symbol_conf {
*dso_to_list,
*sym_from_list,
*sym_to_list;
struct intlist *pid_list,
*tid_list;
const char *symfs;
};
@ -295,5 +300,7 @@ int compare_proc_modules(const char *from, const char *to);
int setup_list(struct strlist **list, const char *list_str,
const char *list_name);
int setup_intlist(struct intlist **list, const char *list_str,
const char *list_name);
#endif /* __PERF_SYMBOL */

View File

@ -123,11 +123,8 @@ int target__strerror(struct target *target, int errnum,
if (errnum >= 0) {
const char *err = strerror_r(errnum, buf, buflen);
if (err != buf) {
size_t len = strlen(err);
memcpy(buf, err, min(buflen - 1, len));
*(buf + min(buflen - 1, len)) = '\0';
}
if (err != buf)
scnprintf(buf, buflen, "%s", err);
return 0;
}

View File

@ -7,6 +7,7 @@
#include <sys/types.h>
#include "symbol.h"
#include <strlist.h>
#include <intlist.h>
struct thread_stack;
@ -100,6 +101,16 @@ static inline bool thread__is_filtered(struct thread *thread)
return true;
}
if (symbol_conf.pid_list &&
!intlist__has_entry(symbol_conf.pid_list, thread->pid_)) {
return true;
}
if (symbol_conf.tid_list &&
!intlist__has_entry(symbol_conf.tid_list, thread->tid)) {
return true;
}
return false;
}